");
pw.println("#include \"awt_p.h\"");
pw.println("#include \"color.h\"");
diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk
index b35e2e03bb8e..12b9075c40a8 100644
--- a/make/modules/java.base/lib/CoreLibraries.gmk
+++ b/make/modules/java.base/lib/CoreLibraries.gmk
@@ -66,6 +66,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
-DARCHPROPNAME='"$(OPENJDK_TARGET_CPU_OSARCH)"', \
DISABLED_WARNINGS_gcc_ProcessImpl_md.c := unused-result, \
DISABLED_WARNINGS_clang_TimeZone_md.c := unused-variable, \
+ DISABLED_WARNINGS_clang_jni_util.c := format-nonliteral, \
JDK_LIBS := libjvm, \
LIBS_linux := $(LIBDL), \
LIBS_aix := $(LIBDL) $(LIBM), \
diff --git a/make/modules/java.desktop/Copy.gmk b/make/modules/java.desktop/Copy.gmk
index 9acca9f688ce..67ed79dccd6e 100644
--- a/make/modules/java.desktop/Copy.gmk
+++ b/make/modules/java.desktop/Copy.gmk
@@ -82,3 +82,13 @@ $(eval $(call SetupCopyLegalFiles, COPY_LEGAL, \
TARGETS += $(COPY_LEGAL)
################################################################################
+
+FONTFILE_SRC_DIR := $(TOPDIR)/src/java.desktop/share
+FONTFILE_SRCS := $(wildcard $(FONTFILE_SRC_DIR)/fonts/*.ttf) $(wildcard $(FONTFILE_SRC_DIR)/fonts/*.otf) $(FONTFILE_SRC_DIR)/fonts/fonts.dir $(FONTFILE_SRC_DIR)/fonts/font.conf
+FONTFILE_TARGET_FILES := $(subst $(FONTFILE_SRC_DIR),$(LIB_DST_DIR),$(FONTFILE_SRCS))
+
+$(LIB_DST_DIR)/fonts/%: $(FONTFILE_SRC_DIR)/fonts/%
+ $(call install-file)
+
+
+TARGETS += $(FONTFILE_TARGET_FILES)
diff --git a/make/modules/java.desktop/Gensrc.gmk b/make/modules/java.desktop/Gensrc.gmk
index ceeade7a96d8..9479ddc3cda6 100644
--- a/make/modules/java.desktop/Gensrc.gmk
+++ b/make/modules/java.desktop/Gensrc.gmk
@@ -37,6 +37,10 @@ endif
include gensrc/GensrcSwing.gmk
+ifeq ($(call isTargetOs, windows macosx), false)
+ include gensrc/GensrcWayland.gmk
+endif
+
################################################################################
include GensrcProperties.gmk
diff --git a/make/modules/java.desktop/Java.gmk b/make/modules/java.desktop/Java.gmk
index 61c7fa44e0e8..0516df417874 100644
--- a/make/modules/java.desktop/Java.gmk
+++ b/make/modules/java.desktop/Java.gmk
@@ -60,7 +60,10 @@ ifeq ($(call isTargetOs, macosx), true)
EXCLUDES += \
sun/awt/screencast \
sun/awt/X11 \
+ sun/awt/wl \
+ sun/java2d/wl \
sun/java2d/x11 \
+ sun/java2d/vulkan \
sun/java2d/jules \
sun/java2d/xr \
com/sun/java/swing/plaf/gtk \
diff --git a/make/modules/java.desktop/Lib.gmk b/make/modules/java.desktop/Lib.gmk
index 830e0553c39d..81994a5f20f9 100644
--- a/make/modules/java.desktop/Lib.gmk
+++ b/make/modules/java.desktop/Lib.gmk
@@ -94,6 +94,7 @@ ifeq ($(call isTargetOs, macosx), true)
format-nonliteral, \
DISABLED_WARNINGS_clang_QueuingApplicationDelegate.m := \
objc-method-access, \
+ DISABLED_WARNINGS_clang_ThreadUtilities.m := format-nonliteral, \
JDK_LIBS := java.base:libjava, \
LIBS_macosx := \
-framework Accelerate \
diff --git a/make/modules/java.desktop/gensrc/GensrcWayland.gmk b/make/modules/java.desktop/gensrc/GensrcWayland.gmk
new file mode 100644
index 000000000000..d57f1ae77854
--- /dev/null
+++ b/make/modules/java.desktop/gensrc/GensrcWayland.gmk
@@ -0,0 +1,62 @@
+#
+# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2024, 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. 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.
+#
+
+#
+# Generate C header and source files for Wayland protocols
+#
+
+WAYLAND_GENSRC_DIR = $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland
+WAYLAND_BASIC_PROTOCOL_FILES := \
+ $(WAYLAND_PROTOCOLS_ROOT)/stable/viewporter/viewporter.xml \
+ $(WAYLAND_PROTOCOLS_ROOT)/stable/xdg-shell/xdg-shell.xml \
+ $(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-activation/xdg-activation-v1.xml \
+ $(WAYLAND_PROTOCOLS_ROOT)/unstable/primary-selection/primary-selection-unstable-v1.xml \
+ $(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-output/xdg-output-unstable-v1.xml \
+ $(GTK_SHELL1_PROTOCOL_PATH) \
+ #
+
+WAYLAND_PROTOCOL_FILES := \
+ $(WAYLAND_BASIC_PROTOCOL_FILES) \
+ $(MODULE_SRC)/share/native/libwakefield/protocol/wakefield.xml \
+ #
+
+$(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/_the.generated_wayland_protocols: $(WAYLAND_PROTOCOL_FILES)
+ $(call LogInfo, Generating Wayland protocol source and header files)
+ $(call MakeTargetDir)
+ $(call MakeDir, $(WAYLAND_GENSRC_DIR))
+ @for p in $^; do \
+ HEADER=$$(basename "$$p" | $(SED) 's/\.[^.]*$$//').h; \
+ $(WAYLAND_SCANNER) client-header $$p $(WAYLAND_GENSRC_DIR)/$$HEADER; \
+ done
+ @for p in $^; do \
+ CODE=$$(basename "$$p" | $(SED) 's/\.[^.]*$$//').c; \
+ $(WAYLAND_SCANNER) private-code $$p $(WAYLAND_GENSRC_DIR)/$$CODE; \
+ done
+ $(TOUCH) $@
+
+GENSRC_WAYLAND := $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/_the.generated_wayland_protocols
+
+TARGETS += $(GENSRC_WAYLAND)
diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk
index 23b1e6e9d59a..66c2940aeccf 100644
--- a/make/modules/java.desktop/lib/AwtLibraries.gmk
+++ b/make/modules/java.desktop/lib/AwtLibraries.gmk
@@ -58,9 +58,14 @@ ifeq ($(call isTargetOs, windows), true)
$(TOPDIR)/src/$(MODULE)/share/native/common/font \
$(TOPDIR)/src/$(MODULE)/share/native/common/java2d/opengl \
$(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt/systemscale \
+ $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/launcher \
#
endif
+ifeq ($(DBUS_FOUND), false)
+ LIBAWT_EXCLUDE_FILES += dbus_interface.c dbus_interface.h
+endif
+
ifeq ($(call isTargetOs, linux macosx aix), true)
LIBAWT_EXCLUDE_FILES += awt_Font.c CUPSfuncs.c fontpath.c X11Color.c
endif
@@ -88,6 +93,10 @@ LIBAWT_EXTRA_HEADER_DIRS := \
LIBAWT_CFLAGS := -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES -DMLIB_NO_LIBSUNMATH
+ifeq ($(DBUS_FOUND), true)
+ LIBAWT_CFLAGS += -DDBUS_FOUND
+endif
+
ifeq ($(call isTargetOs, windows), true)
LIBAWT_CFLAGS += -EHsc -DUNICODE -D_UNICODE -DMLIB_OS64BIT
LIBAWT_RCFLAGS ?= -I$(TOPDIR)/src/java.base/windows/native/launcher/icons
@@ -95,6 +104,36 @@ ifeq ($(call isTargetOs, windows), true)
$(TOPDIR)/src/$(MODULE)/windows/native/libawt/windows/awt.rc
endif
+# Setup NVDA a11y announcer
+ifeq ($(call isTargetOs, windows), true)
+ A11Y_NVDA_ANNOUNCING_CFLAGS := -DNO_A11Y_NVDA_ANNOUNCING
+ A11Y_NVDA_ANNOUNCING_LIBS :=
+ A11Y_NVDA_ANNOUNCING_DEPENDENCIES :=
+
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+ ifeq ($(A11Y_NVDA_ANNOUNCING_ENABLED), true)
+ A11Y_NVDA_ANNOUNCING_CFLAGS := $(NVDACONTROLLERCLIENT_CFLAGS)
+ A11Y_NVDA_ANNOUNCING_LIBS := $(NVDACONTROLLERCLIENT_LIB)
+ A11Y_NVDA_ANNOUNCING_DEPENDENCIES := $(NVDACONTROLLERCLIENT_DLL)
+ endif
+ endif
+
+ LIBAWT_CFLAGS += $(A11Y_NVDA_ANNOUNCING_CFLAGS)
+endif
+
+# Setup the client for the JAWS screen reader
+ifeq ($(call isTargetOs, windows), true)
+ A11Y_JAWS_ANNOUNCING_CFLAGS := -DNO_A11Y_JAWS_ANNOUNCING
+
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+ ifeq ($(A11Y_JAWS_ANNOUNCING_ENABLED), true)
+ A11Y_JAWS_ANNOUNCING_CFLAGS :=
+ endif
+ endif
+
+ LIBAWT_CFLAGS += $(A11Y_JAWS_ANNOUNCING_CFLAGS)
+endif
+
# -fgcse-after-reload improves performance of MaskFill in Java2D by 20% for
# some gcc
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
@@ -102,7 +141,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
EXTRA_SRC := $(LIBAWT_EXTRA_SRC), \
EXCLUDE_FILES := $(LIBAWT_EXCLUDE_FILES), \
OPTIMIZATION := HIGHEST, \
- CFLAGS := $(LIBAWT_CFLAGS) $(X_CFLAGS), \
+ CFLAGS := $(LIBAWT_CFLAGS) $(X_CFLAGS) $(DBUS_CFLAGS), \
CXXFLAGS := $(LIBAWT_CFLAGS) $(X_CFLAGS), \
CFLAGS_gcc := -fgcse-after-reload, \
EXTRA_HEADER_DIRS := $(LIBAWT_EXTRA_HEADER_DIRS), \
@@ -118,10 +157,11 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
DISABLED_WARNINGS_clang_debug_trace.c := format-nonliteral, \
DISABLED_WARNINGS_clang_Trace.c := format-nonliteral, \
DISABLED_WARNINGS_clang_TransformHelper.c := sign-compare, \
+ DISABLED_WARNINGS_clang_system_properties.c := format-nonliteral, \
DISABLED_WARNINGS_microsoft := 4244 4996, \
DISABLED_WARNINGS_microsoft_awt_Toolkit.cpp := 4267, \
LDFLAGS_windows := -delayload:comctl32.dll -delayload:comdlg32.dll \
- -delayload:gdi32.dll -delayload:imm32.dll -delayload:ole32.dll \
+ -delayload:gdi32.dll -delayload:gdiplus.dll -delayload:imm32.dll -delayload:ole32.dll \
-delayload:shell32.dll -delayload:shlwapi.dll -delayload:user32.dll \
-delayload:winmm.dll -delayload:winspool.drv, \
JDK_LIBS := java.base:libjava java.base:libjvm, \
@@ -136,12 +176,52 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
-framework Metal \
-framework OpenGL, \
LIBS_windows := advapi32.lib comctl32.lib comdlg32.lib delayimp.lib \
- gdi32.lib imm32.lib kernel32.lib ole32.lib shell32.lib shlwapi.lib \
- user32.lib uuid.lib winmm.lib winspool.lib, \
+ gdi32.lib gdiplus.lib imm32.lib kernel32.lib ole32.lib shell32.lib shlwapi.lib \
+ user32.lib uuid.lib winmm.lib winspool.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS), \
VERSIONINFO_RESOURCE := $(LIBAWT_VERSIONINFO_RESOURCE), \
EXTRA_RCFLAGS := $(LIBAWT_RCFLAGS), \
))
+ifeq ($(call isTargetOs, windows)+$(ENABLE_HEADLESS_ONLY)+$(A11Y_NVDA_ANNOUNCING_ENABLED), true+false+true)
+ $(eval $(call SetupCopyFiles, COPY_NVDA_DEPENDENCIES, \
+ FILES := $(A11Y_NVDA_ANNOUNCING_DEPENDENCIES), \
+ DEST := $(BUILD_LIBAWT_OUTPUT_DIR), \
+ ))
+
+ $(COPY_NVDA_DEPENDENCIES): $(A11Y_NVDA_ANNOUNCING_DEPENDENCIES)
+ $(BUILD_LIBAWT): $(COPY_NVDA_DEPENDENCIES)
+
+ TARGETS += $(COPY_NVDA_DEPENDENCIES)
+endif
+
+# Compile Vulkan shaders
+define compile-spirv
+ $(call MakeTargetDir)
+ $(VULKAN_SHADER_COMPILER) '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
+endef
+spirv-name = $(strip $1).h
+
+ifeq ($(VULKAN_ENABLED), true)
+ $(eval $(call SetupCopyFiles, COMPILE_VULKAN_SHADERS, \
+ SRC := $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan, \
+ FILES := $(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan), \
+ DEST := $(SUPPORT_OUTPUTDIR)/headers/java.desktop/vulkan/spirv, \
+ MACRO := compile-spirv, \
+ NAME_MACRO := spirv-name, \
+ ))
+ VULKAN_SHADER_LIST = $(SUPPORT_OUTPUTDIR)/headers/java.desktop/vulkan/shader_list.h
+ $(VULKAN_SHADER_LIST): $(COMPILE_VULKAN_SHADERS)
+ > $(VULKAN_SHADER_LIST) $(NEWLINE) \
+ $(foreach f, $(patsubst $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan/%,%,$(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan)), \
+ $(ECHO) SHADER_ENTRY\($(subst .,$(COMMA),$(subst /,_,$f))\) >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
+ $(ECHO) '#ifdef INCLUDE_BYTECODE' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
+ $(ECHO) '#include "spirv/$f.h"' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
+ $(ECHO) BYTECODE_END >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
+ $(ECHO) '#endif' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
+)
+ $(BUILD_LIBAWT): $(VULKAN_SHADER_LIST)
+endif
+
TARGETS += $(BUILD_LIBAWT)
# Mac and Windows only use the native AWT lib, do not build libawt_headless
@@ -161,6 +241,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/awt/debug \
common/font \
common/java2d/opengl \
+ common/java2d/vulkan \
java.base:libjvm \
#
@@ -180,11 +261,12 @@ ifeq ($(call isTargetOs, windows macosx), false)
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT_HEADLESS, \
NAME := awt_headless, \
EXTRA_SRC := $(LIBAWT_HEADLESS_EXTRA_SRC), \
- EXCLUDES := medialib, \
+ EXCLUDES := medialib wl vulkan, \
ONLY_EXPORTED := $(LIBAWT_HEADLESS_ONLY_EXPORTED), \
OPTIMIZATION := LOW, \
CFLAGS := -DHEADLESS=true $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) \
- $(X_CFLAGS), \
+ $(X_CFLAGS) $(DBUS_CFLAGS), \
+ CXXFLAGS := $(CXXFLAGS_JDKLIB), \
EXTRA_HEADER_DIRS := $(LIBAWT_HEADLESS_EXTRA_HEADER_DIRS), \
DISABLED_WARNINGS_gcc := unused-variable, \
DISABLED_WARNINGS_clang := unused-variable, \
@@ -211,6 +293,20 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
common/font \
#
+ ifeq ($(A11Y_SPEECHD_ANNOUNCING_ENABLED), true)
+ A11Y_SPEECHD_ANNOUNCING_CFLAGS :=
+ else
+ A11Y_SPEECHD_ANNOUNCING_CFLAGS := -DNO_A11Y_SPEECHD_ANNOUNCING
+ endif
+
+ ifeq ($(call isTargetOs, linux), true)
+ LIBAWT_XAWT_EXTRA_SRC += \
+ $(TOPDIR)/src/$(MODULE)/linux/native/libawt_xawt \
+ #
+ endif
+
+ LIBAWT_XAWT_CFLAGS += $(SPEECHD_CFLAGS) $(A11Y_SPEECHD_ANNOUNCING_CFLAGS)
+
LIBAWT_XAWT_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
libawt_xawt/awt \
@@ -242,9 +338,9 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
NAME := awt_xawt, \
EXTRA_SRC := $(LIBAWT_XAWT_EXTRA_SRC), \
EXTRA_HEADER_DIRS := $(LIBAWT_XAWT_EXTRA_HEADER_DIRS), \
- EXCLUDES := medialib debug, \
+ EXCLUDES := medialib debug wl vulkan, \
OPTIMIZATION := LOW, \
- CFLAGS := -DXAWT -DXAWT_HACK $(LIBAWT_XAWT_CFLAGS) \
+ CFLAGS := -DXAWT -DXAWT_HACK $(LIBAWT_XAWT_CFLAGS) $(DBUS_CFLAGS) \
$(FONTCONFIG_CFLAGS) $(CUPS_CFLAGS) $(X_CFLAGS), \
DISABLED_WARNINGS_gcc := int-to-pointer-cast unused-variable, \
DISABLED_WARNINGS_clang := unused-variable, \
@@ -252,6 +348,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
DISABLED_WARNINGS_gcc_GLXSurfaceData.c := unused-function, \
DISABLED_WARNINGS_gcc_gtk3_interface.c := parentheses type-limits \
unused-function, \
+ DISABLED_WARNINGS_gcc_keycode_cache.c := unused-function, \
DISABLED_WARNINGS_gcc_OGLBufImgOps.c := format-nonliteral, \
DISABLED_WARNINGS_gcc_OGLPaints.c := format-nonliteral, \
DISABLED_WARNINGS_gcc_screencast_pipewire.c := undef, \
@@ -265,6 +362,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
DISABLED_WARNINGS_gcc_XRBackendNative.c := maybe-uninitialized, \
DISABLED_WARNINGS_gcc_XToolkit.c := unused-result, \
DISABLED_WARNINGS_gcc_XWindow.c := unused-function, \
+ DISABLED_WARNINGS_gcc_awt_InputMethod.c := unused-label, \
DISABLED_WARNINGS_clang_awt_Taskbar.c := parentheses, \
DISABLED_WARNINGS_clang_gtk3_interface.c := unused-function parentheses, \
DISABLED_WARNINGS_clang_GLXSurfaceData.c := unused-function, \
@@ -272,6 +370,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
DISABLED_WARNINGS_clang_OGLPaints.c := format-nonliteral, \
DISABLED_WARNINGS_clang_screencast_pipewire.c := format-nonliteral, \
DISABLED_WARNINGS_clang_sun_awt_X11_GtkFileDialogPeer.c := parentheses, \
+ DISABLED_WARNINGS_clang_awt_InputMethod.c := unused-label, \
DISABLED_WARNINGS_clang_XWindow.c := unused-function, \
DISABLED_WARNINGS_clang_aix := deprecated-non-prototype, \
DISABLED_WARNINGS_clang_aix_awt_Taskbar.c := parentheses, \
@@ -283,7 +382,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
parentheses, \
DISABLED_WARNINGS_clang_aix_awt_InputMethod.c := unused-function sign-compare, \
JDK_LIBS := libawt java.base:libjava, \
- LIBS_unix := $(LIBDL) $(LIBM) $(X_LIBS) -lX11 -lXext -lXi -lXrender \
+ LIBS_unix := $(LIBDL) $(LIBM) $(SPEECHD_LIBS) $(X_LIBS) -lX11 -lXext -lXi -lXrender \
-lXtst, \
LIBS_linux := $(LIBPTHREAD), \
STATIC_LIB_EXCLUDE_OBJS := $(LIBAWT_XAWT_STATIC_EXCLUDE_OBJS), \
@@ -292,6 +391,103 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
TARGETS += $(BUILD_LIBAWT_XAWT)
endif
+ifeq ($(call isTargetOs, windows macosx), false)
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+
+ LIBAWT_WLAWT_EXTRA_SRC := \
+ common/awt \
+ common/java2d \
+ common/font \
+ common/wayland \
+ $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland \
+ #
+
+ LIBAWT_WLAWT_EXCLUDES := medialib debug opengl x11
+ LIBAWT_WLAWT_EXCLUDE_FILES := common/awt/X11Color.c common/awt/awt_Font.c
+
+ # Substitute Vulkan with stubs if disabled.
+ ifeq ($(VULKAN_ENABLED), false)
+ LIBAWT_WLAWT_EXCLUDES += vulkan
+ LIBAWT_WLAWT_EXTRA_FILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan/VKStubs.c
+ endif
+
+ LIBAWT_WLAWT_EXTRA_HEADER_DIRS := \
+ $(LIBAWT_DEFAULT_HEADER_DIRS) \
+ libawt_wlawt/awt \
+ $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland \
+ include \
+ common/awt/debug \
+ common/awt/systemscale \
+ common/font \
+ common/java2d \
+ common/java2d/wl \
+ common/java2d/vulkan \
+ common/wayland \
+ java.base:libjvm \
+ java.base:libjava \
+ libvmahpp \
+ #
+
+ # Enable 'wakefield' extension for java.awt.Robot support
+ WAKEFIELD_ROBOT_CFLAGS=-DWAKEFIELD_ROBOT
+
+ LIBAWT_WLAWT_CFLAGS += -DWLAWT \
+ $(WAKEFIELD_ROBOT_CFLAGS) \
+ $(FONTCONFIG_CFLAGS) \
+ $(VULKAN_FLAGS) \
+ $(WAYLAND_CFLAGS) \
+ $(CUPS_CFLAGS)
+
+ LIBAWT_WLAWT_CXXFLAGS += $(VULKAN_FLAGS)
+
+ ifeq ($(TOOLCHAIN_TYPE), gcc)
+ # Turn off all warnings for the following files since they contain warnings
+ # that cannot be turned of individually.
+ # redefining a macro
+ BUILD_LIBAWT_WLAWT_awt_Font.c_CFLAGS := -w
+ # initializing a declared 'extern'
+ BUILD_LIBAWT_WLAWT_debug_mem.c_CFLAGS := -w
+ endif
+
+ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT_WLAWT, \
+ NAME := awt_wlawt, \
+ EXCLUDE_FILES := $(LIBAWT_WLAWT_EXCLUDE_FILES), \
+ EXTRA_FILES := $(LIBAWT_WLAWT_EXTRA_FILES), \
+ EXTRA_SRC := $(LIBAWT_WLAWT_EXTRA_SRC), \
+ EXTRA_HEADER_DIRS := $(LIBAWT_WLAWT_EXTRA_HEADER_DIRS), \
+ EXCLUDES := $(LIBAWT_WLAWT_EXCLUDES), \
+ OPTIMIZATION := LOW, \
+ LINK_TYPE := C++, \
+ CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_WLAWT_CFLAGS), \
+ CXXFLAGS := $(CXXFLAGS_JDKLIB) $(LIBAWT_WLAWT_CXXFLAGS), \
+ WARNINGS_AS_ERRORS_xlc := false, \
+ DISABLED_WARNINGS_C_gcc := type-limits pointer-to-int-cast \
+ unused-result maybe-uninitialized format unused-variable \
+ format-security int-to-pointer-cast parentheses \
+ implicit-fallthrough undef unused-function, \
+ DISABLED_WARNINGS_CXX_gcc := undef, \
+ DISABLED_WARNINGS_clang := parentheses format undef \
+ logical-op-parentheses format-nonliteral int-conversion, \
+ DISABLED_WARNINGS_gcc_awt_InputMethod.c := unused-label, \
+ DISABLED_WARNINGS_gcc_CUPSfuncs.c := unused-variable, \
+ DISABLED_WARNINGS_gcc_VKRenderQueue.c := unused-variable unused-but-set-variable, \
+ DISABLED_WARNINGS_clang_awt_InputMethod.c := unused-label, \
+ DISABLED_WARNINGS_clang_CUPSfuncs.c := unused-variable, \
+ DISABLED_WARNINGS_clang_VKRenderQueue.c := unused-variable unused-but-set-variable, \
+ JDK_LIBS := libawt java.base:libjava java.base:libjvm, \
+ LIBS := $(WAYLAND_LIBS), \
+ LIBS_unix := $(LIBDL) $(LIBM), \
+ LIBS_linux := -lpthread -lrt, \
+ ))
+
+ $(BUILD_LIBAWT_WLAWT): $(call FindLib, java.base, java)
+
+ $(BUILD_LIBAWT_WLAWT): $(BUILD_LIBAWT)
+
+ TARGETS += $(BUILD_LIBAWT_WLAWT)
+ endif
+endif
+
ifeq ($(call isTargetOs, macosx), true)
##############################################################################
## Build libawt_lwawt
@@ -307,16 +503,21 @@ ifeq ($(call isTargetOs, macosx), true)
$(LIBAWT_DEFAULT_HEADER_DIRS) \
libawt_lwawt/awt \
libawt_lwawt/font \
+ libawt_lwawt/java2d \
libawt_lwawt/java2d/opengl \
libawt_lwawt/java2d/metal \
include \
common/awt/debug \
+ common/java2d \
common/java2d/opengl \
java.base:libjvm \
#
LIBAWT_LWAWT_EXCLUDE_FILES := fontpath.c awt_Font.c X11Color.c
- LIBAWT_LWAWT_EXCLUDES := $(TOPDIR)/src/$(MODULE)/unix/native/common/awt/medialib
+ LIBAWT_LWAWT_EXCLUDES := \
+ $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan \
+ $(TOPDIR)/src/$(MODULE)/unix/native/common/awt/medialib \
+ #
LIBAWT_LWAWT_STATIC_EXCLUDE_OBJS := systemScale.o
@@ -345,6 +546,8 @@ ifeq ($(call isTargetOs, macosx), true)
DISABLED_WARNINGS_clang_OGLBufImgOps.c := format-nonliteral, \
DISABLED_WARNINGS_clang_OGLPaints.c := format-nonliteral, \
DISABLED_WARNINGS_clang_PrinterView.m := enum-conversion, \
+ DISABLED_WARNINGS_clang_SystemHotkey.m := format-nonliteral, \
+ DISABLED_WARNINGS_clang_system_properties.c := format-nonliteral, \
JDK_LIBS := libawt java.base:libjava libosxapp, \
LIBS_macosx := $(LIBM) \
-framework Accelerate \
@@ -352,11 +555,13 @@ ifeq ($(call isTargetOs, macosx), true)
-framework AudioToolbox \
-framework Carbon \
-framework Cocoa \
+ -framework IOKit \
-framework ExceptionHandling \
-framework JavaRuntimeSupport \
-framework Metal \
-framework OpenGL \
-framework QuartzCore \
+ -framework UniformTypeIdentifiers \
-framework Security, \
STATIC_LIB_EXCLUDE_OBJS := $(LIBAWT_LWAWT_STATIC_EXCLUDE_OBJS), \
))
diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk
index 5587b13a6c99..13b5f678fcb3 100644
--- a/make/modules/java.desktop/lib/ClientLibraries.gmk
+++ b/make/modules/java.desktop/lib/ClientLibraries.gmk
@@ -145,7 +145,9 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
#
LIBSPLASHSCREEN_HEADER_DIRS := \
+ libsplashscreen \
common/awt/utility \
+ common/awt/systemscale \
libosxapp \
java.base:libjava \
java.base:libjvm \
@@ -215,14 +217,21 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
else ifeq ($(call isTargetOs, windows), true)
LIBSPLASHSCREEN_CFLAGS += -DWITH_WIN32
else
+ LIBWLSPLASHSCREEN_CFLAGS := -DWITH_WL $(LIBSPLASHSCREEN_CFLAGS) $(X_CFLAGS) $(WAYLAND_CFLAGS)
LIBSPLASHSCREEN_CFLAGS += -DWITH_X11 $(X_CFLAGS)
endif
LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS += systemScale.o
+ LIBSPLASHSCREEN_EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c
+ LIBSPLASHSCREEN_LIBS := $(GIFLIB_LIBS) $(LIBJPEG_LIBS) $(LIBZ_LIBS) $(PNG_LIBS)
+ LIBSPLASHSCREEN_DISABLED_WARNINGS_gcc := sign-compare implicit-fallthrough shift-negative-value maybe-uninitialized unused-function type-limits unused-result unused-but-set-variable
+ LIBSPLASHSCREEN_DISABLED_WARNINGS_clang := deprecated-non-prototype format-nonliteral sign-compare incompatible-pointer-types deprecated-declarations
+ LIBWLSPLASHSCREEN_HEADER_DIRS := common/wayland $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland
+ LIBWLSPLASHSCREEN_EXTRA_SRC := common/wayland
$(eval $(call SetupJdkLibrary, BUILD_LIBSPLASHSCREEN, \
NAME := splashscreen, \
- EXTRA_SRC := $(LIBSPLASHSCREEN_EXTRA_SRC), \
+ EXTRA_SRC := $(LIBSPLASHSCREEN_EXTRA_SRC) libxsplashscreen libsplashscreen, \
EXCLUDE_SRC_PATTERNS := $(LIBSPLASHSCREEN_EXCLUDE_SRC_PATTERNS), \
EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c, \
EXCLUDES := $(LIBSPLASHSCREEN_EXCLUDES), \
@@ -233,7 +242,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
CXXFLAGS := $(LIBSPLASHSCREEN_CFLAGS) \
$(GIFLIB_CFLAGS) $(LIBJPEG_CFLAGS) $(PNG_CFLAGS) $(LIBZ_CFLAGS) \
$(ICONV_CFLAGS), \
- EXTRA_HEADER_DIRS := $(LIBSPLASHSCREEN_HEADER_DIRS), \
+ EXTRA_HEADER_DIRS := libxsplashscreen $(LIBSPLASHSCREEN_HEADER_DIRS), \
DISABLED_WARNINGS_gcc_dgif_lib.c := sign-compare, \
DISABLED_WARNINGS_gcc_jcmaster.c := implicit-fallthrough, \
DISABLED_WARNINGS_gcc_jdphuff.c := shift-negative-value, \
@@ -243,8 +252,9 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
maybe-uninitialized, \
DISABLED_WARNINGS_gcc_splashscreen_impl.c := implicit-fallthrough \
sign-compare unused-function, \
- DISABLED_WARNINGS_gcc_splashscreen_sys.c := type-limits \
+ DISABLED_WARNINGS_gcc_splashscreen_sys_common.c := type-limits \
unused-but-set-variable unused-result unused-variable, \
+ DISABLED_WARNINGS_gcc_splashscreen_sys.c := unused-variable \
DISABLED_WARNINGS_clang := deprecated-non-prototype, \
DISABLED_WARNINGS_clang_dgif_lib.c := sign-compare, \
DISABLED_WARNINGS_clang_gzwrite.c := format-nonliteral, \
@@ -262,7 +272,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
LDFLAGS_windows := -delayload:user32.dll, \
JDK_LIBS_windows := java.base:libjava, \
JDK_LIBS_macosx := libosxapp, \
- LIBS := $(GIFLIB_LIBS) $(LIBJPEG_LIBS) $(LIBZ_LIBS) $(PNG_LIBS) $(ICONV_LIBS), \
+ LIBS := $(LIBSPLASHSCREEN_LIBS) $(ICONV_LIBS), \
LIBS_unix := $(LIBM) $(LIBPTHREAD), \
LIBS_linux := $(LIBDL) $(X_LIBS) -lX11 -lXext, \
LIBS_macosx := \
@@ -276,7 +286,29 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
STATIC_LIB_EXCLUDE_OBJS := $(LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS), \
))
+ $(eval $(call SetupJdkLibrary, BUILD_LIBWLSPLASHSCREEN, \
+ NAME := wlsplashscreen, \
+ EXTRA_SRC := $(LIBSPLASHSCREEN_EXTRA_SRC) $(LIBWLSPLASHSCREEN_EXTRA_SRC) libwlsplashscreen libsplashscreen, \
+ EXTRA_FILES := $(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland/xdg-shell.c, \
+ EXCLUDE_SRC_PATTERNS := $(LIBSPLASHSCREEN_EXCLUDE_SRC_PATTERNS), \
+ DISABLED_WARNINGS_gcc := $(LIBSPLASHSCREEN_DISABLED_WARNINGS_gcc), \
+ DISABLED_WARNINGS_clang := $(LIBSPLASHSCREEN_DISABLED_WARNINGS_clang), \
+ EXCLUDE_FILES := $(LIBSPLASHSCREEN_EXCLUDE_FILES), \
+ EXCLUDES := $(LIBSPLASHSCREEN_EXCLUDES), \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(LIBWLSPLASHSCREEN_CFLAGS) \
+ $(GIFLIB_CFLAGS) $(LIBJPEG_CFLAGS) $(PNG_CFLAGS) $(LIBZ_CFLAGS), \
+ EXTRA_HEADER_DIRS := $(LIBSPLASHSCREEN_HEADER_DIRS) $(LIBWLSPLASHSCREEN_HEADER_DIRS) libwlsplashscreen, \
+ LIBS := -lwayland-client -lwayland-cursor $(LIBSPLASHSCREEN_LIBS) -lrt, \
+ LIBS_unix := $(LIBDL) $(LIBM) -lpthread, \
+ LIBS_aix := -liconv, \
+ ))
+
TARGETS += $(BUILD_LIBSPLASHSCREEN)
+ ifeq ($(call isTargetOs, linux), true)
+ TARGETS += $(BUILD_LIBWLSPLASHSCREEN)
+ endif
+
endif
################################################################################
@@ -351,6 +383,7 @@ else
endif
LIBFONTMANAGER_EXTRA_HEADER_DIRS := \
+ java.base:libjvm \
common/awt \
common/awt/utility \
common/font \
@@ -379,9 +412,9 @@ ifeq ($(call isTargetOs, windows), true)
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c X11TextRenderer.c
else ifeq ($(call isTargetOs, macosx), true)
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c X11TextRenderer.c \
- fontpath.c lcdglyph.c
+ lcdglyph.c lcdglyphDW.cpp
else
- LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c lcdglyph.c
+ LIBFONTMANAGER_EXCLUDE_FILES += lcdglyph.c lcdglyphDW.cpp
endif
LIBFONTMANAGER_CFLAGS += $(X_CFLAGS) -DLE_STANDALONE -DHEADLESS
@@ -401,7 +434,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER, \
CFLAGS_windows = -DCC_NOEX, \
EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS), \
EXTRA_SRC := $(LIBFONTMANAGER_EXTRA_SRC), \
- DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc), \
+ DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc) unused-result, \
DISABLED_WARNINGS_CXX_gcc := $(HARFBUZZ_DISABLED_WARNINGS_CXX_gcc), \
DISABLED_WARNINGS_clang := $(HARFBUZZ_DISABLED_WARNINGS_clang), \
DISABLED_WARNINGS_microsoft := $(HARFBUZZ_DISABLED_WARNINGS_microsoft), \
@@ -422,6 +455,48 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER, \
TARGETS += $(BUILD_LIBFONTMANAGER)
+ifeq ($(call isTargetOs, windows macosx), false)
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+
+ LIBFONTMANAGER_XAWT_EXCLUDE_FILES := $(LIBFONTMANAGER_EXCLUDE_FILES)
+ LIBFONTMANAGER_XAWT_CFLAGS := $(LIBFONTMANAGER_CFLAGS)
+ LIBFONTMANAGER_XAWT_OPTIMIZATION := $(LIBFONTMANAGER_OPTIMIZATION)
+ LIBFONTMANAGER_XAWT_EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS) \
+ libfontmanager java.base:libjava
+ LIBFONTMANAGER_XAWT_EXTRA_SRC :=
+
+ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER_XAWT, \
+ NAME := fontmanager_xawt, \
+ EXCLUDE_FILES := $(LIBFONTMANAGER_XAWT_EXCLUDE_FILES) \
+ AccelGlyphCache.c, \
+ LINK_TYPE := C++, \
+ CFLAGS := $(CFLAGS_JDKLIB) $(LIBFONTMANAGER_XAWT_CFLAGS), \
+ CXXFLAGS := $(CXXFLAGS_JDKLIB) $(LIBFONTMANAGER_XAWT_CFLAGS), \
+ OPTIMIZATION := $(LIBFONTMANAGER_XAWT_OPTIMIZATION), \
+ CFLAGS_windows = -DCC_NOEX, \
+ EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_XAWT_EXTRA_HEADER_DIRS), \
+ EXTRA_SRC := $(LIBFONTMANAGER_XAWT_EXTRA_SRC), \
+ WARNINGS_AS_ERRORS_xlc := false, \
+ DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc), \
+ DISABLED_WARNINGS_CXX_gcc := $(HARFBUZZ_DISABLED_WARNINGS_CXX_gcc), \
+ DISABLED_WARNINGS_clang := $(HARFBUZZ_DISABLED_WARNINGS_clang), \
+ DISABLED_WARNINGS_microsoft := $(HARFBUZZ_DISABLED_WARNINGS_microsoft), \
+ LDFLAGS := $(LDFLAGS_CXX_JDK), \
+ LDFLAGS_FILTER_OUT :=-Wl$(COMMA)-z$(COMMA)defs, \
+ LDFLAGS_aix := -Wl$(COMMA)-berok, \
+ JDK_LIBS := libfontmanager libawt java.base:libjava $(LIBFONTMANAGER_JDK_LIBS), \
+ LIBS := $(LIBFONTMANAGER_LIBS), \
+ LIBS_unix := $(LIBM), \
+ ))
+
+ $(BUILD_LIBFONTMANAGER_XAWT): $(BUILD_LIBFONTMANAGER)
+ $(BUILD_LIBFONTMANAGER_XAWT): $(BUILD_LIBAWT_XAWT)
+
+ TARGETS += $(BUILD_LIBFONTMANAGER_XAWT)
+
+ endif
+endif
+
ifeq ($(call isTargetOs, macosx), true)
##############################################################################
## Build libosxui
diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk
index 60a88ca1c9a5..213bb0126217 100644
--- a/make/test/JtregNativeJdk.gmk
+++ b/make/test/JtregNativeJdk.gmk
@@ -57,6 +57,8 @@ BUILD_JDK_JTREG_EXECUTABLES_JDK_LIBS_exeNullCallerTest := java.base:libjvm
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libstringPlatformChars := java.base:libjava
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libGetXSpace := java.base:libjava
+BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libFatalErrorTest := java.base:libjava
+BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libLogEventTest := java.base:libjava
# Platform specific setup
ifeq ($(call isTargetOs, windows), true)
@@ -93,11 +95,25 @@ ifeq ($(call isTargetOs, macosx), true)
BUILD_JDK_JTREG_EXECUTABLES_JDK_LIBS_exeJniInvocationTest := java.base:libjli
BUILD_JDK_JTREG_LIBRARIES_LIBS_libTestDynamicStore := \
-framework Cocoa -framework SystemConfiguration
+ BUILD_JDK_JTREG_LIBRARIES_LIBS_libSharedTexturesTest := \
+ -framework Cocoa -framework Metal
else
BUILD_JDK_JTREG_EXCLUDE += libTestMainKeyWindow.m
BUILD_JDK_JTREG_EXCLUDE += libTestDynamicStore.m
BUILD_JDK_JTREG_EXCLUDE += exeJniInvocationTest.c
BUILD_JDK_JTREG_EXCLUDE += exeLibraryCache.c
+ BUILD_JDK_JTREG_EXCLUDE += libSharedTexturesTest.m
+endif
+
+ifeq ($(OPENJDK_TARGET_OS), windows)
+ BUILD_JDK_JTREG_LIBRARIES_LIBS_libwindows_touch_robot := user32.lib
+ BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c
+else
+ ifeq ($(OPENJDK_TARGET_OS), linux)
+ BUILD_JDK_JTREG_EXCLUDE += libwindows_touch_robot.c
+ else
+ BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c libwindows_touch_robot.c
+ endif
endif
ifeq ($(call isTargetOs, linux), true)
diff --git a/src/demo/share/java2d/J2DBench/src/j2dbench/report/TCChartReporter.java b/src/demo/share/java2d/J2DBench/src/j2dbench/report/TCChartReporter.java
new file mode 100644
index 000000000000..b293ac475ea4
--- /dev/null
+++ b/src/demo/share/java2d/J2DBench/src/j2dbench/report/TCChartReporter.java
@@ -0,0 +1,320 @@
+package j2dbench.report;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+/**
+ * The class reads J2DBench scores and reports them into output stream in format applicable for TeamCity charts.
+ * The directory, where J2DBench result files placed, is specified via command line parameter like follows:
+ *
+ * -basexml | -b
+ *
+ * This directory must contain one file with pattern *{openjdk}*.{res} which is considered as a container
+ * of reference scores and several *{jbsdk}*.{res}.
+ *
+ *
Names of these files have several mandatory fields separated by "_" and look like
+ * osName_jdkName_renderName_*.res
+ *
+ *
+ * If any of score is less than corresponding reference value by 5% then exit code 1 is returned otherwise
+ * exit code 0 is returned.
+ *
+ * Standard output will contain scores in format required for TeamCity charts.
+ *
+ * Created by vprovodin on 13/02/2017.
+ */
+public class TCChartReporter {
+
+ private static boolean printTCValues = false;
+ private static boolean printValues = false;
+
+ private static final DecimalFormat decimalFormat =
+ new DecimalFormat("0.00");
+
+ private static FileSystem defaultFileSystem = FileSystems.getDefault();
+
+ private static double getMeasurementError(String testCaseName, String osName) {
+ if (testCaseName.contains("text.Rendering.tests.drawString") && osName.toLowerCase().contains("lin") )
+ return 0.18;
+ return 0.1;
+ }
+
+ /**
+ * Level at which tests are grouped to be displayed in summary
+ */
+ private static final int LEVEL = 2;
+
+ /**
+ * Holds the groups and corresponding group-display-names
+ */
+ private static List groups = new ArrayList<>();
+ private static Map referenceValues = new HashMap<>();
+ private static boolean testFailed = false;
+
+ private static void printUsage() {
+ String usage =
+ "\njava TCChartReporter [options] " +
+ " \n\n" +
+ "where options include: " +
+ " \n" +
+ " -basexml | -b " +
+ "path to base-build result";
+ System.out.println(usage);
+ System.exit(0);
+ }
+
+ /**
+ * String = getTestResultsTableForSummary()
+ */
+ private static double generateTestCaseReport(
+ Object key,
+ Map testCaseResult,
+ Map testCaseResultCount) {
+
+ Integer curTestCountObj = testCaseResultCount.get(key.toString());
+ int curTestCount = 0;
+ if (curTestCountObj != null) {
+ curTestCount = curTestCountObj;
+ }
+
+ double totalScore = 0;
+
+ for (int i = 0; i < curTestCount; i++) {
+ J2DAnalyzer.ResultHolder resultTCR = testCaseResult.get(key.toString() + "_" + i);
+ totalScore = totalScore + resultTCR.getScore();
+ }
+
+ return totalScore;
+ }
+
+ /**
+ * Generate Testcase Summary Report for TC - *.out
+ */
+ private static void generateTestCaseSummaryReport(
+ String OJRname,
+ Map consoleResult,
+ Map testCaseResult,
+ Map testCaseResultCount,
+ boolean rememberReference) {
+
+ String curGroupName, curTestName;
+
+ Object[] groupNameArray = groups.toArray();
+
+ Object[] testCaseList = consoleResult.keySet().toArray();
+ Arrays.sort(testCaseList);
+
+ for (Object aGroupNameArray : groupNameArray) {
+
+ double value;
+ curGroupName = aGroupNameArray.toString();
+
+ for (Object aTestCaseList : testCaseList) {
+
+ curTestName = aTestCaseList.toString();
+
+ if (curTestName.contains(curGroupName)) {
+
+ value = generateTestCaseReport(curTestName, testCaseResult, testCaseResultCount);
+
+ if (printTCValues)
+ System.out.println("##teamcity[buildStatisticValue key='" + (OJRname.isEmpty() ? "" : OJRname + ".") + curTestName
+ + "' value='" + decimalFormat.format(value) + "']");
+ if (printValues)
+ System.out.println((OJRname.isEmpty() ? "" : OJRname + ".") + curTestName + "," + decimalFormat.format(value));
+ if (rememberReference) {
+ referenceValues.put(curTestName, value);
+ } else {
+ double refValue = referenceValues.getOrDefault(curTestName, 0.);
+ if (Math.abs(value/refValue - 1) >= getMeasurementError(curTestName, OJRname)) {
+ System.err.println(OJRname);
+ System.err.println(curTestName);
+ System.err.println("\treferenceValue=" + refValue);
+ System.err.println("\t actualValue=" + value);
+ System.err.println("\t diff:" + ((value / refValue - 1) * 100));
+ testFailed = (value < refValue);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * main
+ */
+ public static void main(String args[]) {
+
+ String baseXML = null;
+ int group = 2;
+
+ /* ---- Analysis Mode ----
+ BEST = 1;
+ WORST = 2;
+ AVERAGE = 3;
+ MIDAVG = 4;
+ ------------------------ */
+ int analyzerMode = 4;
+
+ try {
+
+ for (int i = 0; i < args.length; i++) {
+
+ if (args[i].startsWith("-basexml") ||
+ args[i].startsWith("-b")) {
+ i++;
+ baseXML = args[i];
+ } else if (args[i].startsWith("-tc")) {
+ printTCValues = true;
+ } else if (args[i].startsWith("-v"))
+ printValues = true;
+ }
+ } catch (Exception e) {
+ printUsage();
+ }
+
+ XMLHTMLReporter.setGroupLevel(group);
+ J2DAnalyzer.setMode(analyzerMode);
+ if (baseXML != null) {
+ generateComparisonReport(defaultFileSystem.getPath(baseXML));
+ } else {
+ printUsage();
+ }
+
+ if (testFailed)
+ System.exit(1);
+ }
+
+ /**
+ * Add Test Group to the list
+ */
+ private static void addGroup(String testName) {
+
+ String testNameSplit[] = testName.replace('.', '_').split("_");
+ StringBuilder group = new StringBuilder(testNameSplit[0]);
+ for (int i = 1; i < LEVEL; i++) {
+ group.append(".").append(testNameSplit[i]);
+ }
+
+ if (!groups.contains(group.toString()))
+ groups.add(group.toString());
+ }
+
+ private static List listResFiles(Path dir, String pattern) throws IOException {
+ List result = new ArrayList<>();
+ try (DirectoryStream stream = Files.newDirectoryStream(dir, pattern)) {
+ for (Path entry : stream) {
+ result.add(entry);
+ }
+ } catch (DirectoryIteratorException ex) {
+ throw ex.getCause();
+ }
+ return result;
+ }
+
+ /**
+ * Generate the reports from the base & target result XML
+ */
+ private static void generateComparisonReport(Path directoryToResFiles) {
+
+ if (directoryToResFiles.toFile().isDirectory()) {
+ List jbsdkFiles, openjdkFiles;
+
+ try {
+ jbsdkFiles = listResFiles(directoryToResFiles, "*{jbsdk,jbre}*.{res}");
+ openjdkFiles = listResFiles(directoryToResFiles, "*{openjdk}*.{res}");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ readScores(openjdkFiles.get(0), true);
+
+ for (Path file : jbsdkFiles) {
+ readScores(file, false);
+ }
+ } else {
+ readScores(directoryToResFiles, true);
+ }
+ }
+
+ private static void readScores(Path file, boolean rememberReference) {
+ String fileName = file.getName(file.getNameCount() - 1).toString();
+ String osName="", jdkName="", renderName="";
+ if (fileName.contains("win") || fileName.contains("linux") || fileName.contains("osx")) {
+ String[] fileNameComponents = fileName.split("_");
+ if (fileNameComponents.length > 0)
+ osName = fileNameComponents[0];
+ if (fileNameComponents.length > 1)
+ jdkName = fileNameComponents[1];
+ if (fileNameComponents.length > 2)
+ renderName = fileNameComponents[2];
+ }
+
+ String resultXMLFileName = file.toString();
+
+ J2DAnalyzer.results = new Vector();
+ J2DAnalyzer.readResults(resultXMLFileName);
+ J2DAnalyzer.SingleResultSetHolder baseSRSH =
+ (J2DAnalyzer.SingleResultSetHolder) J2DAnalyzer.results.elementAt(0);
+ Enumeration baseEnum_ = baseSRSH.getKeyEnumeration();
+ Vector baseKeyvector = new Vector<>();
+ while (baseEnum_.hasMoreElements()) {
+ baseKeyvector.add((String) baseEnum_.nextElement());
+ }
+ String baseKeys[] = new String[baseKeyvector.size()];
+ baseKeyvector.copyInto(baseKeys);
+ J2DAnalyzer.sort(baseKeys);
+
+ Map consoleBaseRes = new HashMap<>();
+
+ Map testCaseBaseResult = new HashMap<>();
+ Map testCaseResultCount = new HashMap<>();
+
+ for (String baseKey : baseKeys) {
+
+ J2DAnalyzer.ResultHolder baseTCR =
+ baseSRSH.getResultByKey(baseKey);
+
+ Integer curTestCountObj = testCaseResultCount.get(baseTCR.getName());
+ int curTestCount = 0;
+ if (curTestCountObj != null) {
+ curTestCount = curTestCountObj;
+ }
+ curTestCount++;
+ testCaseBaseResult.put(baseTCR.getName() + "_" + (curTestCount - 1), baseTCR);
+ testCaseResultCount.put(baseTCR.getName(), curTestCount);
+
+ addGroup(baseTCR.getName());
+
+ Double curTotalScoreObj = consoleBaseRes.get(baseTCR.getName());
+ double curTotalScore = 0;
+ if (curTotalScoreObj != null) {
+ curTotalScore = curTotalScoreObj;
+ }
+ curTotalScore = curTotalScore + baseTCR.getScore();
+ consoleBaseRes.put(baseTCR.getName(), curTotalScore);
+ }
+
+ String OJRname = osName + "." + jdkName + "." + renderName;
+ generateTestCaseSummaryReport((OJRname.length() == 2? "": OJRname),
+ consoleBaseRes,
+ testCaseBaseResult,
+ testCaseResultCount,
+ rememberReference);
+
+ }
+}
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index aee15e4c55a5..5401923de01d 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -1092,6 +1092,10 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) {
return result;
}
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
void os::print_dll_info(outputStream *st) {
st->print_cr("Dynamic libraries:");
LoadedLibraries::print(st);
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index faa3efa2384f..070d1da26590 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -1260,6 +1260,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
}
#endif // !__APPLE__
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
static int _print_dll_info_cb(const char * name, address base_address,
address top_address, void * param) {
outputStream * out = (outputStream *) param;
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 57b8a37baf25..f781c0c185f5 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -1881,6 +1881,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
return nullptr;
}
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
#ifndef IA32
bool ieee_handling = IEEE_subnormal_handling_OK();
diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp
index 2c0ab6732c16..2045994079d8 100644
--- a/src/hotspot/os/posix/signals_posix.cpp
+++ b/src/hotspot/os/posix/signals_posix.cpp
@@ -551,6 +551,12 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info,
{
assert(info != nullptr && ucVoid != nullptr, "sanity");
+ if (sig == SIGABRT) {
+ // Re-set the handler so that we don't recurse if/when abort() is called
+ // from here.
+ PosixSignals::install_generic_signal_handler(SIGABRT, (void*)SIG_DFL);
+ }
+
// Note: it's not uncommon that JNI code uses signal/sigset to install,
// then restore certain signal handler (e.g. to temporarily block SIGPIPE,
// or have a SIGILL handler when detecting CPU type). When that happens,
@@ -1332,6 +1338,10 @@ static void install_signal_handlers() {
assert(ret == 0, "check");
}
+ if (CatchSIGABRT) {
+ set_signal_handler(SIGABRT);
+ }
+
#if defined(__APPLE__)
// lldb (gdb) installs both standard BSD signal handlers, and mach exception
// handlers. By replacing the existing task exception handler, we disable lldb's mach
diff --git a/src/hotspot/os/windows/globals_windows.hpp b/src/hotspot/os/windows/globals_windows.hpp
index 8f0a6261cc0d..7c8911e50324 100644
--- a/src/hotspot/os/windows/globals_windows.hpp
+++ b/src/hotspot/os/windows/globals_windows.hpp
@@ -43,7 +43,13 @@ product(bool, EnableAllLargePageSizesForWindows, false, \
"Windows Server") \
\
product(bool, UseOSErrorReporting, false, \
- "Let VM fatal error propagate to the OS (ie. WER on Windows)")
+ "Let VM fatal error propagate to the OS (ie. WER on Windows)") \
+ \
+product(bool, UseCriticalSection, true, EXPERIMENTAL, \
+ "Use the critical section API instead of WaitForSingleObject") \
+ \
+product(bool, UseModernSynchAPI, true, EXPERIMENTAL, \
+ "Use more modern WinAPI for synchronization instead of WaitForSingleObject")
// end of RUNTIME_OS_FLAGS
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 7ad9f80141c2..59b9afc26c1b 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -1684,9 +1684,221 @@ static int _print_module(const char* fname, address base_address,
return 0;
}
+static errno_t convert_to_UTF16(char const* source_str, UINT source_encoding, LPWSTR* dest_utf16_str) {
+ const int len_estimated = MultiByteToWideChar(source_encoding,
+ MB_ERR_INVALID_CHARS,
+ source_str,
+ -1, // source is null-terminated
+ nullptr,
+ 0); // estimate characters count
+ if (len_estimated == 0) {
+ // Probably source_str contains characters that cannot be represented in the source_encoding given.
+ *dest_utf16_str = nullptr;
+ return EINVAL;
+ }
+
+ *dest_utf16_str = NEW_C_HEAP_ARRAY(WCHAR, len_estimated, mtInternal);
+
+ const int len_real = MultiByteToWideChar(source_encoding,
+ MB_ERR_INVALID_CHARS,
+ source_str,
+ -1, // source is null-terminated
+ *dest_utf16_str, len_estimated);
+ assert(len_real == len_estimated, "length already checked above");
+
+ return ERROR_SUCCESS;
+}
+
+// Converts a string in the "platform" encoding to UTF16.
+static errno_t convert_to_UTF16(char const* platform_str, LPWSTR* utf16_str) {
+ return convert_to_UTF16(platform_str, CP_ACP, utf16_str);
+}
+
+static errno_t convert_UTF8_to_UTF16(char const* utf8_str, LPWSTR* utf16_str) {
+ return convert_to_UTF16(utf8_str, CP_UTF8, utf16_str);
+}
+
+// Converts a wide-character string in UTF-16 encoding to the 8-bit "platform" encoding.
+// Unless the platform encoding is UTF-8, not all characters in the source string can be represented in the dest string.
+// The function succeeds in this case anyway and just replaces these with a certain character.
+static errno_t convert_UTF16_to_platform(LPWSTR source_utf16_str, char*& dest_str) {
+ const int len_estimated = WideCharToMultiByte(CP_ACP,
+ 0,
+ source_utf16_str,
+ -1, // source is null-terminated
+ nullptr,
+ 0, // estimate characters count
+ nullptr, nullptr);
+ if (len_estimated == 0) {
+ dest_str = nullptr;
+ return EINVAL;
+ }
+
+ dest_str = NEW_C_HEAP_ARRAY(CHAR, len_estimated, mtInternal);
+
+ const int len_real = WideCharToMultiByte(CP_ACP,
+ 0,
+ source_utf16_str,
+ -1, // source is null-terminated
+ dest_str, len_estimated, nullptr, nullptr);
+ assert(len_real == len_estimated, "length already checked above");
+
+ return ERROR_SUCCESS;
+}
+
+class MemoryReleaserW : public StackObj {
+private:
+ WCHAR* _object_ptr;
+
+public:
+ MemoryReleaserW(WCHAR * object_ptr) : _object_ptr(object_ptr) {}
+ ~MemoryReleaserW() { if (_object_ptr != nullptr) FREE_C_HEAP_ARRAY(WCHAR, _object_ptr); }
+};
+
+class MemoryReleaser : public StackObj {
+private:
+ CHAR* _object_ptr;
+
+public:
+ MemoryReleaser(CHAR * object_ptr) : _object_ptr(object_ptr) {}
+ ~MemoryReleaser() { if (_object_ptr != nullptr) FREE_C_HEAP_ARRAY(CHAR, _object_ptr); }
+};
+
// Loads .dll/.so and
// in case of error it checks if .dll/.so was built for the
// same architecture as Hotspot is running on
+void * os::dll_load_utf8(const char *utf8_name, char *ebuf, int ebuflen) {
+ LPWSTR utf16_name = nullptr;
+ errno_t err = convert_UTF8_to_UTF16(utf8_name, &utf16_name);
+ MemoryReleaserW release_utf16_name(utf16_name);
+ if (err != ERROR_SUCCESS) {
+ errno = err;
+ return nullptr;
+ }
+
+ char* platform_name = nullptr; // name of the library converted to the "platform" encoding for use in log messages
+ errno_t ignored_err = convert_UTF16_to_platform(utf16_name, platform_name);
+ MemoryReleaser release_platform_name(platform_name);
+
+ log_info(os)("attempting shared library load of %s", platform_name);
+
+ void * result = LoadLibraryW(utf16_name);
+
+ if (result != nullptr) {
+ Events::log(nullptr, "Loaded shared library %s", platform_name);
+ // Recalculate pdb search path if a DLL was loaded successfully.
+ SymbolEngine::recalc_search_path();
+ log_info(os)("shared library load of %s was successful", platform_name);
+ return result;
+ }
+ DWORD errcode = GetLastError();
+ // Read system error message into ebuf
+ // It may or may not be overwritten below (in the for loop and just above)
+ lasterror(ebuf, (size_t) ebuflen);
+ ebuf[ebuflen - 1] = '\0';
+ Events::log(nullptr, "Loading shared library %s failed, error code %lu", platform_name, errcode);
+ log_info(os)("shared library load of %s failed, error code %lu", platform_name, errcode);
+
+ if (errcode == ERROR_MOD_NOT_FOUND) {
+ strncpy(ebuf, "Can't find dependent libraries", ebuflen - 1);
+ ebuf[ebuflen - 1] = '\0';
+ return nullptr;
+ }
+
+ // Parsing dll below
+ // If we can read dll-info and find that dll was built
+ // for an architecture other than Hotspot is running in
+ // - then print to buffer "DLL was built for a different architecture"
+ // else call os::lasterror to obtain system error message
+ int fd = ::wopen(utf16_name, O_RDONLY | O_BINARY, 0);
+ if (fd < 0) {
+ return nullptr;
+ }
+
+ uint32_t signature_offset;
+ uint16_t lib_arch = 0;
+ bool failed_to_get_lib_arch =
+ ( // Go to position 3c in the dll
+ (os::seek_to_file_offset(fd, IMAGE_FILE_PTR_TO_SIGNATURE) < 0)
+ ||
+ // Read location of signature
+ (sizeof(signature_offset) !=
+ (::read(fd, (void*)&signature_offset, sizeof(signature_offset))))
+ ||
+ // Go to COFF File Header in dll
+ // that is located after "signature" (4 bytes long)
+ (os::seek_to_file_offset(fd,
+ signature_offset + IMAGE_FILE_SIGNATURE_LENGTH) < 0)
+ ||
+ // Read field that contains code of architecture
+ // that dll was built for
+ (sizeof(lib_arch) != (::read(fd, (void*)&lib_arch, sizeof(lib_arch))))
+ );
+
+ ::close(fd);
+ if (failed_to_get_lib_arch) {
+ // file i/o error - report os::lasterror(...) msg
+ return nullptr;
+ }
+
+ typedef struct {
+ uint16_t arch_code;
+ char* arch_name;
+ } arch_t;
+
+ static const arch_t arch_array[] = {
+ {IMAGE_FILE_MACHINE_I386, (char*)"IA 32"},
+ {IMAGE_FILE_MACHINE_AMD64, (char*)"AMD 64"},
+ {IMAGE_FILE_MACHINE_ARM64, (char*)"ARM 64"}
+ };
+#if (defined _M_ARM64)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_ARM64;
+#elif (defined _M_AMD64)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_AMD64;
+#elif (defined _M_IX86)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_I386;
+#else
+ #error Method os::dll_load requires that one of following \
+ is defined :_M_AMD64 or _M_IX86 or _M_ARM64
+#endif
+
+
+ // Obtain a string for printf operation
+ // lib_arch_str shall contain string what platform this .dll was built for
+ // running_arch_str shall string contain what platform Hotspot was built for
+ char *running_arch_str = nullptr, *lib_arch_str = nullptr;
+ for (unsigned int i = 0; i < ARRAY_SIZE(arch_array); i++) {
+ if (lib_arch == arch_array[i].arch_code) {
+ lib_arch_str = arch_array[i].arch_name;
+ }
+ if (running_arch == arch_array[i].arch_code) {
+ running_arch_str = arch_array[i].arch_name;
+ }
+ }
+
+ assert(running_arch_str,
+ "Didn't find running architecture code in arch_array");
+
+ // If the architecture is right
+ // but some other error took place - report os::lasterror(...) msg
+ if (lib_arch == running_arch) {
+ return nullptr;
+ }
+
+ if (lib_arch_str != nullptr) {
+ jio_snprintf(ebuf, ebuflen - 1,
+ "Can't load %s-bit .dll on a %s-bit platform",
+ lib_arch_str, running_arch_str);
+ } else {
+ // don't know what architecture this dll was build for
+ jio_snprintf(ebuf, ebuflen - 1,
+ "Can't load this .dll (machine code=0x%x) on a %s-bit platform",
+ lib_arch, running_arch_str);
+ }
+
+ return nullptr;
+}
+
void * os::dll_load(const char *name, char *ebuf, int ebuflen) {
log_info(os)("attempting shared library load of %s", name);
void* result;
@@ -4644,7 +4856,7 @@ static wchar_t* wide_abs_unc_path(char const* path, errno_t & err, int additiona
set_path_prefix(buf, &prefix, &prefix_off, &needs_fullpath);
LPWSTR unicode_path = nullptr;
- err = convert_to_unicode(buf, &unicode_path);
+ err = convert_to_UTF16(buf, &unicode_path);
FREE_C_HEAP_ARRAY(char, buf);
if (err != ERROR_SUCCESS) {
return nullptr;
@@ -5405,7 +5617,9 @@ int PlatformEvent::park(jlong Millis) {
// 1 => 0 : pass - return immediately
// 0 => -1 : block; then set _Event to 0 before returning
- guarantee(_ParkHandle != nullptr , "Invariant");
+ if (!UseModernSynchAPI) {
+ guarantee(_ParkHandle != nullptr , "Invariant");
+ }
guarantee(Millis > 0 , "Invariant");
// CONSIDER: defer assigning a CreateEvent() handle to the Event until
@@ -5437,25 +5651,37 @@ int PlatformEvent::park(jlong Millis) {
// adjust Millis accordingly if we encounter a spurious wakeup.
const int MAXTIMEOUT = 0x10000000;
- DWORD rv = WAIT_TIMEOUT;
- while (_Event < 0 && Millis > 0) {
- DWORD prd = Millis; // set prd = MAX (Millis, MAXTIMEOUT)
- if (Millis > MAXTIMEOUT) {
- prd = MAXTIMEOUT;
- }
+
+ if (UseModernSynchAPI) {
HighResolutionInterval *phri = nullptr;
if (!ForceTimeHighResolution) {
- phri = new HighResolutionInterval(prd);
+ phri = new HighResolutionInterval(Millis);
}
- rv = ::WaitForSingleObject(_ParkHandle, prd);
- assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
- assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed with return value: %lu", rv);
- if (rv == WAIT_TIMEOUT) {
- Millis -= prd;
+ if ((v = Atomic::load_acquire(&_Event)) < 0) {
+ ::WaitOnAddress(&_Event, &v, sizeof(_Event), Millis);
}
delete phri; // if it is null, harmless
+ } else {
+ DWORD rv = WAIT_TIMEOUT;
+ while (_Event < 0 && Millis > 0) {
+ DWORD prd = Millis; // set prd = MAX (Millis, MAXTIMEOUT)
+ if (Millis > MAXTIMEOUT) {
+ prd = MAXTIMEOUT;
+ }
+ HighResolutionInterval *phri = nullptr;
+ if (!ForceTimeHighResolution) {
+ phri = new HighResolutionInterval(prd);
+ }
+ rv = ::WaitForSingleObject(_ParkHandle, prd);
+ assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
+ assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed with return value: %lu", rv);
+ if (rv == WAIT_TIMEOUT) {
+ Millis -= prd;
+ }
+ delete phri; // if it is null, harmless
+ }
+ v = _Event;
}
- v = _Event;
_Event = 0;
// see comment at end of PlatformEvent::park() below:
OrderAccess::fence();
@@ -5471,7 +5697,9 @@ void PlatformEvent::park() {
// 1 => 0 : pass - return immediately
// 0 => -1 : block; then set _Event to 0 before returning
- guarantee(_ParkHandle != nullptr, "Invariant");
+ if (!UseModernSynchAPI) {
+ guarantee(_ParkHandle != nullptr, "Invariant");
+ }
// Invariant: Only the thread associated with the Event/PlatformEvent
// may call park().
// Consider: use atomic decrement instead of CAS-loop
@@ -5483,21 +5711,35 @@ void PlatformEvent::park() {
guarantee((v == 0) || (v == 1), "invariant");
if (v != 0) return;
- // Do this the hard way by blocking ...
- // TODO: consider a brief spin here, gated on the success of recent
- // spin attempts by this thread.
- while (_Event < 0) {
- // The following code is only here to maintain the
- // characteristics/performance from when an ObjectMonitor
- // "responsible" thread used to issue timed parks.
- HighResolutionInterval *phri = nullptr;
- if (!ForceTimeHighResolution) {
- phri = new HighResolutionInterval((jlong)1);
+ if (UseModernSynchAPI) {
+ while ((v = Atomic::load_acquire(&_Event)) < 0) {
+ // The following code is only here to maintain the
+ // characteristics/performance from when an ObjectMonitor
+ // "responsible" thread used to issue timed parks.
+ HighResolutionInterval *phri = nullptr;
+ if (!ForceTimeHighResolution) {
+ phri = new HighResolutionInterval((jlong)1);
+ }
+ ::WaitOnAddress(&_Event, &v, sizeof(_Event), INFINITE);
+ delete phri; // if it is null, harmless
+ }
+ } else {
+ // Do this the hard way by blocking ...
+ // TODO: consider a brief spin here, gated on the success of recent
+ // spin attempts by this thread.
+ while (_Event < 0) {
+ // The following code is only here to maintain the
+ // characteristics/performance from when an ObjectMonitor
+ // "responsible" thread used to issue timed parks.
+ HighResolutionInterval *phri = nullptr;
+ if (!ForceTimeHighResolution) {
+ phri = new HighResolutionInterval((jlong)1);
+ }
+ DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
+ delete phri; // if it is null, harmless
+ assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
+ assert(rv == WAIT_OBJECT_0, "WaitForSingleObject failed with return value: %lu", rv);
}
- DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
- delete phri; // if it is null, harmless
- assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
- assert(rv == WAIT_OBJECT_0, "WaitForSingleObject failed with return value: %lu", rv);
}
// Usually we'll find _Event == 0 at this point, but as
@@ -5509,7 +5751,9 @@ void PlatformEvent::park() {
}
void PlatformEvent::unpark() {
- guarantee(_ParkHandle != nullptr, "Invariant");
+ if (!UseModernSynchAPI) {
+ guarantee(_ParkHandle != nullptr, "Invariant");
+ }
// Transitions for _Event:
// 0 => 1 : just return
@@ -5525,9 +5769,14 @@ void PlatformEvent::unpark() {
// from the first park() call after an unpark() call which will help
// shake out uses of park() and unpark() without condition variables.
- if (Atomic::xchg(&_Event, 1) >= 0) return;
-
- ::SetEvent(_ParkHandle);
+ if (UseModernSynchAPI) {
+ if (Atomic::xchg(&_Event, 1) >= 0) return;
+ // Changed from -1 to 1; the target thread's WaitOnAddress() must return now
+ ::WakeByAddressAll((PVOID) &_Event);
+ } else {
+ if (Atomic::xchg(&_Event, 1) >= 0) return;
+ ::SetEvent(_ParkHandle);
+ }
}
@@ -5539,7 +5788,6 @@ void PlatformEvent::unpark() {
// use them directly.
void Parker::park(bool isAbsolute, jlong time) {
- guarantee(_ParkHandle != nullptr, "invariant");
// First, demultiplex/decode time arguments
if (time < 0) { // don't wait
return;
@@ -5559,32 +5807,60 @@ void Parker::park(bool isAbsolute, jlong time) {
JavaThread* thread = JavaThread::current();
- // Don't wait if interrupted or already triggered
- if (thread->is_interrupted(false)) {
- ResetEvent(_ParkHandle);
- return;
+ if (UseModernSynchAPI) {
+ // Don't wait if interrupted or already triggered
+ if (thread->is_interrupted(false)) {
+ Atomic::release_store(&_TargetValue, 0);
+ return;
+ } else {
+ int curHandle = Atomic::load_acquire(&_TargetValue);
+ if (curHandle > 0) {
+ // Already unparked
+ Atomic::release_store(&_TargetValue, 0);
+ return;
+ } else {
+ ThreadBlockInVM tbivm(thread);
+ OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
+
+ // Spurios wakeups are fine as per Unsafe.park() promise
+ ::WaitOnAddress(&_TargetValue, &curHandle, sizeof(_TargetValue), time);
+ Atomic::release_store(&_TargetValue, 0);
+ }
+ }
} else {
- DWORD rv = WaitForSingleObject(_ParkHandle, 0);
- assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
- assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed with return value: %lu", rv);
- if (rv == WAIT_OBJECT_0) {
+ guarantee(_ParkHandle != nullptr, "invariant");
+ // Don't wait if interrupted or already triggered
+ if (thread->is_interrupted(false)) {
ResetEvent(_ParkHandle);
return;
} else {
- ThreadBlockInVM tbivm(thread);
- OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
-
- rv = WaitForSingleObject(_ParkHandle, time);
+ DWORD rv = WaitForSingleObject(_ParkHandle, 0);
assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed with return value: %lu", rv);
- ResetEvent(_ParkHandle);
+ if (rv == WAIT_OBJECT_0) {
+ ResetEvent(_ParkHandle);
+ return;
+ } else {
+ ThreadBlockInVM tbivm(thread);
+ OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
+
+ rv = WaitForSingleObject(_ParkHandle, time);
+ assert(rv != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
+ assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed with return value: %lu", rv);
+ ResetEvent(_ParkHandle);
+ }
}
}
}
void Parker::unpark() {
- guarantee(_ParkHandle != nullptr, "invariant");
- SetEvent(_ParkHandle);
+ if (UseModernSynchAPI) {
+ Atomic::release_store(&_TargetValue, 1);
+ ::WakeByAddressAll(&_TargetValue);
+ } else {
+ guarantee(_ParkHandle != nullptr, "invariant");
+ SetEvent(_ParkHandle);
+ }
}
// Platform Mutex/Monitor implementation
diff --git a/src/hotspot/os/windows/park_windows.hpp b/src/hotspot/os/windows/park_windows.hpp
index 41bf18b39f9e..40e590ce7234 100644
--- a/src/hotspot/os/windows/park_windows.hpp
+++ b/src/hotspot/os/windows/park_windows.hpp
@@ -41,8 +41,10 @@ class PlatformEvent : public CHeapObj {
public:
PlatformEvent() {
_Event = 0 ;
- _ParkHandle = CreateEvent (nullptr, false, false, nullptr) ;
- guarantee (_ParkHandle != nullptr, "invariant") ;
+ if (!UseModernSynchAPI) {
+ _ParkHandle = CreateEvent (nullptr, false, false, nullptr) ;
+ guarantee (_ParkHandle != nullptr, "invariant") ;
+ }
}
// Exercise caution using reset() and fired() - they may require MEMBARs
@@ -57,16 +59,23 @@ class PlatformEvent : public CHeapObj {
class PlatformParker {
NONCOPYABLE(PlatformParker);
- protected:
+protected:
HANDLE _ParkHandle;
+ int _TargetValue;
- public:
+public:
PlatformParker() {
- _ParkHandle = CreateEvent (nullptr, true, false, nullptr) ;
- guarantee(_ParkHandle != nullptr, "invariant") ;
+ if (UseModernSynchAPI) {
+ _TargetValue = 0;
+ } else {
+ _ParkHandle = CreateEvent (nullptr, true, false, nullptr) ;
+ guarantee(_ParkHandle != nullptr, "invariant") ;
+ }
}
~PlatformParker() {
- CloseHandle(_ParkHandle);
+ if (! UseModernSynchAPI) {
+ CloseHandle(_ParkHandle);
+ }
}
};
diff --git a/src/hotspot/os/windows/threadCritical_windows.cpp b/src/hotspot/os/windows/threadCritical_windows.cpp
index 35aa0839089b..8b92d8255209 100644
--- a/src/hotspot/os/windows/threadCritical_windows.cpp
+++ b/src/hotspot/os/windows/threadCritical_windows.cpp
@@ -38,6 +38,8 @@ static INIT_ONCE initialized = INIT_ONCE_STATIC_INIT;
static int lock_count = 0;
static HANDLE lock_event;
static DWORD lock_owner = 0;
+static CRITICAL_SECTION critical_section;
+
//
// Note that Microsoft's critical region code contains a race
@@ -51,8 +53,13 @@ static DWORD lock_owner = 0;
//
static BOOL WINAPI initialize(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) {
- lock_event = CreateEvent(nullptr, false, true, nullptr);
- assert(lock_event != nullptr, "unexpected return value from CreateEvent");
+ if (UseCriticalSection) {
+ bool success = InitializeCriticalSectionAndSpinCount(&critical_section, 0x00000400);
+ assert(success, "unexpected return value from InitializeCriticalSectionAndSpinCount");
+ } else {
+ lock_event = CreateEvent(nullptr, false, true, nullptr);
+ assert(lock_event != nullptr, "unexpected return value from CreateEvent");
+ }
return true;
}
@@ -61,10 +68,14 @@ ThreadCritical::ThreadCritical() {
DWORD current_thread = GetCurrentThreadId();
if (lock_owner != current_thread) {
- // Grab the lock before doing anything.
- DWORD ret = WaitForSingleObject(lock_event, INFINITE);
- assert(ret != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
- assert(ret == WAIT_OBJECT_0, "WaitForSingleObject failed with return value: %lu", ret);
+ if (UseCriticalSection) {
+ EnterCriticalSection(&critical_section);
+ } else {
+ // Grab the lock before doing anything.
+ DWORD ret = WaitForSingleObject(lock_event, INFINITE);
+ assert(ret != WAIT_FAILED, "WaitForSingleObject failed with error code: %lu", GetLastError());
+ assert(ret == WAIT_OBJECT_0, "WaitForSingleObject failed with return value: %lu", ret);
+ }
lock_owner = current_thread;
}
// Atomicity isn't required. Bump the recursion count.
@@ -79,8 +90,12 @@ ThreadCritical::~ThreadCritical() {
if (lock_count == 0) {
// We're going to unlock
lock_owner = 0;
- // No lost wakeups, lock_event stays signaled until reset.
- DWORD ret = SetEvent(lock_event);
- assert(ret != 0, "unexpected return value from SetEvent");
+ if (UseCriticalSection) {
+ LeaveCriticalSection(&critical_section);
+ } else {
+ // No lost wakeups, lock_event stays signaled until reset.
+ DWORD ret = SetEvent(lock_event);
+ assert(ret != 0, "unexpected return value from SetEvent");
+ }
}
}
diff --git a/src/hotspot/share/c1/c1_Instruction.cpp b/src/hotspot/share/c1/c1_Instruction.cpp
index 92affba99b24..45e851b26260 100644
--- a/src/hotspot/share/c1/c1_Instruction.cpp
+++ b/src/hotspot/share/c1/c1_Instruction.cpp
@@ -809,6 +809,19 @@ bool BlockBegin::try_merge(ValueStack* new_state, bool has_irreducible_loops) {
}
}
+ for_each_stack_value(existing_state, index, existing_value) {
+ if ( !(existing_value->as_Phi() != nullptr && existing_value->as_Phi()->block() == this)) {
+ TRACE_PHI(tty->print_cr("Re-entered loop head without existing phi for stack - bailout"));
+ return false;
+ }
+ }
+ for_each_local_value(existing_state, index, existing_value) {
+ if (!(existing_value == new_state->local_at(index) || (existing_value->as_Phi() != nullptr && existing_value->as_Phi()->as_Phi()->block() == this))) {
+ TRACE_PHI(tty->print_cr("Re-entered loop head without existing phi for modified local - bailout"));
+ return false;
+ }
+ }
+
#ifdef ASSERT
// check that all necessary phi functions are present
for_each_stack_value(existing_state, index, existing_value) {
diff --git a/src/hotspot/share/cds/cdsProtectionDomain.cpp b/src/hotspot/share/cds/cdsProtectionDomain.cpp
index dc3d8621db1b..8600afbf2cbd 100644
--- a/src/hotspot/share/cds/cdsProtectionDomain.cpp
+++ b/src/hotspot/share/cds/cdsProtectionDomain.cpp
@@ -219,6 +219,17 @@ oop CDSProtectionDomain::to_file_URL(const char* path, Handle url_h, TRAPS) {
return result.get_oop();
}
+// https://bugs.openjdk.org/browse/JDK-8324259 -- the AppCDS archive must not contain any class from java.base.
+// Make sure that new classes loaded when jbr.java.io.use.nio=true do make into the archive.
+void CDSProtectionDomain::cds_preload_helper_init(TRAPS) {
+ JavaValue result(T_VOID);
+ JavaCalls::call_static(&result,
+ vmClasses::jdk_internal_misc_CDSPreloadHelper_klass(),
+ vmSymbols::CDSPreloadHelper_init_name(),
+ vmSymbols::void_method_signature(),
+ CATCH);
+}
+
// Get the ProtectionDomain associated with the CodeSource from the classloader.
Handle CDSProtectionDomain::get_protection_domain_from_classloader(Handle class_loader,
Handle url, TRAPS) {
diff --git a/src/hotspot/share/cds/cdsProtectionDomain.hpp b/src/hotspot/share/cds/cdsProtectionDomain.hpp
index 8eaec96f9fd4..246eebadf839 100644
--- a/src/hotspot/share/cds/cdsProtectionDomain.hpp
+++ b/src/hotspot/share/cds/cdsProtectionDomain.hpp
@@ -81,6 +81,7 @@ class CDSProtectionDomain : AllStatic {
static Handle get_shared_jar_manifest(int shared_path_index, TRAPS);
static Handle get_shared_jar_url(int shared_path_index, TRAPS);
static oop to_file_URL(const char* path, Handle url_h, TRAPS);
+ static void cds_preload_helper_init(TRAPS);
static Handle get_protection_domain_from_classloader(Handle class_loader,
Handle url, TRAPS);
static Handle get_shared_protection_domain(Handle class_loader,
diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp
index 6fe1c820becb..1bd0053208f8 100644
--- a/src/hotspot/share/cds/metaspaceShared.cpp
+++ b/src/hotspot/share/cds/metaspaceShared.cpp
@@ -912,6 +912,7 @@ void MetaspaceShared::exercise_runtime_cds_code(TRAPS) {
// Exercise FileSystem and URL code
CDSProtectionDomain::to_file_URL("dummy.jar", Handle(), CHECK);
+ CDSProtectionDomain::cds_preload_helper_init(CHECK);
}
void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS) {
diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
index fca6a9e74ad3..52934cb46dcf 100644
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
@@ -46,6 +46,7 @@
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
+#include "utilities/vmError.hpp"
volatile size_t ClassLoaderDataGraph::_num_array_classes = 0;
volatile size_t ClassLoaderDataGraph::_num_instance_classes = 0;
@@ -185,7 +186,8 @@ ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool has_class_mirror_
inline void assert_is_safepoint_or_gc() {
assert(SafepointSynchronize::is_at_safepoint() ||
Thread::current()->is_ConcurrentGC_thread() ||
- Thread::current()->is_Worker_thread(),
+ Thread::current()->is_Worker_thread() ||
+ VMError::is_error_reported(), // don't crash here again if we have already crashed
"Must be called by safepoint or GC");
}
diff --git a/src/hotspot/share/classfile/vmClassMacros.hpp b/src/hotspot/share/classfile/vmClassMacros.hpp
index 351b9b0b53f2..05127ebe8fee 100644
--- a/src/hotspot/share/classfile/vmClassMacros.hpp
+++ b/src/hotspot/share/classfile/vmClassMacros.hpp
@@ -143,6 +143,7 @@
do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders ) \
do_klass(jdk_internal_loader_ClassLoaders_AppClassLoader_klass, jdk_internal_loader_ClassLoaders_AppClassLoader) \
do_klass(jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass, jdk_internal_loader_ClassLoaders_PlatformClassLoader) \
+ do_klass(jdk_internal_misc_CDSPreloadHelper_klass, jdk_internal_misc_CDSPreloadHelper ) \
do_klass(CodeSource_klass, java_security_CodeSource ) \
do_klass(ConcurrentHashMap_klass, java_util_concurrent_ConcurrentHashMap ) \
do_klass(ArrayList_klass, java_util_ArrayList ) \
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index e66066738ef3..037e4f7a4f27 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -738,6 +738,8 @@ class SerializeClosure;
template(runtimeSetup, "runtimeSetup") \
template(toFileURL_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
+ template(jdk_internal_misc_CDSPreloadHelper, "jdk/internal/misc/CDSPreloadHelper") \
+ template(CDSPreloadHelper_init_name, "init") \
\
/* jcmd Thread.dump_to_file */ \
template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 4c0436ad1e22..1f0fd2122554 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -1853,6 +1853,13 @@ bool G1CollectedHeap::try_collect_fullgc(GCCause::Cause cause,
const G1GCCounters& counters_before) {
assert_heap_not_locked();
+ if (cause == GCCause::_jbr_gc_run) {
+ VM_G1CollectForAllocation op(0, // no following allocation
+ counters_before.total_collections(),
+ cause);
+ VMThread::execute(&op);
+ }
+
while(true) {
VM_G1CollectFull op(counters_before.total_collections(),
counters_before.total_full_collections(),
diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
index c35ce7c356d2..38792fb97fa5 100644
--- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
@@ -233,8 +233,13 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
// results.
_g1h->eden_regions_count() * G1HeapRegion::GrainBytes;
+ uintx max_heap_free_ratio = MaxHeapFreeRatio;
+ if (_g1h->gc_cause() == GCCause::_jbr_shrinking_gc_run) {
+ max_heap_free_ratio = MAX2(MinHeapFreeRatio, MIN2(JbrShrinkingGcMaxHeapFreeRatio, max_heap_free_ratio));
+ }
+
size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio);
- size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio);
+ size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, max_heap_free_ratio);
// This assert only makes sense here, before we adjust them
// with respect to the min and max heap size.
@@ -271,7 +276,7 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity). "
"Capacity: %zuB occupancy: %zuB live: %zuB "
"maximum_desired_capacity: %zuB (%zu %%)",
- capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, MaxHeapFreeRatio);
+ capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, max_heap_free_ratio);
expand = false;
return shrink_bytes;
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp
index 9552bd6d4181..8350de4a8c60 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.cpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp
@@ -50,8 +50,13 @@ bool VM_G1CollectFull::skip_operation() const {
void VM_G1CollectFull::doit() {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
GCCauseSetter x(g1h, _gc_cause);
- _gc_succeeded = g1h->do_full_collection(false /* clear_all_soft_refs */,
- false /* do_maximal_compaction */);
+ if (_gc_cause == GCCause::_jbr_gc_run) {
+ _gc_succeeded = g1h->do_full_collection(true /* clear_all_soft_refs */,
+ true /* do_maximal_compaction */);
+ } else {
+ _gc_succeeded = g1h->do_full_collection(false /* clear_all_soft_refs */,
+ false /* do_maximal_compaction */);
+ }
}
VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before,
diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp
index fcf57909a032..517c33665948 100644
--- a/src/hotspot/share/gc/serial/serialHeap.cpp
+++ b/src/hotspot/share/gc/serial/serialHeap.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "gc/shared/gcCause.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
@@ -383,7 +384,8 @@ HeapWord* SerialHeap::mem_allocate(size_t size,
bool SerialHeap::must_clear_all_soft_refs() {
return _gc_cause == GCCause::_metadata_GC_clear_soft_refs ||
- _gc_cause == GCCause::_wb_full_gc;
+ _gc_cause == GCCause::_wb_full_gc ||
+ _gc_cause == GCCause::_jbr_gc_run;
}
bool SerialHeap::is_young_gc_safe() const {
diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
index 31a224597a04..1350f588ee80 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -278,7 +278,7 @@ class CollectedHeap : public CHeapObj {
DEBUG_ONLY(bool is_in_or_null(const void* p) const { return p == nullptr || is_in(p); })
void set_gc_cause(GCCause::Cause v);
- GCCause::Cause gc_cause() { return _gc_cause; }
+ GCCause::Cause gc_cause() const { return _gc_cause; }
oop obj_allocate(Klass* klass, size_t size, TRAPS);
virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS);
diff --git a/src/hotspot/share/gc/shared/gcCause.cpp b/src/hotspot/share/gc/shared/gcCause.cpp
index 51ce73861e08..99055b014ac1 100644
--- a/src/hotspot/share/gc/shared/gcCause.cpp
+++ b/src/hotspot/share/gc/shared/gcCause.cpp
@@ -122,6 +122,12 @@ const char* GCCause::to_string(GCCause::Cause cause) {
case _last_gc_cause:
return "ILLEGAL VALUE - last gc cause - ILLEGAL VALUE";
+ case _jbr_gc_run:
+ return "JBR full GC";
+
+ case _jbr_shrinking_gc_run:
+ return "JBR shrinking GC";
+
default:
return "unknown GCCause";
}
diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp
index ef96bf21567b..122564f56ef9 100644
--- a/src/hotspot/share/gc/shared/gcCause.hpp
+++ b/src/hotspot/share/gc/shared/gcCause.hpp
@@ -71,6 +71,8 @@ class GCCause : public AllStatic {
_g1_periodic_collection,
_dcmd_gc_run,
+ _jbr_gc_run,
+ _jbr_shrinking_gc_run,
_shenandoah_stop_vm,
_shenandoah_allocation_failure_evac,
@@ -90,7 +92,9 @@ class GCCause : public AllStatic {
inline static bool is_user_requested_gc(GCCause::Cause cause) {
return (cause == GCCause::_java_lang_system_gc ||
- cause == GCCause::_dcmd_gc_run);
+ cause == GCCause::_dcmd_gc_run ||
+ cause == GCCause::_jbr_gc_run ||
+ cause == GCCause::_jbr_shrinking_gc_run);
}
inline static bool is_explicit_full_gc(GCCause::Cause cause) {
diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp
index c43090f9699f..a79896774b06 100644
--- a/src/hotspot/share/gc/z/zCollectedHeap.cpp
+++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp
@@ -190,6 +190,8 @@ void ZCollectedHeap::collect(GCCause::Cause cause) {
case GCCause::_wb_full_gc:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
+ case GCCause::_jbr_gc_run:
+ case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
diff --git a/src/hotspot/share/gc/z/zDriver.cpp b/src/hotspot/share/gc/z/zDriver.cpp
index e8f7af179cb4..3d8c321276ab 100644
--- a/src/hotspot/share/gc/z/zDriver.cpp
+++ b/src/hotspot/share/gc/z/zDriver.cpp
@@ -235,12 +235,14 @@ static bool should_clear_all_soft_references(GCCause::Cause cause) {
case GCCause::_wb_full_gc:
case GCCause::_metadata_GC_clear_soft_refs:
case GCCause::_z_allocation_stall:
+ case GCCause::_jbr_gc_run:
return true;
case GCCause::_heap_dump:
case GCCause::_heap_inspection:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
+ case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
@@ -275,6 +277,8 @@ static bool should_preclean_young(GCCause::Cause cause) {
case GCCause::_wb_full_gc:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
+ case GCCause::_jbr_gc_run:
+ case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
@@ -337,6 +341,8 @@ void ZDriverMajor::collect(const ZDriverRequest& request) {
case GCCause::_heap_inspection:
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
+ case GCCause::_jbr_gc_run:
+ case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
diff --git a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
index c18248e10566..a821fe14afac 100644
--- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
+++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
@@ -112,10 +112,12 @@ class JfrNetworkInterfaceName : public JfrSerializer {
void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
void on_rotation() {
- for (int i = 0; i < _interfaces->length(); ++i) {
- const InterfaceEntry& entry = _interfaces->at(i);
- if (entry.written) {
- entry.written = false;
+ if (_interfaces != nullptr) {
+ for (int i = 0; i < _interfaces->length(); ++i) {
+ const InterfaceEntry &entry = _interfaces->at(i);
+ if (entry.written) {
+ entry.written = false;
+ }
}
}
}
diff --git a/src/hotspot/share/memory/metaspace/runningCounters.cpp b/src/hotspot/share/memory/metaspace/runningCounters.cpp
index db497cf33872..a6a2fad224a7 100644
--- a/src/hotspot/share/memory/metaspace/runningCounters.cpp
+++ b/src/hotspot/share/memory/metaspace/runningCounters.cpp
@@ -41,8 +41,8 @@ size_t RunningCounters::reserved_words_class() {
}
size_t RunningCounters::reserved_words_nonclass() {
- assert(VirtualSpaceList::vslist_nonclass() != nullptr, "Metaspace not yet initialized");
- return VirtualSpaceList::vslist_nonclass()->reserved_words();
+ VirtualSpaceList* vs = VirtualSpaceList::vslist_nonclass();
+ return vs != nullptr ? vs->reserved_words() : 0;
}
// Return total committed size, in words, for Metaspace
diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
index fe9c960b7f23..86b8b58f7528 100644
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -87,6 +87,7 @@
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#include "utilities/preserveException.hpp"
+#include "utilities/vmError.hpp"
// A helper class for caching a Method* when the user of the cache
// only cares about the latest version of the Method*. This cache safely
@@ -917,6 +918,8 @@ jint universe_init() {
ResolvedMethodTable::create_table();
+ VMError::init();
+
return JNI_OK;
}
diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp
index 517808a115f0..f31d8649610b 100644
--- a/src/hotspot/share/prims/jni.cpp
+++ b/src/hotspot/share/prims/jni.cpp
@@ -530,6 +530,22 @@ JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message))
InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)));
Symbol* name = k->name();
+ if (name->equals("java/lang/Exception$JB$$Assertion")) {
+ // A special exception name to trigger a fatal error
+ report_fatal(INTERNAL_ERROR, "", 0, "%s", message);
+ ShouldNotReachHere();
+ return 0;
+ } else if (name->equals("java/lang/Exception$JB$$Event")) {
+ Events::log(THREAD, "%s", message);
+ return 0;
+ } else if (name->equals("java/lang/Exception$JB$$FullGC")) {
+ Universe::heap()->collect(GCCause::_jbr_gc_run);
+ return 0;
+ } else if (name->equals("java/lang/Exception$JB$$ShrinkingGC")) {
+ Universe::heap()->collect(GCCause::_jbr_shrinking_gc_run);
+ return 0;
+ }
+
Handle class_loader (THREAD, k->class_loader());
THROW_MSG_LOADER_(name, (char *)message, class_loader, JNI_OK);
ShouldNotReachHere();
@@ -3525,6 +3541,19 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) {
return ret;
}
+#ifdef LINUX
+static void unsetLdPreload() {
+ // Workaround for: JBR-2253 Preload libjsig.so to fix JNA crashes
+ // NOTE: Probably it still can affect child processes, see
+ // https://stackoverflow.com/questions/3275015/ld-preload-affects-new-child-even-after-unsetenvld-preload
+ char * envUnset = ::getenv("UNSET_LD_PRELOAD");
+ if (envUnset != nullptr && strlen(envUnset) > 3 && strncmp("true", envUnset, 4) == 0) {
+ unsetenv("LD_PRELOAD");
+ fprintf(stdout, "LD_PRELOAD was unset\n");
+ }
+}
+#endif //LINUX
+
DT_RETURN_MARK_DECL(CreateJavaVM, jint
, HOTSPOT_JNI_CREATEJAVAVM_RETURN(_ret_ref));
@@ -3631,6 +3660,10 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
// Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
ThreadStateTransition::transition_from_vm(thread, _thread_in_native);
MACOS_AARCH64_ONLY(thread->enable_wx(WXExec));
+
+#ifdef LINUX
+ unsetLdPreload();
+#endif //LINUX
} else {
// If create_vm exits because of a pending exception, exit with that
// exception. In the future when we figure out how to reclaim memory,
diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp
index aa158490eabd..9187a15fc889 100644
--- a/src/hotspot/share/prims/jniCheck.cpp
+++ b/src/hotspot/share/prims/jniCheck.cpp
@@ -504,6 +504,12 @@ void jniCheck::validate_throwable_klass(JavaThread* thr, Klass* klass) {
ASSERT_OOPS_ALLOWED;
assert(klass != nullptr, "klass argument must have a value");
+ if (klass->name()->index_of_at(0, "/Exception$JB$$", (int) strlen("/Exception$JB$$")) > 0) {
+ // This is a special "marker" class that is never really thrown and
+ // therefore it does not have to be Throwable.
+ return;
+ }
+
if (!klass->is_instance_klass() ||
!klass->is_subclass_of(vmClasses::Throwable_klass())) {
ReportJNIFatalError(thr, fatal_class_not_a_throwable_class);
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 343940f086df..61154bbb8d4d 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -3180,34 +3180,30 @@ JVM_LEAF(void*, JVM_LoadZipLibrary())
return ZipLibrary::handle();
JVM_END
-JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name, jboolean throwException))
+JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* utf8_name, jboolean throwException))
//%note jvm_ct
char ebuf[1024];
void *load_result;
{
ThreadToNativeFromVM ttnfvm(thread);
- load_result = os::dll_load(name, ebuf, sizeof ebuf);
+ load_result = os::dll_load_utf8(utf8_name, ebuf, sizeof ebuf);
}
if (load_result == nullptr) {
if (throwException) {
char msg[1024];
- jio_snprintf(msg, sizeof msg, "%s: %s", name, ebuf);
- // Since 'ebuf' may contain a string encoded using
- // platform encoding scheme, we need to pass
- // Exceptions::unsafe_to_utf8 to the new_exception method
- // as the last argument. See bug 6367357.
+ jio_snprintf(msg, sizeof msg, "%s: %s", utf8_name, ebuf);
Handle h_exception =
Exceptions::new_exception(thread,
vmSymbols::java_lang_UnsatisfiedLinkError(),
- msg, Exceptions::unsafe_to_utf8);
+ msg);
THROW_HANDLE_NULL(h_exception);
} else {
- log_info(library)("Failed to load library %s", name);
+ log_info(library)("Failed to load library %s", utf8_name);
return load_result;
}
}
- log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, name, p2i(load_result));
+ log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, utf8_name, p2i(load_result));
return load_result;
JVM_END
diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp
index 05cccd4fe456..22c83e9acb2d 100644
--- a/src/hotspot/share/runtime/abstract_vm_version.cpp
+++ b/src/hotspot/share/runtime/abstract_vm_version.cpp
@@ -127,7 +127,7 @@ const char* Abstract_VM_Version::vm_name() {
# define VENDOR_PADDING 64
#endif
#ifndef VENDOR
-# define VENDOR "Oracle Corporation"
+# define VENDOR "JetBrains s.r.o."
#endif
static const char vm_vendor_string[sizeof(VENDOR) < VENDOR_PADDING ? VENDOR_PADDING : sizeof(VENDOR)] = VENDOR;
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index c6651c46e029..6774ea29dded 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -36,6 +36,7 @@
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "jvm.h"
+#include "jfr/periodic/jfrOSInterface.hpp"
#include "logging/log.hpp"
#include "logging/logConfiguration.hpp"
#include "logging/logStream.hpp"
@@ -417,6 +418,30 @@ void Arguments::init_system_properties() {
os::init_system_properties_values();
}
+static const char * get_virtualization_type() {
+ VirtualizationType vrt = VM_Version::get_detected_virtualization();
+ if (vrt == XenHVM || vrt == XenPVHVM) {
+ return "Xen";
+ } else if (vrt == KVM) {
+ return "KVM";
+ } else if (vrt == VMWare) {
+ return "VMWare";
+ } else if (vrt == HyperV) {
+ return "HyperV";
+ } else if (vrt == HyperVRole) {
+ // Enables users to create and manage virtual machines (VMs) on a physical host machine,
+ // but doesn't mean we are running in virtual.
+ return "none";
+ }
+
+ return "none";
+}
+
+void Arguments::post_init_system_properties() {
+ const char *virtualization_type = get_virtualization_type();
+ PropertyList_add(&_system_properties, new SystemProperty("intellij.os.virtualization", virtualization_type, false));
+}
+
// Update/Initialize System properties after JDK version number is known
void Arguments::init_version_specific_system_properties() {
enum { bufsz = 16 };
@@ -1299,6 +1324,8 @@ bool Arguments::add_property(const char* prop, PropertyWriteable writeable, Prop
if (old_java_vendor_url_bug != nullptr) {
os::free((void *)old_java_vendor_url_bug);
}
+ } else if (strcmp(key, "jbr.catch.SIGABRT") == 0) {
+ CatchSIGABRT = (strcmp(value, "true") == 0);
}
// Create new property and add at the end of the list
@@ -3976,3 +4003,10 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
*b = '\0';
return (p == src_end); // return false if not all of the source was copied
}
+
+void Arguments::add_virtualization_information_property()
+{
+ const char *virt_name = "undetected";
+ JFR_ONLY(virt_name = JfrOSInterface::virtualization_name();)
+ PropertyList_add(&_system_properties, new SystemProperty("jbr.virtualization.information", virt_name, false));
+}
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 741c3e18082a..bbfb6f2fd7aa 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -445,6 +445,8 @@ class Arguments : AllStatic {
// System properties
static void init_system_properties();
+ static void post_init_system_properties();
+
// Update/Initialize System properties after JDK version number is known
static void init_version_specific_system_properties();
@@ -453,6 +455,8 @@ class Arguments : AllStatic {
_vm_info->set_value(vm_info);
}
+ static void add_virtualization_information_property();
+
// Property List manipulation
static void PropertyList_add(SystemProperty *element);
static void PropertyList_add(SystemProperty** plist, SystemProperty *element);
diff --git a/src/hotspot/share/runtime/globals.cpp b/src/hotspot/share/runtime/globals.cpp
index ef3764debca9..d658e87353ea 100644
--- a/src/hotspot/share/runtime/globals.cpp
+++ b/src/hotspot/share/runtime/globals.cpp
@@ -41,3 +41,5 @@ ALL_FLAGS(MATERIALIZE_DEVELOPER_FLAG,
MATERIALIZE_PD_PRODUCT_FLAG,
IGNORE_RANGE,
IGNORE_CONSTRAINT)
+
+MATERIALIZE_PRODUCT_FLAG(bool, CatchSIGABRT, false);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index ab975b1b375c..89ff3c6cb463 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -1423,6 +1423,14 @@ const int ObjectAlignmentInBytes = 8;
range(0, 100) \
constraint(MaxHeapFreeRatioConstraintFunc,AfterErgo) \
\
+ product(uintx, JbrShrinkingGcMaxHeapFreeRatio, 100, MANAGEABLE, \
+ "The maximum percentage of heap free after GC to avoid shrinking" \
+ " when using JBR Shrinking GC run." \
+ " The value is clamped between MinHeapFreeRatio" \
+ " and MaxHeapFreeRatio." \
+ " Defaults to MaxHeapFreeRatio option value.") \
+ range(0, 100) \
+ \
product(intx, SoftRefLRUPolicyMSPerMB, 1000, \
"Number of milliseconds per MB of free space in the heap") \
range(0, max_intx) \
@@ -2011,4 +2019,6 @@ DECLARE_ARCH_FLAGS(ARCH_FLAGS)
DECLARE_FLAGS(RUNTIME_FLAGS)
DECLARE_FLAGS(RUNTIME_OS_FLAGS)
+DECLARE_PRODUCT_FLAG(bool, CatchSIGABRT, false);
+
#endif // SHARE_RUNTIME_GLOBALS_HPP
diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp
index 417bba6c3fa0..698dc023b053 100644
--- a/src/hotspot/share/runtime/init.cpp
+++ b/src/hotspot/share/runtime/init.cpp
@@ -34,6 +34,7 @@
#include "prims/downcallLinker.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/methodHandles.hpp"
+#include "runtime/arguments.hpp"
#include "runtime/atomic.hpp"
#include "runtime/continuation.hpp"
#include "runtime/flags/jvmFlag.hpp"
@@ -134,6 +135,8 @@ jint init_globals() {
if (status != JNI_OK)
return status;
+ Arguments::post_init_system_properties();
+
#ifdef LEAK_SANITIZER
{
// Register the Java heap with LSan.
diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp
index b4fc32947dd3..7e05a5f29de5 100644
--- a/src/hotspot/share/runtime/jniHandles.cpp
+++ b/src/hotspot/share/runtime/jniHandles.cpp
@@ -257,6 +257,23 @@ void JNIHandles::print_on(outputStream* st) {
st->flush();
}
+// Called while generating crash log, possibly not at safepoint
+void JNIHandles::print_on_unsafe(outputStream* st) {
+ st->print_cr("JNI global refs: %zu, weak refs: %zu",
+ global_handles() != nullptr ? global_handles()->allocation_count() : 0,
+ weak_global_handles() != nullptr ? weak_global_handles()->allocation_count() : 0);
+ st->cr();
+ st->flush();
+}
+
+void JNIHandles::print_memory_usage_on(outputStream *st) {
+ st->print_cr("JNI global refs memory usage: %zu, weak refs: %zu",
+ global_handles() != nullptr ? global_handles()->total_memory_usage() : 0,
+ global_handles() != nullptr ? weak_global_handles()->total_memory_usage() : 0);
+ st->cr();
+ st->flush();
+}
+
void JNIHandles::print() { print_on(tty); }
class VerifyJNIHandles: public OopClosure {
diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp
index 230a3f5ca428..66e440cb8f19 100644
--- a/src/hotspot/share/runtime/jniHandles.hpp
+++ b/src/hotspot/share/runtime/jniHandles.hpp
@@ -110,6 +110,8 @@ class JNIHandles : AllStatic {
// Debugging
static void print_on(outputStream* st);
+ static void print_on_unsafe(outputStream* st);
+ static void print_memory_usage_on(outputStream* st);
static void print();
static void verify();
// The category predicates all require handle != nullptr.
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
index 2733cbcb14c2..32771ab01ce0 100644
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -154,6 +154,8 @@ Monitor* JVMCI_lock = nullptr;
Monitor* JVMCIRuntime_lock = nullptr;
#endif
+Mutex* OOMEStacks_lock = nullptr;
+
// Only one RecursiveMutex
RecursiveMutex* MultiArray_lock = nullptr;
@@ -344,6 +346,7 @@ void mutex_init() {
#endif
MUTEX_DEFL(JvmtiThreadState_lock , PaddedMutex , JvmtiVTMSTransition_lock); // Used by JvmtiThreadState/JvmtiEventController
MUTEX_DEFL(SharedDecoder_lock , PaddedMutex , NmtVirtualMemory_lock); // Must be lower than NmtVirtualMemory_lock due to MemTracker::print_containing_region
+ MUTEX_DEFN(OOMEStacks_lock , PaddedMutex , safepoint-1);
// Allocate RecursiveMutex
MultiArray_lock = new RecursiveMutex();
diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
index aa8850b8b63d..54ab021f8cc8 100644
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -157,6 +157,8 @@ extern Mutex* Bootclasspath_lock;
extern Mutex* tty_lock; // lock to synchronize output.
+extern Mutex* OOMEStacks_lock;
+
// A MutexLocker provides mutual exclusion with respect to a given mutex
// for the scope which contains the locker. The lock is an OS lock, not
// an object lock, and the two do not interoperate. Do not use Mutex-based
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index be9b0eef4553..592e2f2e3c15 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -1007,6 +1007,7 @@ static void print_hex_location(outputStream* st, const_address p, int unitsize,
}
}
+ATTRIBUTE_NO_SANITIZE_ADDRESS("Memory is read raw here without any regard for objects' boundaries")
void os::print_hex_dump(outputStream* st, const_address start, const_address end, int unitsize,
bool print_ascii, int bytes_per_line, const_address logical_start, const_address highlight_address) {
constexpr int max_bytes_per_line = 64;
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index 242959034afb..a39ba1b5c7e2 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -763,6 +763,7 @@ class os: AllStatic {
// same architecture as HotSpot is running on
// in case of an error null is returned and an error message is stored in ebuf
static void* dll_load(const char *name, char *ebuf, int ebuflen);
+ static void* dll_load_utf8(const char *name, char *ebuf, int ebuflen);
// lookup symbol in a shared library
static void* dll_lookup(void* handle, const char* name);
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 32859bf27186..7f29df601fbd 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -609,6 +609,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
JFR_ONLY(Jfr::on_create_vm_1();)
+ Arguments::add_virtualization_information_property();
+
// Should be done after the heap is fully created
main_thread->cache_global_variables();
diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp
index da833150d746..fc156dfde444 100644
--- a/src/hotspot/share/runtime/vmOperations.cpp
+++ b/src/hotspot/share/runtime/vmOperations.cpp
@@ -26,6 +26,7 @@
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/vmSymbols.hpp"
+#include "classfile/symbolTable.hpp"
#include "code/codeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "gc/shared/collectedHeap.hpp"
@@ -38,6 +39,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/symbol.hpp"
+#include "oops/symbolHandle.hpp"
#include "runtime/arguments.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/frame.inline.hpp"
@@ -51,7 +53,7 @@
#include "runtime/threadSMR.inline.hpp"
#include "runtime/vmOperations.hpp"
#include "services/threadService.hpp"
-#include "utilities/ticks.hpp"
+#include "javaCalls.hpp"
#define VM_OP_NAME_INITIALIZE(name) #name,
@@ -169,6 +171,37 @@ void VM_ZombieAll::doit() {
#endif // !PRODUCT
+
+// Prints out additional information supplied by the application
+// through the use of JBR API. The data in the form of a String
+// are obtained from Throwable.$$jb$getAdditionalInfoForJstack()
+// and, not not null, are included into the output.
+void VM_PrintThreads::print_additional_info() {
+ JavaThread *THREAD = JavaThread::current();
+ HandleMark hm(THREAD);
+ ResourceMark rm;
+
+ InstanceKlass *klass = vmClasses::Throwable_klass();
+ if (klass != nullptr) {
+ TempNewSymbol method_name = SymbolTable::new_symbol("$$jb$getAdditionalInfoForJstack");
+ Symbol *signature = vmSymbols::void_string_signature();
+ Method *method = klass->find_method(method_name, signature);
+ if (method != nullptr) {
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_static(&result, klass,
+ method_name, signature, THREAD);
+ oop dump_oop = result.get_oop();
+ if (dump_oop != nullptr) {
+ // convert Java String to utf8 string
+ char *s = java_lang_String::as_utf8_string(dump_oop);
+ _out->cr();
+ _out->print_raw_cr(s);
+ _out->cr();
+ }
+ }
+ }
+}
+
bool VM_PrintThreads::doit_prologue() {
// Get Heap_lock if concurrent locks will be dumped
if (_print_concurrent_locks) {
@@ -181,6 +214,7 @@ void VM_PrintThreads::doit() {
Threads::print_on(_out, true, false, _print_concurrent_locks, _print_extended_info);
if (_print_jni_handle_info) {
JNIHandles::print_on(_out);
+ JNIHandles::print_memory_usage_on(_out);
}
}
@@ -189,6 +223,13 @@ void VM_PrintThreads::doit_epilogue() {
// Release Heap_lock
Heap_lock->unlock();
}
+
+ // We should be on the "signal handler" thread, which is a JavaThread
+ if (Thread::current()->is_Java_thread()) {
+ // .. but best play it safe as we're going to need to make
+ // Java calls on the current thread.
+ print_additional_info();
+ }
}
void VM_PrintMetadata::doit() {
diff --git a/src/hotspot/share/runtime/vmOperations.hpp b/src/hotspot/share/runtime/vmOperations.hpp
index baeea722dcef..35eba95d0811 100644
--- a/src/hotspot/share/runtime/vmOperations.hpp
+++ b/src/hotspot/share/runtime/vmOperations.hpp
@@ -169,6 +169,9 @@ class VM_PrintThreads: public VM_Operation {
void doit();
bool doit_prologue();
void doit_epilogue();
+
+private:
+ void print_additional_info();
};
class VM_PrintMetadata : public VM_Operation {
diff --git a/src/hotspot/share/utilities/compilerWarnings.hpp b/src/hotspot/share/utilities/compilerWarnings.hpp
index ddcaf022fa69..9d2772ede65d 100644
--- a/src/hotspot/share/utilities/compilerWarnings.hpp
+++ b/src/hotspot/share/utilities/compilerWarnings.hpp
@@ -107,4 +107,10 @@
#define ALLOW_C_FUNCTION(name, ...) __VA_ARGS__
#endif
+#if defined(__clang__) || defined (__GNUC__)
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS(reason_for_exclusion) __attribute__((no_sanitize_address))
+#else
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS(reason_for_exclusion)
+#endif
+
#endif // SHARE_UTILITIES_COMPILERWARNINGS_HPP
diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index 9413f1f72d7a..68a142bac7fc 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -262,6 +262,8 @@ void report_untested(const char* file, int line, const char* message) {
void report_java_out_of_memory(const char* message) {
static int out_of_memory_reported = 0;
+ VMError::record_oome_stack(message);
+
// A number of threads may attempt to report OutOfMemoryError at around the
// same time. To avoid dumping the heap or executing the data collection
// commands multiple times we just do it once when the first threads reports
diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
index 83d075572877..1bb3ae1fdf5f 100644
--- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
+++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
@@ -103,6 +103,11 @@ inline int g_isnan(jdouble f) { return _isnan(f); }
inline int g_isfinite(jfloat f) { return _finite(f); }
inline int g_isfinite(jdouble f) { return _finite(f); }
+// Miscellaneous
+
+// Visual Studio 2005 deprecates POSIX names - use ISO C++ names instead
+#define wopen _wopen
+
#define offset_of(klass,field) offsetof(klass,field)
#define THREAD_LOCAL __declspec(thread)
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index bbf1fcf9d6f4..d5e30ac51c13 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -25,6 +25,7 @@
*/
#include "cds/metaspaceShared.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
#include "code/codeCache.hpp"
#include "compiler/compilationFailureInfo.hpp"
#include "compiler/compilationMemoryStatistic.hpp"
@@ -48,6 +49,7 @@
#include "runtime/frame.inline.hpp"
#include "runtime/init.hpp"
#include "runtime/javaThread.inline.hpp"
+#include "runtime/jniHandles.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/osThread.hpp"
#include "runtime/safefetch.hpp"
@@ -81,6 +83,18 @@
#include
#endif // PRODUCT
+#ifdef LINUX
+#include "os_linux.hpp"
+#endif
+
+#ifdef _WINDOWS
+#include
+#endif
+
+#ifdef __APPLE__
+#include
+#endif
+
bool VMError::coredump_status;
char VMError::coredump_message[O_BUFLEN];
int VMError::_current_step;
@@ -593,6 +607,7 @@ void VMError::clear_step_start_time() {
// could be nested report_and_die() calls on stack (see above). Only one
// thread can report error, so large buffers are statically allocated in data
// segment.
+ATTRIBUTE_NO_SANITIZE_ADDRESS("Memory is read raw here without any regard for objects' boundaries")
void VMError::report(outputStream* st, bool _verbose) {
// Used by reattempt step logic
static int continuation = 0;
@@ -1274,6 +1289,25 @@ void VMError::report(outputStream* st, bool _verbose) {
NativeHeapTrimmer::print_state(st);
st->cr();
+ STEP("JNI global references")
+ st->print_cr("JNI global refs:");
+ JNIHandles::print_on_unsafe(st);
+ JNIHandles::print_memory_usage_on(st);
+
+ STEP_IF("Process memory usage", _verbose)
+ print_process_memory_usage(st);
+
+ STEP("OOME stack traces")
+ st->print_cr("OOME stack traces (most recent first):");
+ print_oome_stacks(st);
+
+ STEP("Classloader stats")
+ st->print_cr("Classloader memory used:");
+ FREE_C_HEAP_ARRAY(void*, _ballast_memory);
+ _ballast_memory = nullptr;
+ print_classloaders_stats(st);
+ print_dup_classes(st); // do it separately in case we're low on memory
+
STEP_IF("printing system", _verbose)
st->print_cr("--------------- S Y S T E M ---------------");
st->cr();
@@ -1456,6 +1490,7 @@ void VMError::print_vm_info(outputStream* st) {
NativeHeapTrimmer::print_state(st);
st->cr();
+ print_process_memory_usage(st);
// STEP("printing system")
st->print_cr("--------------- S Y S T E M ---------------");
@@ -2135,3 +2170,246 @@ VMErrorCallbackMark::~VMErrorCallbackMark() {
assert(_thread->_vm_error_callbacks != nullptr, "Popped too far");
_thread->_vm_error_callbacks = _thread->_vm_error_callbacks->_next;
}
+
+char VMError::_oome_stacktrace[OOME_STACKTRACE_COUNT][OOME_STACKTRACE_BUFSIZE];
+int VMError::_oome_free_index;
+void * VMError::_ballast_memory;
+
+void VMError::init() {
+ _oome_free_index = 0;
+ // This will be released before dumping classes statistics as the latter
+ // may need to allocate some memory in an OOME situation.
+ _ballast_memory = NEW_C_HEAP_ARRAY(void*, 36000*4, mtStatistics);
+}
+
+void VMError::record_oome_stack(const char *message) {
+ Thread * thread = Thread::current();
+ if (!thread->is_Java_thread()) {
+ // The following code can't work in non-Java thread.
+ // Besides, it's not a true OOM *exception* if there's no Java on the stack.
+ return;
+ }
+
+ {
+ MutexLocker ml(OOMEStacks_lock);
+ ResourceMark rm;
+
+ stringStream st(_oome_stacktrace[_oome_free_index], OOME_STACKTRACE_BUFSIZE);
+ st.print_cr("OutOfMemoryError(\"%s\") on the thread \"%s\"", message, thread->name());
+ JavaThread::cast(thread)->print_stack_on(&st);
+
+ st.cr();
+
+ _oome_free_index = (_oome_free_index + 1) % OOME_STACKTRACE_COUNT;
+ }
+}
+
+void VMError::print_oome_stacks(outputStream *st) {
+ int index = _oome_free_index == 0 ? OOME_STACKTRACE_COUNT - 1 : _oome_free_index - 1;
+
+ for (int i = 0; i < OOME_STACKTRACE_COUNT; i++) {
+ const char * const trace = _oome_stacktrace[index];
+ if (strcmp(trace, "") != 0) st->print_raw_cr(trace);
+ index = index == 0 ? OOME_STACKTRACE_COUNT - 1 : index - 1;
+ }
+}
+
+class CLDCounterClosure : public CLDClosure {
+public:
+ // Loader Klass -> memory used in words
+ using TABLE = ResizeableResourceHashtable;
+
+private:
+ TABLE& _loaded_size;
+
+public:
+ explicit CLDCounterClosure(TABLE& loaded_size) : _loaded_size(loaded_size) {}
+
+ void do_cld(ClassLoaderData* cld) override {
+ Klass * const loader = cld->class_loader_klass();
+ ClassLoaderMetaspace * const ms = cld->metaspace_or_null();
+ if (ms != nullptr) {
+ size_t used_words = 0;
+ ms->usage_numbers(&used_words, nullptr, nullptr);
+
+ const size_t * const new_size_ptr = _loaded_size.get(loader);
+ size_t new_size = new_size_ptr == nullptr
+ ? used_words
+ : *new_size_ptr + used_words;
+ _loaded_size.put(loader, new_size);
+ }
+ }
+};
+
+static int compare_by_size(Pair* p1, Pair* p2) {
+ return p2->second == p1->second
+ ? 0
+ : p2->second > p1->second ? 1 : -1;
+}
+
+void VMError::print_classloaders_stats(outputStream *st) {
+ if (_thread) {
+ ResourceMark rm(_thread);
+ MutexLocker ml(_thread, ClassLoaderDataGraph_lock);
+
+ CLDCounterClosure::TABLE sizes(10, 100);
+ CLDCounterClosure counter(sizes);
+ ClassLoaderDataGraph::cld_do(&counter);
+ GrowableArray> loaders_stats;
+ sizes.iterate_all([&] (Klass* loader_klass, size_t total_words) {
+ loaders_stats.push(Pair(loader_klass, total_words));
+ });
+
+ loaders_stats.sort(compare_by_size);
+
+ for (const auto loaders_stat : loaders_stats) {
+ Klass * const loader_klass = loaders_stat.first;
+ const size_t total_words = loaders_stat.second;
+ const char *name = loader_klass != nullptr
+ ? loader_klass->external_name()
+ : BOOTSTRAP_LOADER_NAME;
+ const size_t total_bytes = total_words * BytesPerWord;
+ st->print_cr("Loader %-80s: %zu %s",
+ name,
+ byte_size_in_proper_unit(total_bytes),
+ proper_unit_for_byte_size(total_bytes));
+ }
+
+ st->cr();
+ } else {
+ st->print_cr("Not available (crashed in non-Java thread)");
+ }
+}
+
+class DuplicateKlassClosure : public KlassClosure {
+ static bool klass_equals(Klass* const & klass1, Klass* const & klass2) {
+ return strcmp(klass1->external_name(), klass2->external_name()) == 0;
+ }
+
+ static unsigned klass_hash(Klass* const & k) {
+ int h = 0;
+ const char* p = k->external_name();
+ while (*p != '\0') {
+ h = 31 * h + *p;
+ p++;
+ }
+ return h;
+ }
+
+public:
+ // Klass -> number times loaded
+ using TABLE = ResizeableResourceHashtable;
+
+private:
+ TABLE& _classes_count;
+
+public:
+ explicit DuplicateKlassClosure(TABLE& classes_count) : _classes_count(classes_count) {}
+
+ void do_klass(Klass* klass) override {
+ const size_t * const new_count_ptr = _classes_count.get(klass);
+ const size_t new_count = new_count_ptr != nullptr ? *new_count_ptr + 1 : 1;
+ _classes_count.put(klass, new_count);
+ }
+};
+
+void VMError::print_dup_classes(outputStream *st) {
+ if (_thread) {
+ ResourceMark rm(_thread);
+ RecursiveLocker rl(MultiArray_lock, _thread);
+ MutexLocker mcld(_thread, ClassLoaderDataGraph_lock);
+
+ DuplicateKlassClosure::TABLE classes_count(100, 200);
+ DuplicateKlassClosure counter(classes_count);
+ ClassLoaderDataGraph::loaded_classes_do_keepalive(&counter);
+
+ GrowableArray> dup_classes;
+ classes_count.iterate_all([&] (Klass* const name, size_t count) {
+ if (count > 1) dup_classes.push(Pair(name, count));
+ });
+
+ if (dup_classes.length() > 0) {
+ st->print_cr("Classes loaded by more than one classloader:");
+ dup_classes.sort(compare_by_size);
+ for (const auto dup : dup_classes) {
+ const char * const name = dup.first->external_name();
+ const size_t size = dup.first->size();
+ const size_t count = dup.second;
+ st->print_cr("Class %-80s: loaded %zu times (x %zuB)",
+ name, count, size);
+ }
+ st->cr();
+ }
+ }
+}
+
+#ifdef LINUX
+static void print_process_memory_usage_platform(outputStream *st)
+{
+ // See also os::Linux::print_process_memory_info()
+ os::Linux::meminfo_t info;
+ if (os::Linux::query_process_memory_info(&info)) {
+ ssize_t phys_total_kb = os::physical_memory() / K;
+ ssize_t phys_avail_kb = os::available_memory() / K;
+ int rss_percentile = (int)(info.vmrss * 100.0 / phys_total_kb);
+ st->print_cr("Resident Set Size: %zdK (%d%% of "
+ "%zdK total physical memory with %zdK free physical memory)",
+ info.vmrss, rss_percentile, phys_total_kb, phys_avail_kb);
+ } else {
+ st->print_cr("Could not open /proc/self/status to get process memory related information");
+ }
+}
+#endif
+
+#ifdef _WINDOWS
+static void print_process_memory_usage_platform(outputStream *st)
+{
+ // See os::jfr_report_memory_info() in os_windows.cpp
+ PROCESS_MEMORY_COUNTERS_EX pmex;
+ ZeroMemory(&pmex, sizeof(PROCESS_MEMORY_COUNTERS_EX));
+ pmex.cb = sizeof(pmex);
+
+ BOOL ret = GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmex, sizeof(pmex));
+ if (ret != 0) {
+ ssize_t rss_kb = pmex.WorkingSetSize / K;
+ ssize_t phys_total_kb = os::physical_memory() / K;
+ ssize_t phys_avail_kb = os::available_memory() / K;
+ int rss_percentile = (int)(rss_kb * 100.0 / phys_total_kb);
+ st->print_cr("Resident Set Size: %zdK (%d%% of "
+ "%zdK total physical memory with %zdK free physical memory)",
+ rss_kb, rss_percentile, phys_total_kb, phys_avail_kb);
+ } else {
+ st->print_cr("GetProcessMemoryInfo() call did not succeed");
+ }
+}
+#endif
+
+#ifdef __APPLE__
+static void print_process_memory_usage_platform(outputStream *st)
+{
+ // See os::jfr_report_memory_info() in os_bsd.cpp
+ mach_task_basic_info info;
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+
+ kern_return_t ret = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count);
+ if (ret == KERN_SUCCESS) {
+ ssize_t rss_kb = info.resident_size / K;
+ ssize_t phys_total_kb = os::physical_memory() / K;
+ ssize_t phys_avail_kb = os::available_memory() / K;
+ int rss_percentile = (int)(rss_kb * 100.0 / phys_total_kb);
+ st->print_cr("Resident Set Size: %zdK (%d%% of "
+ "%zdK total physical memory with %zdK free physical memory)",
+ rss_kb, rss_percentile, phys_total_kb, phys_avail_kb);
+ } else {
+ st->print_cr("task_info() call did not succeed");
+ }
+}
+#endif
+
+void VMError::print_process_memory_usage(outputStream *st)
+{
+ st->print_cr("Process memory usage:");
+ print_process_memory_usage_platform(st);
+ st->cr();
+}
diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp
index 832f44223be0..b619572adebd 100644
--- a/src/hotspot/share/utilities/vmError.hpp
+++ b/src/hotspot/share/utilities/vmError.hpp
@@ -69,6 +69,12 @@ class VMError : public AllStatic {
// used for reattempt step logic
static const size_t _reattempt_required_stack_headroom;
+ static const int OOME_STACKTRACE_BUFSIZE = 16*K;
+ static const int OOME_STACKTRACE_COUNT = 4;
+ static char _oome_stacktrace[OOME_STACKTRACE_COUNT][OOME_STACKTRACE_BUFSIZE];
+ static int _oome_free_index;
+ static void * _ballast_memory;
+
// Thread id of the first error. We must be able to handle native thread,
// so use thread id instead of Thread* to identify thread.
static volatile intptr_t _first_error_tid;
@@ -220,6 +226,13 @@ class VMError : public AllStatic {
static int prepare_log_file(const char* pattern, const char* default_pattern, bool overwrite_existing, char* buf, size_t buflen);
static bool was_assert_poison_crash(const void* sigInfo);
+
+ static void init();
+ static void record_oome_stack(const char *message);
+ static void print_oome_stacks(outputStream *st);
+ static void print_classloaders_stats(outputStream *st);
+ static void print_dup_classes(outputStream *st);
+ static void print_process_memory_usage(outputStream *st);
};
class VMErrorCallback {
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
index 69a5d775b0b0..b5958a2776ed 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
@@ -52,8 +52,8 @@ class BsdFileSystem extends UnixFileSystem {
public WatchService newWatchService()
throws IOException
{
- // use polling implementation until we implement a BSD/kqueue one
- return new PollingWatchService();
+ final boolean usePollingWatchService = Boolean.getBoolean("watch.service.polling");
+ return usePollingWatchService ? new PollingWatchService() : new MacOSXWatchService();
}
// lazy initialization of the list of supported attribute views
diff --git a/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java b/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java
new file mode 100644
index 000000000000..bed579991bed
--- /dev/null
+++ b/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+package sun.nio.fs;
+
+import sun.util.logging.PlatformLogger;
+
+import jdk.internal.misc.Unsafe;
+
+import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayDeque;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.Consumer;
+
+class MacOSXWatchService extends AbstractWatchService {
+ // Controls tracing in the native part of the watcher.
+ private static boolean tracingEnabled = false;
+ private static final PlatformLogger logger = PlatformLogger.getLogger("sun.nio.fs.MacOSXWatchService");
+
+ private final HashMap dirKeyToWatchKey = new HashMap<>();
+ private final HashMap eventStreamToWatchKey = new HashMap<>();
+ private final Object watchKeysLock = new Object();
+
+ private final CFRunLoopThread runLoopThread;
+
+ MacOSXWatchService() throws IOException {
+ runLoopThread = new CFRunLoopThread();
+ runLoopThread.setDaemon(true);
+ runLoopThread.start();
+
+ try {
+ // In order to be able to schedule any FSEventStream's, a reference to a run loop is required.
+ runLoopThread.waitForRunLoopRef();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ WatchKey register(Path dir, WatchEvent.Kind>[] events, WatchEvent.Modifier... modifiers) throws IOException {
+ checkIsOpen();
+
+ final UnixPath unixDir = (UnixPath)dir;
+ final Object dirKey = checkPath(unixDir);
+ final EnumSet eventSet = FSEventKind.setOf(events);
+ final EnumSet modifierSet = WatchModifier.setOf(modifiers);
+ synchronized (closeLock()) {
+ checkIsOpen();
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("register for " + dir);
+
+ synchronized (watchKeysLock) {
+ MacOSXWatchKey watchKey = dirKeyToWatchKey.get(dirKey);
+ final boolean keyForDirAlreadyExists = (watchKey != null);
+ if (keyForDirAlreadyExists) {
+ eventStreamToWatchKey.remove(watchKey.getEventStreamRef());
+ watchKey.disable();
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("re-used existing watch key");
+ } else {
+ watchKey = new MacOSXWatchKey(this, unixDir, dirKey);
+ dirKeyToWatchKey.put(dirKey, watchKey);
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("created a new watch key");
+ }
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("starting to [re-]populate directory cache with data");
+ watchKey.enable(runLoopThread, eventSet, modifierSet);
+ eventStreamToWatchKey.put(watchKey.getEventStreamRef(), watchKey);
+ watchKeysLock.notify(); // So that run loop gets running again if stopped due to lack of event streams
+ return watchKey;
+ }
+ }
+ }
+
+ /**
+ * Invoked on the CFRunLoopThread by the native code to report directories that need to be re-scanned.
+ */
+ private void callback(final long eventStreamRef, final String[] paths, final long eventFlagsPtr) {
+ synchronized (watchKeysLock) {
+ final MacOSXWatchKey watchKey = eventStreamToWatchKey.get(eventStreamRef);
+ if (watchKey != null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Callback fired for '" + watchKey.getRealRootPath() + "'");
+ watchKey.handleEvents(paths, eventFlagsPtr);
+ } else {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Callback fired for watch key that is no longer there");
+ }
+ }
+ }
+
+ void cancel(final MacOSXWatchKey watchKey) {
+ synchronized (watchKeysLock) {
+ dirKeyToWatchKey.remove(watchKey.getRootPathKey());
+ eventStreamToWatchKey.remove(watchKey.getEventStreamRef());
+ }
+ }
+
+ void waitForEventSource() {
+ synchronized (watchKeysLock) {
+ if (isOpen() && eventStreamToWatchKey.isEmpty()) {
+ try {
+ watchKeysLock.wait();
+ } catch (InterruptedException ignore) {}
+ }
+ }
+ }
+
+ @Override
+ void implClose() {
+ synchronized (watchKeysLock) {
+ eventStreamToWatchKey.clear();
+ dirKeyToWatchKey.forEach((key, watchKey) -> watchKey.invalidate());
+ dirKeyToWatchKey.clear();
+ watchKeysLock.notify(); // Let waitForEventSource() go if it was waiting
+ runLoopThread.runLoopStop(); // Force exit from CFRunLoopRun()
+ }
+ }
+
+ private static void traceLine(final String text) {
+ logger.finest("NATIVE trace: " + text);
+ }
+
+ private class CFRunLoopThread extends Thread {
+ // Native reference to the CFRunLoop object of the watch service run loop.
+ private long runLoopRef;
+
+ public CFRunLoopThread() {
+ super("FileSystemWatcher");
+ }
+
+ synchronized void waitForRunLoopRef() throws InterruptedException {
+ if (runLoopRef == 0)
+ runLoopThread.wait(); // ...for CFRunLoopRef to become available
+ }
+
+ long getRunLoopRef() {
+ return runLoopRef;
+ }
+
+ synchronized void runLoopStop() {
+ if (runLoopRef != 0) {
+ // The run loop may have stuck in CFRunLoopRun() even though all of its input sources
+ // have been removed. Need to terminate it explicitly so that it can run to completion.
+ MacOSXWatchService.CFRunLoopStop(runLoopRef);
+ }
+ }
+
+ @Override
+ public void run() {
+ synchronized (this) {
+ runLoopRef = CFRunLoopGetCurrent();
+ notify();
+ }
+
+ while (isOpen()) {
+ CFRunLoopRun(MacOSXWatchService.this);
+ waitForEventSource();
+ }
+
+ synchronized (this) {
+ runLoopRef = 0; // CFRunLoopRef is no longer usable when the loop has been terminated
+ }
+ }
+ }
+
+ private void checkIsOpen() {
+ if (!isOpen())
+ throw new ClosedWatchServiceException();
+ }
+
+ private Object checkPath(UnixPath dir) throws IOException {
+ if (dir == null)
+ throw new NullPointerException("No path to watch");
+
+ UnixFileAttributes attrs;
+ try {
+ attrs = UnixFileAttributes.get(dir, true);
+ } catch (UnixException x) {
+ throw x.asIOException(dir);
+ }
+
+ if (!attrs.isDirectory())
+ throw new NotDirectoryException(dir.getPathForExceptionMessage());
+
+ final Object fileKey = attrs.fileKey();
+ if (fileKey == null)
+ throw new AssertionError("File keys must be supported");
+
+ return fileKey;
+ }
+
+ private enum FSEventKind {
+ CREATE, MODIFY, DELETE, OVERFLOW;
+
+ public static FSEventKind of(final WatchEvent.Kind> watchEventKind) {
+ if (StandardWatchEventKinds.ENTRY_CREATE == watchEventKind) {
+ return CREATE;
+ } else if (StandardWatchEventKinds.ENTRY_MODIFY == watchEventKind) {
+ return MODIFY;
+ } else if (StandardWatchEventKinds.ENTRY_DELETE == watchEventKind) {
+ return DELETE;
+ } else if (StandardWatchEventKinds.OVERFLOW == watchEventKind) {
+ return OVERFLOW;
+ } else {
+ throw new UnsupportedOperationException(watchEventKind.name());
+ }
+ }
+
+ public static EnumSet setOf(final WatchEvent.Kind>[] events) {
+ final EnumSet eventSet = EnumSet.noneOf(FSEventKind.class);
+ for (final WatchEvent.Kind> event: events) {
+ if (event == null) {
+ throw new NullPointerException("An element in event set is 'null'");
+ } else if (event == StandardWatchEventKinds.OVERFLOW) {
+ continue;
+ }
+
+ eventSet.add(FSEventKind.of(event));
+ }
+
+ if (eventSet.isEmpty())
+ throw new IllegalArgumentException("No events to register");
+
+ return eventSet;
+ }
+
+ }
+
+ private enum WatchModifier {
+ FILE_TREE, SENSITIVITY_HIGH, SENSITIVITY_MEDIUM, SENSITIVITY_LOW;
+
+ public static WatchModifier of(final WatchEvent.Modifier watchEventModifier) {
+ if (ExtendedOptions.FILE_TREE.matches(watchEventModifier)) {
+ return FILE_TREE;
+ } if (ExtendedOptions.SENSITIVITY_HIGH.matches(watchEventModifier)) {
+ return SENSITIVITY_HIGH;
+ } if (ExtendedOptions.SENSITIVITY_MEDIUM.matches(watchEventModifier)) {
+ return SENSITIVITY_MEDIUM;
+ } if (ExtendedOptions.SENSITIVITY_LOW.matches(watchEventModifier)) {
+ return SENSITIVITY_LOW;
+ } else {
+ throw new UnsupportedOperationException(watchEventModifier.name());
+ }
+ }
+
+ public static EnumSet setOf(final WatchEvent.Modifier[] modifiers) {
+ final EnumSet modifierSet = EnumSet.noneOf(WatchModifier.class);
+ for (final WatchEvent.Modifier modifier : modifiers) {
+ if (modifier == null)
+ throw new NullPointerException("An element in modifier set is 'null'");
+
+ modifierSet.add(WatchModifier.of(modifier));
+ }
+
+ return modifierSet;
+ }
+
+ public static double sensitivityOf(final EnumSet modifiers) {
+ if (modifiers.contains(SENSITIVITY_HIGH)) {
+ return 0.1;
+ } else if (modifiers.contains(SENSITIVITY_LOW)) {
+ return 1;
+ } else {
+ return 0.5; // aka SENSITIVITY_MEDIUM
+ }
+ }
+ }
+
+ private static class MacOSXWatchKey extends AbstractWatchKey {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private static final long kFSEventStreamEventFlagMustScanSubDirs = 0x00000001;
+ private static final long kFSEventStreamEventFlagRootChanged = 0x00000020;
+
+ private final Path relativeRootPath;
+
+ // Full path to this key's watch root directory.
+ private final Path realRootPath;
+ private final int realRootPathLength;
+ private final Object rootPathKey;
+
+ // Kinds of events to be reported.
+ private EnumSet eventsToWatch;
+
+ // Should events in directories below realRootPath reported?
+ private boolean watchFileTree;
+
+ // Native FSEventStreamRef as returned by FSEventStreamCreate().
+ private long eventStreamRef;
+ private final Object eventStreamRefLock = new Object();
+
+ private final DirectoryTreeSnapshot directoryTreeSnapshot = new DirectoryTreeSnapshot();
+
+ MacOSXWatchKey(final MacOSXWatchService watchService, final UnixPath dir, final Object rootPathKey) throws IOException {
+ super(dir, watchService);
+ this.relativeRootPath = dir.getFileSystem().getPath("");
+ this.realRootPath = dir.toRealPath().normalize();
+ this.realRootPathLength = realRootPath.toString().length() + 1;
+ this.rootPathKey = rootPathKey;
+ }
+
+ synchronized void enable(final CFRunLoopThread runLoopThread,
+ final EnumSet eventsToWatch,
+ final EnumSet modifierSet) throws IOException {
+ assert(!isValid());
+
+ this.eventsToWatch = eventsToWatch;
+ this.watchFileTree = modifierSet.contains(WatchModifier.FILE_TREE);
+
+ directoryTreeSnapshot.build();
+
+ synchronized (eventStreamRefLock) {
+ final int kFSEventStreamCreateFlagWatchRoot = 0x00000004;
+ eventStreamRef = MacOSXWatchService.eventStreamCreate(
+ realRootPath.toString(),
+ WatchModifier.sensitivityOf(modifierSet),
+ kFSEventStreamCreateFlagWatchRoot);
+
+ if (eventStreamRef == 0)
+ throw new IOException("Unable to create FSEventStream");
+
+ MacOSXWatchService.eventStreamSchedule(eventStreamRef, runLoopThread.getRunLoopRef());
+ }
+ }
+
+ synchronized void disable() {
+ invalidate();
+ directoryTreeSnapshot.reset();
+ }
+
+ synchronized void handleEvents(final String[] paths, long eventFlagsPtr) {
+ if (paths == null) {
+ reportOverflow(null);
+ return;
+ }
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): will handle " + paths.length + " events");
+
+ final Set dirsToScan = new LinkedHashSet<>(paths.length);
+ final Set dirsToScanRecursively = new LinkedHashSet<>();
+ collectDirsToScan(paths, eventFlagsPtr, dirsToScan, dirsToScanRecursively);
+
+ for (final Path recurseDir : dirsToScanRecursively) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): scanning directory recursively " + recurseDir);
+ dirsToScan.removeIf(dir -> dir.startsWith(recurseDir));
+ assert(watchFileTree);
+ directoryTreeSnapshot.update(recurseDir, true);
+ }
+
+ for (final Path dir : dirsToScan) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): scanning directory " + dir);
+ directoryTreeSnapshot.update(dir, false);
+ }
+ }
+
+ private Path toRelativePath(final String absPath) {
+ return (absPath.length() > realRootPathLength)
+ ? relativeRootPath.getFileSystem().getPath(absPath.substring(realRootPathLength))
+ : relativeRootPath;
+ }
+
+ private void collectDirsToScan(final String[] paths, long eventFlagsPtr,
+ final Set dirsToScan,
+ final Set dirsToScanRecursively) {
+ for (final String absPath : paths) {
+ if (absPath == null) {
+ reportOverflow(null);
+ continue;
+ }
+
+ Path path = toRelativePath(absPath);
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): event path name " + path);
+
+ if (!watchFileTree && !relativeRootPath.equals(path)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): skipping event for a nested directory");
+ continue;
+ }
+
+ final int flags = unsafe.getInt(eventFlagsPtr);
+ if ((flags & kFSEventStreamEventFlagRootChanged) != 0) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): watch root changed, path=" + path);
+ cancel();
+ signal();
+ break;
+ } else if ((flags & kFSEventStreamEventFlagMustScanSubDirs) != 0 && watchFileTree) {
+ dirsToScanRecursively.add(path);
+ } else {
+ dirsToScan.add(path);
+ }
+
+ final long SIZEOF_FS_EVENT_STREAM_EVENT_FLAGS = 4L; // FSEventStreamEventFlags is UInt32
+ eventFlagsPtr += SIZEOF_FS_EVENT_STREAM_EVENT_FLAGS;
+ }
+ }
+
+ /**
+ * Represents a snapshot of a directory tree.
+ * The snapshot includes subdirectories iff watchFileTree is true.
+ */
+ private class DirectoryTreeSnapshot {
+ private final HashMap snapshots;
+
+ DirectoryTreeSnapshot() {
+ this.snapshots = new HashMap<>(watchFileTree ? 256 : 1);
+ }
+
+ void build() throws IOException {
+ final Queue pathToDo = new ArrayDeque<>();
+ pathToDo.offer(relativeRootPath);
+
+ while (!pathToDo.isEmpty()) {
+ final Path path = pathToDo.poll();
+ try {
+ createForOneDirectory(path, watchFileTree ? pathToDo : null);
+ } catch (IOException e) {
+ final boolean exceptionForRootPath = relativeRootPath.equals(path);
+ if (exceptionForRootPath)
+ throw e; // report to the user as the watch root may have disappeared
+
+ // Ignore for sub-directories as some may have been removed during the scan.
+ // That's OK, those kinds of changes in the directory hierarchy is what
+ // WatchService is used for. However, it's impossible to catch all changes
+ // at this point, so we may fail to report some events that had occurred before
+ // FSEventStream has been created to watch for those changes.
+ }
+ }
+ }
+
+ private DirectorySnapshot createForOneDirectory(
+ final Path directory,
+ final Queue newDirectoriesFound) throws IOException {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Creating snapshot for one directory " + directory);
+
+ final DirectorySnapshot snapshot = DirectorySnapshot.create(getRealRootPath(), directory);
+ snapshots.put(directory, snapshot);
+ if (newDirectoriesFound != null)
+ snapshot.forEachDirectory(newDirectoriesFound::offer);
+
+ return snapshot;
+ }
+
+ void reset() {
+ snapshots.clear();
+ }
+
+ void update(final Path directory, final boolean recurse) {
+ if (!recurse) {
+ directoryTreeSnapshot.update(directory, null);
+ } else {
+ final Queue pathToDo = new ArrayDeque<>();
+ pathToDo.offer(directory);
+ while (!pathToDo.isEmpty()) {
+ final Path dir = pathToDo.poll();
+ directoryTreeSnapshot.update(dir, pathToDo);
+ }
+ }
+ }
+
+ private void update(final Path directory, final Queue modifiedDirs) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("update for " + directory);
+ final DirectorySnapshot snapshot = snapshots.get(directory);
+ if (snapshot == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("no snapshot for directory " + directory);
+ // This means that we missed a notification about an update of our parent.
+ // Report overflow (who knows what else we weren't notified about?) and
+ // do our best to recover from this mess by queueing our parent for an update.
+ reportOverflow(directory);
+ if (modifiedDirs != null)
+ modifiedDirs.offer(getParentOf(directory));
+
+ return;
+ }
+
+ // FSEvents API does not generate events for directories that got moved from/to the directory
+ // being watched, so we have to watch for new/deleted directories ourselves. If we still
+ // receive an event for, say, one of the new directories, it won't be reported again as this
+ // will count as refresh with no modifications detected.
+ final Queue createdDirs = new ArrayDeque<>();
+ final Queue deletedDirs = new ArrayDeque<>();
+ snapshot.update(MacOSXWatchKey.this, createdDirs, deletedDirs, modifiedDirs);
+
+ handleNewDirectories(createdDirs);
+ handleDeletedDirectories(deletedDirs);
+ }
+
+ private Path getParentOf(final Path directory) {
+ Path parent = directory.getParent();
+ if (parent == null)
+ parent = relativeRootPath;
+ return parent;
+ }
+
+ private void handleDeletedDirectories(final Queue deletedDirs) {
+ // We don't know the exact sequence in which these were deleted,
+ // so at least maintain a sensible order, i.e. children are deleted before the parent.
+ final LinkedList dirsToReportDeleted = new LinkedList<>();
+ while (!deletedDirs.isEmpty()) {
+ final Path path = deletedDirs.poll();
+ dirsToReportDeleted.addFirst(path);
+ final DirectorySnapshot directorySnapshot = snapshots.get(path);
+ if (directorySnapshot != null) // May be null if we're not watching the whole file tree.
+ directorySnapshot.forEachDirectory(deletedDirs::offer);
+ }
+
+ for(final Path path : dirsToReportDeleted) {
+ final DirectorySnapshot directorySnapshot = snapshots.remove(path);
+ if (directorySnapshot != null) {
+ // This is needed in case a directory tree was moved (mv -f) out of this directory.
+ directorySnapshot.forEachFile(MacOSXWatchKey.this::reportDeleted);
+ }
+ reportDeleted(path);
+ }
+ }
+
+ private void handleNewDirectories(final Queue createdDirs) {
+ // We don't know the exact sequence in which these were created,
+ // so at least maintain a sensible order, i.e. the parent created before its children.
+ while (!createdDirs.isEmpty()) {
+ final Path path = createdDirs.poll();
+ reportCreated(path);
+ if (watchFileTree) {
+ if (!snapshots.containsKey(path)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Just noticed yet another directory: " + path);
+ // Happens when a directory tree gets moved (mv -f) into this directory.
+ DirectorySnapshot newSnapshot = null;
+ try {
+ newSnapshot = createForOneDirectory(path, createdDirs);
+ } catch(IOException ignore) { }
+
+ if (newSnapshot != null)
+ newSnapshot.forEachFile(MacOSXWatchKey.this::reportCreated);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Represents a snapshot of a directory with a millisecond precision timestamp of the last modification.
+ */
+ private static class DirectorySnapshot {
+ // Path to this directory relative to the watch root.
+ private final Path directory;
+
+ // Maps file names to their attributes.
+ private final Map files;
+
+ // A counter to keep track of files that have disappeared since the last run.
+ private long currentTick;
+
+ private DirectorySnapshot(final Path directory) {
+ this.directory = directory;
+ this.files = new HashMap<>();
+ }
+
+ static DirectorySnapshot create(final Path realRootPath, final Path directory) throws IOException {
+ final DirectorySnapshot snapshot = new DirectorySnapshot(directory);
+ try (final DirectoryStream directoryStream = Files.newDirectoryStream(realRootPath.resolve(directory))) {
+ for (final Path file : directoryStream) {
+ try {
+ final BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ final Entry entry = new Entry(attrs.isDirectory(), attrs.lastModifiedTime().toMillis(), 0);
+ snapshot.files.put(file.getFileName(), entry);
+ } catch (IOException ignore) {}
+ }
+ } catch (DirectoryIteratorException e) {
+ throw e.getCause();
+ }
+
+ return snapshot;
+ }
+
+ void forEachDirectory(final Consumer consumer) {
+ files.forEach((path, entry) -> { if (entry.isDirectory) consumer.accept(directory.resolve(path)); } );
+ }
+
+ void forEachFile(final Consumer consumer) {
+ files.forEach((path, entry) -> { if (!entry.isDirectory) consumer.accept(directory.resolve(path)); } );
+ }
+
+ void update(final MacOSXWatchKey watchKey,
+ final Queue createdDirs,
+ final Queue deletedDirs,
+ final Queue modifiedDirs) {
+ currentTick++;
+
+ try (final DirectoryStream directoryStream = Files.newDirectoryStream(watchKey.getRealRootPath().resolve(directory))) {
+ for (final Path file : directoryStream) {
+ try {
+ final BasicFileAttributes attrs
+ = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ final Path fileName = file.getFileName();
+ final Entry entry = files.get(fileName);
+ final boolean isNew = (entry == null);
+ final long lastModified = attrs.lastModifiedTime().toMillis();
+ final Path relativePath = directory.resolve(fileName);
+
+ if (attrs.isDirectory()) {
+ if (isNew) {
+ files.put(fileName, new Entry(true, lastModified, currentTick));
+ if (createdDirs != null) createdDirs.offer(relativePath);
+ } else {
+ if (!entry.isDirectory) { // Used to be a file, now a directory
+ if (createdDirs != null) createdDirs.offer(relativePath);
+
+ files.put(fileName, new Entry(true, lastModified, currentTick));
+ watchKey.reportDeleted(relativePath);
+ } else if (entry.isModified(lastModified)) {
+ if (modifiedDirs != null) modifiedDirs.offer(relativePath);
+ watchKey.reportModified(relativePath);
+ }
+ entry.update(lastModified, currentTick);
+ }
+ } else {
+ if (isNew) {
+ files.put(fileName, new Entry(false, lastModified, currentTick));
+ watchKey.reportCreated(relativePath);
+ } else {
+ if (entry.isDirectory) { // Used to be a directory, now a file.
+ if (deletedDirs != null) deletedDirs.offer(relativePath);
+
+ files.put(fileName, new Entry(false, lastModified, currentTick));
+ watchKey.reportCreated(directory.resolve(fileName));
+ } else if (entry.isModified(lastModified)) {
+ watchKey.reportModified(relativePath);
+ }
+ entry.update(lastModified, currentTick);
+ }
+ }
+ } catch (IOException ignore) {
+ // Simply skip the file we couldn't read; it'll get marked as deleted later.
+ }
+ }
+ } catch (IOException | DirectoryIteratorException ignore) {
+ // Most probably this directory has just been deleted; our parent will notice that.
+ }
+
+ checkDeleted(watchKey, deletedDirs);
+ }
+
+ private void checkDeleted(final MacOSXWatchKey watchKey, final Queue deletedDirs) {
+ final Iterator> it = files.entrySet().iterator();
+ while (it.hasNext()) {
+ final Map.Entry mapEntry = it.next();
+ final Entry entry = mapEntry.getValue();
+ if (entry.lastTickCount != currentTick) {
+ final Path file = mapEntry.getKey();
+ it.remove();
+
+ if (entry.isDirectory) {
+ if (deletedDirs != null) deletedDirs.offer(directory.resolve(file));
+ } else {
+ watchKey.reportDeleted(directory.resolve(file));
+ }
+ }
+ }
+ }
+
+ /**
+ * Information about an entry in a directory.
+ */
+ private static class Entry {
+ private long lastModified;
+ private long lastTickCount;
+ private final boolean isDirectory;
+
+ Entry(final boolean isDirectory, final long lastModified, final long lastTickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = lastTickCount;
+ this.isDirectory = isDirectory;
+ }
+
+ boolean isModified(final long lastModified) {
+ return (this.lastModified != lastModified);
+ }
+
+ void update(final long lastModified, final long lastTickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = lastTickCount;
+ }
+ }
+ }
+
+ private void reportCreated(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report CREATE for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.CREATE))
+ signalEvent(StandardWatchEventKinds.ENTRY_CREATE, path);
+ }
+
+ private void reportDeleted(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report DELETE for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.DELETE))
+ signalEvent(StandardWatchEventKinds.ENTRY_DELETE, path);
+ }
+
+ private void reportModified(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report MODIFIED for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.MODIFY))
+ signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, path);
+ }
+
+ private void reportOverflow(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report OVERFLOW for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.OVERFLOW))
+ signalEvent(StandardWatchEventKinds.OVERFLOW, path);
+ }
+
+ public Object getRootPathKey() {
+ return rootPathKey;
+ }
+
+ public Path getRealRootPath() {
+ return realRootPath;
+ }
+
+ @Override
+ public boolean isValid() {
+ synchronized (eventStreamRefLock) {
+ return eventStreamRef != 0;
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (!isValid()) return;
+
+ // First, must stop the corresponding run loop:
+ ((MacOSXWatchService) watcher()).cancel(this);
+
+ // Next, invalidate the corresponding native FSEventStream.
+ invalidate();
+ }
+
+ void invalidate() {
+ synchronized (eventStreamRefLock) {
+ if (isValid()) {
+ eventStreamStop(eventStreamRef);
+ eventStreamRef = 0;
+ }
+ }
+ }
+
+ long getEventStreamRef() {
+ synchronized (eventStreamRefLock) {
+ assert (isValid());
+ return eventStreamRef;
+ }
+ }
+ }
+
+ /* native methods */
+
+ private static native long eventStreamCreate(String dir, double latencyInSeconds, int flags);
+ private static native void eventStreamSchedule(long eventStreamRef, long runLoopRef);
+ private static native void eventStreamStop(long eventStreamRef);
+ private static native long CFRunLoopGetCurrent();
+ private static native void CFRunLoopRun(final MacOSXWatchService watchService);
+ private static native void CFRunLoopStop(long runLoopRef);
+
+ private static native void initIDs();
+
+ @SuppressWarnings("restricted")
+ private static void init() {
+ tracingEnabled = logger.isLoggable(PlatformLogger.Level.FINEST);
+ System.loadLibrary("nio");
+ initIDs();
+ }
+
+ static {
+ init();
+ }
+}
diff --git a/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c b/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c
new file mode 100644
index 000000000000..72c08c9608b2
--- /dev/null
+++ b/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "nio_util.h"
+
+#include
+
+#include
+#include
+
+#if defined(__GNUC__) || defined(__clang__)
+# ifndef ATTRIBUTE_PRINTF
+# define ATTRIBUTE_PRINTF(fmt,vargs) __attribute__((format(printf, fmt, vargs)))
+# endif
+#endif
+
+static void
+traceLine(JNIEnv* env, const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3);
+
+// Controls exception stack trace output and debug trace.
+// Set by raising the logging level of sun.nio.fs.MacOSXWatchService to or above FINEST.
+static jboolean tracingEnabled;
+
+static jmethodID callbackMID; // MacOSXWatchService.callback()
+static __thread jobject watchService; // The instance of MacOSXWatchService that is associated with this thread
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_initIDs(JNIEnv* env, __unused jclass clazz)
+{
+ jfieldID tracingEnabledFieldID = (*env)->GetStaticFieldID(env, clazz, "tracingEnabled", "Z");
+ CHECK_NULL(tracingEnabledFieldID);
+ tracingEnabled = (*env)->GetStaticBooleanField(env, clazz, tracingEnabledFieldID);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ }
+
+ callbackMID = (*env)->GetMethodID(env, clazz, "callback", "(J[Ljava/lang/String;J)V");
+}
+
+extern CFStringRef toCFString(JNIEnv *env, jstring javaString);
+
+static void
+traceLine(JNIEnv* env, const char* fmt, ...)
+{
+ if (tracingEnabled) {
+ va_list vargs;
+ va_start(vargs, fmt);
+ char* buf = (char*)malloc(1024);
+ vsnprintf(buf, 1024, fmt, vargs);
+ const jstring text = JNU_NewStringPlatform(env, buf);
+ free(buf);
+ va_end(vargs);
+
+ jboolean ignoreException;
+ JNU_CallStaticMethodByName(env, &ignoreException, "sun/nio/fs/MacOSXWatchService", "traceLine", "(Ljava/lang/String;)V", text);
+ }
+}
+
+static jboolean
+convertToJavaStringArray(JNIEnv* env, char **eventPaths,
+ const jsize numEventsToReport, jobjectArray javaEventPathsArray)
+{
+ for (jsize i = 0; i < numEventsToReport; i++) {
+ const jstring path = JNU_NewStringPlatform(env, eventPaths[i]);
+ CHECK_NULL_RETURN(path, FALSE);
+ (*env)->SetObjectArrayElement(env, javaEventPathsArray, i, path);
+ }
+
+ return JNI_TRUE;
+}
+
+static void
+callJavaCallback(JNIEnv* env, jlong streamRef, jobjectArray javaEventPathsArray, jlong eventFlags)
+{
+ if (callbackMID != NULL && watchService != NULL) {
+ // We are called on the run loop thread, so it's OK to use the thread-local reference
+ // to the watch service.
+ (*env)->CallVoidMethod(env, watchService, callbackMID, streamRef, javaEventPathsArray, eventFlags);
+ }
+}
+
+/**
+ * Callback that is invoked on the run loop thread and informs of new file-system events from an FSEventStream.
+ */
+static void
+callback(__unused ConstFSEventStreamRef streamRef,
+ __unused void *clientCallBackInfo,
+ size_t numEventsTotal,
+ void *eventPaths,
+ const FSEventStreamEventFlags eventFlags[],
+ __unused const FSEventStreamEventId eventIds[])
+{
+ JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
+ if (!env) { // Shouldn't happen as run loop starts from Java code
+ return;
+ }
+
+ // We can get more events at once than the number of Java array elements,
+ // so report them in chunks.
+ const size_t MAX_EVENTS_TO_REPORT_AT_ONCE = (INT_MAX - 2);
+
+ jboolean success = JNI_TRUE;
+ for(size_t eventIndex = 0; success && (eventIndex < numEventsTotal); ) {
+ const size_t numEventsRemaining = (numEventsTotal - eventIndex);
+ const jsize numEventsToReport = (numEventsRemaining > MAX_EVENTS_TO_REPORT_AT_ONCE)
+ ? MAX_EVENTS_TO_REPORT_AT_ONCE
+ : numEventsRemaining;
+
+ const jboolean localFramePushed = ((*env)->PushLocalFrame(env, numEventsToReport + 5) == JNI_OK);
+ success = localFramePushed;
+
+ jobjectArray javaEventPathsArray = NULL;
+ if (success) {
+ javaEventPathsArray = (*env)->NewObjectArray(env, (jsize)numEventsToReport, JNU_ClassString(env), NULL);
+ success = (javaEventPathsArray != NULL);
+ }
+
+ if (success) {
+ success = convertToJavaStringArray(env, &((char**)eventPaths)[eventIndex], numEventsToReport, javaEventPathsArray);
+ }
+
+ callJavaCallback(env, (jlong)streamRef, javaEventPathsArray, (jlong)&eventFlags[eventIndex]);
+
+ if ((*env)->ExceptionCheck(env)) {
+ if (tracingEnabled) (*env)->ExceptionDescribe(env);
+ }
+
+ if (localFramePushed) {
+ (*env)->PopLocalFrame(env, NULL);
+ }
+
+ eventIndex += numEventsToReport;
+ }
+}
+
+/**
+ * Creates a new FSEventStream and returns FSEventStreamRef for it.
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamCreate(JNIEnv* env, __unused jclass clazz,
+ jstring dir, jdouble latencyInSeconds, jint flags)
+{
+ const CFStringRef path = toCFString(env, dir);
+ CHECK_NULL_RETURN(path, 0);
+ const CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **) &path, 1, NULL);
+ CHECK_NULL_RETURN(pathsToWatch, 0);
+
+ const FSEventStreamRef stream = FSEventStreamCreate(
+ NULL,
+ &callback,
+ NULL,
+ pathsToWatch,
+ kFSEventStreamEventIdSinceNow,
+ (CFAbsoluteTime) latencyInSeconds,
+ flags
+ );
+
+ traceLine(env, "created event stream 0x%p", stream);
+
+ return (jlong)stream;
+}
+
+
+/**
+ * Schedules the given FSEventStream on the run loop of the current thread. Starts the stream
+ * so that the run loop can receive events from the stream.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamSchedule(__unused JNIEnv* env, __unused jclass clazz,
+ jlong eventStreamRef, jlong runLoopRef)
+{
+ const FSEventStreamRef stream = (FSEventStreamRef)eventStreamRef;
+ const CFRunLoopRef runLoop = (CFRunLoopRef)runLoopRef;
+
+ FSEventStreamScheduleWithRunLoop(stream, runLoop, kCFRunLoopDefaultMode);
+ FSEventStreamStart(stream);
+
+ traceLine(env, "scheduled stream 0x%p on thread 0x%p", stream, CFRunLoopGetCurrent());
+}
+
+/**
+ * Performs the steps necessary to dispose of the given FSEventStreamRef.
+ * The stream must have been started and scheduled with a run loop.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamStop(__unused JNIEnv* env, __unused jclass clazz, jlong eventStreamRef)
+{
+ const FSEventStreamRef streamRef = (FSEventStreamRef)eventStreamRef;
+
+ FSEventStreamStop(streamRef); // Unregister with the FS Events service. No more callbacks from this stream
+ FSEventStreamInvalidate(streamRef); // Unschedule from any runloops
+ FSEventStreamRelease(streamRef); // Decrement the stream's refcount
+}
+
+/**
+ * Returns the CFRunLoop object for the current thread.
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopGetCurrent(__unused JNIEnv* env, __unused jclass clazz)
+{
+ const CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
+ traceLine(env, "get current run loop: 0x%p", currentRunLoop);
+ return (jlong)currentRunLoop;
+}
+
+/**
+ * Simply calls CFRunLoopRun() to run current thread's run loop for as long as there are event sources
+ * attached to it.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopRun(__unused JNIEnv* env, __unused jclass clazz, jlong watchServiceObject)
+{
+ traceLine(env, "running run loop on 0x%p", CFRunLoopGetCurrent());
+
+ // Thread-local pointer to the WatchService instance will be used by the callback
+ // on this thread.
+ watchService = (*env)->NewGlobalRef(env, (jobject)watchServiceObject);
+ CFRunLoopRun();
+ (*env)->DeleteGlobalRef(env, (jobject)watchService);
+ watchService = NULL;
+
+ traceLine(env, "run loop done on 0x%p", CFRunLoopGetCurrent());
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopStop(__unused JNIEnv* env, __unused jclass clazz, jlong runLoopRef)
+{
+ traceLine(env, "stopping run loop 0x%p", (void*)runLoopRef);
+ CFRunLoopStop((CFRunLoopRef)runLoopRef);
+}
diff --git a/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c b/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
index 5e9451e850c1..2820bafbce9a 100644
--- a/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
+++ b/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
* If a memory error occurs, and OutOfMemoryError is thrown and
* NULL is returned.
*/
-static CFStringRef toCFString(JNIEnv *env, jstring javaString)
+CFStringRef toCFString(JNIEnv *env, jstring javaString)
{
if (javaString == NULL) {
return NULL;
diff --git a/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java b/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java
new file mode 100644
index 000000000000..8d321fc625ba
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.bootstrap;
+
+import com.jetbrains.internal.JBRApi;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+
+
+/**
+ * Bootstrap class, used to initialize {@linkplain JBRApi JBR API}.
+ * @deprecated replaced by {@link com.jetbrains.exported.JBRApiSupport}
+ */
+@Deprecated
+public class JBRApiBootstrap {
+ private JBRApiBootstrap() {}
+
+ /**
+ * Old version of bootstrap method without metadata parameter.
+ * @param outerLookup lookup context inside {@code jetbrains.api} module
+ * @return implementation for {@link com.jetbrains.JBR.ServiceApi} interface
+ */
+ public static synchronized Object bootstrap(MethodHandles.Lookup outerLookup) {
+ if (!JBRApi.ENABLED) return null;
+ if (JBRApi.VERBOSE) {
+ System.out.println("JBR API bootstrap in compatibility mode: Object bootstrap(MethodHandles.Lookup)");
+ }
+ Class> apiInterface;
+ try {
+ apiInterface = outerLookup.findClass("com.jetbrains.JBR$ServiceApi");
+ } catch (ClassNotFoundException | IllegalAccessException e) {
+ throw new RuntimeException("Failed to retrieve JBR API metadata", e);
+ }
+ return com.jetbrains.exported.JBRApiSupport.bootstrap(apiInterface, null, null, null, Map.of(), m -> null);
+ }
+
+}
diff --git a/src/java.base/share/classes/com/jetbrains/exported/JBRApi.java b/src/java.base/share/classes/com/jetbrains/exported/JBRApi.java
new file mode 100644
index 000000000000..19a85cfe3524
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/exported/JBRApi.java
@@ -0,0 +1,92 @@
+package com.jetbrains.exported;
+
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
+
+import java.io.Serial;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JBR API utility class.
+ */
+public class JBRApi {
+ private JBRApi() {}
+
+ /**
+ * Marks classes and interfaces whose implementation is provided by JBR API client.
+ * These types must not be inherited by JBR unless explicitly marked with {@link Provides}.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Provided {
+ /**
+ * Binding target.
+ * This is a fully qualified name of corresponding {@code @Provides} class/interface,
+ * e.g. {@code com.jetbrains.My.Nested.Class}.
+ * Package {@code com.jetbrains} may be omitted, e.g. {@code My.Nested.Class}, {@code MyClass#myMethod}.
+ */
+ String value();
+ }
+
+ /**
+ * Marks classes, interfaces and static methods which provide their functionality to JBR API.
+ */
+ @Target({ElementType.TYPE, ElementType.METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Provides {
+ /**
+ * Binding target.
+ * For type binding this is a fully qualified name of corresponding {@code @Provided} class/interface,
+ * e.g. {@code com.jetbrains.My.Nested.Class}.
+ * For static method binding this is a fully qualified name of corresponding {@code @Provided} class/interface with optional
+ * method name appended after '#', e.g. {@code com.jetbrains.MyClass#myMethod}.
+ * If method name is omitted, name of annotated method itself is used.
+ * In all cases, package {@code com.jetbrains} may be omitted, e.g. {@code My.Nested.Class}, {@code MyClass#myMethod}.
+ */
+ String value();
+ }
+
+ /**
+ * Marks JBR API service.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Service {}
+
+ /**
+ * Creates new internal service instance of caller class type.
+ * Intended usage: {@code public static final MyService INSTANCE = JBRApi.internalService();}
+ * @return internal service instance
+ */
+ @CallerSensitive
+ @SuppressWarnings("unchecked")
+ public static T internalService() {
+ var caller = (Class) Reflection.getCallerClass();
+ if (caller == null) throw new IllegalCallerException("No caller frame");
+ return com.jetbrains.internal.JBRApi.getInternalService(caller);
+ }
+
+ /**
+ * If a JBR API service has a defined implementation class, it is instantiated via static factory method
+ * {@code create()} or a no-arg constructor. {@link ServiceNotAvailableException}
+ * may be thrown from that constructor or factory method to indicate that service is unavailable for some reason.
+ * Exception is not propagated to user, but rather {@code null} is returned for that service.
+ * Optional message and cause can be specified, but only used for logging (when explicitly enabled by VM flag).
+ */
+ public static class ServiceNotAvailableException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public ServiceNotAvailableException() { super(); }
+
+ public ServiceNotAvailableException(String message) { super(message); }
+
+ public ServiceNotAvailableException(String message, Throwable cause) { super(message, cause); }
+
+ public ServiceNotAvailableException(Throwable cause) { super(cause); }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/exported/JBRApiSupport.java b/src/java.base/share/classes/com/jetbrains/exported/JBRApiSupport.java
new file mode 100644
index 000000000000..f4bdc0af35b9
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/exported/JBRApiSupport.java
@@ -0,0 +1,63 @@
+package com.jetbrains.exported;
+
+import java.lang.annotation.Annotation;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * Supporting exported API for JBR API backend. Not intended to be used by JBR or client code directly.
+ */
+public class JBRApiSupport {
+ private JBRApiSupport() {}
+
+ /**
+ * Initializes JBR API.
+ * @param apiInterface internal {@code JBR.ServiceApi} interface
+ * @param serviceAnnotation {@code @Service} annotation class
+ * @param providedAnnotation {@code @Provided} annotation class
+ * @param providesAnnotation {@code @Provides} annotation class
+ * @param knownExtensions map of known extension enums and classes defining them
+ * @param extensionExtractor receives method, returns its extension enum, or null
+ * @return implementation for {@code JBR.ServiceApi} interface
+ */
+ @SuppressWarnings("rawtypes")
+ public static synchronized Object bootstrap(Class> apiInterface,
+ Class extends Annotation> serviceAnnotation,
+ Class extends Annotation> providedAnnotation,
+ Class extends Annotation> providesAnnotation,
+ Map, Class[]> knownExtensions,
+ Function> extensionExtractor) {
+ if (!com.jetbrains.internal.JBRApi.ENABLED) return null;
+ com.jetbrains.internal.JBRApi.init(
+ null,
+ serviceAnnotation,
+ providedAnnotation,
+ providesAnnotation,
+ knownExtensions,
+ extensionExtractor);
+ return com.jetbrains.internal.JBRApi.getService(apiInterface);
+ }
+
+ /**
+ * Bootstrap method for JBR API dynamic invocations.
+ */
+ public static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
+ return new ConstantCallSite(com.jetbrains.internal.JBRApi.bindDynamic(caller, name, type));
+ }
+
+ /**
+ * JBR API proxy class. Every generated proxy class implements this interface.
+ */
+ public interface Proxy {
+ /**
+ * We really don't want to clash with other possible method names.
+ * @return target object
+ */
+ Object $getProxyTarget();
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java b/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
new file mode 100644
index 000000000000..c70c98d88066
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.ClassFileFormatVersion;
+import java.lang.reflect.Method;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * Utility class that helps with bytecode generation
+ */
+class ASMUtils {
+
+ private static final MethodHandle genericSignatureGetter;
+ static {
+ try {
+ genericSignatureGetter = MethodHandles.privateLookupIn(Method.class, MethodHandles.lookup())
+ .findVirtual(Method.class, "getGenericSignature", MethodType.methodType(String.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new Error(e);
+ }
+ }
+
+ public static final int CLASSFILE_VERSION = ClassFileFormatVersion.latest().major();
+
+ public static void generateUnsupportedMethod(ClassVisitor writer, Method interfaceMethod) {
+ InternalMethodInfo methodInfo = getInternalMethodInfo(interfaceMethod);
+ MethodVisitor p = writer.visitMethod(ACC_PUBLIC | ACC_FINAL, methodInfo.name(),
+ methodInfo.descriptor(), methodInfo.genericSignature(), methodInfo.exceptionNames());
+ p.visitCode();
+ throwException(p, "java/lang/UnsupportedOperationException", "No implementation found for this method");
+ p.visitMaxs(0, 0);
+ p.visitEnd();
+ }
+
+ public static void throwException(MethodVisitor p, String type, String message) {
+ p.visitTypeInsn(NEW, type);
+ p.visitInsn(DUP);
+ p.visitLdcInsn(message);
+ p.visitMethodInsn(INVOKESPECIAL, type, "", "(Ljava/lang/String;)V", false);
+ p.visitInsn(ATHROW);
+ }
+
+ public record InternalMethodInfo(String name, String descriptor, String genericSignature,
+ String[] exceptionNames) {}
+
+ public static InternalMethodInfo getInternalMethodInfo(Method method) {
+ try {
+ return new InternalMethodInfo(
+ method.getName(),
+ Type.getMethodDescriptor(method),
+ (String) genericSignatureGetter.invoke(method),
+ getExceptionNames(method));
+ } catch (Throwable e) {
+ throw new Error(e);
+ }
+ }
+
+ private static String[] getExceptionNames(Method method) {
+ Class>[] exceptionTypes = method.getExceptionTypes();
+ String[] exceptionNames = new String[exceptionTypes.length];
+ for (int i = 0; i < exceptionTypes.length; i++) {
+ exceptionNames[i] = Type.getInternalName(exceptionTypes[i]);
+ }
+ return exceptionNames;
+ }
+
+ public static int getParameterSize(Class> c) {
+ if (c == Void.TYPE) {
+ return 0;
+ } else if (c == Long.TYPE || c == Double.TYPE) {
+ return 2;
+ }
+ return 1;
+ }
+
+ public static int getLoadOpcode(Class> c) {
+ if (c == Void.TYPE) {
+ throw new InternalError("Unexpected void type of load opcode");
+ }
+ return ILOAD + getOpcodeOffset(c);
+ }
+
+ public static int getReturnOpcode(Class> c) {
+ if (c == Void.TYPE) {
+ return RETURN;
+ }
+ return IRETURN + getOpcodeOffset(c);
+ }
+
+ private static int getOpcodeOffset(Class> c) {
+ if (c.isPrimitive()) {
+ if (c == Long.TYPE) {
+ return 1;
+ } else if (c == Float.TYPE) {
+ return 2;
+ } else if (c == Double.TYPE) {
+ return 3;
+ }
+ return 0;
+ } else {
+ return 4;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/AccessContext.java b/src/java.base/share/classes/com/jetbrains/internal/AccessContext.java
new file mode 100644
index 000000000000..c6a72a488e8f
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/AccessContext.java
@@ -0,0 +1,140 @@
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodType;
+import java.util.*;
+import java.util.function.Supplier;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
+import static jdk.internal.org.objectweb.asm.Type.getInternalName;
+
+/**
+ * Context used by {@link ProxyGenerator} to determine whether class or method is
+ * accessible from a caller class and generate appropriate direct or dynamic calls.
+ */
+class AccessContext {
+
+ private final Map, Boolean> accessibleClasses = new HashMap<>();
+ final Map dependencies = new HashMap<>(); // true for required, false for optional
+ final List dynamicCallTargets = new ArrayList<>();
+ final Lookup caller;
+
+ AccessContext(Lookup caller) {
+ this.caller = caller;
+ }
+
+ record DynamicCallTarget(String name, String descriptor, Supplier futureHandle) {}
+
+ class Method {
+ final MethodVisitor writer;
+ private final boolean methodRequired;
+
+ Method(MethodVisitor writer, boolean methodRequired) {
+ this.writer = writer;
+ this.methodRequired = methodRequired;
+ }
+
+ AccessContext access() {
+ return AccessContext.this;
+ }
+
+ void addDependency(Proxy p) {
+ if (methodRequired) dependencies.put(p, true);
+ else dependencies.putIfAbsent(p, false);
+ }
+
+ void invokeDynamic(MethodHandle handle) {
+ invokeDynamic(handle.type(), () -> handle);
+ }
+
+ void invokeDynamic(MethodType type, Supplier futureHandle) {
+ String descriptor = erase(type).descriptorString();
+ DynamicCallTarget t = new DynamicCallTarget("dynamic" + dynamicCallTargets.size(), descriptor, futureHandle);
+ dynamicCallTargets.add(t);
+ writer.visitInvokeDynamicInsn(t.name, descriptor,
+ new Handle(H_INVOKESTATIC, "com/jetbrains/exported/JBRApiSupport", "bootstrapDynamic",
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false));
+ }
+
+ void invokeDirect(MethodHandleInfo handleInfo) {
+ int opcode = switch (handleInfo.getReferenceKind()) {
+ default -> throw new RuntimeException("Unknown reference type");
+ case MethodHandleInfo.REF_getField -> GETFIELD;
+ case MethodHandleInfo.REF_getStatic -> GETSTATIC;
+ case MethodHandleInfo.REF_putField -> PUTFIELD;
+ case MethodHandleInfo.REF_putStatic -> PUTSTATIC;
+ case MethodHandleInfo.REF_invokeVirtual -> INVOKEVIRTUAL;
+ case MethodHandleInfo.REF_invokeStatic -> INVOKESTATIC;
+ case MethodHandleInfo.REF_invokeSpecial -> INVOKESPECIAL;
+ case MethodHandleInfo.REF_newInvokeSpecial -> NEW;
+ case MethodHandleInfo.REF_invokeInterface -> INVOKEINTERFACE;
+ };
+ String targetName = getInternalName(handleInfo.getDeclaringClass());
+ if (opcode == NEW) {
+ writer.visitTypeInsn(NEW, targetName);
+ writer.visitInsn(DUP);
+ opcode = INVOKESPECIAL;
+ }
+ switch (opcode) {
+ case GETFIELD, GETSTATIC -> writer.visitFieldInsn(opcode, targetName, handleInfo.getName(),
+ handleInfo.getMethodType().returnType().descriptorString());
+ case PUTFIELD, PUTSTATIC -> writer.visitFieldInsn(opcode, targetName, handleInfo.getName(),
+ handleInfo.getMethodType().parameterType(0).descriptorString());
+ default -> writer.visitMethodInsn(opcode, targetName, handleInfo.getName(),
+ handleInfo.getMethodType().descriptorString(), handleInfo.getDeclaringClass().isInterface());
+ }
+ }
+ }
+
+ MethodHandleInfo resolveDirect(MethodHandle handle) {
+ try {
+ MethodHandleInfo handleInfo = caller.revealDirect(handle);
+ if (isClassLoaderAccessible(caller.lookupClass().getClassLoader(),
+ handleInfo.getDeclaringClass().getClassLoader())) {
+ // Accessible directly.
+ return handleInfo;
+ }
+ } catch (Exception ignore) {}
+ // Not accessible directly.
+ return null;
+ }
+
+ /**
+ * Replaces all inaccessible types with {@link Object}.
+ * @param type original method type
+ * @return erased method type
+ */
+ private MethodType erase(MethodType type) {
+ if (!canAccess(type.returnType())) type = type.changeReturnType(Object.class);
+ for (int i = 0; i < type.parameterCount(); i++) {
+ if (!canAccess(type.parameterType(i))) type = type.changeParameterType(i, Object.class);
+ }
+ return type;
+ }
+
+ boolean canAccess(Class> clazz) {
+ return accessibleClasses.computeIfAbsent(clazz, c -> canAccess(caller, c));
+ }
+
+ static boolean canAccess(Lookup caller, Class> target) {
+ try {
+ if (isClassLoaderAccessible(caller.lookupClass().getClassLoader(),
+ caller.accessClass(target).getClassLoader())) return true;
+ } catch (IllegalAccessException ignore) {}
+ return false;
+ }
+
+ private static boolean isClassLoaderAccessible(ClassLoader caller, ClassLoader target) {
+ if (target == null) return true;
+ for (ClassLoader cl = caller; cl != null; cl = cl.getParent()) {
+ if (cl == target) return true;
+ }
+ return false;
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/IoOverNio.java b/src/java.base/share/classes/com/jetbrains/internal/IoOverNio.java
new file mode 100644
index 000000000000..a2e0ea4163c9
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/IoOverNio.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.misc.VM;
+import sun.nio.ch.FileChannelImpl;
+
+import java.io.Closeable;
+import java.nio.file.FileSystems;
+
+/**
+ * Internal methods to control the feature of using {@link java.nio.file} inside {@link java.io}.
+ */
+public class IoOverNio {
+ /**
+ * Preferences of debug logging.
+ */
+ public static final Debug DEBUG;
+ public static final boolean IS_ENABLED_IN_GENERAL =
+ System.getProperty("jbr.java.io.use.nio", "true").equalsIgnoreCase("true");
+ private static final ThreadLocal ALLOW_IN_THIS_THREAD = new ThreadLocal<>();
+
+ static {
+ String value = System.getProperty("jbr.java.io.use.nio.debug", "");
+ switch (value) {
+ case "error":
+ DEBUG = Debug.ERROR;
+ break;
+ case "no_error":
+ DEBUG = Debug.NO_ERROR;
+ break;
+ case "all":
+ DEBUG = Debug.ALL;
+ break;
+ default:
+ DEBUG = Debug.NO;
+ break;
+ }
+ }
+
+ private IoOverNio() {
+ }
+
+ public static boolean isAllowedInThisThread() {
+ return ALLOW_IN_THIS_THREAD.get() == null;
+ }
+
+ /**
+ * This method helps to prevent infinite recursion in specific areas like class loading.
+ * An attempt to access {@link FileSystems#getDefault()} during class loading
+ * would require loading the class of the default file system provider,
+ * which would recursively require the access to {@link FileSystems#getDefault()}.
+ *
+ * Usage:
+ *
+ * {@code
+ * @SuppressWarnings("try")
+ * void method() {
+ * try (var ignored = IoOverNio.disableInThisThread()) {
+ * // do something here.
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * An implementation note.
+ * There may be a temptation to call {@link FileSystems#getDefault()} at some very early stage of loading JVM.
+ * However, it won't be enough.
+ * {@link FileSystems#getDefault()} forces an immediate load only of
+ * the corresponding instance of {@link java.nio.file.spi.FileSystemProvider}.
+ * Meanwhile, a correct operation of a NIO filesystem requires also implementations of {@link java.nio.file.Path},
+ * {@link java.nio.file.FileSystem}, {@link java.nio.file.attribute.BasicFileAttributes} and many other classes.
+ * Without this method, the classloader still can dive into an infinite recursion.
+ */
+ public static ThreadLocalCloseable disableInThisThread() {
+ Integer value = ALLOW_IN_THIS_THREAD.get();
+ if (value == null) {
+ value = 0;
+ }
+ ++value;
+ ALLOW_IN_THIS_THREAD.set(value);
+ return ThreadLocalCloseable.INSTANCE;
+ }
+
+ public enum Debug {
+ NO(false, false),
+ ERROR(true, false),
+ NO_ERROR(false, true),
+ ALL(true, true);
+
+ private final boolean writeErrors;
+ private final boolean writeTraces;
+
+ Debug(boolean writeErrors, boolean writeTraces) {
+ this.writeErrors = writeErrors;
+ this.writeTraces = writeTraces;
+ }
+
+ private boolean mayWriteAnything() {
+ return VM.isBooted() && isAllowedInThisThread();
+ }
+
+ public boolean writeErrors() {
+ return writeErrors && mayWriteAnything();
+ }
+
+ public boolean writeTraces() {
+ return writeTraces && mayWriteAnything();
+ }
+ }
+
+ public static class ThreadLocalCloseable implements AutoCloseable {
+ private static final ThreadLocalCloseable INSTANCE = new ThreadLocalCloseable();
+
+ private ThreadLocalCloseable() {
+ }
+
+ @Override
+ public void close() {
+ Integer value = ALLOW_IN_THIS_THREAD.get();
+ --value;
+ if (value == 0) {
+ value = null;
+ }
+ ALLOW_IN_THIS_THREAD.set(value);
+ }
+ }
+
+ /**
+ * Since it's complicated to change Java API, some new function arguments are transferred via thread local variables.
+ * This variable allows to set {@code parent} in {@link FileChannelImpl} and therefore to avoid closing the file
+ * descriptor too early.
+ *
+ * The problem was found with the test {@code jtreg:test/jdk/java/io/FileDescriptor/Sharing.java}.
+ */
+ public static final ThreadLocal PARENT_FOR_FILE_CHANNEL_IMPL = new ThreadLocal<>();
+}
\ No newline at end of file
diff --git a/src/java.base/share/classes/com/jetbrains/internal/IoToNioErrorMessageHolder.java b/src/java.base/share/classes/com/jetbrains/internal/IoToNioErrorMessageHolder.java
new file mode 100644
index 000000000000..eb6124b181fa
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/IoToNioErrorMessageHolder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.io.IOException;
+import java.util.WeakHashMap;
+
+/**
+ * The only purpose of this class is to mimic exception messages of `java.io`
+ * when `java.nio.file` is used as a backend.
+ */
+public class IoToNioErrorMessageHolder {
+ private IoToNioErrorMessageHolder() { }
+
+ /**
+ * While an exception is being thrown and handled inside {@code catch}-blocks,
+ * at least one strong reference exists in the stack.
+ * GC won't bring harm if {@link #removeMessage(IOException)} is called inside a {@code catch}-block.
+ */
+ private static final WeakHashMap messages = new WeakHashMap<>();
+
+ public static void addMessage(IOException e, String message) {
+ if (IoOverNio.IS_ENABLED_IN_GENERAL) {
+ synchronized (messages) {
+ messages.put(e, message);
+ }
+ }
+ }
+
+ public static String removeMessage(IOException e) {
+ if (IoOverNio.IS_ENABLED_IN_GENERAL) {
+ synchronized (messages) {
+ return messages.remove(e);
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java b/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
new file mode 100644
index 000000000000..97ee533ce714
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import com.jetbrains.exported.JBRApi.Provides;
+
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * JBR API is a collection of JBR-specific features that are accessed by client though
+ * {@link com.jetbrains.JBR jetbrains.runtime.api} module. Actual implementation is linked by
+ * JBR at runtime by generating {@linkplain Proxy proxy objects}.
+ * Mapping between interfaces and implementation code is defined using
+ * {@link com.jetbrains.exported.JBRApi.Provided} and {@link com.jetbrains.exported.JBRApi.Provides} annotations.
+ *
+ * This class is an entry point into JBR API backend.
+ * @see Proxy
+ */
+public class JBRApi {
+ /**
+ * Enable JBR API, it wouldn't init when disabled. Enabled by default.
+ */
+ public static final boolean ENABLED = Utils.property("jetbrains.runtime.api.enabled", true);
+ /**
+ * Enable API extensions. When disabled, extension methods are treated like any other method,
+ * {@link JBRApi#isExtensionSupported} always returns false, {@link JBRApi#getService(Class, Enum[])}
+ * behaves the same as {@link JBRApi#getService(Class)}. Enabled by default.
+ */
+ static final boolean EXTENSIONS_ENABLED = Utils.property("jetbrains.runtime.api.extensions.enabled", true);
+ /**
+ * Enable extensive debugging logging. Disabled by default.
+ */
+ public static final boolean VERBOSE = Utils.property("jetbrains.runtime.api.verbose", false);
+ /**
+ * Print warnings about usage of deprecated interfaces and methods to {@link System#err}. Enabled by default.
+ */
+ static final boolean LOG_DEPRECATED = Utils.property("jetbrains.runtime.api.logDeprecated", true);
+ /**
+ * Enable additional verification of generated bytecode. Disabled by default.
+ */
+ static final boolean VERIFY_BYTECODE = Utils.property("jetbrains.runtime.api.verifyBytecode", false);
+ /**
+ * Allow extending registry. Disabled by default, used for tests.
+ */
+ private static final boolean EXTEND_REGISTRY = Utils.property("jetbrains.runtime.api.extendRegistry", false);
+
+ record DynamicCallTargetKey(Class> proxy, String name, String descriptor) {}
+ static final ConcurrentMap> dynamicCallTargets = new ConcurrentHashMap<>();
+ private static final ProxyRepository proxyRepository = new ProxyRepository();
+
+ private static Boolean[] supportedExtensions;
+ private static long[] emptyExtensionsBitfield;
+ @SuppressWarnings("rawtypes")
+ private static Map, Class[]> knownExtensions;
+ static Function> extensionExtractor;
+
+ @SuppressWarnings("rawtypes")
+ public static void init(InputStream extendedRegistryStream,
+ Class extends Annotation> serviceAnnotation,
+ Class extends Annotation> providedAnnotation,
+ Class extends Annotation> providesAnnotation,
+ Map, Class[]> knownExtensions,
+ Function> extensionExtractor) {
+ if (extendedRegistryStream != null && !EXTEND_REGISTRY) {
+ throw new Error("Extending JBR API registry is not supported");
+ }
+ proxyRepository.init(extendedRegistryStream, serviceAnnotation, providedAnnotation, providesAnnotation);
+
+ if (EXTENSIONS_ENABLED) {
+ JBRApi.knownExtensions = knownExtensions;
+ JBRApi.extensionExtractor = extensionExtractor;
+ supportedExtensions = new Boolean[
+ knownExtensions.keySet().stream().mapToInt(Enum::ordinal).max().orElse(-1) + 1];
+ emptyExtensionsBitfield = new long[(supportedExtensions.length + 63) / 64];
+ }
+
+ if (VERBOSE) {
+ System.out.println("JBR API init\n knownExtensions = " + (EXTENSIONS_ENABLED ? knownExtensions.keySet() : "DISABLED"));
+ }
+ }
+
+ public static MethodHandle bindDynamic(Lookup caller, String name, MethodType type) {
+ if (VERBOSE) {
+ System.out.println("Binding call site " + caller.lookupClass().getName() + "#" + name + ": " + type);
+ }
+ if (!caller.hasFullPrivilegeAccess()) throw new Error("Caller lookup must have full privilege access"); // Authenticity check.
+ return dynamicCallTargets.get(new DynamicCallTargetKey(caller.lookupClass(), name, type.descriptorString())).get().asType(type);
+ }
+
+ /**
+ * @return JBR API version supported by current implementation.
+ */
+ @Provides("JBR.ServiceApi")
+ public static String getImplVersion() {
+ return proxyRepository.getVersion();
+ }
+
+ /**
+ * @param extension extension name
+ * @return true if all methods belonging to given extension are supported
+ * @apiNote this method is a part of internal {@link com.jetbrains.JBR.ServiceApi}
+ * service, but is not directly exposed to user.
+ */
+ @Provides("JBR.ServiceApi")
+ public static boolean isExtensionSupported(Enum> extension) {
+ if (!EXTENSIONS_ENABLED) return false;
+ int i = extension.ordinal();
+ if (supportedExtensions[i] == null) {
+ synchronized (JBRApi.class) {
+ if (supportedExtensions[i] == null) {
+ boolean result = true;
+ for (Class> c : knownExtensions.get(extension)) {
+ result &= proxyRepository.getProxy(c, null).isExtensionSupported(extension);
+ }
+ supportedExtensions[i] = result;
+ }
+ }
+ }
+ return supportedExtensions[i];
+ }
+
+ /**
+ * @return fully supported service implementation for the given interface with specified extensions, or null
+ * @apiNote this method is a part of internal {@link com.jetbrains.JBR.ServiceApi}
+ * service, but is not directly exposed to user.
+ */
+ @Provides("JBR.ServiceApi")
+ public static T getService(Class interFace, Enum>... extensions) {
+ if (!EXTENSIONS_ENABLED) return getService(interFace);
+
+ long[] bitfield = new long[emptyExtensionsBitfield.length];
+ for (Enum> e : extensions) {
+ if (isExtensionSupported(e)) {
+ int i = e.ordinal() / 64;
+ int j = e.ordinal() % 64;
+ bitfield[i] |= 1L << j;
+ } else {
+ if (VERBOSE) {
+ Utils.log(Utils.BEFORE_JBR, System.err, "Warning: Extension not supported: " + e.name());
+ }
+ return null;
+ }
+ }
+
+ return getService(interFace, bitfield, true);
+ }
+
+ /**
+ * @return fully supported service implementation for the given interface, or null
+ * @apiNote this method is a part of internal {@link com.jetbrains.JBR.ServiceApi}
+ * service, but is not directly exposed to user.
+ */
+ @Provides("JBR.ServiceApi")
+ public static T getService(Class interFace) {
+ return getService(interFace, emptyExtensionsBitfield, true);
+ }
+
+ public static T getInternalService(Class interFace) {
+ return getService(interFace, emptyExtensionsBitfield, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static T getService(Class interFace, long[] extensions, boolean publicService) {
+ Proxy p = proxyRepository.getProxy(interFace, null);
+ if ((p.getFlags() & Proxy.SERVICE) == 0 || (publicService && (p.getFlags() & Proxy.INTERNAL) != 0)) {
+ if (VERBOSE) {
+ Utils.log(Utils.BEFORE_JBR, System.err, "Warning: Not allowed as a service: " + interFace.getCanonicalName());
+ }
+ return null;
+ }
+ if (!p.init()) {
+ if (VERBOSE) {
+ Utils.log(Utils.BEFORE_JBR, System.err, "Warning: Service not supported: " + interFace.getCanonicalName());
+ }
+ return null;
+ }
+ try {
+ MethodHandle constructor = p.getConstructor();
+ return (T) (EXTENSIONS_ENABLED ? constructor.invoke(extensions) : constructor.invoke());
+ } catch (com.jetbrains.exported.JBRApi.ServiceNotAvailableException | NullPointerException e) {
+ if (VERBOSE) {
+ synchronized (System.err) {
+ Utils.log(Utils.BEFORE_JBR, System.err, "Warning: Service not available: " + interFace.getCanonicalName());
+ System.err.print("Caused by: ");
+ e.printStackTrace(System.err);
+ }
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/Mapping.java b/src/java.base/share/classes/com/jetbrains/internal/Mapping.java
new file mode 100644
index 000000000000..8a0792ab42db
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/Mapping.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.stream.Stream;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static jdk.internal.org.objectweb.asm.Type.getInternalName;
+
+/**
+ * Mapping defines conversion of parameters and return types between source and destination method.
+ */
+abstract class Mapping {
+
+ static class Query {
+ boolean valid = true;
+ boolean needsExtensions = false;
+ }
+
+ final Class> from, to;
+
+ private Mapping(Class> from, Class> to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ void convert(AccessContext.Method context) {
+ MethodVisitor m = context.writer;
+ Label skipConvert = new Label();
+ m.visitInsn(DUP);
+ m.visitJumpInsn(IFNULL, skipConvert);
+ convertNonNull(context);
+ m.visitLabel(skipConvert);
+ }
+
+ abstract void convertNonNull(AccessContext.Method context);
+
+ void cast(AccessContext.Method context) {
+ if (context.access().canAccess(to)) context.writer.visitTypeInsn(CHECKCAST, getInternalName(to));
+ }
+
+ abstract Mapping inverse();
+
+ void query(Query q) {}
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract String toString();
+
+ static class Identity extends Mapping {
+ private Identity(Class> c) {
+ super(c, c);
+ }
+ @Override
+ void convert(AccessContext.Method context) {}
+ @Override
+ void convertNonNull(AccessContext.Method context) {}
+ @Override
+ Mapping inverse() { return this; }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Identity i = (Identity) o;
+ return from.equals(i.from);
+ }
+ @Override
+ public int hashCode() { return from.hashCode(); }
+ @Override
+ public String toString() { return from.getName(); }
+ }
+
+ static class Invalid extends Identity {
+ private Invalid(Class> c) {
+ super(c);
+ }
+ @Override
+ void query(Query q) { q.valid = false; }
+ @Override
+ public String toString() { return "INVALID(" + from.getName() + ")"; }
+ }
+
+ static abstract class Nesting extends Mapping {
+ final Mapping component;
+ Nesting(Class> from, Class> to, Mapping component) {
+ super(from, to);
+ this.component = component;
+ }
+ @Override
+ void query(Query q) { component.query(q); }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Nesting nesting = (Nesting) o;
+ if (!Objects.equals(from, nesting.from)) return false;
+ if (!Objects.equals(to, nesting.to)) return false;
+ return component.equals(nesting.component);
+ }
+ @Override
+ public int hashCode() {
+ int result = from != null ? from.hashCode() : 0;
+ result = 31 * result + (to != null ? to.hashCode() : 0);
+ result = 31 * result + component.hashCode();
+ return result;
+ }
+ }
+
+ static class Array extends Nesting {
+ private Array(Mapping component) {
+ super(component.from.arrayType(), component.to.arrayType(), component);
+ }
+ static Mapping wrap(Mapping m) {
+ if (m instanceof Identity) return new Identity(m.from.arrayType());
+ else return new Array(m);
+ }
+ @Override
+ void convert(AccessContext.Method context) {
+ super.convert(context);
+ cast(context); // Explicitly cast to result type after non-null branch
+ }
+ @Override
+ void convertNonNull(AccessContext.Method context) {
+ final int TEMP_COUNTER_SLOT = 1; // Warning! We overwrite 1st local slot.
+ MethodVisitor m = context.writer;
+ Label loopStart = new Label(), loopEnd = new Label();
+ // Stack: fromArray -> toArray, fromArray, i=length
+ if (!context.access().canAccess(from)) m.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
+ m.visitInsn(DUP);
+ m.visitInsn(ARRAYLENGTH);
+ if (context.access().canAccess(to)) {
+ m.visitTypeInsn(ANEWARRAY, getInternalName(Objects.requireNonNull(to.componentType())));
+ } else context.invokeDynamic(MethodHandles.arrayConstructor(to));
+ m.visitInsn(SWAP);
+ m.visitInsn(DUP);
+ m.visitInsn(ARRAYLENGTH);
+ // Check loop conditions
+ m.visitLabel(loopStart);
+ m.visitInsn(DUP);
+ m.visitJumpInsn(IFLE, loopEnd);
+ // Stack: toArray, fromArray, i -> toArray, fromArray, i, toArray, i, from
+ m.visitVarInsn(ISTORE, TEMP_COUNTER_SLOT);
+ m.visitInsn(DUP2);
+ m.visitIincInsn(TEMP_COUNTER_SLOT, -1);
+ m.visitVarInsn(ILOAD, TEMP_COUNTER_SLOT);
+ m.visitInsn(DUP_X2);
+ m.visitInsn(DUP_X1);
+ m.visitInsn(AALOAD);
+ // Stack from -> to
+ component.convert(context);
+ // Stack: toArray, fromArray, i, toArray, i, to -> toArray, fromArray, i
+ m.visitInsn(AASTORE);
+ m.visitJumpInsn(GOTO, loopStart);
+ m.visitLabel(loopEnd);
+ // Stack: toArray, fromArray, i -> toArray
+ m.visitInsn(POP2);
+ }
+ @Override
+ Mapping inverse() { return new Array(component.inverse()); }
+ @Override
+ public String toString() { return "[" + component + "]"; }
+ }
+
+ private static abstract class ProxyConversion extends Mapping {
+ final Proxy fromProxy, toProxy;
+ private ProxyConversion(Class> from, Class> to, Proxy fromProxy, Proxy toProxy) {
+ super(from, to);
+ this.fromProxy = fromProxy;
+ this.toProxy = toProxy;
+ }
+ void wrapNonNull(AccessContext.Method context) {
+ context.addDependency(toProxy);
+ MethodType mt;
+ if (JBRApi.EXTENSIONS_ENABLED) {
+ context.writer.visitVarInsn(ALOAD, 0);
+ mt = MethodType.methodType(to, from, long[].class);
+ } else {
+ mt = MethodType.methodType(to, from);
+ }
+ context.invokeDynamic(mt, toProxy::getConstructor);
+ }
+ void extractNonNull(AccessContext.Method context) {
+ context.addDependency(fromProxy);
+ context.writer.visitMethodInsn(INVOKEINTERFACE,
+ "com/jetbrains/exported/JBRApiSupport$Proxy", "$getProxyTarget", "()Ljava/lang/Object;", true);
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ProxyConversion i = (ProxyConversion) o;
+ return from.equals(i.from) && to.equals(i.to) &&
+ Objects.equals(fromProxy, i.fromProxy) && Objects.equals(toProxy, i.toProxy);
+ }
+ @Override
+ public int hashCode() {
+ int result = from.hashCode();
+ result = 31 * result + to.hashCode();
+ result = 31 * result + Objects.hashCode(fromProxy);
+ result = 31 * result + Objects.hashCode(toProxy);
+ return result;
+ }
+ }
+
+ private static class Wrap extends ProxyConversion {
+ private Wrap(Class> from, Class> to, Proxy proxy) {
+ super(from, to, null, proxy);
+ }
+ @Override
+ void convertNonNull(AccessContext.Method context) { wrapNonNull(context); }
+ @Override
+ Mapping inverse() { return new Extract(to, from, toProxy); }
+ @Override
+ void query(Query q) { q.needsExtensions = JBRApi.EXTENSIONS_ENABLED; }
+ @Override
+ public String toString() { return from.getName() + " --wrap-> " + to.getName(); }
+ }
+
+ private static class Extract extends ProxyConversion {
+ private Extract(Class> from, Class> to, Proxy proxy) {
+ super(from, to, proxy, null);
+ }
+ @Override
+ void convert(AccessContext.Method context) {
+ super.convert(context);
+ cast(context); // Explicitly cast to result type after non-null branch
+ }
+ @Override
+ void convertNonNull(AccessContext.Method context) { extractNonNull(context); }
+ @Override
+ Mapping inverse() { return new Wrap(to, from, fromProxy); }
+ @Override
+ public String toString() { return from.getName() + " --extract-> " + to.getName(); }
+ }
+
+ private static class Dynamic2Way extends ProxyConversion {
+ private Dynamic2Way(Class> from, Class> to, Proxy fromProxy, Proxy toProxy) {
+ super(from, to, fromProxy, toProxy);
+ }
+ @Override
+ void convert(AccessContext.Method context) {
+ Label elseBranch = new Label(), afterBranch = new Label();
+ MethodVisitor m = context.writer;
+ m.visitInsn(DUP);
+ m.visitJumpInsn(IFNULL, afterBranch);
+ m.visitInsn(DUP);
+ m.visitTypeInsn(INSTANCEOF, "com/jetbrains/exported/JBRApiSupport$Proxy");
+ m.visitJumpInsn(IFEQ, elseBranch);
+ extractNonNull(context);
+ m.visitJumpInsn(GOTO, afterBranch);
+ m.visitLabel(elseBranch);
+ wrapNonNull(context);
+ m.visitLabel(afterBranch);
+ cast(context); // Explicitly cast to result type after non-null branch
+ }
+ @Override
+ void convertNonNull(AccessContext.Method context) {}
+ @Override
+ Mapping inverse() { return new Dynamic2Way(to, from, toProxy, fromProxy); }
+ @Override
+ void query(Query q) { q.needsExtensions = JBRApi.EXTENSIONS_ENABLED; }
+ @Override
+ public String toString() { return from.getName() + " --2way-> " + to.getName(); }
+ }
+
+ private static class CustomOptional extends Nesting {
+ private CustomOptional(Mapping component) {
+ super(Optional.class, Optional.class, component);
+ }
+ static Mapping wrap(Mapping m) {
+ if (m instanceof Identity) return new Identity(Optional.class);
+ else return new CustomOptional(m);
+ }
+ @Override
+ void convertNonNull(AccessContext.Method context) {
+ MethodVisitor m = context.writer;
+ m.visitInsn(ACONST_NULL);
+ m.visitMethodInsn(INVOKEVIRTUAL, "java/util/Optional", "orElse", "(Ljava/lang/Object;)Ljava/lang/Object;", false);
+ component.convert(context);
+ m.visitMethodInsn(INVOKESTATIC, "java/util/Optional", "ofNullable", "(Ljava/lang/Object;)Ljava/util/Optional;", false);
+ }
+ @Override
+ Mapping inverse() { return new CustomOptional(component.inverse()); }
+ @Override
+ public String toString() { return "Optional<" + component + ">"; }
+ }
+
+ record Method(MethodType type, Mapping returnMapping, Mapping[] parameterMapping, Query query) {
+ @Override
+ public String toString() {
+ return returnMapping + "(" + Arrays.toString(parameterMapping) + ")";
+ }
+ }
+
+ static class Context {
+
+ private final ProxyRepository proxyRepository;
+ private final Map, Mapping> tvMappings = new HashMap<>();
+
+ Context(ProxyRepository proxyRepository) {
+ this.proxyRepository = proxyRepository;
+ }
+
+ void initTypeParameters(Class> type, Stream typeParameters) {
+ if (type == null) return;
+ if (typeParameters != null) {
+ TypeVariable>[] tvs = type.getTypeParameters();
+ Iterator tpIterator = typeParameters.iterator();
+ for (int i = 0;; i++) {
+ if ((i < tvs.length) ^ tpIterator.hasNext()) throw new RuntimeException("Number of type parameters doesn't match");
+ if (i >= tvs.length) break;
+ tvMappings.put(tvs[i], tpIterator.next());
+ }
+ }
+ initTypeParameters(type.getGenericSuperclass());
+ for (Type t : type.getGenericInterfaces()) initTypeParameters(t);
+ }
+
+ void initTypeParameters(Type supertype) {
+ if (supertype instanceof ParameterizedType type) {
+ initTypeParameters((Class>) type.getRawType(),
+ Stream.of(type.getActualTypeArguments()).map(this::getMapping));
+ } else if (supertype instanceof Class> c) {
+ initTypeParameters(c, null);
+ } else if (supertype != null) {
+ throw new RuntimeException("Unknown supertype kind: " + supertype.getClass());
+ }
+ }
+
+ Method getMapping(java.lang.reflect.Method interfaceMethod) {
+ Type[] params = interfaceMethod.getGenericParameterTypes();
+ List> ptypes = new ArrayList<>(params.length);
+ Mapping[] paramMappings = new Mapping[params.length];
+ for (int i = 0; i < params.length; i++) {
+ paramMappings[i] = getMapping(params[i]);
+ ptypes.add(paramMappings[i].to);
+ }
+ Mapping returnMapping = getMapping(interfaceMethod.getGenericReturnType()).inverse();
+ return new Method(MethodType.methodType(returnMapping.from, ptypes), returnMapping, paramMappings,
+ query(returnMapping, paramMappings));
+ }
+
+ private Mapping getMapping(Type userType) {
+ if (userType instanceof Class> t) {
+ return getMapping(t, null);
+ } else if (userType instanceof GenericArrayType t) {
+ return Array.wrap(getMapping(t.getGenericComponentType()));
+ } else if (userType instanceof ParameterizedType t) {
+ return getMapping(t);
+ } else if (userType instanceof TypeVariable> t) {
+ Mapping tvMapping = tvMappings.get(t);
+ if (tvMapping != null) return tvMapping;
+ Type[] bounds = t.getBounds();
+ return getMapping(bounds.length > 0 ? bounds[0] : Object.class);
+ } else if (userType instanceof WildcardType t) {
+ Type[] bounds = t.getUpperBounds();
+ return getMapping(bounds.length > 0 ? bounds[0] : Object.class);
+ } else {
+ throw new RuntimeException("Unknown type kind: " + userType.getClass());
+ }
+ }
+
+ private Mapping getMapping(ParameterizedType userType) {
+ Type[] actual = userType.getActualTypeArguments();
+ Mapping[] specialization = null;
+ for (int i = 0; i < actual.length; i++) {
+ Mapping m = getMapping(actual[i]);
+ if (m instanceof Identity) continue;
+ if (specialization == null) specialization = new Mapping[actual.length];
+ specialization[i] = m;
+ }
+ return getMapping((Class>) userType.getRawType(), specialization);
+ }
+
+ private Mapping getMapping(Class> userType, Mapping[] specialization) {
+ if (userType.isArray()) {
+ return Array.wrap(getMapping(userType.componentType()));
+ }
+ if (specialization != null && specialization.length == 1 && specialization[0] != null &&
+ userType.equals(Optional.class)) {
+ return CustomOptional.wrap(specialization[0]);
+ }
+ Proxy p = proxyRepository.getProxy(userType, specialization);
+ if (p.supported() != Boolean.FALSE && p.inverse().supported() != Boolean.FALSE &&
+ (p.getFlags() & Proxy.SERVICE) == 0 && (p.inverse().getFlags() & Proxy.SERVICE) == 0) {
+ if (p.getInterface() != null && p.inverse().getInterface() != null) {
+ return new Dynamic2Way(userType, p.inverse().getInterface(), p, p.inverse());
+ } else if (p.getInterface() != null) {
+ if (p.getTarget() != null) return new Extract(userType, p.getTarget(), p);
+ } else if (p.inverse().getInterface() != null) {
+ return new Wrap(userType, p.inverse().getInterface(), p.inverse());
+ } else {
+ return new Identity(userType);
+ }
+ }
+ return new Invalid(userType);
+ }
+
+ private static Query query(Mapping returnMapping, Mapping[] parameterMapping) {
+ Query q = new Query();
+ returnMapping.query(q);
+ for (Mapping p : parameterMapping) p.query(q);
+ return q;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/Proxy.java b/src/java.base/share/classes/com/jetbrains/internal/Proxy.java
new file mode 100644
index 000000000000..0a70b7792a4d
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/Proxy.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.util.*;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * Proxy is needed to dynamically link JBR API interfaces and implementation at runtime.
+ * Proxy implements or extends a base interface or class and delegates method calls to
+ * target object or static methods. Calls may be delegated even to methods inaccessible by base type.
+ *
+ * Mapping between interfaces and implementation code is defined using
+ * {@link com.jetbrains.exported.JBRApi.Provided} and {@link com.jetbrains.exported.JBRApi.Provides} annotations.
+ *
+ * When JBR API interface or imeplementation type is used as a method parameter or return type,
+ * JBR API backend performs conversions to ensure each side receives object or correct type,
+ * for example, given following types:
+ *
{@code
+ * interface A {
+ * B foo(B b);
+ * }
+ * interface B {
+ * void bar();
+ * }
+ * class AImpl {
+ * BImpl foo(BImpl b) {}
+ * }
+ * class BImpl {
+ * void bar() {}
+ * }
+ * }
+ * {@code A#foo(B)} will be mapped to {@code AImpl#foo(BImpl)}.
+ * Conversion between {@code B} and {@code BImpl} will be done by JBR API backend.
+ * Here's what generated proxies would look like:
+ * {@code
+ * final class AProxy implements A {
+ * final AImpl target;
+ * AProxy(AImpl t) {
+ * target = t;
+ * }
+ * @Override
+ * B foo(B b) {
+ * BImpl ret = t.foo(b.target);
+ * return new BProxy(ret);
+ * }
+ * }
+ * final class BProxy implements B {
+ * final BImpl target;
+ * BProxy(BImpl t) {
+ * target = t;
+ * }
+ * @Override
+ * void bar() {
+ * t.bar();
+ * }
+ * }
+ * }
+ *
+ * Method signatures of base type and implementation are validated to ensure that proxy can
+ * properly delegate call to the target implementation code. If there's no implementation found for some
+ * interface methods, corresponding proxy is considered unsupported. Proxy is also considered unsupported
+ * if any proxy used by it is unsupported.
+ *
+ * Extensions are an exception to this rule. Methods of base type may be marked as extension methods,
+ * which makes them optional for support checks. Moreover, extension must be explicitly enabled for a
+ * proxy instance to enable usage of corresponding methods.
+ */
+class Proxy {
+ /**
+ * @see Proxy.Info#flags
+ */
+ static final int
+ INTERNAL = 1,
+ SERVICE = 2;
+
+ private final Proxy inverse;
+ private final Class> interFace, target;
+ private final int flags;
+ private volatile ProxyGenerator generator;
+ private volatile Boolean supported;
+
+ private volatile Set directDependencies = Set.of();
+ private volatile Set> supportedExtensions = Set.of();
+
+ private volatile MethodHandle constructor;
+
+ /**
+ * Creates empty proxy.
+ */
+ static Proxy empty(Boolean supported) {
+ return new Proxy(supported);
+ }
+
+ /**
+ * Creates a new proxy (and possibly an inverse one) from {@link Info}.
+ */
+ static Proxy create(ProxyRepository repository,
+ Info info, Mapping[] specialization,
+ Info inverseInfo, Mapping[] inverseSpecialization) {
+ return new Proxy(repository, info, specialization, null, inverseInfo, inverseSpecialization);
+ }
+
+ private Proxy(Boolean supported) {
+ interFace = target = null;
+ flags = 0;
+ inverse = this;
+ this.supported = supported;
+ }
+
+ private Proxy(ProxyRepository repository, Info info, Mapping[] specialization,
+ Proxy inverseProxy, Info inverseInfo, Mapping[] inverseSpecialization) {
+ if (info != null) {
+ interFace = info.interfaceLookup.lookupClass();
+ target = info.targetLookup == null ? null : info.targetLookup.lookupClass();
+ flags = info.flags;
+ generator = new ProxyGenerator(repository, info, specialization);
+ } else {
+ interFace = target = null;
+ flags = 0;
+ }
+ inverse = inverseProxy == null ? new Proxy(repository, inverseInfo, inverseSpecialization, this, null, null) : inverseProxy;
+ if (inverse.getInterface() != null) directDependencies = Set.of(inverse);
+ }
+
+ /**
+ * @return inverse proxy
+ */
+ Proxy inverse() {
+ return inverse;
+ }
+
+ /**
+ * @return interface class
+ */
+ Class> getInterface() {
+ return interFace;
+ }
+
+ /**
+ * @return target class
+ */
+ Class> getTarget() {
+ return target;
+ }
+
+ /**
+ * @return flags
+ */
+ int getFlags() {
+ return flags;
+ }
+
+ /**
+ * Checks if all methods are implemented for this proxy and all proxies it uses.
+ * {@code null} means not known yet.
+ */
+ Boolean supported() {
+ return supported;
+ }
+
+ private synchronized boolean generate() {
+ if (supported != null) return supported;
+ if (generator == null) return false;
+ Set deps = new HashSet<>(directDependencies);
+ supported = generator.generate();
+ for (Map.Entry e : generator.getDependencies().entrySet()) {
+ if (e.getKey().generate()) deps.add(e.getKey());
+ else if (e.getValue()) {
+ supported = false;
+ break;
+ }
+ }
+ if (supported) {
+ directDependencies = deps;
+ supportedExtensions = generator.getSupportedExtensions();
+ } else generator = null; // Release for gc
+ return supported;
+ }
+
+ /**
+ * Checks if specified extension is implemented by this proxy.
+ * Implicitly runs bytecode generation.
+ */
+ boolean isExtensionSupported(Enum> extension) {
+ if (supported == null) generate();
+ return supportedExtensions.contains(extension);
+ }
+
+ private synchronized boolean define() {
+ if (constructor != null) return true;
+ if (!generate()) return false;
+ constructor = generator.define((flags & SERVICE) != 0);
+ if (constructor == null) {
+ supported = false;
+ return false;
+ }
+ for (Proxy p : directDependencies) {
+ if (!p.define()) return false;
+ }
+ return true;
+ }
+
+ synchronized boolean init() {
+ if (!define()) return false;
+ if (generator != null) {
+ ProxyGenerator gen = generator;
+ generator = null;
+ gen.init();
+ for (Proxy p : directDependencies) p.init();
+ }
+ return supported;
+ }
+
+ /**
+ * @return method handle for the constructor of this proxy.
+ * First parameter is target object to which it would delegate method calls.
+ * Second parameter is extensions bitfield (if extensions are enabled).
+ */
+ MethodHandle getConstructor() throws NullPointerException {
+ if (JBRApi.LOG_DEPRECATED && interFace.isAnnotationPresent(Deprecated.class)) {
+ Utils.log(Utils.BEFORE_BOOTSTRAP_DYNAMIC, System.err,
+ "Warning: using deprecated JBR API interface " + interFace.getCanonicalName());
+ }
+ return constructor;
+ }
+
+ /**
+ * Proxy descriptor with all classes and lookup contexts resolved.
+ * Contains all necessary information to create a {@linkplain Proxy proxy}.
+ */
+ static class Info {
+ private record StaticMethod(String name, MethodType targetType) {}
+
+ private final Map staticMethods = new HashMap<>();
+ final Lookup interfaceLookup;
+ final Lookup targetLookup;
+ private final int flags;
+
+ Info(Lookup interfaceLookup, Lookup targetLookup, int flags) {
+ this.interfaceLookup = interfaceLookup;
+ this.targetLookup = targetLookup;
+ this.flags = flags;
+ }
+
+ void addStaticMethod(String name, MethodHandle target) {
+ staticMethods.put(new StaticMethod(name, target.type()), target);
+ }
+
+ MethodHandle getStaticMethod(String name, MethodType targetType) {
+ return staticMethods.get(new StaticMethod(name, targetType));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java b/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java
new file mode 100644
index 000000000000..d5bcfb740a98
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.*;
+import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.jetbrains.exported.JBRApi.ServiceNotAvailableException;
+import static com.jetbrains.internal.ASMUtils.*;
+import static com.jetbrains.internal.JBRApi.EXTENSIONS_ENABLED;
+import static java.lang.invoke.MethodHandles.Lookup;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static jdk.internal.org.objectweb.asm.Type.getInternalName;
+
+/**
+ * Generates {@linkplain Proxy proxy} classes.
+ *
+ * Proxy class always implements/extends a base interface/class. It's defined as a
+ * {@linkplain MethodHandles.Lookup#defineHiddenClass(byte[], boolean, Lookup.ClassOption...) hidden class}
+ * sharing the nest with either interface or target implementation class.
+ * Defining proxy as a nestmate of a target class is preferred,
+ * as in this case proxy can call implementation methods directly.
+ * However, this may not be possible if interface is not accessible by target
+ * (thus making it impossible for proxy to implement it), or if there is no target class at all
+ * (e.g. a service may delegate all calls exclusively to static methods).
+ *
+ * Proxy invokes target methods in two ways: either directly, when accessible
+ * ({@code invokestatic}, {@code invokevirtual}, {@code invokeinterface}),
+ * or via {@code invokedynamic} in cases when target method is inaccessible
+ * (see {@link com.jetbrains.exported.JBRApiSupport#bootstrapDynamic(Lookup, String, MethodType)}).
+ * @see Proxy
+ */
+class ProxyGenerator {
+ private static final String PROXY_INTERFACE_NAME = getInternalName(com.jetbrains.exported.JBRApiSupport.Proxy.class);
+
+ private final Proxy.Info info;
+ private final Class> interFace;
+ private final Lookup proxyGenLookup;
+ private final Mapping[] specialization;
+ private final Mapping.Context mappingContext;
+ private final AccessContext accessContext;
+
+ private final Class> constructorTargetParameterType;
+ private final String targetDescriptor, proxyName, superclassName;
+ private final String[] superinterfaceNames;
+ private final ClassWriter originalProxyWriter;
+ private final ClassVisitor proxyWriter;
+
+ private final Map, Boolean> supportedExtensions = new HashMap<>();
+
+ private boolean supported = true;
+ private Lookup generatedProxy;
+
+ /**
+ * Creates new proxy generator from given {@link Proxy.Info},
+ */
+ ProxyGenerator(ProxyRepository proxyRepository, Proxy.Info info, Mapping[] specialization) {
+ this.info = info;
+ this.interFace = info.interfaceLookup.lookupClass();
+ this.specialization = specialization;
+
+ // Placing our proxy implementation into the target's nest is preferred, as it gives us direct method calls.
+ this.proxyGenLookup = info.targetLookup != null && AccessContext.canAccess(info.targetLookup, interFace) ?
+ info.targetLookup : info.interfaceLookup;
+
+ this.mappingContext = new Mapping.Context(proxyRepository);
+ this.accessContext = new AccessContext(proxyGenLookup);
+
+ constructorTargetParameterType = info.targetLookup == null ? null :
+ accessContext.canAccess(info.targetLookup.lookupClass()) ? info.targetLookup.lookupClass() : Object.class;
+ targetDescriptor = constructorTargetParameterType == null ? "" : constructorTargetParameterType.descriptorString();
+
+ // Even though generated proxy is hidden and therefore has no qualified name,
+ // it can reference itself via internal name, which can lead to name collisions.
+ // Let's consider specialized proxy for java/util/List - if we name proxy similarly,
+ // methods calls to java/util/List will be treated by VM as calls to proxy class,
+ // not standard library interface. Therefore we append $$$ to proxy name to avoid name collision.
+ proxyName = proxyGenLookup.lookupClass().getPackageName().replace('.', '/') + "/" + interFace.getSimpleName() + "$$$";
+
+ originalProxyWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES) {
+ @Override
+ protected ClassLoader getClassLoader() {
+ return ProxyGenerator.this.proxyGenLookup.lookupClass().getClassLoader();
+ }
+ };
+ proxyWriter = JBRApi.VERIFY_BYTECODE ? new CheckClassAdapter(originalProxyWriter, true) : originalProxyWriter;
+ if (interFace.isInterface()) {
+ superclassName = "java/lang/Object";
+ superinterfaceNames = new String[] {getInternalName(interFace), PROXY_INTERFACE_NAME};
+ } else {
+ superclassName = getInternalName(interFace);
+ superinterfaceNames = new String[] {PROXY_INTERFACE_NAME};
+ }
+ }
+
+ /**
+ * Direct (non-transitive) only. These are the proxies accessed by current one.
+ * True for required and false for optional.
+ */
+ Map getDependencies() {
+ return accessContext.dependencies;
+ }
+
+ Set> getSupportedExtensions() {
+ return supportedExtensions.entrySet().stream()
+ .filter(Map.Entry::getValue).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet());
+ }
+
+ /**
+ * @return service target instance, if any
+ */
+ private Object createServiceTarget() throws Throwable {
+ Exception exception = null;
+ MethodHandle constructor = null;
+ try {
+ constructor = info.targetLookup.findStatic(
+ info.targetLookup.lookupClass(), "create", MethodType.methodType(info.targetLookup.lookupClass()));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ exception = e;
+ }
+ try {
+ if (constructor == null) constructor = info.targetLookup.findConstructor(
+ info.targetLookup.lookupClass(), MethodType.methodType(void.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ if (exception == null) exception = e;
+ else exception.addSuppressed(e);
+ }
+ if (constructor == null) throw new ServiceNotAvailableException("No service factory method or constructor found", exception);
+ return constructor.invoke();
+ }
+
+ /**
+ * First parameter is target object to which it would delegate method calls (if target exists).
+ * Second parameter is extensions bitfield (if extensions are enabled).
+ * @return method handle to constructor of generated proxy class, or null.
+ */
+ private MethodHandle findConstructor() throws NoSuchMethodException, IllegalAccessException {
+ MethodType constructorType = MethodType.methodType(void.class);
+ if (info.targetLookup != null) constructorType = constructorType.appendParameterTypes(constructorTargetParameterType);
+ if (EXTENSIONS_ENABLED) constructorType = constructorType.appendParameterTypes(long[].class);
+ return generatedProxy.findConstructor(generatedProxy.lookupClass(), constructorType);
+ }
+
+ /**
+ * Initialize method handles used by generated class via {@code invokedynamic}.
+ */
+ void init() {
+ if (JBRApi.VERBOSE) {
+ System.out.println("Initializing proxy " + interFace.getName());
+ }
+ for (var t : accessContext.dynamicCallTargets) {
+ JBRApi.dynamicCallTargets.put(new JBRApi.DynamicCallTargetKey(
+ generatedProxy.lookupClass(), t.name(), t.descriptor()
+ ), t.futureHandle());
+ }
+ }
+
+ /**
+ * Define generated class.
+ * @return method handle to constructor of generated proxy class, or null.
+ */
+ MethodHandle define(boolean service) {
+ try {
+ Object sericeTarget = service && info.targetLookup != null ? createServiceTarget() : null;
+ generatedProxy = proxyGenLookup.defineHiddenClass(
+ originalProxyWriter.toByteArray(), false, Lookup.ClassOption.STRONG, Lookup.ClassOption.NESTMATE);
+ MethodHandle constructor = findConstructor();
+ if (sericeTarget != null) constructor = MethodHandles.insertArguments(constructor, 0, sericeTarget);
+ return constructor;
+ } catch (ServiceNotAvailableException e) {
+ if (JBRApi.VERBOSE) e.printStackTrace(System.err);
+ return null;
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Generate bytecode for class.
+ * @return true if generated proxy is considered supported
+ */
+ boolean generate() {
+ if (!supported) return false;
+ proxyWriter.visit(CLASSFILE_VERSION, ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC, proxyName, null,
+ superclassName, superinterfaceNames);
+ if (JBRApi.VERBOSE) {
+ synchronized (System.out) {
+ System.out.print("Generating proxy " + interFace.getName());
+ if (specialization != null) System.out.print(" <" +
+ Stream.of(specialization).map(t -> t == null ? "?" : t.toString()).collect(Collectors.joining(", ")) + ">");
+ System.out.println();
+ }
+ }
+ if (specialization != null) {
+ mappingContext.initTypeParameters(interFace, Stream.of(specialization));
+ }
+ mappingContext.initTypeParameters(interFace);
+ generateFields();
+ generateConstructor();
+ generateTargetGetter();
+ generateMethods(interFace);
+ if (interFace.isInterface()) generateMethods(Object.class);
+ proxyWriter.visitEnd();
+ return supported;
+ }
+
+ private void generateFields() {
+ if (info.targetLookup != null) {
+ proxyWriter.visitField(ACC_PRIVATE | ACC_FINAL, "target", targetDescriptor, null, null);
+ }
+ if (EXTENSIONS_ENABLED) {
+ proxyWriter.visitField(ACC_PRIVATE | ACC_FINAL, "extensions", "[J", null, null);
+ }
+ }
+
+ private void generateConstructor() {
+ MethodVisitor m = proxyWriter.visitMethod(ACC_PRIVATE, "", "(" + targetDescriptor +
+ (EXTENSIONS_ENABLED ? "[J" : "") + ")V", null, null);
+ m.visitCode();
+ m.visitVarInsn(ALOAD, 0);
+ if (info.targetLookup != null) {
+ m.visitInsn(DUP);
+ m.visitVarInsn(ALOAD, 1);
+ m.visitFieldInsn(PUTFIELD, proxyName, "target", targetDescriptor);
+ }
+ if (EXTENSIONS_ENABLED) {
+ m.visitInsn(DUP);
+ m.visitVarInsn(ALOAD, info.targetLookup != null ? 2 : 1);
+ m.visitFieldInsn(PUTFIELD, proxyName, "extensions", "[J");
+ }
+ m.visitMethodInsn(INVOKESPECIAL, superclassName, "", "()V", false);
+ m.visitInsn(RETURN);
+ m.visitMaxs(0, 0);
+ m.visitEnd();
+ }
+
+ private void generateTargetGetter() {
+ MethodVisitor m = proxyWriter.visitMethod(ACC_PUBLIC | ACC_FINAL, "$getProxyTarget", "()" +
+ Object.class.descriptorString(), null, null);
+ m.visitCode();
+ if (info.targetLookup != null) {
+ m.visitVarInsn(ALOAD, 0);
+ m.visitFieldInsn(GETFIELD, proxyName, "target", targetDescriptor);
+ } else m.visitInsn(ACONST_NULL);
+ m.visitInsn(ARETURN);
+ m.visitMaxs(0, 0);
+ m.visitEnd();
+ }
+
+ private void generateMethods(Class> interFace) {
+ for (Method method : interFace.getMethods()) {
+ int mod = method.getModifiers();
+ if ((mod & (Modifier.STATIC | Modifier.FINAL)) != 0) continue;
+
+ Exception exception = null;
+ Enum> extension = EXTENSIONS_ENABLED && JBRApi.extensionExtractor != null ?
+ JBRApi.extensionExtractor.apply(method) : null;
+ Mapping.Method methodMapping = mappingContext.getMapping(method);
+ MethodHandle handle;
+ boolean passInstance;
+
+ if (methodMapping.query().valid) {
+ // Try static method.
+ handle = info.getStaticMethod(method.getName(), methodMapping.type());
+ passInstance = false;
+
+ // Try target class.
+ if (handle == null && info.targetLookup != null) {
+ try {
+ handle = interFace.equals(info.targetLookup.lookupClass()) ?
+ info.targetLookup.unreflect(method) : info.targetLookup.findVirtual(
+ info.targetLookup.lookupClass(), method.getName(), methodMapping.type());
+ passInstance = true;
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ exception = e;
+ }
+ }
+
+ if (handle != null) {
+ // Method found.
+ generateMethod(method, handle, methodMapping, extension, passInstance);
+ if (extension != null) supportedExtensions.putIfAbsent(extension, true);
+ continue;
+ }
+ } else {
+ exception = new Exception("Method mapping is invalid: " + methodMapping);
+ }
+
+ // Skip if possible.
+ if (!Modifier.isAbstract(mod)) continue;
+
+ // Generate unsupported stub.
+ generateUnsupportedMethod(proxyWriter, method);
+ if (JBRApi.VERBOSE) {
+ synchronized (System.err) {
+ System.err.println("Couldn't generate method " + method.getName());
+ if (exception != null) exception.printStackTrace(System.err);
+ }
+ }
+ if (extension == null) supported = false;
+ else supportedExtensions.put(extension, false);
+ }
+ }
+
+ private void generateMethod(Method interfaceMethod, MethodHandle handle, Mapping.Method mapping, Enum> extension, boolean passInstance) {
+ boolean passExtensions = mapping.query().needsExtensions;
+ InternalMethodInfo methodInfo = getInternalMethodInfo(interfaceMethod);
+ MethodHandleInfo directCall = accessContext.resolveDirect(handle);
+ Supplier futureHandle = () -> handle;
+
+ // Check usage of deprecated API.
+ if (JBRApi.LOG_DEPRECATED && interfaceMethod.isAnnotationPresent(Deprecated.class)) {
+ directCall = null; // Force invokedynamic.
+ futureHandle = () -> { // Log warning when binding the call site.
+ Utils.log(Utils.BEFORE_BOOTSTRAP_DYNAMIC, System.err, "Warning: using deprecated JBR API method " +
+ interfaceMethod.getDeclaringClass().getCanonicalName() + "." + interfaceMethod.getName());
+ return handle;
+ };
+ }
+
+ if (JBRApi.VERBOSE) {
+ System.out.println(" " +
+ mapping.returnMapping() + " " +
+ interfaceMethod.getName() + "(" +
+ Stream.of(mapping.parameterMapping()).map(Mapping::toString).collect(Collectors.joining(", ")) +
+ ")" + (directCall != null ? " (direct)" : "")
+ );
+ }
+
+ MethodVisitor m = proxyWriter.visitMethod(ACC_PUBLIC | ACC_FINAL, methodInfo.name(),
+ methodInfo.descriptor(), methodInfo.genericSignature(), methodInfo.exceptionNames());
+ AccessContext.Method methodContext = accessContext.new Method(m, extension == null);
+ m.visitCode();
+ // Load `this`.
+ if (passInstance || passExtensions || extension != null) {
+ m.visitVarInsn(ALOAD, 0);
+ if (passInstance && (passExtensions || extension != null)) m.visitInsn(DUP);
+ }
+ // Check enabled extension.
+ if (passExtensions || extension != null) {
+ m.visitFieldInsn(GETFIELD, proxyName, "extensions", "[J");
+ if (passExtensions && extension != null) m.visitInsn(DUP);
+ if (passExtensions) {
+ // If this method converts any parameters, we need to store extensions for later use.
+ // We overwrite `this` slot, but we have it on stack, so it's ok.
+ m.visitVarInsn(ASTORE, 0);
+ }
+ if (extension != null) {
+ // Check the specific bit inside long[].
+ Label afterExtensionCheck = new Label();
+ m.visitIntInsn(extension.ordinal() < 8192 ? BIPUSH : SIPUSH, extension.ordinal() / 64);
+ m.visitInsn(LALOAD);
+ m.visitLdcInsn(1L << (extension.ordinal() % 64));
+ m.visitInsn(LAND);
+ m.visitInsn(LCONST_0);
+ m.visitInsn(LCMP);
+ m.visitJumpInsn(IFNE, afterExtensionCheck);
+ throwException(m, "java/lang/UnsupportedOperationException",
+ interFace.getCanonicalName() + '.' + interfaceMethod.getName() +
+ " - extension " + extension.name() + " is disabled");
+ m.visitLabel(afterExtensionCheck);
+ }
+ }
+ // Extract target from `this`.
+ if (passInstance) {
+ // We already have `this` on stack.
+ m.visitFieldInsn(GETFIELD, proxyName, "target", targetDescriptor);
+ }
+ // Load and convert parameters.
+ for (int lvIndex = 1, i = 0; i < mapping.parameterMapping().length; i++) {
+ Mapping param = mapping.parameterMapping()[i];
+ int opcode = getLoadOpcode(param.from);
+ m.visitVarInsn(opcode, lvIndex);
+ lvIndex += getParameterSize(param.from);
+ param.convert(methodContext);
+ }
+ // Invoke target method.
+ if (directCall != null) methodContext.invokeDirect(directCall);
+ else methodContext.invokeDynamic(handle.type(), futureHandle);
+ // Convert return value.
+ mapping.returnMapping().convert(methodContext);
+ int opcode = getReturnOpcode(mapping.returnMapping().to);
+ m.visitInsn(opcode);
+ m.visitMaxs(0, 0);
+ m.visitEnd();
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ProxyRepository.java b/src/java.base/share/classes/com/jetbrains/internal/ProxyRepository.java
new file mode 100644
index 000000000000..c59744da2744
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ProxyRepository.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.loader.BootLoader;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * Proxy repository keeps track of all generated proxies.
+ * @see ProxyRepository#getProxy(Class, Mapping[])
+ */
+class ProxyRepository {
+ private static final Proxy NONE = Proxy.empty(null), INVALID = Proxy.empty(false);
+
+ private final Registry registry = new Registry();
+ private final Map proxies = new HashMap<>();
+
+ void init(InputStream extendedRegistryStream,
+ Class extends Annotation> serviceAnnotation,
+ Class extends Annotation> providedAnnotation,
+ Class extends Annotation> providesAnnotation) {
+ registry.initAnnotations(serviceAnnotation, providedAnnotation, providesAnnotation);
+ if (extendedRegistryStream != null) registry.readEntries(extendedRegistryStream);
+ }
+
+ String getVersion() {
+ return registry.version;
+ }
+
+ synchronized Proxy getProxy(Class> clazz, Mapping[] specialization) {
+ Key key = new Key(clazz, specialization);
+ Proxy p = proxies.get(key);
+ if (p == null) {
+ registry.updateClassLoader(clazz.getClassLoader());
+ Mapping[] inverseSpecialization = specialization == null ? null :
+ Stream.of(specialization).map(m -> m == null ? null : m.inverse()).toArray(Mapping[]::new);
+ Key inverseKey = null;
+
+ Registry.Entry entry = registry.entries.get(key.clazz().getCanonicalName());
+ if (entry != null) { // This is a registered proxy
+ Proxy.Info infoByInterface = entry.resolve(),
+ infoByTarget = entry.inverse != null ? entry.inverse.resolve() : null;
+ inverseKey = infoByTarget != null && infoByTarget.interfaceLookup != null ?
+ new Key(infoByTarget.interfaceLookup.lookupClass(), inverseSpecialization) : null;
+ if ((infoByInterface == null && infoByTarget == null) ||
+ infoByInterface == Registry.Entry.INVALID ||
+ infoByTarget == Registry.Entry.INVALID) {
+ p = INVALID;
+ } else {
+ p = Proxy.create(this, infoByInterface, specialization, infoByTarget, inverseSpecialization);
+ }
+ } else if (!Arrays.equals(specialization, inverseSpecialization)) { // Try implicit proxy
+ inverseKey = new Key(clazz, inverseSpecialization);
+ Lookup lookup = SharedSecrets.getJavaLangInvokeAccess().lookupIn(clazz);
+ Proxy.Info info = new Proxy.Info(lookup, lookup, 0);
+ p = Proxy.create(this, info, specialization, info, inverseSpecialization);
+ } else p = NONE;
+ proxies.put(key, p);
+ if (inverseKey != null) proxies.put(inverseKey, p.inverse());
+ }
+ return p;
+ }
+
+ private record Key(Class> clazz, Mapping[] specialization) {
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Key key = (Key) o;
+ if (!clazz.equals(key.clazz)) return false;
+ return Arrays.equals(specialization, key.specialization);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clazz.hashCode();
+ result = 31 * result + Arrays.hashCode(specialization);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return clazz.getName() + "<" + Arrays.toString(specialization) + ">";
+ }
+ }
+
+ /**
+ * Registry contains all information about mapping between JBR API interfaces and implementation.
+ * This mapping information can be {@linkplain Entry#resolve() resolved} into {@link Proxy.Info}.
+ */
+ private static class Registry {
+
+ private record StaticKey(String methodName, String targetMethodDescriptor) {}
+ private record StaticValue(String targetType, String targetMethodName) {}
+
+ private class Entry {
+ private static final Proxy.Info INVALID = new Proxy.Info(null, null, 0);
+
+ private final Map staticMethods = new HashMap<>();
+ private String type, target;
+ private Entry inverse;
+ private int flags;
+
+ private Entry(String type) { this.type = type; }
+
+ private Proxy.Info resolve() {
+ if (type == null) return null;
+ Lookup l, t;
+ try {
+ l = resolveType(type, classLoader);
+ t = target != null ? resolveType(target, classLoader) : null;
+ } catch (ClassNotFoundException e) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(type + " not eligible");
+ e.printStackTrace(System.err);
+ }
+ return INVALID;
+ }
+ if (l.lookupClass().isAnnotation() || l.lookupClass().isEnum() || l.lookupClass().isRecord()) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(type + " not eligible: not a class or interface");
+ }
+ return INVALID;
+ }
+ if (Modifier.isFinal(l.lookupClass().getModifiers()) || l.lookupClass().isSealed()) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(type + " not eligible: invalid type (final/sealed)");
+ }
+ return INVALID;
+ }
+ if (target == null) flags |= Proxy.SERVICE;
+ if (needsAnnotation(l.lookupClass())) {
+ if (!hasAnnotation(l.lookupClass(), providedAnnotation)) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(type + " not eligible: no @Provided annotation");
+ }
+ return INVALID;
+ }
+ if (!hasAnnotation(l.lookupClass(), serviceAnnotation)) flags &= ~Proxy.SERVICE;
+ }
+ Proxy.Info info;
+ if (t != null) {
+ if (needsAnnotation(t.lookupClass()) && !hasAnnotation(t.lookupClass(), providesAnnotation)) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(target + " not eligible: no @Provides annotation");
+ }
+ return INVALID;
+ }
+ info = new Proxy.Info(l, t, flags);
+ } else info = new Proxy.Info(l, null, flags);
+ for (var method : staticMethods.entrySet()) {
+ String methodName = method.getKey().methodName;
+ String targetMethodDescriptor = method.getKey().targetMethodDescriptor;
+ String targetType = method.getValue().targetType;
+ String targetMethodName = method.getValue().targetMethodName;
+ try {
+ Lookup lookup = resolveType(targetType, classLoader);
+ MethodType mt = MethodType.fromMethodDescriptorString(targetMethodDescriptor, classLoader);
+ MethodHandle handle = lookup.findStatic(lookup.lookupClass(), targetMethodName, mt);
+ info.addStaticMethod(methodName, handle);
+ } catch (ClassNotFoundException | IllegalArgumentException | TypeNotPresentException |
+ NoSuchMethodException | IllegalAccessException | ClassCastException e) {
+ if (JBRApi.VERBOSE) {
+ System.err.println(targetType + "#" + targetMethodName + " cannot be resolved");
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+ return info;
+ }
+
+ private static Lookup resolveType(String type, ClassLoader classLoader) throws ClassNotFoundException {
+ Class> c = null;
+ try {
+ c = Class.forName(type, false, classLoader);
+ } catch (ClassNotFoundException e) {
+ String dollars = type.replace('.', '$');
+ for (int i = 0;; i++) {
+ i = type.indexOf('.', i);
+ if (i == -1) break;
+ String name = type.substring(0, i) + dollars.substring(i);
+ try {
+ c = Class.forName(name, false, classLoader);
+ break;
+ } catch (ClassNotFoundException ignore) {}
+ }
+ if (c == null) throw e;
+ }
+ return SharedSecrets.getJavaLangInvokeAccess().lookupIn(c);
+ }
+
+ @Override
+ public String toString() { return type; }
+ }
+
+ private Class extends Annotation> serviceAnnotation, providedAnnotation, providesAnnotation;
+ private Module annotationsModule;
+ private ClassLoader classLoader;
+ private final Map entries = new HashMap<>();
+ private final String version;
+
+ private Registry() {
+ try (InputStream registryStream = BootLoader.findResourceAsStream("java.base", "META-INF/jbrapi.registry")) {
+ version = readEntries(registryStream);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void initAnnotations(Class extends Annotation> serviceAnnotation,
+ Class extends Annotation> providedAnnotation,
+ Class extends Annotation> providesAnnotation) {
+ this.serviceAnnotation = serviceAnnotation;
+ this.providedAnnotation = providedAnnotation;
+ this.providesAnnotation = providesAnnotation;
+ annotationsModule = serviceAnnotation == null ? null : serviceAnnotation.getModule();
+ if (annotationsModule != null) classLoader = annotationsModule.getClassLoader();
+ }
+
+ private String readEntries(InputStream inputStream) {
+ String version = null;
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String s;
+ while ((s = reader.readLine()) != null) {
+ String[] tokens = s.split(" ");
+ switch (tokens[0]) {
+ case "TYPE" -> {
+ Entry a = entries.computeIfAbsent(tokens[1], Entry::new);
+ Entry b = entries.computeIfAbsent(tokens[2], Entry::new);
+ if ((a.inverse != null || b.inverse != null) && (a.inverse != b || b.inverse != a)) {
+ throw new RuntimeException("Conflicting mapping: " +
+ tokens[1] + " <-> " + tokens[2] +
+ ", " + a + " -> " + a.inverse +
+ ", " + b + " -> " + b.inverse);
+ }
+ a.inverse = b;
+ b.inverse = a;
+ a.target = tokens[2];
+ b.target = tokens[1];
+ switch (tokens[3]) {
+ case "SERVICE" -> {
+ a.type = null;
+ b.flags |= Proxy.SERVICE;
+ }
+ case "PROVIDES" -> a.type = null;
+ case "PROVIDED" -> b.type = null;
+ }
+ if (tokens.length > 4 && tokens[4].equals("INTERNAL")) {
+ a.flags |= Proxy.INTERNAL;
+ b.flags |= Proxy.INTERNAL;
+ }
+ }
+ case "STATIC" -> {
+ Entry entry = entries.computeIfAbsent(tokens[4], Entry::new);
+ StaticValue target = new StaticValue(tokens[1], tokens[2]);
+ StaticValue prev = entry.staticMethods.put(new StaticKey(tokens[5], tokens[3]), target);
+ if (prev != null && !prev.equals(target)) {
+ throw new RuntimeException("Conflicting mapping: " +
+ target.targetType + "#" + target.targetMethodName + " <- " +
+ tokens[4] + "#" + tokens[5] + " -> " +
+ prev.targetType + "#" + prev.targetMethodName);
+ }
+ if (tokens.length > 6 && tokens[6].equals("INTERNAL")) entry.flags |= Proxy.INTERNAL;
+ }
+ case "VERSION" -> version = tokens[1];
+ }
+ }
+ } catch (IOException e) {
+ entries.clear();
+ throw new RuntimeException(e);
+ } catch (RuntimeException | Error e) {
+ entries.clear();
+ throw e;
+ }
+ return version;
+ }
+
+ private synchronized void updateClassLoader(ClassLoader newLoader) {
+ // New loader is descendant of current one -> update
+ for (ClassLoader cl = newLoader;; cl = cl.getParent()) {
+ if (cl == classLoader) {
+ classLoader = newLoader;
+ return;
+ }
+ if (cl == null) break;
+ }
+ // Current loader is descendant of the new one -> leave
+ for (ClassLoader cl = classLoader;; cl = cl.getParent()) {
+ if (cl == newLoader) return;
+ if (cl == null) break;
+ }
+ // Independent classloaders -> error? Or maybe reset cache and start fresh?
+ throw new RuntimeException("Incompatible classloader");
+ }
+
+ private boolean needsAnnotation(Class> c) {
+ return annotationsModule != null && annotationsModule.equals(c.getModule());
+ }
+
+ private static boolean hasAnnotation(Class> c, Class extends Annotation> a) {
+ return c.getAnnotation(a) != null;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/Utils.java b/src/java.base/share/classes/com/jetbrains/internal/Utils.java
new file mode 100644
index 000000000000..8660ae8e3c89
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/Utils.java
@@ -0,0 +1,49 @@
+package com.jetbrains.internal;
+
+import jdk.internal.misc.VM;
+
+import java.io.PrintStream;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+class Utils {
+
+ static final Function, Stream>
+ BEFORE_BOOTSTRAP_DYNAMIC = st -> st
+ .dropWhile(e -> !e.getClassName().equals("com.jetbrains.exported.JBRApiSupport") ||
+ !e.getMethodName().equals("bootstrapDynamic")).skip(1)
+ .dropWhile(e -> e.getClassName().startsWith("java.lang.invoke."));
+ static final Function, Stream>
+ BEFORE_JBR = st -> st
+ .dropWhile(e -> !e.getClassName().equals("com.jetbrains.JBR"))
+ .dropWhile(e -> e.getClassName().equals("com.jetbrains.JBR"));
+
+ static void log(Function, Stream> preprocessor,
+ PrintStream out, String message) {
+ StackTraceElement[] st = Thread.currentThread().getStackTrace();
+ synchronized (out) {
+ out.println(message);
+ preprocessor.apply(Stream.of(st)).forEach(e -> out.println("\tat " + e));
+ }
+ }
+
+ /**
+ * System properties obtained this way are immune to runtime override via {@link System#setProperty(String, String)}.
+ */
+ static boolean property(String name, boolean defaultValue) {
+ String value = VM.getSavedProperty(name);
+ if (value != null) {
+ if (value.equalsIgnoreCase("true")) return true;
+ if (value.equalsIgnoreCase("false")) return false;
+ }
+ return defaultValue;
+ }
+
+ /**
+ * System properties obtained this way are immune to runtime override via {@link System#setProperty(String, String)}.
+ */
+ static String property(String name, String defaultValue) {
+ String value = VM.getSavedProperty(name);
+ return value != null ? value : defaultValue;
+ }
+}
diff --git a/src/java.base/share/classes/java/io/ExternalChannelHolder.java b/src/java.base/share/classes/java/io/ExternalChannelHolder.java
new file mode 100644
index 000000000000..92d718dc7b50
--- /dev/null
+++ b/src/java.base/share/classes/java/io/ExternalChannelHolder.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.io;
+
+import sun.nio.ch.FileChannelImpl;
+
+import java.nio.channels.FileChannel;
+
+/**
+ * A channel returned by {@link #getInterruptibleChannel()} must be interruptible,
+ * but the channel used inside this class must be uninterruptible.
+ * This class holds the channel for external usage, which may differ from {@link #channel}.
+ */
+class ExternalChannelHolder {
+ private final Object synchronizationPoint;
+ private final FileChannel channel;
+ private final NioChannelCleanable channelCleanable;
+ private FileChannel externallySharedChannel;
+
+ ExternalChannelHolder(Object owner, FileChannel channel, NioChannelCleanable channelCleanable) {
+ this.synchronizationPoint = owner;
+ this.channel = channel;
+ this.channelCleanable = channelCleanable;
+ }
+
+ FileChannel getInterruptibleChannel() {
+ synchronized (synchronizationPoint) {
+ if (externallySharedChannel == null) {
+ externallySharedChannel = channel;
+ if (externallySharedChannel instanceof FileChannelImpl fci) {
+ fci = fci.clone();
+ fci.setInterruptible();
+ externallySharedChannel = fci;
+ }
+ }
+ }
+ channelCleanable.setChannel(null);
+ return externallySharedChannel;
+ }
+
+ void close() throws IOException {
+ FileChannel ch = externallySharedChannel;
+ if (ch != null) {
+ ch.close();
+ }
+ }
+}
diff --git a/src/java.base/share/classes/java/io/File.java b/src/java.base/share/classes/java/io/File.java
index 9c9fbc837db8..d5d652b7eff1 100644
--- a/src/java.base/share/classes/java/io/File.java
+++ b/src/java.base/share/classes/java/io/File.java
@@ -35,6 +35,8 @@
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
+
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.util.StaticProperty;
/**
@@ -157,7 +159,15 @@ public class File
/**
* The FileSystem object representing the platform's local file system.
*/
- private static final FileSystem FS = DefaultFileSystem.getFileSystem();
+ private static final FileSystem FS;
+
+ static {
+ if (IoOverNio.IS_ENABLED_IN_GENERAL) {
+ FS = new IoOverNioFileSystem(DefaultFileSystem.getFileSystem());
+ } else {
+ FS = DefaultFileSystem.getFileSystem();
+ }
+ }
/**
* This abstract pathname's normalized pathname string. A normalized
diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java
index ab312fc8c5be..5b2f8fea8de3 100644
--- a/src/java.base/share/classes/java/io/FileInputStream.java
+++ b/src/java.base/share/classes/java/io/FileInputStream.java
@@ -25,13 +25,24 @@
package java.io;
+import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
import java.util.Arrays;
+
+import java.util.Set;
+
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.util.ArraysSupport;
import jdk.internal.event.FileReadEvent;
import jdk.internal.vm.annotation.Stable;
import sun.nio.ch.FileChannelImpl;
+import static com.jetbrains.internal.IoOverNio.DEBUG;
+
/**
* A {@code FileInputStream} obtains input bytes
* from a file in a file system. What files
@@ -86,6 +97,16 @@ public class FileInputStream extends InputStream
// operations need the current position which requires seeking
private @Stable Boolean isRegularFile;
+ private final boolean useNio;
+
+ @SuppressWarnings({
+ "FieldCanBeLocal",
+ "this-escape", // It immediately converts into a phantom reference.
+ })
+ private final NioChannelCleanable channelCleanable = new NioChannelCleanable(this);
+
+ private final ExternalChannelHolder externalChannelHolder;
+
/**
* Creates a {@code FileInputStream} to read from an existing file
* named by the path name {@code name}.
@@ -134,10 +155,39 @@ public FileInputStream(File file) throws FileNotFoundException {
throw new FileNotFoundException("Invalid file path");
}
path = file.getPath();
- fd = new FileDescriptor();
- fd.attach(this);
- open(path);
- FileCleanable.register(fd); // open set the fd, register the cleanup
+
+ java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
+ Path nioPath = null;
+ if (nioFs != null && path != null) {
+ try {
+ nioPath = nioFs.getPath(path);
+ isRegularFile = Files.isRegularFile(nioPath);
+ } catch (InvalidPathException _) {
+ // Nothing.
+ }
+ }
+
+ // Two significant differences between the legacy java.io and java.nio.files:
+ // * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
+ // * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
+ useNio = nioPath != null && isRegularFile == Boolean.TRUE;
+
+ if (useNio) {
+ var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
+ this, nioFs, file, nioPath, Set.of(StandardOpenOption.READ), channelCleanable);
+ channel = bundle.channel();
+ fd = bundle.fd();
+ externalChannelHolder = bundle.externalChannelHolder();
+ } else {
+ fd = new FileDescriptor();
+ fd.attach(this);
+ open(path);
+ FileCleanable.register(fd); // open set the fd, register the cleanup
+ externalChannelHolder = null;
+ }
+ if (DEBUG.writeTraces()) {
+ System.err.printf("Created a FileInputStream for %s%n", file);
+ }
}
/**
@@ -157,6 +207,9 @@ public FileInputStream(File file) throws FileNotFoundException {
*/
@SuppressWarnings("this-escape")
public FileInputStream(FileDescriptor fdObj) {
+ useNio = false;
+ externalChannelHolder = null;
+
if (fdObj == null) {
throw new NullPointerException();
}
@@ -195,10 +248,21 @@ private void open(String name) throws FileNotFoundException {
*/
@Override
public int read() throws IOException {
- if (jfrTracing && FileReadEvent.enabled()) {
+ if (jfrTracing && FileReadEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
return traceRead0();
}
- return read0();
+ return implRead();
+ }
+
+ private int implRead() throws IOException {
+ if (!useNio) {
+ return read0();
+ } else {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ int nRead = channel.read(buffer);
+ buffer.rewind();
+ return nRead == 1 ? (buffer.get() & 0xFF) : -1;
+ }
}
private native int read0() throws IOException;
@@ -210,7 +274,7 @@ private int traceRead0() throws IOException {
long start = 0;
try {
start = FileReadEvent.timestamp();
- result = read0();
+ result = implRead();
if (result < 0) {
endOfFile = true;
} else {
@@ -239,7 +303,7 @@ private int traceReadBytes(byte b[], int off, int len) throws IOException {
long start = 0;
try {
start = FileReadEvent.timestamp();
- bytesRead = readBytes(b, off, len);
+ bytesRead = implRead(b, off, len);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
@@ -266,10 +330,25 @@ private int traceReadBytes(byte b[], int off, int len) throws IOException {
*/
@Override
public int read(byte[] b) throws IOException {
- if (jfrTracing && FileReadEvent.enabled()) {
+ if (jfrTracing && FileReadEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
return traceReadBytes(b, 0, b.length);
}
- return readBytes(b, 0, b.length);
+
+ return implRead(b);
+ }
+
+ private int implRead(byte[] b) throws IOException {
+ if (!useNio) {
+ return readBytes(b, 0, b.length);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b);
+ return channel.read(buffer);
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ return readBytes(b, 0, b.length);
+ }
+ }
}
/**
@@ -288,10 +367,24 @@ public int read(byte[] b) throws IOException {
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
- if (jfrTracing && FileReadEvent.enabled()) {
+ if (jfrTracing && FileReadEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
return traceReadBytes(b, off, len);
}
- return readBytes(b, off, len);
+ return implRead(b, off, len);
+ }
+
+ private int implRead(byte[] b, int off, int len) throws IOException {
+ if (!useNio) {
+ return readBytes(b, off, len);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+ return channel.read(buffer);
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ return readBytes(b, off, len);
+ }
+ }
}
@Override
@@ -390,7 +483,7 @@ public byte[] readNBytes(int len) throws IOException {
public long transferTo(OutputStream out) throws IOException {
long transferred = 0L;
if (out instanceof FileOutputStream fos && isRegularFile()) {
- FileChannel fc = getChannel();
+ FileChannel fc = useNio ? channel : getChannel();
long pos = fc.position();
transferred = fc.transferTo(pos, Long.MAX_VALUE, fos.getChannel());
long newPos = pos + transferred;
@@ -407,12 +500,21 @@ public long transferTo(OutputStream out) throws IOException {
}
private long length() throws IOException {
- return length0();
+ if (!useNio) {
+ return length0();
+ } else {
+ return channel.size();
+ }
}
+
private native long length0() throws IOException;
private long position() throws IOException {
- return position0();
+ if (!useNio) {
+ return position0();
+ } else {
+ return channel.position();
+ }
}
private native long position0() throws IOException;
@@ -442,10 +544,15 @@ private long position() throws IOException {
*/
@Override
public long skip(long n) throws IOException {
- if (isRegularFile())
+ if (!isRegularFile()) {
+ return super.skip(n);
+ } else if (!useNio) {
return skip0(n);
-
- return super.skip(n);
+ } else {
+ long startPos = channel.position();
+ channel.position(startPos + n);
+ return channel.position() - startPos;
+ }
}
private native long skip0(long n) throws IOException;
@@ -469,7 +576,14 @@ public long skip(long n) throws IOException {
*/
@Override
public int available() throws IOException {
- return available0();
+ if (!useNio) {
+ return available0();
+ } else {
+ long size = channel.size();
+ long pos = channel.position();
+ long avail = size > pos ? (size - pos) : 0;
+ return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)avail;
+ }
}
private native int available0() throws IOException;
@@ -515,6 +629,10 @@ public void close() throws IOException {
fc.close();
}
+ if (externalChannelHolder != null) {
+ externalChannelHolder.close();
+ }
+
fd.closeAll(new Closeable() {
public void close() throws IOException {
fd.close();
@@ -555,6 +673,10 @@ public final FileDescriptor getFD() throws IOException {
* @since 1.4
*/
public FileChannel getChannel() {
+ if (externalChannelHolder != null) {
+ return externalChannelHolder.getInterruptibleChannel();
+ }
+
FileChannel fc = this.channel;
if (fc == null) {
synchronized (this) {
@@ -580,10 +702,17 @@ public FileChannel getChannel() {
/**
* Determine whether the file is a regular file.
*/
- private boolean isRegularFile() {
+ private boolean isRegularFile() { // TODO Shouldn't it be used together with checkIsNotDirectoryForStreams?
Boolean isRegularFile = this.isRegularFile;
if (isRegularFile == null) {
- this.isRegularFile = isRegularFile = isRegularFile0(fd);
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
+ if (path == null) {
+ this.isRegularFile = isRegularFile = false;
+ } else if (nioFs == null) {
+ this.isRegularFile = isRegularFile = isRegularFile0(fd);
+ } else {
+ this.isRegularFile = isRegularFile = Files.isRegularFile(nioFs.getPath(path));
+ }
}
return isRegularFile;
}
diff --git a/src/java.base/share/classes/java/io/FileOutputStream.java b/src/java.base/share/classes/java/io/FileOutputStream.java
index 6c5a30ea4323..5a8738aa0b88 100644
--- a/src/java.base/share/classes/java/io/FileOutputStream.java
+++ b/src/java.base/share/classes/java/io/FileOutputStream.java
@@ -25,11 +25,23 @@
package java.io;
+import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.DosFileAttributeView;
+import java.nio.file.attribute.DosFileAttributes;
+import java.util.Set;
+
+import java.nio.file.Path;
+
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.event.FileWriteEvent;
import sun.nio.ch.FileChannelImpl;
+import static com.jetbrains.internal.IoOverNio.DEBUG;
/**
@@ -95,6 +107,16 @@ public class FileOutputStream extends OutputStream
private volatile boolean closed;
+ private final boolean useNio;
+
+ @SuppressWarnings({
+ "FieldCanBeLocal",
+ "this-escape", // It immediately converts into a phantom reference.
+ })
+ private final NioChannelCleanable channelCleanable = new NioChannelCleanable(this);
+
+ private final ExternalChannelHolder externalChannelHolder;
+
/**
* Creates a file output stream to write to the file with the
* specified name. If the file exists, it is truncated, otherwise a
@@ -204,11 +226,45 @@ public FileOutputStream(File file, boolean append)
}
this.path = file.getPath();
- this.fd = new FileDescriptor();
- fd.attach(this);
+ java.nio.file.FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
+ useNio = path != null && nioFs != null;
+ if (useNio) {
+ Path nioPath = nioFs.getPath(path);
+
+ // java.io backend doesn't open DOS hidden files for writing, but java.nio.file opens.
+ // This code mimics the old behavior.
+ if (nioFs.getSeparator().equals("\\")) {
+ DosFileAttributes attrs;
+ try {
+ attrs = Files.getFileAttributeView(nioPath, DosFileAttributeView.class).readAttributes();
+ } catch (IOException | UnsupportedOperationException _) {
+ // Windows paths without DOS attributes? Not a problem in this case.
+ attrs = null;
+ }
+ if (attrs != null && (attrs.isHidden() || attrs.isDirectory())) {
+ throw new FileNotFoundException(file.getPath() + " (Access is denied)");
+ }
+ }
- open(this.path, append);
- FileCleanable.register(fd); // open sets the fd, register the cleanup
+ Set options = append
+ ? Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)
+ : Set.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
+ this, nioFs, file, nioPath, options, channelCleanable);
+ channel = bundle.channel();
+ fd = bundle.fd();
+ externalChannelHolder = bundle.externalChannelHolder();
+ } else {
+ this.fd = new FileDescriptor();
+ fd.attach(this);
+
+ open(this.path, append);
+ FileCleanable.register(fd); // open sets the fd, register the cleanup
+ externalChannelHolder = null;
+ }
+ if (DEBUG.writeTraces()) {
+ System.err.printf("Created a FileOutputStream for %s%n", file);
+ }
}
/**
@@ -228,6 +284,9 @@ public FileOutputStream(File file, boolean append)
*/
@SuppressWarnings("this-escape")
public FileOutputStream(FileDescriptor fdObj) {
+ useNio = false;
+ externalChannelHolder = null;
+
if (fdObj == null) {
throw new NullPointerException();
}
@@ -269,7 +328,7 @@ private void traceWrite(int b, boolean append) throws IOException {
long start = 0;
try {
start = FileWriteEvent.timestamp();
- write(b, append);
+ implWrite(b, append);
bytesWritten = 1;
} finally {
long duration = FileWriteEvent.timestamp() - start;
@@ -289,11 +348,25 @@ private void traceWrite(int b, boolean append) throws IOException {
@Override
public void write(int b) throws IOException {
boolean append = FD_ACCESS.getAppend(fd);
- if (jfrTracing && FileWriteEvent.enabled()) {
+ if (jfrTracing && FileWriteEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
traceWrite(b, append);
return;
}
- write(b, append);
+ implWrite(b, append);
+ }
+
+ private void implWrite(int b, boolean append) throws IOException {
+ if (!useNio) {
+ write(b, append);
+ } else {
+ // 'append' is ignored; the channel is supposed to obey the mode in which the file was opened
+ byte[] array = new byte[1];
+ array[0] = (byte) b;
+ ByteBuffer buffer = ByteBuffer.wrap(array);
+ do {
+ channel.write(buffer);
+ } while (buffer.hasRemaining());
+ }
}
/**
@@ -313,7 +386,7 @@ private void traceWriteBytes(byte b[], int off, int len, boolean append) throws
long start = 0;
try {
start = FileWriteEvent.timestamp();
- writeBytes(b, off, len, append);
+ implWriteBytes(b, off, len, append);
bytesWritten = len;
} finally {
long duration = FileWriteEvent.timestamp() - start;
@@ -333,11 +406,12 @@ private void traceWriteBytes(byte b[], int off, int len, boolean append) throws
@Override
public void write(byte[] b) throws IOException {
boolean append = FD_ACCESS.getAppend(fd);
- if (jfrTracing && FileWriteEvent.enabled()) {
+ if (jfrTracing && FileWriteEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
traceWriteBytes(b, 0, b.length, append);
return;
}
- writeBytes(b, 0, b.length, append);
+
+ implWriteBytes(b, 0, b.length, append);
}
/**
@@ -353,11 +427,28 @@ public void write(byte[] b) throws IOException {
@Override
public void write(byte[] b, int off, int len) throws IOException {
boolean append = FD_ACCESS.getAppend(fd);
- if (jfrTracing && FileWriteEvent.enabled()) {
+ if (jfrTracing && FileWriteEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
traceWriteBytes(b, off, len, append);
return;
}
- writeBytes(b, off, len, append);
+ implWriteBytes(b, off, len, append);
+ }
+
+ private void implWriteBytes(byte[] b, int off, int len, boolean append) throws IOException {
+ if (!useNio) {
+ writeBytes(b, off, len, append);
+ } else {
+ // 'append' is ignored; the channel is supposed to obey the mode in which the file was opened
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+ do {
+ channel.write(buffer);
+ } while (buffer.hasRemaining());
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ writeBytes(b, off, len, append);
+ }
+ }
}
/**
@@ -402,6 +493,10 @@ public void close() throws IOException {
fc.close();
}
+ if (externalChannelHolder != null) {
+ externalChannelHolder.close();
+ }
+
fd.closeAll(new Closeable() {
public void close() throws IOException {
fd.close();
@@ -443,6 +538,10 @@ public final FileDescriptor getFD() throws IOException {
* @since 1.4
*/
public FileChannel getChannel() {
+ if (externalChannelHolder != null) {
+ return externalChannelHolder.getInterruptibleChannel();
+ }
+
FileChannel fc = this.channel;
if (fc == null) {
synchronized (this) {
diff --git a/src/java.base/share/classes/java/io/IoOverNioFileSystem.java b/src/java.base/share/classes/java/io/IoOverNioFileSystem.java
new file mode 100644
index 000000000000..066e1f5285f3
--- /dev/null
+++ b/src/java.base/share/classes/java/io/IoOverNioFileSystem.java
@@ -0,0 +1,1215 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.io;
+
+import com.jetbrains.internal.IoOverNio;
+import com.jetbrains.internal.IoToNioErrorMessageHolder;
+import jdk.internal.misc.VM;
+import sun.nio.ch.FileChannelImpl;
+
+import java.nio.channels.FileChannel;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.AccessMode;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystemException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributeView;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.jetbrains.internal.IoOverNio.DEBUG;
+
+class IoOverNioFileSystem extends FileSystem {
+ /**
+ * The filesystem created by default in the original OpenJDK.
+ */
+ private final FileSystem parent;
+
+ IoOverNioFileSystem(FileSystem parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * If this method returns a non-null {@link java.nio.file.FileSystem},
+ * then {@code java.io} primitives must use this filesystem for all operations inside wherever it is possible.
+ * Otherwise, {@link java.io} must use legacy functions for accessing the filesystem.
+ * It is allowed to not use NIO instead of the original java.io code
+ * if some function is not implemented in NIO.
+ */
+ static java.nio.file.FileSystem acquireNioFs(String path) {
+ if (!VM.isBooted()) {
+ return null;
+ }
+
+ if (!IoOverNio.IS_ENABLED_IN_GENERAL) {
+ return null;
+ }
+
+ if (!IoOverNio.isAllowedInThisThread()) {
+ return null;
+ }
+
+ java.nio.file.FileSystem nioFs = FileSystems.getDefault();
+
+ if (nioFs.getSeparator().equals("\\") && Objects.equals(path, "NUL:")) {
+ return null;
+ }
+
+ return nioFs;
+ }
+
+ /**
+ * To be used in {@link java.io.FileInputStream} and so on.
+ * Constructors of these classes may throw only {@link FileNotFoundException} whatever error actually happens.
+ */
+ static FileNotFoundException convertNioToIoExceptionInStreams(IOException source) {
+ if (source instanceof FileNotFoundException nsfe) {
+ return nsfe;
+ }
+ String message = IoToNioErrorMessageHolder.removeMessage(source);
+ if (source instanceof FileSystemException s && s.getFile() != null) {
+ if (message == null) {
+ message = s.getFile();
+ } else {
+ message = s.getFile() + " (" + message + ")";
+ }
+ }
+ FileNotFoundException result = new FileNotFoundException(message);
+ result.initCause(source);
+ return result;
+ }
+
+ private static boolean setPermission0(java.nio.file.FileSystem nioFs, File f, int access, boolean enable, boolean owneronly) {
+ if (f.getPath().isEmpty()) {
+ if (nioFs.getSeparator().equals("\\")) {
+ // This behavior is hardcoded in Java_java_io_WinNTFileSystem_setPermission0,
+ // and this check happens there before calling GetFileAttributesW.
+ return (ACCESS_READ == access || ACCESS_EXECUTE == access) && enable;
+ } else {
+ // Unlike on Windows,
+ // Java_java_io_UnixFileSystem_checkAccess0 has no return statements before chmod(),
+ // and chmod() returns an error for an empty path.
+ return false;
+ }
+ }
+
+ Path path;
+ try {
+ path = nioFs.getPath(f.getPath());
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ }
+
+ // See the comment inside getBooleanAttributes that explains the order of file attribute view classes.
+ BasicFileAttributeView view;
+ try {
+ view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
+ } catch (UnsupportedOperationException _) {
+ view = null;
+ }
+ if (view == null) {
+ try {
+ view = Files.getFileAttributeView(path, DosFileAttributeView.class);
+ } catch (UnsupportedOperationException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("File system %s supports neither posix nor dos attributes", nioFs), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ }
+ }
+
+ boolean result = true;
+ if (view instanceof DosFileAttributeView dosView) {
+ result = false;
+ try {
+ // Both the original java.io.File.setReadOnly
+ // and sun.nio.fs.WindowsFileAttributeViews.Dos.setReadOnly
+ // use SetFileAttributesW.
+ //
+ // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileattributesw
+ // The documentation states:
+ // > This attribute is not honored on directories.
+ // However, SetFileAttributesW always returns 0, not changing anything.
+ // In java.io.File (see Java_java_io_WinNTFileSystem_setReadOnly0), there's an explicit check
+ // if it is a directory.
+ // On the contrary, there's no such check in WindowsFileAttributeViews.
+ if ((access & ACCESS_WRITE) != 0 && !dosView.readAttributes().isDirectory()) {
+ dosView.setReadOnly(!enable);
+ result = true;
+ } else if (access == ACCESS_READ || access == ACCESS_EXECUTE) {
+ result = enable; // Like in Java_java_io_WinNTFileSystem_setPermission0
+ }
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't set read only attributes for %s", f), err)
+ .printStackTrace(System.err);
+ }
+ }
+ } else if (view instanceof PosixFileAttributeView posixView) {
+ try {
+ Set perms = posixView.readAttributes().permissions();
+
+ if ((access & ACCESS_READ) != 0) {
+ if (enable) {
+ perms.add(PosixFilePermission.OWNER_READ);
+ } else {
+ perms.remove(PosixFilePermission.OWNER_READ);
+ }
+ if (!owneronly) {
+ if (enable) {
+ perms.add(PosixFilePermission.GROUP_READ);
+ perms.add(PosixFilePermission.OTHERS_READ);
+ } else {
+ perms.remove(PosixFilePermission.GROUP_READ);
+ perms.remove(PosixFilePermission.OTHERS_READ);
+ }
+ }
+ }
+
+ if ((access & ACCESS_WRITE) != 0) {
+ if (enable) {
+ perms.add(PosixFilePermission.OWNER_WRITE);
+ } else {
+ perms.remove(PosixFilePermission.OWNER_WRITE);
+ }
+ if (!owneronly) {
+ if (enable) {
+ perms.add(PosixFilePermission.GROUP_WRITE);
+ perms.add(PosixFilePermission.OTHERS_WRITE);
+ } else {
+ perms.remove(PosixFilePermission.GROUP_WRITE);
+ perms.remove(PosixFilePermission.OTHERS_WRITE);
+ }
+ }
+ }
+
+ if ((access & ACCESS_EXECUTE) != 0) {
+ if (enable) {
+ perms.add(PosixFilePermission.OWNER_EXECUTE);
+ } else {
+ perms.remove(PosixFilePermission.OWNER_EXECUTE);
+ }
+ if (!owneronly) {
+ if (enable) {
+ perms.add(PosixFilePermission.GROUP_EXECUTE);
+ perms.add(PosixFilePermission.OTHERS_EXECUTE);
+ } else {
+ perms.remove(PosixFilePermission.GROUP_EXECUTE);
+ perms.remove(PosixFilePermission.OTHERS_EXECUTE);
+ }
+ }
+ }
+
+ posixView.setPermissions(perms);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't set posix attributes for %s", f), err)
+ .printStackTrace(System.err);
+ }
+ result = false;
+ }
+ }
+
+ return result;
+ }
+
+ record StreamInitializationBundle(
+ FileDescriptor fd,
+ FileChannel channel,
+ ExternalChannelHolder externalChannelHolder
+ ) {
+ }
+
+ static StreamInitializationBundle initializeStreamUsingNio(
+ Closeable owner,
+ java.nio.file.FileSystem nioFs,
+ File file,
+ Path nioPath,
+ Set optionsForChannel,
+ NioChannelCleanable channelCleanable) throws FileNotFoundException {
+ try {
+ // This tricky thread local variable allows specifying an argument for sun.nio.ch.FileChannelImpl.
+ // which is not present in the NIO public API and which is not easy to specify another way.
+ IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.set(owner);
+ return initializeStreamsUsingNio0(owner, nioFs, file, nioPath, optionsForChannel, channelCleanable);
+ } finally {
+ IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.remove();
+ }
+ }
+
+ private static StreamInitializationBundle initializeStreamsUsingNio0(
+ Closeable owner,
+ java.nio.file.FileSystem nioFs,
+ File file,
+ Path nioPath,
+ Set optionsForChannel,
+ NioChannelCleanable channelCleanable) throws FileNotFoundException {
+ FileChannel channel = null;
+ try {
+ channel = nioFs.provider().newFileChannel(nioPath, optionsForChannel);
+ return new StreamInitializationBundle(
+ initializeFdUsingNio(owner, channel, channelCleanable),
+ channel,
+ new ExternalChannelHolder(owner, channel, channelCleanable)
+ );
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't create a %s for %s with %s", owner.getClass().getSimpleName(), file, nioFs), err)
+ .printStackTrace(System.err);
+ }
+ if (channel != null) {
+ try {
+ channel.close();
+ } catch (IOException _) {
+ // Nothing.
+ }
+ }
+ throw IoOverNioFileSystem.convertNioToIoExceptionInStreams(err);
+ }
+ }
+
+ private static FileDescriptor initializeFdUsingNio(
+ Closeable owner,
+ FileChannel channel,
+ NioChannelCleanable channelCleanable) {
+ final FileDescriptor fd;
+ if (channel instanceof FileChannelImpl fci) {
+ fd = fci.getFD();
+ fd.attach(owner);
+ FileCleanable.register(fd);
+ fci.setUninterruptible();
+ } else {
+ channelCleanable.setChannel(channel);
+ fd = new FileDescriptor();
+ fd.registerCleanup(new NoOpCleanable(fd));
+ }
+ return fd;
+ }
+
+ @Override
+ public char getSeparator() {
+ return parent.getSeparator();
+ }
+
+ @Override
+ public char getPathSeparator() {
+ return parent.getPathSeparator();
+ }
+
+ @Override
+ public String normalize(String path) {
+ // java.nio.file.Path.normalize works differently compared to this normalizer.
+ // Especially, Path.normalize converts "." into an empty string, which breaks
+ // even tests in OpenJDK.
+ return parent.normalize(path);
+ }
+
+ @Override
+ public int prefixLength(String path) {
+ // Although it's possible to represent this function via `Path.getRoot`,
+ // the default file system and java.nio handle corner cases with incorrect paths differently.
+ return parent.prefixLength(path);
+ }
+
+ @Override
+ public String resolve(String parent, String child) {
+ // java.nio is stricter to various invalid symbols than java.io.
+ return this.parent.resolve(parent, child);
+ }
+
+ @Override
+ public String getDefaultParent() {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(null);
+ if (nioFs != null) {
+ return nioFs.getSeparator();
+ }
+ return parent.getDefaultParent();
+ }
+
+ @Override
+ public String fromURIPath(String path) {
+ return parent.fromURIPath(path);
+ }
+
+ @Override
+ public boolean isAbsolute(File f) {
+ boolean result = isAbsolute0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.isAbsolute(%s) = %b%n", f, result);
+ }
+ return result;
+ }
+
+ private boolean isAbsolute0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ try {
+ return nioFs.getPath(f.getPath()).isAbsolute();
+ } catch (InvalidPathException err) {
+ // Path parsing in java.nio is stricter than in java.io.
+ // Giving a chance to the original implementation.
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't check if a path %s is absolute", f), err)
+ .printStackTrace(System.err);
+ }
+ }
+ }
+ return parent.isAbsolute(f);
+ }
+
+ @Override
+ public boolean isInvalid(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ if (nioFs.getSeparator().equals("\\") && f.getPath().equals("NUL:")) {
+ return false;
+ }
+ try {
+ Path ignored = nioFs.getPath(f.getPath());
+ return false;
+ } catch (InvalidPathException _) {
+ return true;
+ }
+ }
+ return parent.isInvalid(f);
+ }
+
+ @Override
+ public String resolve(File f) {
+ try {
+ String result = resolve0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.resolve(%s) = %s%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.resolve(%s) threw an error%n", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private String resolve0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ String initialPath = f.toString();
+ if (getSeparator() == '\\' && prefixLength(initialPath) == 2 && initialPath.charAt(0) == '\\') {
+ // This behavior is covered by `test/jdk/java/io/File/GetAbsolutePath.java`.
+ // If this condition passed through Path.toAbsolutePath, an additional excessive slash
+ // might be added at the end, and the behavior would differ from the original java.io.File.getAbsolutePath.
+ return initialPath;
+ }
+
+ try {
+ String resolvedPath = nioFs.getPath(f.getPath()).toAbsolutePath().toString();
+ if (
+ initialPath.length() > 2
+ && getSeparator() == '\\'
+ && initialPath.charAt(1) == ':'
+ && initialPath.charAt(2) != '\\'
+ && initialPath.charAt(0) != System.getProperty("user.dir", "?").charAt(0)
+ ) {
+ // The source is a tricky drive-relative path,
+ // and the current working directory is on a different drive.
+ // For unclear reasons, the resolved path must have two backslashes after the colon.
+ // This behavior is covered by `test/jdk/java/io/File/GetAbsolutePath.java`.
+ resolvedPath = resolvedPath.substring(0, 2) + '\\' + resolvedPath.substring(2);
+ }
+ return resolvedPath;
+ } catch (InvalidPathException err) {
+ // Path parsing in java.nio is stricter than in java.io.
+ // Giving a chance to the original implementation.
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't resolve a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ }
+ }
+ return parent.resolve(f);
+ }
+
+ @Override
+ public String canonicalize(String path) throws IOException {
+ try {
+ String result = canonicalize0(path);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.canonicalize(%s) = %s%n", path, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.canonicalize(%s) threw an error%n", path), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private String canonicalize0(String path) throws IOException {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(path);
+ if (nioFs != null) {
+ try {
+ // Unlike java.nio.file.Path.toRealPath, File.getCanonicalFile works with non-existent files
+ // and resolves symlinks for the first existing parent.
+ Path nioPath = nioFs.getPath(path);
+ if (!nioPath.isAbsolute()) {
+ nioPath = Path.of(System.getProperty("user.dir")).resolve(nioPath);
+ }
+
+ boolean isTrickyDriveRelativePath = !nioPath.toString().startsWith(nioPath.getRoot().toString());
+
+ Path suffix = nioFs.getPath("");
+
+ while (!nioPath.equals(nioPath.getRoot())) {
+ Path parent = nioPath.getParent();
+ if (parent == null) {
+ parent = nioPath.getRoot();
+ }
+
+ String fileNameStr = nioPath.getFileName().toString();
+
+ if (!fileNameStr.isEmpty() && !fileNameStr.equals(".")) {
+ try {
+ nioPath = nioPath.toRealPath();
+ break;
+ } catch (IOException _) {
+ // Nothing.
+ }
+ suffix = nioPath.getFileName().resolve(suffix);
+ }
+ nioPath = parent;
+ }
+
+ Path result = nioPath.resolve(suffix).normalize();
+
+ // It's not clear why it should work like that,
+ // but this behaviour is checked by the test `GetAbsolutePath.windowsDriveRelative`.
+ if (isTrickyDriveRelativePath) {
+ StringBuilder sb = new StringBuilder(result.getRoot().toString());
+ for (int i = 0; i < result.getNameCount(); i++) {
+ sb.append(getSeparator());
+ sb.append(result.getName(i).toString());
+ }
+ return sb.toString();
+ } else {
+ return result.toString();
+ }
+ } catch (InvalidPathException err) {
+ // Path parsing in java.nio is stricter than in java.io.
+ // Giving a chance to the original implementation.
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't canonicalize a path %s", path), err)
+ .printStackTrace(System.err);
+ }
+ }
+ }
+ return parent.canonicalize(path);
+ }
+
+ @Override
+ public boolean hasBooleanAttributes(File f, int attributes) {
+ try {
+ boolean result = hasBooleanAttributes0(f, attributes);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.hasBooleanAttributes(%s, %s) = %b%n", f, attributes, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.hasBooleanAttributes(%s, %s) threw an error", f, attributes), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ @SuppressWarnings("resource")
+ private boolean hasBooleanAttributes0(File f, int attributes) {
+ boolean result;
+ if (acquireNioFs(f.getPath()) != null) {
+ result = (getBooleanAttributes0(f) & attributes) == attributes;
+ } else {
+ result = parent.hasBooleanAttributes(f, attributes);
+ }
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.hasBooleanAttributes(%s, %s) = %b%n", f, attributes, result);
+ }
+ return result;
+ }
+
+ @Override
+ public int getBooleanAttributes(File f) {
+ int result = getBooleanAttributes0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.getBooleanAttributes(%s) = %d%n", f, result);
+ }
+ return result;
+ }
+
+ private int getBooleanAttributes0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ try {
+ Path path = nioFs.getPath(f.getPath());
+
+ {
+ boolean isAbsoluteOrDriveRelative;
+ if (path.isAbsolute()) {
+ isAbsoluteOrDriveRelative = true;
+ } else if (getSeparator() == '\\') {
+ String pathString = path.toString();
+ isAbsoluteOrDriveRelative = switch (pathString.length()) {
+ case 0 -> false;
+ case 1 -> pathString.charAt(0) == '\\';
+ case 2 -> Character.isLetter(pathString.charAt(0)) && pathString.charAt(1) == ':';
+ default -> Character.isLetter(pathString.charAt(0)) && pathString.charAt(1) == ':' && pathString.charAt(2) != '\\';
+ };
+ } else {
+ isAbsoluteOrDriveRelative = false;
+ }
+ if (!isAbsoluteOrDriveRelative && (path.getFileName() == null || path.getFileName().toString().isEmpty())) {
+ // The LibC function `stat` returns an error for such calls.
+ return 0;
+ }
+ }
+
+ // The order of checking Posix attributes first and DOS attributes next is deliberate.
+ // WindowsFileSystem supports these attributes: basic, dos, acl, owner, user.
+ // LinuxFileSystem supports: basic, posix, unix, owner, dos, user.
+ // MacOSFileSystem and BsdFileSystem support: basic, posix, unix, owner, user.
+ //
+ // Notice that Linux FS supports DOS attributes, which is unexpected.
+ // The DOS adapter implementation from LinuxFileSystem uses `open` for getting attributes.
+ // It's not only more expensive than `stat`, but also doesn't work with Unix sockets.
+ //
+ // Also, notice that Windows FS does not support Posix attributes, which is expected.
+ // Checking for Posix attributes first prevents from checking DOS attributes on Linux,
+ // even though Posix permissions aren't used in this method.
+ BasicFileAttributes attrs;
+ try {
+ attrs = Files.readAttributes(path, PosixFileAttributes.class);
+ } catch (UnsupportedOperationException _) {
+ attrs = Files.readAttributes(path, DosFileAttributes.class);
+ }
+
+ return BA_EXISTS
+ | (attrs.isDirectory() ? BA_DIRECTORY : 0)
+ | (attrs.isRegularFile() ? BA_REGULAR : 0)
+ | (attrs instanceof DosFileAttributes dosAttrs && dosAttrs.isHidden() ? BA_HIDDEN : 0)
+ // Just like in java.io.UnixFileSystem.isHidden
+ | (attrs instanceof PosixFileAttributes && f.getName().startsWith(".") ? BA_HIDDEN : 0);
+ } catch (InvalidPathException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't get attributes for a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ // Path parsing in java.nio is stricter than in java.io.
+ // Not returning here to give a chance to the original implementation.
+ } catch (@SuppressWarnings("removal") IOException | AccessControlException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't get attributes for a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return getSeparator() == '/' && f.getName().startsWith(".") ? BA_HIDDEN : 0;
+ }
+ }
+ return parent.getBooleanAttributes(f);
+ }
+
+ @Override
+ public boolean checkAccess(File f, int access) {
+ boolean result = checkAccess0(f, access);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.checkAccess(%s, %d) = %b%n", f, access, result);
+ }
+ return result;
+ }
+
+ private boolean checkAccess0(File f, int access) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ if (f.getPath().isEmpty()) {
+ // Both access() in Posix and GetFileAttributesW in Windows treat an empty path
+ // as an invalid argument and return an error.
+ return false;
+ }
+
+ try {
+ Path path = nioFs.getPath(f.getPath());
+
+ int i = 0;
+ AccessMode[] accessMode = new AccessMode[3];
+ if ((access & ACCESS_READ) != 0) {
+ accessMode[i++] = AccessMode.READ;
+ }
+ if ((access & ACCESS_WRITE) != 0) {
+ accessMode[i++] = AccessMode.WRITE;
+ }
+ if ((access & ACCESS_EXECUTE) != 0) {
+ accessMode[i++] = AccessMode.EXECUTE;
+ }
+ accessMode = Arrays.copyOf(accessMode, i);
+
+ nioFs.provider().checkAccess(path, accessMode);
+ return true;
+ } catch (@SuppressWarnings("removal") IOException | AccessControlException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't check access for a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ }
+ }
+ return parent.checkAccess(f, access);
+ }
+
+ @Override
+ public boolean setPermission(File f, int access, boolean enable, boolean owneronly) {
+ boolean result = setPermission0(f, access, enable, owneronly);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.setPermission(%s, %d, %b, %b) = %b%n", f, access, enable, owneronly, result);
+ }
+ return result;
+ }
+
+ private boolean setPermission0(File f, int access, boolean enable, boolean owneronly) {
+ java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ return setPermission0(nioFs, f, access, enable, owneronly);
+ }
+ return parent.setPermission(f, access, enable, owneronly);
+ }
+
+ @Override
+ public long getLastModifiedTime(File f) {
+ try {
+ long result = getLastModifiedTime0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.getLastModifiedTime(%s) = %d%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.getLastModifiedTime(%s) threw an error", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private long getLastModifiedTime0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ try {
+ Path path = nioFs.getPath(f.getPath());
+ nioFs.provider().readAttributes(path, BasicFileAttributes.class).lastModifiedTime();
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't get last modified time for a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return 0;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ }
+ }
+ return parent.getLastModifiedTime(f);
+ }
+
+ @Override
+ public long getLength(File f) {
+ try {
+ long result = getLength0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.getLength(%s) = %d%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.getLength(%s) threw an error%n", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private long getLength0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ try {
+ if (nioFs != null) {
+ Path path = nioFs.getPath(f.getPath());
+ return nioFs.provider().readAttributes(path, BasicFileAttributes.class).size();
+ }
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't get file length for a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return 0;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ }
+ return parent.getLength(f);
+ }
+
+ @Override
+ public boolean createFileExclusively(String pathname) throws IOException {
+ try {
+ boolean result = createFileExclusively0(pathname);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.createFileExclusively(%s) = %b%n", pathname, result);
+ }
+ return result;
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.createFileExclusively(%s) threw an error", pathname), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private boolean createFileExclusively0(String pathname) throws IOException {
+ java.nio.file.FileSystem nioFs = acquireNioFs(pathname);
+ try {
+ // In case of an empty pathname, the default file system always throws an exception.
+ if (pathname != null && !pathname.isEmpty() && nioFs != null) {
+ Path path = nioFs.getPath(pathname);
+ nioFs.provider().newByteChannel(
+ path,
+ EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
+ ).close();
+ return true;
+ }
+ } catch (AccessDeniedException err) {
+ // A special case for Windows. It can happen if it is an already existing directory.
+ if (Files.exists(nioFs.getPath(pathname))) {
+ return false;
+ }
+ } catch (FileAlreadyExistsException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't exclusively create a file %s", pathname), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ } catch (InvalidPathException err) {
+ throw new IOException(err.getMessage(), err); // The default file system would throw IOException too.
+ } catch (IOException err) {
+ String originalMessage = IoToNioErrorMessageHolder.removeMessage(err);
+ if (originalMessage != null) {
+ throw new IOException(originalMessage, err);
+ } else {
+ throw err;
+ }
+ }
+ return parent.createFileExclusively(pathname);
+ }
+
+ @Override
+ public boolean delete(File f) {
+ try {
+ boolean result = delete0(f, true);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.delete(%s) = %b%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.delete(%s) threw an error", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private boolean delete0(File f, boolean mayRepeat) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ Path path = null;
+ try {
+ path = nioFs.getPath(f.getPath());
+ nioFs.provider().delete(path);
+ return true;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (err instanceof AccessDeniedException && mayRepeat && path != null && nioFs.supportedFileAttributeViews().contains("dos")) {
+ try {
+ DosFileAttributeView attrs = nioFs.provider()
+ .getFileAttributeView(path, DosFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
+ attrs.setReadOnly(false);
+ return delete0(f, false);
+ } catch (IOException _) {
+ // Nothing.
+ }
+ }
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't delete a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ }
+ }
+ return parent.delete(f);
+ }
+
+ @Override
+ public String[] list(File f) {
+ try {
+ String[] result = list0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.list(%s) = %s%n", f, Arrays.toString(result));
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.list(%s) threw an error", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private String[] list0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ String pathStr = f.getPath();
+ if (pathStr.isEmpty()) {
+ // Java_java_io_UnixFileSystem_list0 calls opendir(), and it returns an error for an empty path.
+ // Java_java_io_WinNTFileSystem_list0 returns null
+ // after calling fileToNTPath and before any WinAPI call.
+ return null;
+ }
+
+ try {
+ if (getSeparator() == '\\') {
+ // Java_java_io_WinNTFileSystem_list0 deliberately and explicitly removes trailing spaces from the path.
+ // It doesn't happen in Java_java_io_UnixFileSystem_list0
+ int i = pathStr.length();
+ while (i > 0 && pathStr.charAt(i - 1) == ' ') {
+ i--;
+ }
+ pathStr = pathStr.substring(0, i);
+ }
+
+ Path path = nioFs.getPath(pathStr);
+ try (DirectoryStream children = nioFs.provider().newDirectoryStream(path, AcceptAllFilter.FILTER)) {
+ List result = new ArrayList<>();
+ for (Path child : children) {
+ result.add(child.getFileName().toString());
+ }
+ return result.toArray(String[]::new);
+ }
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't list a path %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return null;
+ }
+ }
+ return parent.list(f);
+ }
+
+ @Override
+ public boolean createDirectory(File f) {
+ try {
+ boolean result = createDirectory0(f);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.createDirectory(%s) = %b%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.createDirectory(%s) threw an error", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private boolean createDirectory0(File f) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ try {
+ Path path = nioFs.getPath(f.getPath());
+ nioFs.provider().createDirectory(path);
+ return true;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't create a directory %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ }
+ }
+ return parent.createDirectory(f);
+ }
+
+ @Override
+ public boolean rename(File f1, File f2) {
+ try {
+ boolean result = rename0(f1, f2);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.rename(%s, %s) = %b%n", f1, f2, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.rename(%s, %s) threw an error", f1, f2), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private boolean rename0(File f1, File f2) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(null);
+ if (nioFs != null) {
+ try {
+ Path path1 = nioFs.getPath(f1.getPath());
+ Path path2 = nioFs.getPath(f2.getPath());
+ nioFs.provider().move(path1, path2, StandardCopyOption.REPLACE_EXISTING);
+ return true;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't rename %s to %s", f1, f2), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ }
+ }
+ return parent.rename(f1, f2);
+ }
+
+ @Override
+ public boolean setLastModifiedTime(File f, long time) {
+ try {
+ boolean result = setLastModifiedTime0(f, time);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.setLastModifiedTime(%s, %d) = %b%n", f, time, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.setLastModifiedTime(%s, %d) threw an error", f, time), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private boolean setLastModifiedTime0(File f, long time) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ if (f.getPath().isEmpty()) {
+ // Here happens the same as in checkAccess0.
+ return false;
+ }
+
+ try {
+ Path path = nioFs.getPath(f.getPath());
+ nioFs.provider()
+ .getFileAttributeView(path, BasicFileAttributeView.class)
+ .setTimes(FileTime.fromMillis(time), null, null);
+ return true;
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't set last modified time of %s", f), err)
+ .printStackTrace(System.err);
+ }
+ return false;
+ }
+ }
+ return parent.setLastModifiedTime(f, time);
+ }
+
+ @Override
+ public boolean setReadOnly(File f) {
+ try {
+ java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ boolean result;
+ if (nioFs != null) {
+ result = setPermission0(nioFs, f, ACCESS_EXECUTE | ACCESS_WRITE, false, false);
+ } else {
+ result = parent.setReadOnly(f);
+ }
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.setReadOnly(%s) = %b%n", f, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.setReadOnly(%s) threw an error", f), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ @Override
+ public File[] listRoots() {
+ try {
+ File[] result = listRoots0();
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.listRoots() = %s%n", Arrays.toString(result));
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable("IoOverNioFileSystem.listRoots() threw an error", err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private File[] listRoots0() {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(null);
+ if (nioFs != null) {
+ List roots = new ArrayList<>();
+ for (Path rootDirectory : nioFs.getRootDirectories()) {
+ roots.add(rootDirectory.toFile());
+ }
+ return roots.toArray(File[]::new);
+ }
+ return parent.listRoots();
+ }
+
+ @Override
+ public long getSpace(File f, int t) {
+ try {
+ long result = getSpace0(f, t);
+ if (DEBUG.writeTraces()) {
+ System.err.printf("IoOverNioFileSystem.getSpace(%s, %d) = %d%n", f, t, result);
+ }
+ return result;
+ } catch (RuntimeException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("IoOverNioFileSystem.getSpace(%s, %d) threw an error", f, t), err)
+ .printStackTrace(System.err);
+ }
+ throw err;
+ }
+ }
+
+ private long getSpace0(File f, int t) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(f.getPath());
+ if (nioFs != null) {
+ try {
+ Path path = nioFs.getPath(f.getPath());
+ FileStore store = nioFs.provider().getFileStore(path);
+ return switch (t) {
+ case SPACE_TOTAL -> store.getTotalSpace();
+ case SPACE_USABLE -> store.getUsableSpace();
+ case SPACE_FREE -> store.getUnallocatedSpace();
+ default -> throw new IllegalArgumentException("Invalid space type: " + t);
+ };
+ } catch (InvalidPathException err) {
+ throw new InternalError(err.getMessage(), err);
+ } catch (IOException err) {
+ if (DEBUG.writeErrors()) {
+ new Throwable(String.format("Can't get space %s for a path %s", t, f), err)
+ .printStackTrace(System.err);
+ }
+ return 0;
+ }
+ }
+ return parent.getSpace(f, t);
+ }
+
+ @Override
+ public int getNameMax(String path) {
+ // Seems impossible with java.nio.
+ return parent.getNameMax(path);
+ }
+
+ @Override
+ public int compare(File f1, File f2) {
+ @SuppressWarnings("resource") java.nio.file.FileSystem nioFs = acquireNioFs(null);
+ if (nioFs != null) {
+ try {
+ Path p1 = nioFs.getPath(f1.getPath());
+ Path p2 = nioFs.getPath(f2.getPath());
+ return p1.compareTo(p2);
+ } catch (InvalidPathException _) {
+ // Path parsing in java.nio is stricter than in java.io.
+ // Giving a chance to the original implementation.
+ }
+ }
+ return parent.compare(f1, f2);
+ }
+
+ @Override
+ public int hashCode(File f) {
+ return parent.hashCode(f);
+ }
+
+ private static class AcceptAllFilter implements DirectoryStream.Filter {
+ static final AcceptAllFilter FILTER = new AcceptAllFilter();
+
+ private AcceptAllFilter() {
+ }
+
+ @Override
+ public boolean accept(Path entry) {
+ return true;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/java/io/NioChannelCleanable.java b/src/java.base/share/classes/java/io/NioChannelCleanable.java
new file mode 100644
index 000000000000..e05bbb1ceb39
--- /dev/null
+++ b/src/java.base/share/classes/java/io/NioChannelCleanable.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.io;
+
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
+
+import java.lang.ref.WeakReference;
+import java.nio.channels.FileChannel;
+
+/**
+ * Cleanable for {@link FileInputStream}, {@link FileOutputStream} and {@link RandomAccessFile}
+ * which is in use if {@link com.jetbrains.internal.IoOverNio#IS_ENABLED_IN_GENERAL} is true.
+ *
+ * Implicitly, there MAY be used both this cleaner and {@link FileCleanable} for the same file descriptor,
+ * when the channel is {@link sun.nio.ch.FileChannelImpl}.
+ * However, this class is also able to close other channels.
+ *
+ * @see FileCleanable
+ */
+final class NioChannelCleanable extends PhantomCleanable {
+ private WeakReference channel = new WeakReference<>(null);
+
+ /**
+ * @param channelOwner The owner of a channel.
+ * It is supposed to be {@link FileInputStream}, {@link FileOutputStream} or {@link RandomAccessFile}.
+ */
+ NioChannelCleanable(Object channelOwner) {
+ super(channelOwner, CleanerFactory.cleaner());
+
+ assert channelOwner instanceof FileInputStream ||
+ channelOwner instanceof FileOutputStream ||
+ channelOwner instanceof RandomAccessFile
+ : channelOwner.getClass() + " is not an expected class";
+ }
+
+ /**
+ * The same as {@link FileCleanable#register} but for channels.
+ */
+ void setChannel(FileChannel channel) {
+ this.channel = new WeakReference<>(channel);
+ }
+
+ @Override
+ protected void performCleanup() {
+ var channel = this.channel.get();
+ if (channel != null) {
+ try {
+ channel.close();
+ } catch (IOException | RuntimeException _) {
+ // Ignored.
+ }
+ }
+ }
+}
diff --git a/src/java.base/share/classes/java/io/NoOpCleanable.java b/src/java.base/share/classes/java/io/NoOpCleanable.java
new file mode 100644
index 000000000000..187bd6a7e68f
--- /dev/null
+++ b/src/java.base/share/classes/java/io/NoOpCleanable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2025 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.io;
+
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
+
+/**
+ * An analog of {@link FileCleanable} that does nothing.
+ *
+ * It exists only to fulfill an implicit contract that every {@link FileDescriptor}
+ * from {@link RandomAccessFile} and classes like that
+ * has a registered cleaner.
+ * Some tests rely on this contract and fail without this class.
+ */
+class NoOpCleanable extends PhantomCleanable {
+ NoOpCleanable(FileDescriptor referent) {
+ super(referent, CleanerFactory.cleaner());
+ }
+
+ @Override
+ protected void performCleanup() {
+ // Nothing.
+ }
+}
diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java
index c09f87afcdc4..2d4c549338fc 100644
--- a/src/java.base/share/classes/java/io/RandomAccessFile.java
+++ b/src/java.base/share/classes/java/io/RandomAccessFile.java
@@ -25,8 +25,23 @@
package java.io;
+import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-
+import java.nio.channels.NonWritableChannelException;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import static com.jetbrains.internal.IoOverNio.DEBUG;
+
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.JavaIORandomAccessFileAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Blocker;
@@ -99,6 +114,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private volatile FileChannel channel;
private volatile boolean closed;
+ private final boolean useNio;
+
+ @SuppressWarnings({
+ "FieldCanBeLocal",
+ "this-escape", // It immediately converts into a phantom reference.
+ })
+ private final NioChannelCleanable channelCleanable = new NioChannelCleanable(this);
+
+ private final ExternalChannelHolder externalChannelHolder;
+
/**
* Creates a random access file stream to read from, and optionally
* to write to, a file with the specified pathname. If the file exists
@@ -251,11 +276,63 @@ else if (mode.startsWith("rw")) {
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
- fd = new FileDescriptor();
- fd.attach(this);
path = name;
- open(name, imode);
- FileCleanable.register(fd); // open sets the fd, register the cleanup
+
+ FileSystem nioFs = IoOverNioFileSystem.acquireNioFs(path);
+ Path nioPath = null;
+ if (nioFs != null) {
+ try {
+ nioPath = nioFs.getPath(path);
+ } catch (InvalidPathException _) {
+ // Nothing.
+ }
+ }
+
+ // Two significant differences between the legacy java.io and java.nio.files:
+ // * java.nio.file allows to open directories as streams, java.io.FileInputStream doesn't.
+ // * java.nio.file doesn't work well with pseudo devices, i.e., `seek()` fails, while java.io works well.
+ boolean isRegularFile;
+ try {
+ isRegularFile = nioPath != null &&
+ Files.readAttributes(nioPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).isRegularFile();
+ }
+ catch (NoSuchFileException _) {
+ isRegularFile = true;
+ }
+ catch (IOException _) {
+ isRegularFile = false;
+ }
+
+ useNio = nioPath != null && isRegularFile;
+ if (useNio) {
+ var bundle = IoOverNioFileSystem.initializeStreamUsingNio(
+ this, nioFs, file, nioPath, optionsForChannel(imode), channelCleanable);
+ channel = bundle.channel();
+ fd = bundle.fd();
+ externalChannelHolder = bundle.externalChannelHolder();
+ } else {
+ fd = new FileDescriptor();
+ fd.attach(this);
+ open(name, imode);
+ FileCleanable.register(fd); // open sets the fd, register the cleanup
+ externalChannelHolder = null;
+ }
+ if (DEBUG.writeTraces()) {
+ System.err.printf("Created a RandomAccessFile for %s%n", file);
+ }
+ }
+
+ private static HashSet optionsForChannel(int imode) {
+ HashSet options = new HashSet<>(6);
+ options.add(StandardOpenOption.READ);
+ if ((imode & O_RDONLY) == 0) {
+ options.add(StandardOpenOption.WRITE);
+ options.add(StandardOpenOption.CREATE);
+ }
+ if ((imode & O_SYNC) == O_SYNC) options.add(StandardOpenOption.SYNC);
+ if ((imode & O_DSYNC) == O_DSYNC) options.add(StandardOpenOption.DSYNC);
+ if ((imode & O_TEMPORARY) == O_TEMPORARY) options.add(StandardOpenOption.DELETE_ON_CLOSE);
+ return options;
}
/**
@@ -288,6 +365,10 @@ public final FileDescriptor getFD() throws IOException {
* @since 1.4
*/
public final FileChannel getChannel() {
+ if (externalChannelHolder != null) {
+ return externalChannelHolder.getInterruptibleChannel();
+ }
+
FileChannel fc = this.channel;
if (fc == null) {
synchronized (this) {
@@ -356,10 +437,22 @@ private void open(String name, int mode) throws FileNotFoundException {
* end-of-file has been reached.
*/
public int read() throws IOException {
- if (jfrTracing && FileReadEvent.enabled()) {
+ if (jfrTracing && FileReadEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
return traceRead0();
}
- return read0();
+ return implRead();
+ }
+
+ private int implRead() throws IOException {
+ if (!useNio) {
+ return read0();
+ } else {
+ // Really same to FileInputStream.read()
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ int nRead = channel.read(buffer);
+ buffer.rewind();
+ return nRead == 1 ? (buffer.get() & 0xFF) : -1;
+ }
}
private native int read0() throws IOException;
@@ -371,7 +464,7 @@ private int traceRead0() throws IOException {
long start = 0;
try {
start = FileReadEvent.timestamp();
- result = read0();
+ result = implRead();
if (result < 0) {
endOfFile = true;
} else {
@@ -394,10 +487,24 @@ private int traceRead0() throws IOException {
* @throws IOException If an I/O error has occurred.
*/
private int readBytes(byte[] b, int off, int len) throws IOException {
- if (jfrTracing && FileReadEvent.enabled()) {
+ if (jfrTracing && FileReadEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
return traceReadBytes0(b, off, len);
}
- return readBytes0(b, off, len);
+ return implReadBytes(b, off, len);
+ }
+
+ private int implReadBytes(byte[] b, int off, int len) throws IOException {
+ if (!useNio) {
+ return readBytes0(b, off, len);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+ return channel.read(buffer);
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ return readBytes0(b, off, len);
+ }
+ }
}
private native int readBytes0(byte[] b, int off, int len) throws IOException;
@@ -407,7 +514,7 @@ private int traceReadBytes0(byte b[], int off, int len) throws IOException {
long start = 0;
try {
start = FileReadEvent.timestamp();
- bytesRead = readBytes0(b, off, len);
+ bytesRead = implReadBytes(b, off, len);
} finally {
long duration = FileReadEvent.timestamp() - start;
if (FileReadEvent.shouldCommit(duration)) {
@@ -447,7 +554,17 @@ private int traceReadBytes0(byte b[], int off, int len) throws IOException {
* {@code b.length - off}
*/
public int read(byte[] b, int off, int len) throws IOException {
- return readBytes(b, off, len);
+ if (!useNio) {
+ return readBytes(b, off, len);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+ return channel.read(buffer);
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ return readBytes(b, off, len);
+ }
+ }
}
/**
@@ -470,7 +587,17 @@ public int read(byte[] b, int off, int len) throws IOException {
* @throws NullPointerException If {@code b} is {@code null}.
*/
public int read(byte[] b) throws IOException {
- return readBytes(b, 0, b.length);
+ if (!useNio) {
+ return readBytes(b, 0, b.length);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b);
+ return channel.read(buffer);
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ return readBytes(b, 0, b.length);
+ }
+ }
}
/**
@@ -564,7 +691,7 @@ public int skipBytes(int n) throws IOException {
* @throws IOException if an I/O error occurs.
*/
public void write(int b) throws IOException {
- if (jfrTracing && FileWriteEvent.enabled()) {
+ if (jfrTracing && FileWriteEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
traceImplWrite(b);
return;
}
@@ -574,7 +701,16 @@ public void write(int b) throws IOException {
private void implWrite(int b) throws IOException {
boolean attempted = Blocker.begin(sync);
try {
- write0(b);
+ if (!useNio) {
+ write0(b);
+ } else {
+ byte[] array = new byte[1];
+ array[0] = (byte) b;
+ ByteBuffer buffer = ByteBuffer.wrap(array);
+ do {
+ channel.write(buffer);
+ } while (buffer.hasRemaining());
+ }
} finally {
Blocker.end(attempted);
}
@@ -606,7 +742,7 @@ private void traceImplWrite(int b) throws IOException {
* @throws IOException If an I/O error has occurred.
*/
private void writeBytes(byte[] b, int off, int len) throws IOException {
- if (jfrTracing && FileWriteEvent.enabled()) {
+ if (jfrTracing && FileWriteEvent.enabled() && !IoOverNio.isAllowedInThisThread()) {
traceImplWriteBytes(b, off, len);
return;
}
@@ -616,7 +752,19 @@ private void writeBytes(byte[] b, int off, int len) throws IOException {
private void implWriteBytes(byte[] b, int off, int len) throws IOException {
boolean attempted = Blocker.begin(sync);
try {
- writeBytes0(b, off, len);
+ if (!useNio) {
+ writeBytes0(b, off, len);
+ } else {
+ try {
+ ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+ do {
+ channel.write(buffer);
+ } while (buffer.hasRemaining());
+ } catch (OutOfMemoryError e) {
+ // May fail to allocate direct buffer memory due to small -XX:MaxDirectMemorySize
+ writeBytes0(b, off, len);
+ }
+ }
} finally {
Blocker.end(attempted);
}
@@ -673,7 +821,15 @@ public void write(byte[] b, int off, int len) throws IOException {
* at which the next read or write occurs.
* @throws IOException if an I/O error occurs.
*/
- public native long getFilePointer() throws IOException;
+ public long getFilePointer() throws IOException {
+ if (!useNio) {
+ return getFilePointer0();
+ } else {
+ return channel.position();
+ }
+ }
+
+ private native long getFilePointer0() throws IOException;
/**
* Sets the file-pointer offset, measured from the beginning of this
@@ -692,8 +848,13 @@ public void write(byte[] b, int off, int len) throws IOException {
public void seek(long pos) throws IOException {
if (pos < 0) {
throw new IOException("Negative seek offset");
+ } else {
+ if (!useNio) {
+ seek0(pos);
+ } else {
+ channel.position(pos);
+ }
}
- seek0(pos);
}
private native void seek0(long pos) throws IOException;
@@ -705,7 +866,11 @@ public void seek(long pos) throws IOException {
* @throws IOException if an I/O error occurs.
*/
public long length() throws IOException {
- return length0();
+ if (!useNio) {
+ return length0();
+ } else {
+ return channel.size();
+ }
}
private native long length0() throws IOException;
@@ -737,7 +902,39 @@ public long length() throws IOException {
* @since 1.2
*/
public void setLength(long newLength) throws IOException {
- setLength0(newLength);
+ if (!useNio) {
+ setLength0(newLength);
+ } else {
+ try {
+ long oldSize = channel.size();
+ if (newLength < oldSize) {
+ channel.truncate(newLength);
+ } else {
+ long position = channel.position();
+ channel.position(channel.size());
+ try {
+ byte[] buf = new byte[1 << 14];
+ Arrays.fill(buf, (byte) 0);
+ long remains = newLength - oldSize;
+ while (remains > 0) {
+ ByteBuffer buffer = ByteBuffer.wrap(buf);
+ int length = (int) Math.min(remains, buf.length);
+ buffer.limit(length);
+ int written = channel.write(buffer);
+ remains -= written;
+ }
+ } finally {
+ try {
+ channel.position(position);
+ } catch (IOException _) {
+ // Nothing.
+ }
+ }
+ }
+ } catch (NonWritableChannelException err) {
+ throw new IOException("setLength failed", err);
+ }
+ }
}
private native void setLength0(long newLength) throws IOException;
@@ -776,6 +973,10 @@ public void close() throws IOException {
fc.close();
}
+ if (externalChannelHolder != null) {
+ externalChannelHolder.close();
+ }
+
fd.closeAll(new Closeable() {
public void close() throws IOException {
fd.close();
diff --git a/src/java.base/share/classes/java/lang/Exception.java b/src/java.base/share/classes/java/lang/Exception.java
index ae59e9fc7f81..1e99ea450641 100644
--- a/src/java.base/share/classes/java/lang/Exception.java
+++ b/src/java.base/share/classes/java/lang/Exception.java
@@ -122,4 +122,9 @@ protected Exception(String message, Throwable cause,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
+
+ private static class JB$$Assertion {}
+ private static class JB$$Event {}
+ private static class JB$$FullGC {}
+ private static class JB$$ShrinkingGC {}
}
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index 67a03efa10b0..019f990c72e7 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -46,6 +46,7 @@
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.Locale;
@@ -60,6 +61,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
+import com.jetbrains.exported.JBRApi;
import jdk.internal.javac.Restricted;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.logger.LoggerFinderLoader.TemporaryLoggerFinder;
@@ -1966,6 +1968,16 @@ private static void initPhase3() {
// set TCCL
Thread.currentThread().setContextClassLoader(scl);
+ if (Boolean.getBoolean("java.util.zip.use.nio.for.zip.file.access")
+ && System.getProperty("java.nio.file.spi.DefaultFileSystemProvider") != null) {
+ // Make sure the custom file system(s) are loaded using the "standard" ZipFile operating mode
+ // rather than the one that forwards to NIO. The latter will use the file system that is being loaded to
+ // try to load files, which will result in an NPE from FileSystems.getDefault().
+ // Calling FileSystems.getDefault() here bypasses that because the NIO operating mode of ZipFile only
+ // activates at init level 4.
+ FileSystems.getDefault();
+ }
+
// system is fully initialized
VM.initLevel(4);
}
@@ -2320,4 +2332,10 @@ public boolean bytesCompatible(String string, Charset charset) {
}
});
}
+
+ @JBRApi.Provides("SystemUtils#fullGC")
+ private static native void $$jb$FullGC();
+
+ @JBRApi.Provides("SystemUtils#shrinkingGC")
+ private static native void $$jb$ShrinkingGC();
}
diff --git a/src/java.base/share/classes/java/lang/Throwable.java b/src/java.base/share/classes/java/lang/Throwable.java
index 8c0ce29dbeef..e8b008307f2c 100644
--- a/src/java.base/share/classes/java/lang/Throwable.java
+++ b/src/java.base/share/classes/java/lang/Throwable.java
@@ -27,6 +27,8 @@
import java.io.*;
import java.util.*;
+
+import com.jetbrains.exported.JBRApi;
import jdk.internal.event.ThrowableTracer;
/**
@@ -1135,4 +1137,20 @@ public final synchronized Throwable[] getSuppressed() {
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
+
+ private static volatile java.util.function.Supplier $$jb$additionalInfoSupplier = null;
+
+ @JBRApi.Provides("Jstack#includeInfoFrom")
+ private static void $$jb$additionalInfoForJstack(java.util.function.Supplier supplier) {
+ $$jb$additionalInfoSupplier = supplier;
+ }
+
+ // NB: this is invoked from the VM
+ private static String $$jb$getAdditionalInfoForJstack() {
+ final java.util.function.Supplier supplier = $$jb$additionalInfoSupplier;
+ if (supplier != null) {
+ return supplier.get();
+ }
+ return null;
+ }
}
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
index cd97413182b1..4dce134a20c0 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1636,6 +1636,11 @@ public MethodHandle serializableConstructor(Class> decl, Constructor> ctorTo
return IMPL_LOOKUP.serializableConstructor(decl, ctorToCall);
}
+ @Override
+ public Lookup lookupIn(Class> lookupClass) {
+ return IMPL_LOOKUP.in(lookupClass);
+ }
+
});
}
diff --git a/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java b/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
index 4940e0c4794d..da46a8399a94 100644
--- a/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
+++ b/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
@@ -160,13 +160,19 @@ public static UnixDomainSocketAddress of(String pathname) {
* @throws NullPointerException if path is {@code null}
*/
public static UnixDomainSocketAddress of(Path path) {
- FileSystem fs = path.getFileSystem();
- if (fs != FileSystems.getDefault()) {
- throw new IllegalArgumentException();
- }
- if (fs.getClass().getModule() != Object.class.getModule()) {
- throw new IllegalArgumentException();
+ final FileSystem fs = path.getFileSystem();
+ final FileSystem defaultFS = sun.nio.fs.DefaultFileSystemProvider.theFileSystem();
+ if (fs != defaultFS || fs.getClass().getModule() != Object.class.getModule()) {
+ try {
+ // Check if we'll be able to create a socket from this Path later on by
+ // testing for the presence of a method identical to
+ // AbstractFileSystemProvider.getSunPathForSocketFile()
+ fs.provider().getClass().getMethod("getSunPathForSocketFile", Path.class);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new IllegalArgumentException();
+ }
}
+
return new UnixDomainSocketAddress(path);
}
diff --git a/src/java.base/share/classes/java/nio/file/FileSystems.java b/src/java.base/share/classes/java/nio/file/FileSystems.java
index f60f798c6e27..cc64107e94cf 100644
--- a/src/java.base/share/classes/java/nio/file/FileSystems.java
+++ b/src/java.base/share/classes/java/nio/file/FileSystems.java
@@ -34,6 +34,7 @@
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.misc.VM;
import sun.nio.fs.DefaultFileSystemProvider;
@@ -93,7 +94,14 @@ private static class DefaultFileSystemHolder {
static final FileSystem defaultFileSystem = defaultFileSystem();
// returns default file system
+ @SuppressWarnings("try")
private static FileSystem defaultFileSystem() {
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ return defaultFileSystem0();
+ }
+ }
+
+ private static FileSystem defaultFileSystem0() {
// load default provider
FileSystemProvider provider = getDefaultProvider();
diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java
index 6969fe8a8e14..01f676bafa9f 100644
--- a/src/java.base/share/classes/java/security/Security.java
+++ b/src/java.base/share/classes/java/security/Security.java
@@ -47,6 +47,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.JavaSecurityPropertiesAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.event.EventHelper;
@@ -322,7 +323,14 @@ public Properties getInitialProperties() {
});
}
+ @SuppressWarnings("try")
private static void initialize() {
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ initialize0();
+ }
+ }
+
+ private static void initialize0() {
SecPropLoader.loadAll();
initialSecurityProperties = (Properties) props.clone();
if (sdebug != null) {
diff --git a/src/java.base/share/classes/java/util/concurrent/DelayQueue.java b/src/java.base/share/classes/java/util/concurrent/DelayQueue.java
index abd14142da52..6834c628122b 100644
--- a/src/java.base/share/classes/java/util/concurrent/DelayQueue.java
+++ b/src/java.base/share/classes/java/util/concurrent/DelayQueue.java
@@ -169,7 +169,8 @@ public boolean offer(E e) {
lock.lock();
try {
q.offer(e);
- if (q.peek() == e) {
+ E curPeek = q.peek();
+ if (curPeek == e || curPeek.getDelay(TimeUnit.NANOSECONDS) <= 0L) {
leader = null;
available.signal();
}
diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java
index f736a092099e..eef3c07e9ed8 100644
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java
@@ -26,18 +26,41 @@
package java.util.zip;
import java.io.Closeable;
-import java.io.InputStream;
-import java.io.IOException;
import java.io.EOFException;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.UncheckedIOException;
import java.lang.ref.Cleaner.Cleanable;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
import java.nio.file.InvalidPathException;
+import java.nio.file.OpenOption;
+import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.Files;
-import java.util.*;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.jar.JarEntry;
@@ -49,10 +72,12 @@
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.DecimalDigits;
+import jdk.internal.misc.VM;
import jdk.internal.util.OperatingSystem;
import jdk.internal.perf.PerfCounter;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.vm.annotation.Stable;
+import sun.nio.ch.FileChannelImpl;
import sun.nio.cs.UTF_8;
import sun.nio.fs.DefaultFileSystemProvider;
import sun.security.util.SignatureFileVerifier;
@@ -91,6 +116,9 @@ public class ZipFile implements ZipConstants, Closeable {
// c) the "native" source of this ZIP file.
private final @Stable CleanableResource res;
+ private static final boolean USE_NIO_FOR_ZIP_FILE_ACCESS =
+ Boolean.parseBoolean(System.getProperty("java.util.zip.use.nio.for.zip.file.access", "false"));
+
private static final int STORED = ZipEntry.STORED;
private static final int DEFLATED = ZipEntry.DEFLATED;
@@ -1125,7 +1153,7 @@ private static class Source {
private int refs = 1;
- private RandomAccessFile zfile; // zfile of the underlying ZIP file
+ private FileAccessor zfile; // zfile of the underlying ZIP file
private byte[] cen; // CEN
private long locpos; // position of first LOC header (usually 0)
private byte[] comment; // ZIP file comment
@@ -1136,6 +1164,88 @@ private static class Source {
private Map metaVersions; // Versions found in META-INF/versions/, by entry name hash
private final boolean startsWithLoc; // true, if ZIP file starts with LOCSIG (usually true)
+ private interface FileAccessor {
+ long length() throws IOException;
+ int read(byte[] dst, int off, int len) throws IOException;
+ void readFully(byte[] dst, int off, int len) throws IOException;
+ void seek(long pos) throws IOException;
+ void close() throws IOException;
+ }
+
+ static class RandomAccessFileAccessor implements FileAccessor {
+ private final RandomAccessFile file;
+
+ RandomAccessFileAccessor(RandomAccessFile file) {
+ this.file = file;
+ }
+
+ @Override
+ public long length() throws IOException {
+ return file.length();
+ }
+
+ @Override
+ public int read(byte[] dst, int off, int len) throws IOException {
+ return file.read(dst, off, len);
+ }
+
+ @Override
+ public void readFully(byte[] dst, int off, int len) throws IOException {
+ file.readFully(dst, off, len);
+ }
+
+ @Override
+ public void seek(long pos) throws IOException {
+ file.seek(pos);
+ }
+
+ @Override
+ public void close() throws IOException {
+ file.close();
+ }
+ }
+
+ static class ChannelFileAccessor implements FileAccessor {
+ private final FileChannel channel;
+
+ ChannelFileAccessor(FileChannel file) {
+ this.channel = file;
+ }
+
+ @Override
+ public long length() throws IOException {
+ return channel.size();
+ }
+
+ @Override
+ public int read(byte[] dst, int off, int len) throws IOException {
+ ByteBuffer buf = ByteBuffer.wrap(dst, off, len);
+ return channel.read(buf);
+ }
+
+ @Override
+ public void readFully(byte[] dst, int off, int len) throws IOException {
+ // copy-pasted from java.io.RandomAccessFile.readFully(byte[], int, int)
+ int n = 0;
+ do {
+ int count = this.read(dst, off + n, len - n);
+ if (count < 0)
+ throw new EOFException();
+ n += count;
+ } while (n < len);
+ }
+
+ @Override
+ public void seek(long pos) throws IOException {
+ channel.position(pos);
+ }
+
+ @Override
+ public void close() throws IOException {
+ channel.close();
+ }
+ }
+
// A Hashmap for all entries.
//
// A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR,
@@ -1482,16 +1592,32 @@ static void release(Source src) throws IOException {
private Source(Key key, boolean toDelete, ZipCoder zc) throws IOException {
this.zc = zc;
this.key = key;
- if (toDelete) {
- if (OperatingSystem.isWindows()) {
- this.zfile = SharedSecrets.getJavaIORandomAccessFileAccess()
- .openAndDelete(key.file, "r");
+ if (USE_NIO_FOR_ZIP_FILE_ACCESS && VM.isBooted()) {
+ Set options;
+ if (toDelete) {
+ options = Set.of(StandardOpenOption.READ, StandardOpenOption.DELETE_ON_CLOSE);
} else {
- this.zfile = new RandomAccessFile(key.file, "r");
- key.file.delete();
+ options = Set.of(StandardOpenOption.READ);
}
+ FileChannel channel = FileSystems.getDefault().provider().newFileChannel(key.file.toPath(), options);
+ if (channel instanceof FileChannelImpl) {
+ ((FileChannelImpl) channel).setUninterruptible();
+ }
+ this.zfile = new ChannelFileAccessor(channel);
} else {
- this.zfile = new RandomAccessFile(key.file, "r");
+ RandomAccessFile file;
+ if (toDelete) {
+ if (OperatingSystem.isWindows()) {
+ file = SharedSecrets.getJavaIORandomAccessFileAccess()
+ .openAndDelete(key.file, "r");
+ } else {
+ file = new RandomAccessFile(key.file, "r");
+ key.file.delete();
+ }
+ } else {
+ file = new RandomAccessFile(key.file, "r");
+ }
+ this.zfile = new RandomAccessFileAccessor(file);
}
try {
initCEN(-1);
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
index 722447eece67..05588e692f8f 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
@@ -29,6 +29,7 @@
import java.lang.foreign.MemoryLayout;
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Constructor;
@@ -170,4 +171,11 @@ public interface JavaLangInvokeAccess {
* This method should only be used by ReflectionFactory::newConstructorForSerialization.
*/
MethodHandle serializableConstructor(Class> decl, Constructor> ctorToCall) throws IllegalAccessException;
+
+ /**
+ * Returns a lookup object corresponding to given class with full access privileges.
+ * @param lookupClass lookup class
+ * @return full-privileged lookup object
+ */
+ Lookup lookupIn(Class> lookupClass);
}
diff --git a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
index c845146a838d..493908223796 100644
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java
@@ -39,6 +39,7 @@
import java.util.jar.Manifest;
import java.util.stream.Stream;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.module.Modules;
@@ -141,8 +142,11 @@ public static Class> loadClass(Module module, String name) {
/**
* Loads a native library from the system library path.
*/
+ @SuppressWarnings("try")
public static void loadLibrary(String name) {
- getNativeLibraries().loadLibrary(name);
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ getNativeLibraries().loadLibrary(name);
+ }
}
/**
diff --git a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
index f6cc79fd1277..6a62241e34af 100644
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
@@ -52,6 +52,7 @@
import java.util.jar.Manifest;
import java.util.stream.Stream;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.module.ModulePatcher.PatchedModuleReader;
@@ -674,8 +675,11 @@ private LoadedModule findLoadedModule(String mn, String cn) {
*
* @return the resulting Class or {@code null} if not found
*/
+ @SuppressWarnings("try")
private Class> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
- return defineClass(cn, loadedModule);
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ return defineClass(cn, loadedModule);
+ }
}
/**
@@ -683,14 +687,17 @@ private Class> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
*
* @return the resulting Class or {@code null} if not found
*/
+ @SuppressWarnings("try")
private Class> findClassOnClassPathOrNull(String cn) {
String path = cn.replace('.', '/').concat(".class");
- Resource res = ucp.getResource(path);
- if (res != null) {
- try {
- return defineClass(cn, res);
- } catch (IOException ioe) {
- // TBD on how I/O errors should be propagated
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ Resource res = ucp.getResource(path);
+ if (res != null) {
+ try {
+ return defineClass(cn, res);
+ } catch (IOException ioe) {
+ // TBD on how I/O errors should be propagated
+ }
}
}
return null;
diff --git a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
index 44eaab0e83a6..69c3b48063eb 100644
--- a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
+++ b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
@@ -24,6 +24,7 @@
*/
package jdk.internal.loader;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.misc.VM;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.util.StaticProperty;
@@ -112,6 +113,7 @@ public long find(String name) {
* @param file the path of the native library
* @throws UnsatisfiedLinkError if any error in loading the native library
*/
+ @SuppressWarnings("try")
public NativeLibrary loadLibrary(Class> fromClass, File file) {
// Check to see if we're attempting to access a static library
String name = findBuiltinLib(file.getName());
@@ -321,13 +323,16 @@ boolean open() {
return load(this, name, isBuiltin, throwExceptionIfFail());
}
+ @SuppressWarnings("try")
private boolean throwExceptionIfFail() {
if (loadLibraryOnlyIfPresent) return true;
// If the file exists but fails to load, UnsatisfiedLinkException thrown by the VM
// will include the error message from dlopen to provide diagnostic information
- File file = new File(name);
- return file.exists();
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ File file = new File(name);
+ return file.exists();
+ }
}
/*
diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp b/src/java.base/share/classes/jdk/internal/misc/CDSPreloadHelper.java
similarity index 75%
rename from src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp
rename to src/java.base/share/classes/jdk/internal/misc/CDSPreloadHelper.java
index 638cd100b1fa..a6e18902b0b9 100644
--- a/src/java.desktop/windows/native/libawt/windows/awt_PlatformGraphicsInfo.cpp
+++ b/src/java.base/share/classes/jdk/internal/misc/CDSPreloadHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 SAP SE. All rights reserved.
+ * Copyright 2025 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
@@ -23,15 +23,13 @@
* questions.
*/
-#include
-#include "Devices.h"
+package jdk.internal.misc;
-/*
- * Class: sun_awt_PlatformGraphicsInfo
- * Method: hasDisplays0
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL
-Java_sun_awt_PlatformGraphicsInfo_hasDisplays0(JNIEnv *env, jclass thisClass) {
- return CountMonitors() > 0 ? JNI_TRUE : JNI_FALSE;
+import java.io.File;
+
+public class CDSPreloadHelper {
+ public static void init() {
+ File f = new File(".");
+ boolean ignored = f.isDirectory();
+ }
}
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
index a8540446be0d..f62f724665f7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java annotation. The methods of this class must be called in the following
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
index 179f402db0e6..fb4ae268d822 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/AnnotationWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/AnnotationWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation'
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Attribute.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Attribute.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
index f52ecd26c91b..9c5ce91e3ffc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Attribute.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Attribute.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A non standard class, field, method or Code attribute, as defined in the Java Virtual Machine
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ByteVector.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ByteVector.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ByteVector.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ByteVector.java
index a79276bb55ad..33a66b2c12ee 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ByteVector.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ByteVector.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassReader.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassReader.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
index 7e499412dc2a..e0b8fb5fd214 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassReader.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassTooLargeException.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassTooLargeException.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassTooLargeException.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassTooLargeException.java
index 23485b7b39a9..c110ec34c743 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassTooLargeException.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassTooLargeException.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* Exception thrown when the constant pool of a class produced by a {@link ClassWriter} is too
@@ -75,7 +75,7 @@ public final class ClassTooLargeException extends IndexOutOfBoundsException {
* Constructs a new {@link ClassTooLargeException}.
*
* @param className the internal name of the class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param constantPoolCount the number of constant pool items of the class.
*/
public ClassTooLargeException(final String className, final int constantPoolCount) {
@@ -85,7 +85,7 @@ public ClassTooLargeException(final String className, final int constantPoolCoun
}
/**
- * Returns the internal name of the class (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * Returns the internal name of the class (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*
* @return the internal name of the class.
*/
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassVisitor.java
index 601e4ba26cd8..21a9c7217662 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in the following order:
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassWriter.java
index e3c0ad707a00..d868b61c9654 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ClassWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A {@link ClassVisitor} that generates a corresponding ClassFile structure, as defined in the Java
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ConstantDynamic.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ConstantDynamic.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ConstantDynamic.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ConstantDynamic.java
index 5a0839d0ec80..cb992f839ef2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ConstantDynamic.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ConstantDynamic.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
import java.util.Arrays;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Constants.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Constants.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
index 79458f372b90..0fc081fe391a 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Constants.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
import java.io.DataInputStream;
import java.io.IOException;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Context.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Context.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Context.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Context.java
index da3e2299e8f0..bec67f5333b7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Context.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Context.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* Information about a class being parsed in a {@link ClassReader}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/CurrentFrame.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/CurrentFrame.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/CurrentFrame.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/CurrentFrame.java
index b8793dc32b84..580b9a954156 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/CurrentFrame.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/CurrentFrame.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* Information about the input stack map frame at the "current" instruction of a method. This is
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Edge.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Edge.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Edge.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Edge.java
index 75b958cb2b02..59a7ee83fdb4 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Edge.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Edge.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* An edge in the control flow graph of a method. Each node of this graph is a basic block,
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldVisitor.java
index e24988380881..7d29f3d4ff7c 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java field. The methods of this class must be called in the following order:
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldWriter.java
index 758957af78eb..c3e78d04cd69 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/FieldWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/FieldWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A {@link FieldVisitor} that generates a corresponding 'field_info' structure, as defined in the
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Frame.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Frame.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Frame.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Frame.java
index 9e23a72e8456..cfb892e60b0d 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Frame.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Frame.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* The input and output stack map frames of a basic block.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handle.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handle.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handle.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handle.java
index 453bda26faf9..7e5a9e1a3b36 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handle.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handle.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A reference to a field or a method.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handler.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handler.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handler.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handler.java
index 20fb28de1b61..3d86e222ab8f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Handler.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Handler.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* Information about an exception handler. Corresponds to an element of the exception_table array of
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Label.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Label.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java
index d26fcdef6f4e..c321fc8a200b 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Label.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Label.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A position in the bytecode of a method. Labels are used for jump, goto, and switch instructions,
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodTooLargeException.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodTooLargeException.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodTooLargeException.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodTooLargeException.java
index 6e64a4175f33..a2c9cee1836d 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodTooLargeException.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodTooLargeException.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* Exception thrown when the Code attribute of a method produced by a {@link ClassWriter} is too
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
index eca1beb9ac31..08c3adb21714 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java method. The methods of this class must be called in the following
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
index b296abe594ef..efeffc7b1ebb 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/MethodWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/MethodWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A {@link MethodVisitor} that generates a corresponding 'method_info' structure, as defined in the
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleVisitor.java
index 4350beaaab75..0e8f484368b2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java module. The methods of this class must be called in the following
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleWriter.java
index 820d4a1d1884..18b1a22fb36a 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/ModuleWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ModuleWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A {@link ModuleVisitor} that generates the corresponding Module, ModulePackages and
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Opcodes.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Opcodes.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
index d6d8e36fc15c..12ac4e0a4171 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Opcodes.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* The JVM opcodes, access flags and array type codes. This interface does not define all the JVM
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java
index 6dec81a9585b..ad0aad6b0e60 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentVisitor.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a record component. The methods of this class must be called in the following
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java
index 5ed8fceb8443..3098cf70c8dd 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/RecordComponentWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/RecordComponentWriter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
final class RecordComponentWriter extends RecordComponentVisitor {
/** Where the constants used in this RecordComponentWriter must be stored. */
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Symbol.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Symbol.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Symbol.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Symbol.java
index 88c6e802db46..a45f75a03017 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Symbol.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Symbol.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* An entry of the constant pool, of the BootstrapMethods attribute, or of the (ASM specific) type
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/SymbolTable.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/SymbolTable.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
index cf940d5d1d48..5fc629d68097 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/SymbolTable.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/SymbolTable.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Type.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Type.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
index b01788a366da..739b9444d2ee 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/Type.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Type.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypePath.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypePath.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
index b4dd2bae4246..ea1025dd172b 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypePath.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypePath.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* The path to a type argument, wildcard bound, array element type, or static inner type within an
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypeReference.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypeReference.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypeReference.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypeReference.java
index 5d157999dad0..512dc8fc8ac7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/TypeReference.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/TypeReference.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm;
+package jdk.internal.org.objectweb.asm;
/**
* A reference to a type appearing in a class, field or method declaration, or on an instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AdviceAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AdviceAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
index c3494a9679f8..0d57c484f050 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AdviceAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AdviceAdapter.java
@@ -57,18 +57,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} to insert before, after and around advices in methods and constructors.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnalyzerAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnalyzerAdapter.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnalyzerAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnalyzerAdapter.java
index cbfb0ffd3d8e..36a5a06d4410 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnalyzerAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnalyzerAdapter.java
@@ -57,23 +57,23 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} that keeps track of stack map frame changes between {@link
* #visitFrame(int, int, Object[], int, Object[])} calls. This adapter must be used with the {@link
- * org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each visitX instruction delegates to
+ * jdk.internal.org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each visitX instruction delegates to
* the next visitor in the chain, if any, and then simulates the effect of this instruction on the
* stack map frame, represented by {@link #locals} and {@link #stack}. The next visitor in the chain
* can get the state of the stack map frame before each instruction by reading the value of
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnnotationRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnnotationRemapper.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnnotationRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnnotationRemapper.java
index 1e4a932d605a..4fdfbccc06b0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/AnnotationRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/AnnotationRemapper.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* An {@link AnnotationVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ClassRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ClassRemapper.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ClassRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ClassRemapper.java
index bce6e47937a8..03a576189d42 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ClassRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ClassRemapper.java
@@ -57,18 +57,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link ClassVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/CodeSizeEvaluator.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/CodeSizeEvaluator.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/CodeSizeEvaluator.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/CodeSizeEvaluator.java
index f656f1a42f71..2392cdcd0bee 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/CodeSizeEvaluator.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/CodeSizeEvaluator.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link MethodVisitor} that approximates the size of the methods it visits.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/FieldRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/FieldRemapper.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/FieldRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/FieldRemapper.java
index db7e0b145948..205f985d87e4 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/FieldRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/FieldRemapper.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link FieldVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/GeneratorAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/GeneratorAdapter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/GeneratorAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/GeneratorAdapter.java
index da9e71676cb2..ecf4ac10dfbc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/GeneratorAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/GeneratorAdapter.java
@@ -57,18 +57,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} with convenient methods to generate code. For example, using this
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/InstructionAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/InstructionAdapter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/InstructionAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/InstructionAdapter.java
index 534b71e2e13a..e449c310b3b1 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/InstructionAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/InstructionAdapter.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
-
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+package jdk.internal.org.objectweb.asm.commons;
+
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A {@link MethodVisitor} providing a more detailed API to generate and transform instructions.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/JSRInlinerAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/JSRInlinerAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter.java
index 97dca35b4f1e..2f1565222ec9 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/JSRInlinerAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.AbstractMap;
import java.util.ArrayList;
@@ -67,22 +67,22 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.InsnNode;
-import org.objectweb.asm.tree.JumpInsnNode;
-import org.objectweb.asm.tree.LabelNode;
-import org.objectweb.asm.tree.LocalVariableNode;
-import org.objectweb.asm.tree.LookupSwitchInsnNode;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.TableSwitchInsnNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InsnList;
+import jdk.internal.org.objectweb.asm.tree.InsnNode;
+import jdk.internal.org.objectweb.asm.tree.JumpInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LabelNode;
+import jdk.internal.org.objectweb.asm.tree.LocalVariableNode;
+import jdk.internal.org.objectweb.asm.tree.LookupSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.TableSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
/**
- * A {@link org.objectweb.asm.MethodVisitor} that removes JSR instructions and inlines the
+ * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} that removes JSR instructions and inlines the
* referenced subroutines.
*
* @author Niko Matsakis
@@ -121,7 +121,7 @@ public class JSRInlinerAdapter extends MethodNode implements Opcodes {
* @param descriptor the method's descriptor.
* @param signature the method's signature. May be {@literal null}.
* @param exceptions the internal names of the method's exception classes (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
* @throws IllegalStateException if a subclass calls this constructor.
*/
public JSRInlinerAdapter(
@@ -157,7 +157,7 @@ public JSRInlinerAdapter(
* @param descriptor the method's descriptor.
* @param signature the method's signature. May be {@literal null}.
* @param exceptions the internal names of the method's exception classes (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
protected JSRInlinerAdapter(
final int api,
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/LocalVariablesSorter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/LocalVariablesSorter.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/LocalVariablesSorter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/LocalVariablesSorter.java
index f2c56d703b3e..c40633409432 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/LocalVariablesSorter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/LocalVariablesSorter.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link MethodVisitor} that renumbers local variables in their order of appearance. This adapter
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Method.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Method.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Method.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Method.java
index ce51f6deddac..96b1885308cc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Method.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Method.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.HashMap;
import java.util.Map;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A named method descriptor.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/MethodRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/MethodRemapper.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/MethodRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/MethodRemapper.java
index 3e6b3056dcb9..c2335077e8b3 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/MethodRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/MethodRemapper.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link MethodVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleHashesAttribute.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleHashesAttribute.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleHashesAttribute.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleHashesAttribute.java
index 046bc162078c..7bf0934ab2ea 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleHashesAttribute.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleHashesAttribute.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ByteVector;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ByteVector;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
/**
* A ModuleHashes attribute. This attribute is specific to the OpenJDK and may change in the future.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleRemapper.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleRemapper.java
index dc11c89d5c7e..d843a715451d 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleRemapper.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link ModuleVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleResolutionAttribute.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleResolutionAttribute.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleResolutionAttribute.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleResolutionAttribute.java
index 7d4223aa7e6b..5ccee829a902 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleResolutionAttribute.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleResolutionAttribute.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ByteVector;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ByteVector;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
/**
* A ModuleResolution attribute. This attribute is specific to the OpenJDK and may change in the
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleTargetAttribute.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleTargetAttribute.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleTargetAttribute.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleTargetAttribute.java
index e53b099d7bfc..8d61baadd2e0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/ModuleTargetAttribute.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/ModuleTargetAttribute.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ByteVector;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ByteVector;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
/**
* A ModuleTarget attribute. This attribute is specific to the OpenJDK and may change in the future.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RecordComponentRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RecordComponentRemapper.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RecordComponentRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RecordComponentRemapper.java
index e9aeef2012f6..017f24a74405 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RecordComponentRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RecordComponentRemapper.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link RecordComponentVisitor} that remaps types with a {@link Remapper}.
@@ -91,7 +91,7 @@ public RecordComponentRemapper(
* Constructs a new {@link RecordComponentRemapper}.
*
* @param api the ASM API version supported by this remapper. Must be one of {@link
- * org.objectweb.asm.Opcodes#ASM8} or {@link org.objectweb.asm.Opcodes#ASM9}.
+ * jdk.internal.org.objectweb.asm.Opcodes#ASM8} or {@link jdk.internal.org.objectweb.asm.Opcodes#ASM9}.
* @param recordComponentVisitor the record component visitor this remapper must delegate to.
* @param remapper the remapper to use to remap the types in the visited record component.
*/
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Remapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Remapper.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Remapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Remapper.java
index e6e77e2dfa35..aba7f0f21ff1 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/Remapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/Remapper.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.signature.SignatureReader;
-import org.objectweb.asm.signature.SignatureVisitor;
-import org.objectweb.asm.signature.SignatureWriter;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.signature.SignatureReader;
+import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
+import jdk.internal.org.objectweb.asm.signature.SignatureWriter;
/**
* A class responsible for remapping types and names.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingAnnotationAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingAnnotationAdapter.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingAnnotationAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingAnnotationAdapter.java
index 5fa2818c269a..0f41cff5354c 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingAnnotationAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingAnnotationAdapter.java
@@ -56,10 +56,10 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* An {@link AnnotationVisitor} adapter for type remapping.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingMethodAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingMethodAdapter.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingMethodAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingMethodAdapter.java
index ac3d87c5c117..68ad22a47932 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/RemappingMethodAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/RemappingMethodAdapter.java
@@ -56,14 +56,14 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link LocalVariablesSorter} for type mapping.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SerialVersionUIDAdder.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SerialVersionUIDAdder.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SerialVersionUIDAdder.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SerialVersionUIDAdder.java
index e3b0509adf5f..58173483b191 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SerialVersionUIDAdder.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SerialVersionUIDAdder.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
@@ -68,10 +68,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link ClassVisitor} that adds a serial version unique identifier to a class if missing. A
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SignatureRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SignatureRemapper.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SignatureRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SignatureRemapper.java
index 170dc1c322da..5fb41c84ccac 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SignatureRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SignatureRemapper.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.ArrayList;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.signature.SignatureVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link SignatureVisitor} that remaps types with a {@link Remapper}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SimpleRemapper.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SimpleRemapper.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SimpleRemapper.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SimpleRemapper.java
index d1fd53f6b6ad..9b298b15e398 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/SimpleRemapper.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/SimpleRemapper.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.Collections;
import java.util.Map;
@@ -85,7 +85,7 @@ public class SimpleRemapper extends Remapper {
* attribute (in the form <owner>.<name>), and the value is the new field
* name.
* for internal names, the key is the old internal name, and the value is the new
- * internal name (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * internal name (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*
*/
public SimpleRemapper(final Map mapping) {
@@ -98,7 +98,7 @@ public SimpleRemapper(final Map mapping) {
* @param oldName the key corresponding to a method, field or internal name (see {@link
* #SimpleRemapper(Map)} for the format of these keys).
* @param newName the new method, field or internal name (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public SimpleRemapper(final String oldName, final String newName) {
this.mapping = Collections.singletonMap(oldName, newName);
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/StaticInitMerger.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/StaticInitMerger.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/StaticInitMerger.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/StaticInitMerger.java
index 90975c00cd7f..1a03a9e0923f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/StaticInitMerger.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/StaticInitMerger.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link ClassVisitor} that merges <clinit> methods into a single one. All the existing
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TableSwitchGenerator.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TableSwitchGenerator.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TableSwitchGenerator.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TableSwitchGenerator.java
index a7e0ec7f8be9..0f8ada93b399 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TableSwitchGenerator.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TableSwitchGenerator.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Label;
/**
* A code generator for switch statements.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TryCatchBlockSorter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TryCatchBlockSorter.java
similarity index 93%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TryCatchBlockSorter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TryCatchBlockSorter.java
index decde6faf38d..6cd6b1ec08b9 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/commons/TryCatchBlockSorter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/commons/TryCatchBlockSorter.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.commons;
+package jdk.internal.org.objectweb.asm.commons;
import java.util.Collections;
import java.util.Comparator;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
/**
* A {@link MethodVisitor} adapter to sort the exception handlers. The handlers are sorted in a
@@ -88,11 +88,11 @@ public class TryCatchBlockSorter extends MethodNode {
* @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
* the method is synthetic and/or deprecated.
* @param name the method's name.
- * @param descriptor the method's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param signature the method's signature. May be {@literal null} if the method parameters,
* return type and exceptions do not use generic types.
* @param exceptions the internal names of the method's exception classes (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public TryCatchBlockSorter(
final MethodVisitor methodVisitor,
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureReader.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureReader.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureReader.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureReader.java
index 67c79e325f0a..76901452a4c2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureReader.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureReader.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.signature;
+package jdk.internal.org.objectweb.asm.signature;
/**
* A parser for signature literals, as defined in the Java Virtual Machine Specification (JVMS), to
@@ -86,9 +86,9 @@ public SignatureReader(final String signature) {
* Makes the given visitor visit the signature of this {@link SignatureReader}. This signature is
* the one specified in the constructor (see {@link #SignatureReader}). This method is intended to
* be called on a {@link SignatureReader} that was created using a ClassSignature (such as
- * the signature parameter of the {@link org.objectweb.asm.ClassVisitor#visit}
+ * the signature parameter of the {@link jdk.internal.org.objectweb.asm.ClassVisitor#visit}
* method) or a MethodSignature (such as the signature parameter of the {@link
- * org.objectweb.asm.ClassVisitor#visitMethod} method).
+ * jdk.internal.org.objectweb.asm.ClassVisitor#visitMethod} method).
*
* @param signatureVistor the visitor that must visit this signature.
*/
@@ -163,8 +163,8 @@ public void accept(final SignatureVisitor signatureVistor) {
* the one specified in the constructor (see {@link #SignatureReader}). This method is intended to
* be called on a {@link SignatureReader} that was created using a JavaTypeSignature , such
* as the signature parameter of the {@link
- * org.objectweb.asm.ClassVisitor#visitField} or {@link
- * org.objectweb.asm.MethodVisitor#visitLocalVariable} methods.
+ * jdk.internal.org.objectweb.asm.ClassVisitor#visitField} or {@link
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitLocalVariable} methods.
*
* @param signatureVisitor the visitor that must visit this signature.
*/
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureVisitor.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureVisitor.java
index b4156f06e768..5be537ba3072 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureVisitor.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.signature;
+package jdk.internal.org.objectweb.asm.signature;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A visitor to visit a generic signature. The methods of this interface must be called in one of
@@ -212,7 +212,7 @@ public SignatureVisitor visitArrayType() {
* Starts the visit of a signature corresponding to a class or interface type.
*
* @param name the internal name of the class or interface (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public void visitClassType(final String name) {}
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureWriter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureWriter.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureWriter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureWriter.java
index d22027dfab92..077a9292955f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/signature/SignatureWriter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/signature/SignatureWriter.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.signature;
+package jdk.internal.org.objectweb.asm.signature;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A SignatureVisitor that generates signature literals, as defined in the Java Virtual Machine
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AbstractInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AbstractInsnNode.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AbstractInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AbstractInsnNode.java
index 169efcbea667..1b22e023abce 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AbstractInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AbstractInsnNode.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a bytecode instruction. An instruction can appear at most once in at
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AnnotationNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AnnotationNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AnnotationNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AnnotationNode.java
index 70036ed2e891..2ef0bcfb6246 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/AnnotationNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/AnnotationNode.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents an annotation.
@@ -78,7 +78,7 @@ public class AnnotationNode extends AnnotationVisitor {
* The name value pairs of this annotation. Each name value pair is stored as two consecutive
* elements in the list. The name is a {@link String}, and the value may be a {@link Byte}, {@link
* Boolean}, {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Float},
- * {@link Double}, {@link String} or {@link org.objectweb.asm.Type}, or a two elements String
+ * {@link Double}, {@link String} or {@link jdk.internal.org.objectweb.asm.Type}, or a two elements String
* array (for enumeration values), an {@link AnnotationNode}, or a {@link List} of values of one
* of the preceding types. The list may be {@literal null} if there is no name value pair.
*/
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ClassNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ClassNode.java
similarity index 93%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ClassNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ClassNode.java
index a5c73b63f3cc..0e271d6a159c 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ClassNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ClassNode.java
@@ -57,19 +57,19 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a class.
@@ -85,19 +85,19 @@ public class ClassNode extends ClassVisitor {
public int version;
/**
- * The class's access flags (see {@link org.objectweb.asm.Opcodes}). This field also indicates if
+ * The class's access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). This field also indicates if
* the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link Opcodes#ACC_RECORD}.
*/
public int access;
- /** The internal name of this class (see {@link org.objectweb.asm.Type#getInternalName()}). */
+ /** The internal name of this class (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}). */
public String name;
/** The signature of this class. May be {@literal null}. */
public String signature;
/**
- * The internal of name of the super class (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * The internal of name of the super class (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* For interfaces, the super class is {@link Object}. May be {@literal null}, but only for the
* {@link Object} class.
*/
@@ -105,7 +105,7 @@ public class ClassNode extends ClassVisitor {
/**
* The internal names of the interfaces directly implemented by this class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public List interfaces;
@@ -122,7 +122,7 @@ public class ClassNode extends ClassVisitor {
/**
* The internal name of the enclosing class of this class (see {@link
- * org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if this class has no
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if this class has no
* enclosing class, or if it is a local or anonymous class.
*/
public String outerClass;
@@ -163,19 +163,19 @@ public class ClassNode extends ClassVisitor {
/**
* The internal name of the nest host class of this class (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public String nestHostClass;
/**
* The internal names of the nest members of this class (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public List nestMembers;
/**
* The internal names of the permitted subclasses of this class (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public List permittedSubclasses;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldInsnNode.java
similarity index 91%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldInsnNode.java
index 7257639fc6b1..ce450f9107b3 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldInsnNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a field instruction. A field instruction is an instruction that loads or
@@ -72,14 +72,14 @@ public class FieldInsnNode extends AbstractInsnNode {
/**
* The internal name of the field's owner class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public String owner;
/** The field's name. */
public String name;
- /** The field's descriptor (see {@link org.objectweb.asm.Type}). */
+ /** The field's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String desc;
/**
@@ -88,9 +88,9 @@ public class FieldInsnNode extends AbstractInsnNode {
* @param opcode the opcode of the type instruction to be constructed. This opcode must be
* GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
* @param owner the internal name of the field's owner class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param name the field's name.
- * @param descriptor the field's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the field's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
*/
public FieldInsnNode(
final int opcode, final String owner, final String name, final String descriptor) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldNode.java
similarity index 91%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldNode.java
index 60c36905ae20..2bf3682b669f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FieldNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FieldNode.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a field.
@@ -75,7 +75,7 @@
public class FieldNode extends FieldVisitor {
/**
- * The field's access flags (see {@link org.objectweb.asm.Opcodes}). This field also indicates if
+ * The field's access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). This field also indicates if
* the field is synthetic and/or deprecated.
*/
public int access;
@@ -83,7 +83,7 @@ public class FieldNode extends FieldVisitor {
/** The field's name. */
public String name;
- /** The field's descriptor (see {@link org.objectweb.asm.Type}). */
+ /** The field's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String desc;
/** The field's signature. May be {@literal null}. */
@@ -115,10 +115,10 @@ public class FieldNode extends FieldVisitor {
* Constructs a new {@link FieldNode}. Subclasses must not use this constructor . Instead,
* they must use the {@link #FieldNode(int, int, String, String, String, Object)} version.
*
- * @param access the field's access flags (see {@link org.objectweb.asm.Opcodes}). This parameter
+ * @param access the field's access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). This parameter
* also indicates if the field is synthetic and/or deprecated.
* @param name the field's name.
- * @param descriptor the field's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the field's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param signature the field's signature.
* @param value the field's initial value. This parameter, which may be {@literal null} if the
* field does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link
@@ -142,10 +142,10 @@ public FieldNode(
*
* @param api the ASM API version implemented by this visitor. Must be one of the {@code
* ASM}x values in {@link Opcodes}.
- * @param access the field's access flags (see {@link org.objectweb.asm.Opcodes}). This parameter
+ * @param access the field's access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). This parameter
* also indicates if the field is synthetic and/or deprecated.
* @param name the field's name.
- * @param descriptor the field's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the field's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param signature the field's signature.
* @param value the field's initial value. This parameter, which may be {@literal null} if the
* field does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FrameNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FrameNode.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FrameNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FrameNode.java
index e60fce6830d3..cb8c8fb05ab6 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/FrameNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/FrameNode.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a stack map frame. These nodes are pseudo instruction nodes in order to be
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IincInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IincInsnNode.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IincInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IincInsnNode.java
index 452a5c6875fe..864d540cd8c7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IincInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IincInsnNode.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents an IINC instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InnerClassNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InnerClassNode.java
similarity index 90%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InnerClassNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InnerClassNode.java
index c1d713cfdcc4..a2e103ae6d23 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InnerClassNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InnerClassNode.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
-import org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
/**
* A node that represents an inner class. This inner class is not necessarily a member of the {@link
@@ -72,12 +72,12 @@
*/
public class InnerClassNode {
- /** The internal name of an inner class (see {@link org.objectweb.asm.Type#getInternalName()}). */
+ /** The internal name of an inner class (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}). */
public String name;
/**
* The internal name of the class to which the inner class belongs (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public String outerName;
@@ -96,9 +96,9 @@ public class InnerClassNode {
/**
* Constructs a new {@link InnerClassNode} for an inner class C.
*
- * @param name the internal name of C (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * @param name the internal name of C (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param outerName the internal name of the class or interface C is a member of (see {@link
- * org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if C is not the member
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). Must be {@literal null} if C is not the member
* of a class or interface (e.g. for local or anonymous classes).
* @param innerName the (simple) name of C. Must be {@literal null} for anonymous inner classes.
* @param access the access flags of C originally declared in the source code from which this
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnList.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnList.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnList.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnList.java
index 94170c0c81f4..a63c80d76035 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnList.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnList.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ListIterator;
import java.util.NoSuchElementException;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A doubly linked list of {@link AbstractInsnNode} objects. This implementation is not thread
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnNode.java
index 50f51533b85a..94e043ca9c40 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InsnNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a zero operand instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IntInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IntInsnNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IntInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IntInsnNode.java
index c1acc42898d3..0c044886f900 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/IntInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/IntInsnNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents an instruction with a single int operand.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InvokeDynamicInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
similarity index 90%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
index 66351132edc6..087d3f5acd45 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents an invokedynamic instruction.
@@ -74,7 +74,7 @@ public class InvokeDynamicInsnNode extends AbstractInsnNode {
/** The method's name. */
public String name;
- /** The method's descriptor (see {@link org.objectweb.asm.Type}). */
+ /** The method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String desc;
/** The bootstrap method. */
@@ -87,11 +87,11 @@ public class InvokeDynamicInsnNode extends AbstractInsnNode {
* Constructs a new {@link InvokeDynamicInsnNode}.
*
* @param name the method's name.
- * @param descriptor the method's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param bootstrapMethodHandle the bootstrap method.
* @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be
* an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link
- * org.objectweb.asm.Type} or {@link Handle} value. This method is allowed to modify the
+ * jdk.internal.org.objectweb.asm.Type} or {@link Handle} value. This method is allowed to modify the
* content of the array so a caller should expect that this array may change.
*/
public InvokeDynamicInsnNode(
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/JumpInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/JumpInsnNode.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/JumpInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/JumpInsnNode.java
index 17fb3f82587a..23891297e450 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/JumpInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/JumpInsnNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a jump instruction. A jump instruction is an instruction that may jump to
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LabelNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LabelNode.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LabelNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LabelNode.java
index 5c6c696ff627..935382ad543b 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LabelNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LabelNode.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/** An {@link AbstractInsnNode} that encapsulates a {@link Label}. */
public class LabelNode extends AbstractInsnNode {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LdcInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LdcInsnNode.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LdcInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LdcInsnNode.java
index 35d86cb8c33f..1dac0b7aa851 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LdcInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LdcInsnNode.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A node that represents an LDC instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LineNumberNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LineNumberNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LineNumberNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LineNumberNode.java
index be06a08f0202..4feb13072b5d 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LineNumberNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LineNumberNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a line number declaration. These nodes are pseudo instruction nodes in
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableAnnotationNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableAnnotationNode.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableAnnotationNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableAnnotationNode.java
index a9bd2007cc55..2a593ca16996 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableAnnotationNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableAnnotationNode.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a type annotation on a local or resource variable.
@@ -96,7 +96,7 @@ public class LocalVariableAnnotationNode extends TypeAnnotationNode {
* constructor . Instead, they must use the {@link #LocalVariableAnnotationNode(int, TypePath,
* LabelNode[], LabelNode[], int[], String)} version.
*
- * @param typeRef a reference to the annotated type. See {@link org.objectweb.asm.TypeReference}.
+ * @param typeRef a reference to the annotated type. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
@@ -123,7 +123,7 @@ public LocalVariableAnnotationNode(
*
* @param api the ASM API version implemented by this visitor. Must be one of the {@code
* ASM}x values in {@link Opcodes}.
- * @param typeRef a reference to the annotated type. See {@link org.objectweb.asm.TypeReference}.
+ * @param typeRef a reference to the annotated type. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param start the fist instructions corresponding to the continuous ranges that make the scope
* of this local variable (inclusive).
* @param end the last instructions corresponding to the continuous ranges that make the scope of
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableNode.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableNode.java
index 147bc31bc9e7..7bfb121b88a0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LocalVariableNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LocalVariableNode.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a local variable declaration.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LookupSwitchInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LookupSwitchInsnNode.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LookupSwitchInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LookupSwitchInsnNode.java
index 80dbe11a6b6c..31d3c79b1629 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/LookupSwitchInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/LookupSwitchInsnNode.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a LOOKUPSWITCH instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodInsnNode.java
similarity index 90%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodInsnNode.java
index bff6d1f341fc..77e720b69b93 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodInsnNode.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a method instruction. A method instruction is an instruction that invokes
@@ -73,7 +73,7 @@ public class MethodInsnNode extends AbstractInsnNode {
/**
* The internal name of the method's owner class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*
* For methods of arrays, e.g., {@code clone()}, the array type descriptor.
*/
@@ -82,7 +82,7 @@ public class MethodInsnNode extends AbstractInsnNode {
/** The method's name. */
public String name;
- /** The method's descriptor (see {@link org.objectweb.asm.Type}). */
+ /** The method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String desc;
/** Whether the method's owner class if an interface. */
@@ -94,9 +94,9 @@ public class MethodInsnNode extends AbstractInsnNode {
* @param opcode the opcode of the type instruction to be constructed. This opcode must be
* INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
* @param owner the internal name of the method's owner class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param name the method's name.
- * @param descriptor the method's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
*/
public MethodInsnNode(
final int opcode, final String owner, final String name, final String descriptor) {
@@ -109,9 +109,9 @@ public MethodInsnNode(
* @param opcode the opcode of the type instruction to be constructed. This opcode must be
* INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
* @param owner the internal name of the method's owner class (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param name the method's name.
- * @param descriptor the method's descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the method's descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param isInterface if the method's owner class is an interface.
*/
public MethodInsnNode(
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodNode.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodNode.java
index 31da5e5c5efd..ba680c6ad420 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MethodNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MethodNode.java
@@ -57,20 +57,20 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a method.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleExportNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleExportNode.java
similarity index 93%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleExportNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleExportNode.java
index 9cd806aa5313..b845ebe662e0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleExportNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleExportNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
/**
* A node that represents an exported package with its name and the module that can access to it.
@@ -71,12 +71,12 @@ public class ModuleExportNode {
/**
* The internal name of the exported package (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public String packaze;
/**
- * The access flags (see {@link org.objectweb.asm.Opcodes}). Valid values are {@code
+ * The access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). Valid values are {@code
* ACC_SYNTHETIC} and {@code ACC_MANDATED}.
*/
public int access;
@@ -91,7 +91,7 @@ public class ModuleExportNode {
* Constructs a new {@link ModuleExportNode}.
*
* @param packaze the internal name of the exported package (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param access the package access flags, one or more of {@code ACC_SYNTHETIC} and {@code
* ACC_MANDATED}.
* @param modules a list of modules that can access this exported package, specified with fully
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleNode.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleNode.java
index d131f6275f86..66baee92e1be 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleNode.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a module declaration.
@@ -86,13 +86,13 @@ public class ModuleNode extends ModuleVisitor {
/**
* The internal name of the main class of this module (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public String mainClass;
/**
* The internal name of the packages declared by this module (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public List packages;
@@ -107,7 +107,7 @@ public class ModuleNode extends ModuleVisitor {
/**
* The internal names of the services used by this module (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
*/
public List uses;
@@ -148,7 +148,7 @@ public ModuleNode(final String name, final int access, final String version) {
* @param exports The packages exported by this module. May be {@literal null}.
* @param opens The packages opened by this module. May be {@literal null}.
* @param uses The internal names of the services used by this module (see {@link
- * org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}). May be {@literal null}.
* @param provides The services provided by this module. May be {@literal null}.
*/
public ModuleNode(
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleOpenNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleOpenNode.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleOpenNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleOpenNode.java
index 836827767378..abe48f598db2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleOpenNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleOpenNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
/**
* A node that represents an opened package with its name and the module that can access it.
@@ -70,7 +70,7 @@
public class ModuleOpenNode {
/**
- * The internal name of the opened package (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * The internal name of the opened package (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public String packaze;
@@ -90,7 +90,7 @@ public class ModuleOpenNode {
* Constructs a new {@link ModuleOpenNode}.
*
* @param packaze the internal name of the opened package (see {@link
- * org.objectweb.asm.Type#getInternalName()}).
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}).
* @param access the access flag of the opened package, valid values are among {@code
* ACC_SYNTHETIC} and {@code ACC_MANDATED}.
* @param modules the fully qualified names (using dots) of the modules that can use deep
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleProvideNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleProvideNode.java
similarity index 91%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleProvideNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleProvideNode.java
index 31cbe669ef84..d48239f55b2d 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleProvideNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleProvideNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
/**
* A node that represents a service and its implementation provided by the current module.
@@ -69,12 +69,12 @@
*/
public class ModuleProvideNode {
- /** The internal name of the service (see {@link org.objectweb.asm.Type#getInternalName()}). */
+ /** The internal name of the service (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}). */
public String service;
/**
* The internal names of the implementations of the service (there is at least one provider). See
- * {@link org.objectweb.asm.Type#getInternalName()}.
+ * {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}.
*/
public List providers;
@@ -83,7 +83,7 @@ public class ModuleProvideNode {
*
* @param service the internal name of the service.
* @param providers the internal names of the implementations of the service (there is at least
- * one provider). See {@link org.objectweb.asm.Type#getInternalName()}.
+ * one provider). See {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}.
*/
public ModuleProvideNode(final String service, final List providers) {
this.service = service;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleRequireNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleRequireNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleRequireNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleRequireNode.java
index e188860beec4..319d7af23e35 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ModuleRequireNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ModuleRequireNode.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
-import org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
/**
* A node that represents a required module with its name and access of a module descriptor.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MultiANewArrayInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
similarity index 92%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
index a94a9d8c355a..87a00b69021f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a MULTIANEWARRAY instruction.
@@ -70,7 +70,7 @@
*/
public class MultiANewArrayInsnNode extends AbstractInsnNode {
- /** An array type descriptor (see {@link org.objectweb.asm.Type}). */
+ /** An array type descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String desc;
/** Number of dimensions of the array to allocate. */
@@ -79,7 +79,7 @@ public class MultiANewArrayInsnNode extends AbstractInsnNode {
/**
* Constructs a new {@link MultiANewArrayInsnNode}.
*
- * @param descriptor an array type descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor an array type descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param numDimensions the number of dimensions of the array to allocate.
*/
public MultiANewArrayInsnNode(final String descriptor, final int numDimensions) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ParameterNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ParameterNode.java
similarity index 93%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ParameterNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ParameterNode.java
index e5d60a16bcaa..a2223c8c367f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/ParameterNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/ParameterNode.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a parameter of a method.
@@ -72,7 +72,7 @@ public class ParameterNode {
public String name;
/**
- * The parameter's access flags (see {@link org.objectweb.asm.Opcodes}). Valid values are {@code
+ * The parameter's access flags (see {@link jdk.internal.org.objectweb.asm.Opcodes}). Valid values are {@code
* ACC_FINAL}, {@code ACC_SYNTHETIC} and {@code ACC_MANDATED}.
*/
public int access;
@@ -81,7 +81,7 @@ public class ParameterNode {
* Constructs a new {@link ParameterNode}.
*
* @param access The parameter's access flags. Valid values are {@code ACC_FINAL}, {@code
- * ACC_SYNTHETIC} or/and {@code ACC_MANDATED} (see {@link org.objectweb.asm.Opcodes}).
+ * ACC_SYNTHETIC} or/and {@code ACC_MANDATED} (see {@link jdk.internal.org.objectweb.asm.Opcodes}).
* @param name the parameter's name.
*/
public ParameterNode(final String name, final int access) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/RecordComponentNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/RecordComponentNode.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/RecordComponentNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/RecordComponentNode.java
index 0bbc48ad821c..7cc87a7776e0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/RecordComponentNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/RecordComponentNode.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a record component.
@@ -77,7 +77,7 @@ public class RecordComponentNode extends RecordComponentVisitor {
/** The record component name. */
public String name;
- /** The record component descriptor (see {@link org.objectweb.asm.Type}). */
+ /** The record component descriptor (see {@link jdk.internal.org.objectweb.asm.Type}). */
public String descriptor;
/** The record component signature. May be {@literal null}. */
@@ -103,7 +103,7 @@ public class RecordComponentNode extends RecordComponentVisitor {
* Instead, they must use the {@link #RecordComponentNode(int, String, String, String)} version.
*
* @param name the record component name.
- * @param descriptor the record component descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the record component descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param signature the record component signature.
* @throws IllegalStateException If a subclass calls this constructor.
*/
@@ -120,7 +120,7 @@ public RecordComponentNode(final String name, final String descriptor, final Str
* @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM8}
* or {@link Opcodes#ASM9}.
* @param name the record component name.
- * @param descriptor the record component descriptor (see {@link org.objectweb.asm.Type}).
+ * @param descriptor the record component descriptor (see {@link jdk.internal.org.objectweb.asm.Type}).
* @param signature the record component signature.
*/
public RecordComponentNode(
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TableSwitchInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TableSwitchInsnNode.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TableSwitchInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TableSwitchInsnNode.java
index 58bb3648f72d..94287b55c1c2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TableSwitchInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TableSwitchInsnNode.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A node that represents a TABLESWITCH instruction.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TryCatchBlockNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TryCatchBlockNode.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TryCatchBlockNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TryCatchBlockNode.java
index 495e989bc8d4..2095218ab0bc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TryCatchBlockNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TryCatchBlockNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.List;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a try catch block.
@@ -99,7 +99,7 @@ public class TryCatchBlockNode {
* @param end the end of the exception handler's scope (exclusive).
* @param handler the beginning of the exception handler's code.
* @param type the internal name of the type of exceptions handled by the handler (see {@link
- * org.objectweb.asm.Type#getInternalName()}), or {@literal null} to catch any exceptions (for
+ * jdk.internal.org.objectweb.asm.Type#getInternalName()}), or {@literal null} to catch any exceptions (for
* "finally" blocks).
*/
public TryCatchBlockNode(
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeAnnotationNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeAnnotationNode.java
similarity index 93%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeAnnotationNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeAnnotationNode.java
index bdf79a84d86e..7a6bbb201f36 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeAnnotationNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeAnnotationNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A node that represents a type annotation.
@@ -69,7 +69,7 @@
*/
public class TypeAnnotationNode extends AnnotationNode {
- /** A reference to the annotated type. See {@link org.objectweb.asm.TypeReference}. */
+ /** A reference to the annotated type. See {@link jdk.internal.org.objectweb.asm.TypeReference}. */
public int typeRef;
/**
@@ -83,7 +83,7 @@ public class TypeAnnotationNode extends AnnotationNode {
* Constructs a new {@link AnnotationNode}. Subclasses must not use this constructor .
* Instead, they must use the {@link #TypeAnnotationNode(int, int, TypePath, String)} version.
*
- * @param typeRef a reference to the annotated type. See {@link org.objectweb.asm.TypeReference}.
+ * @param typeRef a reference to the annotated type. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
@@ -102,7 +102,7 @@ public TypeAnnotationNode(final int typeRef, final TypePath typePath, final Stri
*
* @param api the ASM API version implemented by this visitor. Must be one of the {@code
* ASM}x values in {@link Opcodes}.
- * @param typeRef a reference to the annotated type. See {@link org.objectweb.asm.TypeReference}.
+ * @param typeRef a reference to the annotated type. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeInsnNode.java
similarity index 92%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeInsnNode.java
index 419e3f4e3dc5..ba01f50efc16 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/TypeInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/TypeInsnNode.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a type instruction. A type instruction is an instruction which takes an
- * internal name as parameter (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * internal name as parameter (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*
* @author Eric Bruneton
*/
@@ -72,7 +72,7 @@ public class TypeInsnNode extends AbstractInsnNode {
/**
* The operand of this instruction. Despite its name (due to historical reasons), this operand is
- * an internal name (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * an internal name (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public String desc;
@@ -82,7 +82,7 @@ public class TypeInsnNode extends AbstractInsnNode {
* @param opcode the opcode of the type instruction to be constructed. This opcode must be NEW,
* ANEWARRAY, CHECKCAST or INSTANCEOF.
* @param type the operand of the instruction to be constructed. This operand is an internal name
- * (see {@link org.objectweb.asm.Type#getInternalName()}).
+ * (see {@link jdk.internal.org.objectweb.asm.Type#getInternalName()}).
*/
public TypeInsnNode(final int opcode, final String type) {
super(opcode);
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/UnsupportedClassVersionException.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/UnsupportedClassVersionException.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/UnsupportedClassVersionException.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/UnsupportedClassVersionException.java
index 125aa8f98d51..ec9c9d04bb66 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/UnsupportedClassVersionException.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/UnsupportedClassVersionException.java
@@ -56,7 +56,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
/**
* Exception thrown in {@link AnnotationNode#check}, {@link ClassNode#check}, {@link
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/Util.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/Util.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/Util.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/Util.java
index 32800364681b..ae09e55a2d6f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/Util.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/Util.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.List;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/VarInsnNode.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/VarInsnNode.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/VarInsnNode.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/VarInsnNode.java
index d1a14367a604..8e0ddfe8b40b 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/VarInsnNode.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/VarInsnNode.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree;
+package jdk.internal.org.objectweb.asm.tree;
import java.util.Map;
-import org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* A node that represents a local variable instruction. A local variable instruction is an
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Analyzer.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Analyzer.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Analyzer.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Analyzer.java
index 0c2f33e25239..a3c277e69f15 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Analyzer.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Analyzer.java
@@ -57,24 +57,24 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.IincInsnNode;
-import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.JumpInsnNode;
-import org.objectweb.asm.tree.LabelNode;
-import org.objectweb.asm.tree.LookupSwitchInsnNode;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.TableSwitchInsnNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
-import org.objectweb.asm.tree.VarInsnNode;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.IincInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InsnList;
+import jdk.internal.org.objectweb.asm.tree.JumpInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LabelNode;
+import jdk.internal.org.objectweb.asm.tree.LookupSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.TableSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.tree.VarInsnNode;
/**
* A semantic bytecode analyzer. This class does not fully check that JSR and RET instructions
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/AnalyzerException.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/AnalyzerException.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/AnalyzerException.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/AnalyzerException.java
index 61e4afeb9d47..5101b2f534a3 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/AnalyzerException.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/AnalyzerException.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
-import org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
/**
* An exception thrown if a problem occurs during the analysis of a method.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicInterpreter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicInterpreter.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicInterpreter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicInterpreter.java
index 4be7b111b284..7dee3af4405f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicInterpreter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicInterpreter.java
@@ -57,21 +57,21 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.List;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.FieldInsnNode;
-import org.objectweb.asm.tree.IntInsnNode;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
-import org.objectweb.asm.tree.LdcInsnNode;
-import org.objectweb.asm.tree.MethodInsnNode;
-import org.objectweb.asm.tree.MultiANewArrayInsnNode;
-import org.objectweb.asm.tree.TypeInsnNode;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.FieldInsnNode;
+import jdk.internal.org.objectweb.asm.tree.IntInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LdcInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import jdk.internal.org.objectweb.asm.tree.TypeInsnNode;
/**
* An {@link Interpreter} for {@link BasicValue} values.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicValue.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicValue.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicValue.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicValue.java
index 103bc2d1ef75..3d854f636869 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicValue.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicValue.java
@@ -57,9 +57,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.Type;
/**
* A {@link Value} that is represented with its type in a seven types type system. This type system
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicVerifier.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicVerifier.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicVerifier.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicVerifier.java
index e288657fa0eb..ebe738fe4203 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/BasicVerifier.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/BasicVerifier.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.List;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.FieldInsnNode;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
-import org.objectweb.asm.tree.MethodInsnNode;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.FieldInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
/**
* An extended {@link BasicInterpreter} that checks that bytecode instructions are correctly used.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Frame.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Frame.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Frame.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Frame.java
index 92fa234dc43b..5940c9ed91a7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Frame.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Frame.java
@@ -57,19 +57,19 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.IincInsnNode;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
-import org.objectweb.asm.tree.LabelNode;
-import org.objectweb.asm.tree.MethodInsnNode;
-import org.objectweb.asm.tree.MultiANewArrayInsnNode;
-import org.objectweb.asm.tree.VarInsnNode;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.IincInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LabelNode;
+import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import jdk.internal.org.objectweb.asm.tree.VarInsnNode;
/**
* A symbolic execution stack frame. A stack frame contains a set of local variable slots, and an
@@ -162,7 +162,7 @@ public Frame init(final Frame extends V> frame) {
/**
* Initializes a frame corresponding to the target or to the successor of a jump instruction. This
- * method is called by {@link Analyzer#analyze(String, org.objectweb.asm.tree.MethodNode)} while
+ * method is called by {@link Analyzer#analyze(String, jdk.internal.org.objectweb.asm.tree.MethodNode)} while
* interpreting jump instructions. It is called once for each possible target of the jump
* instruction, and once for its successor instruction (except for GOTO and JSR), before the frame
* is merged with the existing frame at this location. The default implementation of this method
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Interpreter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Interpreter.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Interpreter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Interpreter.java
index aedcaf07defb..d2c6103b77e8 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Interpreter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Interpreter.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.List;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
/**
* A semantic bytecode interpreter. More precisely, this interpreter only manages the computation of
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SimpleVerifier.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SimpleVerifier.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SimpleVerifier.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SimpleVerifier.java
index 7743853ef195..6f2f124ab31f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SimpleVerifier.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SimpleVerifier.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.List;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* An extended {@link BasicVerifier} that performs more precise verifications. This verifier
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SmallSet.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SmallSet.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SmallSet.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SmallSet.java
index 493c53c340cb..3c85d565ef87 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SmallSet.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SmallSet.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.AbstractSet;
import java.util.HashSet;
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceInterpreter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceInterpreter.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceInterpreter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceInterpreter.java
index 1c8ecbb826cb..d92ffce0a59b 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceInterpreter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceInterpreter.java
@@ -57,18 +57,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.FieldInsnNode;
-import org.objectweb.asm.tree.InvokeDynamicInsnNode;
-import org.objectweb.asm.tree.LdcInsnNode;
-import org.objectweb.asm.tree.MethodInsnNode;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.FieldInsnNode;
+import jdk.internal.org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LdcInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
/**
* An {@link Interpreter} for {@link SourceValue} values.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceValue.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceValue.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceValue.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceValue.java
index 72d91ecefd91..5403130af8cc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/SourceValue.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/SourceValue.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.Set;
-import org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
/**
* A {@link Value} which keeps track of the bytecode instructions that can produce it.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Subroutine.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Subroutine.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Subroutine.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Subroutine.java
index 827e47383259..67267e0119e2 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Subroutine.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Subroutine.java
@@ -57,12 +57,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.tree.JumpInsnNode;
-import org.objectweb.asm.tree.LabelNode;
+import jdk.internal.org.objectweb.asm.tree.JumpInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LabelNode;
/**
* A method subroutine (corresponds to a JSR instruction).
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Value.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Value.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Value.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Value.java
index 2874e5351dae..7058c959783f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/tree/analysis/Value.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/tree/analysis/Value.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.tree.analysis;
+package jdk.internal.org.objectweb.asm.tree.analysis;
/**
* An immutable symbolic value for the semantic interpretation of bytecode.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifier.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifier.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifier.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifier.java
index 6681e7c34554..074c8029fc20 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifier.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifier.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.IOException;
import java.io.PrintWriter;
@@ -66,13 +66,13 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link Printer} that prints the ASM code to generate the classes it visits.
@@ -236,19 +236,19 @@ public void visit(
simpleName = name.substring(lastSlashIndex + 1).replaceAll("[-\\(\\)]", "_");
}
}
- text.add("import org.objectweb.asm.AnnotationVisitor;\n");
- text.add("import org.objectweb.asm.Attribute;\n");
- text.add("import org.objectweb.asm.ClassReader;\n");
- text.add("import org.objectweb.asm.ClassWriter;\n");
- text.add("import org.objectweb.asm.ConstantDynamic;\n");
- text.add("import org.objectweb.asm.FieldVisitor;\n");
- text.add("import org.objectweb.asm.Handle;\n");
- text.add("import org.objectweb.asm.Label;\n");
- text.add("import org.objectweb.asm.MethodVisitor;\n");
- text.add("import org.objectweb.asm.Opcodes;\n");
- text.add("import org.objectweb.asm.RecordComponentVisitor;\n");
- text.add("import org.objectweb.asm.Type;\n");
- text.add("import org.objectweb.asm.TypePath;\n");
+ text.add("import jdk.internal.org.objectweb.asm.AnnotationVisitor;\n");
+ text.add("import jdk.internal.org.objectweb.asm.Attribute;\n");
+ text.add("import jdk.internal.org.objectweb.asm.ClassReader;\n");
+ text.add("import jdk.internal.org.objectweb.asm.ClassWriter;\n");
+ text.add("import jdk.internal.org.objectweb.asm.ConstantDynamic;\n");
+ text.add("import jdk.internal.org.objectweb.asm.FieldVisitor;\n");
+ text.add("import jdk.internal.org.objectweb.asm.Handle;\n");
+ text.add("import jdk.internal.org.objectweb.asm.Label;\n");
+ text.add("import jdk.internal.org.objectweb.asm.MethodVisitor;\n");
+ text.add("import jdk.internal.org.objectweb.asm.Opcodes;\n");
+ text.add("import jdk.internal.org.objectweb.asm.RecordComponentVisitor;\n");
+ text.add("import jdk.internal.org.objectweb.asm.Type;\n");
+ text.add("import jdk.internal.org.objectweb.asm.TypePath;\n");
text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
text.add("public static byte[] dump () throws Exception {\n\n");
text.add("ClassWriter classWriter = new ClassWriter(0);\n");
@@ -1200,7 +1200,7 @@ public ASMifier visitAnnotation(final String descriptor, final boolean visible)
* Visits a class, field or method type annotation.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
- * {@link org.objectweb.asm.TypeReference#FIELD}. See {@link org.objectweb.asm.TypeReference}.
+ * {@link jdk.internal.org.objectweb.asm.TypeReference#FIELD}. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
@@ -1218,7 +1218,7 @@ public ASMifier visitTypeAnnotation(
*
* @param method the name of the visit method for this type of annotation.
* @param typeRef a reference to the annotated type. The sort of this type reference must be
- * {@link org.objectweb.asm.TypeReference#FIELD}. See {@link org.objectweb.asm.TypeReference}.
+ * {@link jdk.internal.org.objectweb.asm.TypeReference#FIELD}. See {@link jdk.internal.org.objectweb.asm.TypeReference}.
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
* 'typeRef' as a whole.
@@ -1583,7 +1583,7 @@ protected void appendConstant(final Object value) {
*
* @param numTypes the number of stack map frame types in 'frameTypes'.
* @param frameTypes an array of stack map frame types, in the format described in {@link
- * org.objectweb.asm.MethodVisitor#visitFrame}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitFrame}.
*/
private void declareFrameTypes(final int numTypes, final Object[] frameTypes) {
for (int i = 0; i < numTypes; ++i) {
@@ -1598,7 +1598,7 @@ private void declareFrameTypes(final int numTypes, final Object[] frameTypes) {
*
* @param numTypes the number of stack map frame types in 'frameTypes'.
* @param frameTypes an array of stack map frame types, in the format described in {@link
- * org.objectweb.asm.MethodVisitor#visitFrame}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitFrame}.
*/
private void appendFrameTypes(final int numTypes, final Object[] frameTypes) {
for (int i = 0; i < numTypes; ++i) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifierSupport.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifierSupport.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifierSupport.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifierSupport.java
index 16ae203cd160..b592281332fc 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/ASMifierSupport.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/ASMifierSupport.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.Map;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Label;
/**
- * An {@link org.objectweb.asm.Attribute} that can generate the ASM code to create an equivalent
+ * An {@link jdk.internal.org.objectweb.asm.Attribute} that can generate the ASM code to create an equivalent
* attribute.
*
* @author Eugene Kuleshov
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckAnnotationAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckAnnotationAdapter.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckAnnotationAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckAnnotationAdapter.java
index 7e528b19e98c..1133c5ee3611 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckAnnotationAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckAnnotationAdapter.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
/**
* An {@link AnnotationVisitor} that checks that its methods are properly used.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckClassAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckClassAdapter.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckClassAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckClassAdapter.java
index eb2e3c487dd2..49f4bc78387c 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckClassAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckClassAdapter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.FileInputStream;
import java.io.IOException;
@@ -67,28 +67,28 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
-import org.objectweb.asm.tree.ClassNode;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
-import org.objectweb.asm.tree.analysis.Analyzer;
-import org.objectweb.asm.tree.analysis.AnalyzerException;
-import org.objectweb.asm.tree.analysis.BasicValue;
-import org.objectweb.asm.tree.analysis.Frame;
-import org.objectweb.asm.tree.analysis.SimpleVerifier;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.tree.ClassNode;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
+import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
+import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
+import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
+import jdk.internal.org.objectweb.asm.tree.analysis.SimpleVerifier;
/**
* A {@link ClassVisitor} that checks that its methods are properly used. More precisely this class
@@ -121,9 +121,9 @@
* instruction. For example (format is - insnNumber locals : stack):
*
*
- * org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
- * at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:...)
- * at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:...)
+ * jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
+ * at jdk.internal.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:...)
+ * at jdk.internal.org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:...)
* ...
* remove()V
* 00000 LinkedBlockingQueue$Itr . . . . . . . . : ICONST_0
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFieldAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFieldAdapter.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFieldAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFieldAdapter.java
index f50096e84f67..16b0ea8d7d6f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFieldAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFieldAdapter.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
/**
* A {@link FieldVisitor} that checks that its methods are properly used.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFrameAnalyzer.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFrameAnalyzer.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFrameAnalyzer.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFrameAnalyzer.java
index 10e2936c9ff8..754f648ad9ad 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckFrameAnalyzer.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckFrameAnalyzer.java
@@ -57,28 +57,28 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.Collections;
import java.util.List;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.FrameNode;
-import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.InsnNode;
-import org.objectweb.asm.tree.JumpInsnNode;
-import org.objectweb.asm.tree.LabelNode;
-import org.objectweb.asm.tree.LookupSwitchInsnNode;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.TableSwitchInsnNode;
-import org.objectweb.asm.tree.TryCatchBlockNode;
-import org.objectweb.asm.tree.TypeInsnNode;
-import org.objectweb.asm.tree.analysis.Analyzer;
-import org.objectweb.asm.tree.analysis.AnalyzerException;
-import org.objectweb.asm.tree.analysis.Frame;
-import org.objectweb.asm.tree.analysis.Interpreter;
-import org.objectweb.asm.tree.analysis.Value;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
+import jdk.internal.org.objectweb.asm.tree.FrameNode;
+import jdk.internal.org.objectweb.asm.tree.InsnList;
+import jdk.internal.org.objectweb.asm.tree.InsnNode;
+import jdk.internal.org.objectweb.asm.tree.JumpInsnNode;
+import jdk.internal.org.objectweb.asm.tree.LabelNode;
+import jdk.internal.org.objectweb.asm.tree.LookupSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.TableSwitchInsnNode;
+import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
+import jdk.internal.org.objectweb.asm.tree.TypeInsnNode;
+import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
+import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
+import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
+import jdk.internal.org.objectweb.asm.tree.analysis.Interpreter;
+import jdk.internal.org.objectweb.asm.tree.analysis.Value;
/**
* An {@link Analyzer} subclass which checks that methods provide stack map frames where expected
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckMethodAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckMethodAdapter.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckMethodAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckMethodAdapter.java
index 163ad48b9ab3..59c41108628e 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckMethodAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckMethodAdapter.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -67,22 +67,22 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
-import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.analysis.Analyzer;
-import org.objectweb.asm.tree.analysis.AnalyzerException;
-import org.objectweb.asm.tree.analysis.BasicValue;
-import org.objectweb.asm.tree.analysis.BasicVerifier;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
+import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
+import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
+import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier;
/**
* A {@link MethodVisitor} that checks that its methods are properly used. More precisely this
@@ -90,7 +90,7 @@
* preconditions based only on its arguments - such as the fact that the given opcode is
* correct for a given visit method. This adapter can also perform some basic data flow checks (more
* precisely those that can be performed without the full class hierarchy - see {@link
- * org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is
+ * jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is
* {@code void m ()}, the invalid instruction IRETURN, or the invalid sequence IADD L2I will be
* detected if the data flow checks are enabled. These checks are enabled by using the {@link
* #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. They are not performed if
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckModuleAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckModuleAdapter.java
similarity index 96%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckModuleAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckModuleAdapter.java
index 806f7584f0d0..de94a1d32ddd 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckModuleAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckModuleAdapter.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.HashSet;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link ModuleVisitor} that checks that its methods are properly used.
@@ -99,7 +99,7 @@ public class CheckModuleAdapter extends ModuleVisitor {
*
* @param moduleVisitor the module visitor to which this adapter must delegate calls.
* @param isOpen whether the visited module is open. Open modules have their {@link
- * Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
+ * Opcodes#ACC_OPEN} access flag set in {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitModule}.
* @throws IllegalStateException If a subclass calls this constructor.
*/
public CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpen) {
@@ -116,7 +116,7 @@ public CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpe
* ASM}x values in {@link Opcodes}.
* @param moduleVisitor the module visitor to which this adapter must delegate calls.
* @param isOpen whether the visited module is open. Open modules have their {@link
- * Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
+ * Opcodes#ACC_OPEN} access flag set in {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitModule}.
*/
protected CheckModuleAdapter(
final int api, final ModuleVisitor moduleVisitor, final boolean isOpen) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckRecordComponentAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckRecordComponentAdapter.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckRecordComponentAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckRecordComponentAdapter.java
index 7f4c7481b56e..850f1c8dc3e7 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckRecordComponentAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckRecordComponentAdapter.java
@@ -57,14 +57,14 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
/**
* A {@link RecordComponentVisitor} that checks that its methods are properly used.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckSignatureAdapter.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckSignatureAdapter.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckSignatureAdapter.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckSignatureAdapter.java
index fa4682387d37..19a3998f0a21 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/CheckSignatureAdapter.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/CheckSignatureAdapter.java
@@ -57,11 +57,11 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.EnumSet;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.signature.SignatureVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link SignatureVisitor} that checks that its methods are properly used.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Printer.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Printer.java
similarity index 88%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Printer.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Printer.java
index fa30c7e167a1..00378e8dccef 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Printer.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Printer.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.FileInputStream;
import java.io.IOException;
@@ -65,15 +65,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ConstantDynamic;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
/**
* An abstract converter from visit events to text.
@@ -288,7 +288,7 @@ public abstract class Printer {
/**
* The names of the {@code operand} values of the {@link
- * org.objectweb.asm.MethodVisitor#visitIntInsn} method when {@code opcode} is {@code NEWARRAY}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitIntInsn} method when {@code opcode} is {@code NEWARRAY}.
*/
public static final String[] TYPES = {
"",
@@ -305,7 +305,7 @@ public abstract class Printer {
"T_LONG"
};
- /** The names of the {@code tag} field values for {@link org.objectweb.asm.Handle}. */
+ /** The names of the {@code tag} field values for {@link jdk.internal.org.objectweb.asm.Handle}. */
public static final String[] HANDLE_TAG = {
"",
"H_GETFIELD",
@@ -364,7 +364,7 @@ protected Printer(final int api) {
// -----------------------------------------------------------------------------------------------
/**
- * Class header. See {@link org.objectweb.asm.ClassVisitor#visit}.
+ * Class header. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visit}.
*
* @param version the class version. The minor version is stored in the 16 most significant bits,
* and the major version in the 16 least significant bits.
@@ -388,7 +388,7 @@ public abstract void visit(
String[] interfaces);
/**
- * Class source. See {@link org.objectweb.asm.ClassVisitor#visitSource}.
+ * Class source. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitSource}.
*
* @param source the name of the source file from which the class was compiled. May be {@literal
* null}.
@@ -398,7 +398,7 @@ public abstract void visit(
public abstract void visitSource(String source, String debug);
/**
- * Module. See {@link org.objectweb.asm.ClassVisitor#visitModule}.
+ * Module. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitModule}.
*
* @param name the fully qualified name (using dots) of the module.
* @param access the module access flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} and {@code
@@ -426,7 +426,7 @@ public void visitNestHost(final String nestHost) {
}
/**
- * Class outer class. See {@link org.objectweb.asm.ClassVisitor#visitOuterClass}.
+ * Class outer class. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitOuterClass}.
*
* @param owner internal name of the enclosing class of the class (see {@link
* Type#getInternalName()}).
@@ -438,7 +438,7 @@ public void visitNestHost(final String nestHost) {
public abstract void visitOuterClass(String owner, String name, String descriptor);
/**
- * Class annotation. See {@link org.objectweb.asm.ClassVisitor#visitAnnotation}.
+ * Class annotation. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitAnnotation}.
*
* @param descriptor the class descriptor of the annotation class.
* @param visible {@literal true} if the annotation is visible at runtime.
@@ -447,7 +447,7 @@ public void visitNestHost(final String nestHost) {
public abstract Printer visitClassAnnotation(String descriptor, boolean visible);
/**
- * Class type annotation. See {@link org.objectweb.asm.ClassVisitor#visitTypeAnnotation}.
+ * Class type annotation. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitTypeAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
@@ -466,7 +466,7 @@ public Printer visitClassTypeAnnotation(
}
/**
- * Class attribute. See {@link org.objectweb.asm.ClassVisitor#visitAttribute}.
+ * Class attribute. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitAttribute}.
*
* @param attribute an attribute.
*/
@@ -487,7 +487,7 @@ public void visitNestMember(final String nestMember) {
/**
* Visits a permitted subclasses. A permitted subclass is one of the allowed subclasses of the
- * current class. See {@link org.objectweb.asm.ClassVisitor#visitPermittedSubclass(String)}.
+ * current class. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitPermittedSubclass(String)}.
*
* @param permittedSubclass the internal name of a permitted subclass (see {@link
* Type#getInternalName()}).
@@ -497,7 +497,7 @@ public void visitPermittedSubclass(final String permittedSubclass) {
}
/**
- * Class inner name. See {@link org.objectweb.asm.ClassVisitor#visitInnerClass}.
+ * Class inner name. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitInnerClass}.
*
* @param name the internal name of an inner class (see {@link Type#getInternalName()}).
* @param outerName the internal name of the class to which the inner class belongs (see {@link
@@ -511,7 +511,7 @@ public void visitPermittedSubclass(final String permittedSubclass) {
/**
* Visits a record component of the class. See {@link
- * org.objectweb.asm.ClassVisitor#visitRecordComponent(String, String, String)}.
+ * jdk.internal.org.objectweb.asm.ClassVisitor#visitRecordComponent(String, String, String)}.
*
* @param name the field's name.
* @param descriptor the record component descriptor (see {@link Type}).
@@ -526,7 +526,7 @@ public Printer visitRecordComponent(
}
/**
- * Class field. See {@link org.objectweb.asm.ClassVisitor#visitField}.
+ * Class field. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitField}.
*
* @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
* the field is synthetic and/or deprecated.
@@ -546,7 +546,7 @@ public abstract Printer visitField(
int access, String name, String descriptor, String signature, Object value);
/**
- * Class method. See {@link org.objectweb.asm.ClassVisitor#visitMethod}.
+ * Class method. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitMethod}.
*
* @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
* the method is synthetic and/or deprecated.
@@ -561,7 +561,7 @@ public abstract Printer visitField(
public abstract Printer visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions);
- /** Class end. See {@link org.objectweb.asm.ClassVisitor#visitEnd}. */
+ /** Class end. See {@link jdk.internal.org.objectweb.asm.ClassVisitor#visitEnd}. */
public abstract void visitClassEnd();
// -----------------------------------------------------------------------------------------------
@@ -569,7 +569,7 @@ public abstract Printer visitMethod(
// -----------------------------------------------------------------------------------------------
/**
- * Module main class. See {@link org.objectweb.asm.ModuleVisitor#visitMainClass}.
+ * Module main class. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitMainClass}.
*
* @param mainClass the internal name of the main class of the current module (see {@link
* Type#getInternalName()}).
@@ -579,7 +579,7 @@ public void visitMainClass(final String mainClass) {
}
/**
- * Module package. See {@link org.objectweb.asm.ModuleVisitor#visitPackage}.
+ * Module package. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitPackage}.
*
* @param packaze the internal name of a package (see {@link Type#getInternalName()}).
*/
@@ -588,7 +588,7 @@ public void visitPackage(final String packaze) {
}
/**
- * Module require. See {@link org.objectweb.asm.ModuleVisitor#visitRequire}.
+ * Module require. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitRequire}.
*
* @param module the fully qualified name (using dots) of the dependence.
* @param access the access flag of the dependence among {@code ACC_TRANSITIVE}, {@code
@@ -600,7 +600,7 @@ public void visitRequire(final String module, final int access, final String ver
}
/**
- * Module export. See {@link org.objectweb.asm.ModuleVisitor#visitExport}.
+ * Module export. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitExport}.
*
* @param packaze the internal name of the exported package (see {@link Type#getInternalName()}).
* @param access the access flag of the exported package, valid values are among {@code
@@ -613,7 +613,7 @@ public void visitExport(final String packaze, final int access, final String...
}
/**
- * Module open. See {@link org.objectweb.asm.ModuleVisitor#visitOpen}.
+ * Module open. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitOpen}.
*
* @param packaze the internal name of the opened package (see {@link Type#getInternalName()}).
* @param access the access flag of the opened package, valid values are among {@code
@@ -626,7 +626,7 @@ public void visitOpen(final String packaze, final int access, final String... mo
}
/**
- * Module use. See {@link org.objectweb.asm.ModuleVisitor#visitUse}.
+ * Module use. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitUse}.
*
* @param service the internal name of the service (see {@link Type#getInternalName()}).
*/
@@ -635,7 +635,7 @@ public void visitUse(final String service) {
}
/**
- * Module provide. See {@link org.objectweb.asm.ModuleVisitor#visitProvide}.
+ * Module provide. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitProvide}.
*
* @param service the internal name of the service (see {@link Type#getInternalName()}).
* @param providers the internal names of the implementations of the service (there is at least
@@ -645,7 +645,7 @@ public void visitProvide(final String service, final String... providers) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
}
- /** Module end. See {@link org.objectweb.asm.ModuleVisitor#visitEnd}. */
+ /** Module end. See {@link jdk.internal.org.objectweb.asm.ModuleVisitor#visitEnd}. */
public void visitModuleEnd() {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
}
@@ -655,7 +655,7 @@ public void visitModuleEnd() {
// -----------------------------------------------------------------------------------------------
/**
- * Annotation value. See {@link org.objectweb.asm.AnnotationVisitor#visit}.
+ * Annotation value. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visit}.
*
* @param name the value name.
* @param value the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link
@@ -669,7 +669,7 @@ public void visitModuleEnd() {
public abstract void visit(String name, Object value);
/**
- * Annotation enum value. See {@link org.objectweb.asm.AnnotationVisitor#visitEnum}.
+ * Annotation enum value. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitEnum}.
*
* @param name the value name.
* @param descriptor the class descriptor of the enumeration class.
@@ -678,7 +678,7 @@ public void visitModuleEnd() {
public abstract void visitEnum(String name, String descriptor, String value);
/**
- * Nested annotation value. See {@link org.objectweb.asm.AnnotationVisitor#visitAnnotation}.
+ * Nested annotation value. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitAnnotation}.
*
* @param name the value name.
* @param descriptor the class descriptor of the nested annotation class.
@@ -687,14 +687,14 @@ public void visitModuleEnd() {
public abstract Printer visitAnnotation(String name, String descriptor);
/**
- * Annotation array value. See {@link org.objectweb.asm.AnnotationVisitor#visitArray}.
+ * Annotation array value. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitArray}.
*
* @param name the value name.
* @return the printer.
*/
public abstract Printer visitArray(String name);
- /** Annotation end. See {@link org.objectweb.asm.AnnotationVisitor#visitEnd}. */
+ /** Annotation end. See {@link jdk.internal.org.objectweb.asm.AnnotationVisitor#visitEnd}. */
public abstract void visitAnnotationEnd();
// -----------------------------------------------------------------------------------------------
@@ -703,7 +703,7 @@ public void visitModuleEnd() {
/**
* Visits an annotation of the record component. See {@link
- * org.objectweb.asm.RecordComponentVisitor#visitAnnotation}.
+ * jdk.internal.org.objectweb.asm.RecordComponentVisitor#visitAnnotation}.
*
* @param descriptor the class descriptor of the annotation class.
* @param visible {@literal true} if the annotation is visible at runtime.
@@ -716,7 +716,7 @@ public Printer visitRecordComponentAnnotation(final String descriptor, final boo
/**
* Visits an annotation on a type in the record component signature. See {@link
- * org.objectweb.asm.RecordComponentVisitor#visitTypeAnnotation}.
+ * jdk.internal.org.objectweb.asm.RecordComponentVisitor#visitTypeAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
@@ -737,7 +737,7 @@ public Printer visitRecordComponentTypeAnnotation(
/**
* Visits a non standard attribute of the record component. See {@link
- * org.objectweb.asm.RecordComponentVisitor#visitAttribute}.
+ * jdk.internal.org.objectweb.asm.RecordComponentVisitor#visitAttribute}.
*
* @param attribute an attribute.
*/
@@ -747,7 +747,7 @@ public void visitRecordComponentAttribute(final Attribute attribute) {
/**
* Visits the end of the record component. See {@link
- * org.objectweb.asm.RecordComponentVisitor#visitEnd}. This method, which is the last one to be
+ * jdk.internal.org.objectweb.asm.RecordComponentVisitor#visitEnd}. This method, which is the last one to be
* called, is used to inform the visitor that everything have been visited.
*/
public void visitRecordComponentEnd() {
@@ -759,7 +759,7 @@ public void visitRecordComponentEnd() {
// -----------------------------------------------------------------------------------------------
/**
- * Field annotation. See {@link org.objectweb.asm.FieldVisitor#visitAnnotation}.
+ * Field annotation. See {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitAnnotation}.
*
* @param descriptor the class descriptor of the annotation class.
* @param visible {@literal true} if the annotation is visible at runtime.
@@ -768,7 +768,7 @@ public void visitRecordComponentEnd() {
public abstract Printer visitFieldAnnotation(String descriptor, boolean visible);
/**
- * Field type annotation. See {@link org.objectweb.asm.FieldVisitor#visitTypeAnnotation}.
+ * Field type annotation. See {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitTypeAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#FIELD}. See {@link TypeReference}.
@@ -785,13 +785,13 @@ public Printer visitFieldTypeAnnotation(
}
/**
- * Field attribute. See {@link org.objectweb.asm.FieldVisitor#visitAttribute}.
+ * Field attribute. See {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitAttribute}.
*
* @param attribute an attribute.
*/
public abstract void visitFieldAttribute(Attribute attribute);
- /** Field end. See {@link org.objectweb.asm.FieldVisitor#visitEnd}. */
+ /** Field end. See {@link jdk.internal.org.objectweb.asm.FieldVisitor#visitEnd}. */
public abstract void visitFieldEnd();
// -----------------------------------------------------------------------------------------------
@@ -799,7 +799,7 @@ public Printer visitFieldTypeAnnotation(
// -----------------------------------------------------------------------------------------------
/**
- * Method parameter. See {@link org.objectweb.asm.MethodVisitor#visitParameter(String, int)}.
+ * Method parameter. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitParameter(String, int)}.
*
* @param name parameter name or {@literal null} if none is provided.
* @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC}
@@ -810,14 +810,14 @@ public void visitParameter(final String name, final int access) {
}
/**
- * Method default annotation. See {@link org.objectweb.asm.MethodVisitor#visitAnnotationDefault}.
+ * Method default annotation. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAnnotationDefault}.
*
* @return the printer.
*/
public abstract Printer visitAnnotationDefault();
/**
- * Method annotation. See {@link org.objectweb.asm.MethodVisitor#visitAnnotation}.
+ * Method annotation. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAnnotation}.
*
* @param descriptor the class descriptor of the annotation class.
* @param visible {@literal true} if the annotation is visible at runtime.
@@ -826,7 +826,7 @@ public void visitParameter(final String name, final int access) {
public abstract Printer visitMethodAnnotation(String descriptor, boolean visible);
/**
- * Method type annotation. See {@link org.objectweb.asm.MethodVisitor#visitTypeAnnotation}.
+ * Method type annotation. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTypeAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#METHOD_TYPE_PARAMETER}, {@link
@@ -847,7 +847,7 @@ public Printer visitMethodTypeAnnotation(
/**
* Number of method parameters that can have annotations. See {@link
- * org.objectweb.asm.MethodVisitor#visitAnnotableParameterCount}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitAnnotableParameterCount}.
*
* @param parameterCount the number of method parameters than can have annotations. This number
* must be less or equal than the number of parameter types in the method descriptor. It can
@@ -865,7 +865,7 @@ public Printer visitAnnotableParameterCount(final int parameterCount, final bool
/**
* Method parameter annotation. See {@link
- * org.objectweb.asm.MethodVisitor#visitParameterAnnotation}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitParameterAnnotation}.
*
* @param parameter the parameter index. This index must be strictly smaller than the number of
* parameters in the method descriptor, and strictly smaller than the parameter count
@@ -881,17 +881,17 @@ public abstract Printer visitParameterAnnotation(
int parameter, String descriptor, boolean visible);
/**
- * Method attribute. See {@link org.objectweb.asm.MethodVisitor#visitAttribute}.
+ * Method attribute. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitAttribute}.
*
* @param attribute an attribute.
*/
public abstract void visitMethodAttribute(Attribute attribute);
- /** Method start. See {@link org.objectweb.asm.MethodVisitor#visitCode}. */
+ /** Method start. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitCode}. */
public abstract void visitCode();
/**
- * Method stack frame. See {@link org.objectweb.asm.MethodVisitor#visitFrame}.
+ * Method stack frame. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitFrame}.
*
* @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded
* frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link
@@ -912,7 +912,7 @@ public abstract void visitFrame(
int type, int numLocal, Object[] local, int numStack, Object[] stack);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitInsn}
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInsn}
*
* @param opcode the opcode of the instruction to be visited. This opcode is either NOP,
* ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5,
@@ -928,7 +928,7 @@ public abstract void visitFrame(
public abstract void visitInsn(int opcode);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitIntInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitIntInsn}.
*
* @param opcode the opcode of the instruction to be visited. This opcode is either BIPUSH, SIPUSH
* or NEWARRAY.
@@ -944,7 +944,7 @@ public abstract void visitFrame(
public abstract void visitIntInsn(int opcode, int operand);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitVarInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitVarInsn}.
*
* @param opcode the opcode of the local variable instruction to be visited. This opcode is either
* ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
@@ -954,7 +954,7 @@ public abstract void visitFrame(
public abstract void visitVarInsn(int opcode, int varIndex);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitTypeInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTypeInsn}.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either NEW,
* ANEWARRAY, CHECKCAST or INSTANCEOF.
@@ -964,7 +964,7 @@ public abstract void visitFrame(
public abstract void visitTypeInsn(int opcode, String type);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitFieldInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitFieldInsn}.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either
* GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
@@ -975,7 +975,7 @@ public abstract void visitFrame(
public abstract void visitFieldInsn(int opcode, String owner, String name, String descriptor);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitMethodInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMethodInsn}.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either
* INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
@@ -995,7 +995,7 @@ public void visitMethodInsn(
}
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitMethodInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMethodInsn}.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either
* INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
@@ -1015,7 +1015,7 @@ public void visitMethodInsn(
}
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitInvokeDynamicInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInvokeDynamicInsn}.
*
* @param name the method's name.
* @param descriptor the method's descriptor (see {@link Type}).
@@ -1032,7 +1032,7 @@ public abstract void visitInvokeDynamicInsn(
Object... bootstrapMethodArguments);
/**
- * Method jump instruction. See {@link org.objectweb.asm.MethodVisitor#visitJumpInsn}.
+ * Method jump instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitJumpInsn}.
*
* @param opcode the opcode of the type instruction to be visited. This opcode is either IFEQ,
* IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT,
@@ -1043,14 +1043,14 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitJumpInsn(int opcode, Label label);
/**
- * Method label. See {@link org.objectweb.asm.MethodVisitor#visitLabel}.
+ * Method label. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLabel}.
*
* @param label a {@link Label} object.
*/
public abstract void visitLabel(Label label);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitLdcInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLdcInsn}.
*
* @param value the constant to be loaded on the stack. This parameter must be a non null {@link
* Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link
@@ -1062,7 +1062,7 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitLdcInsn(Object value);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitIincInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitIincInsn}.
*
* @param varIndex index of the local variable to be incremented.
* @param increment amount to increment the local variable by.
@@ -1070,7 +1070,7 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitIincInsn(int varIndex, int increment);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitTableSwitchInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTableSwitchInsn}.
*
* @param min the minimum key value.
* @param max the maximum key value.
@@ -1081,7 +1081,7 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn}.
*
* @param dflt beginning of the default handler block.
* @param keys the values of the keys.
@@ -1091,7 +1091,7 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
/**
- * Method instruction. See {@link org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn}.
+ * Method instruction. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn}.
*
* @param descriptor an array type descriptor (see {@link Type}).
* @param numDimensions the number of dimensions of the array to allocate.
@@ -1099,7 +1099,7 @@ public abstract void visitInvokeDynamicInsn(
public abstract void visitMultiANewArrayInsn(String descriptor, int numDimensions);
/**
- * Instruction type annotation. See {@link org.objectweb.asm.MethodVisitor#visitInsnAnnotation}.
+ * Instruction type annotation. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitInsnAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#INSTANCEOF}, {@link TypeReference#NEW}, {@link
@@ -1121,7 +1121,7 @@ public Printer visitInsnAnnotation(
}
/**
- * Method exception handler. See {@link org.objectweb.asm.MethodVisitor#visitTryCatchBlock}.
+ * Method exception handler. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchBlock}.
*
* @param start the beginning of the exception handler's scope (inclusive).
* @param end the end of the exception handler's scope (exclusive).
@@ -1134,7 +1134,7 @@ public Printer visitInsnAnnotation(
/**
* Try catch block type annotation. See {@link
- * org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#EXCEPTION_PARAMETER}. See {@link TypeReference}.
@@ -1151,7 +1151,7 @@ public Printer visitTryCatchAnnotation(
}
/**
- * Method debug info. See {@link org.objectweb.asm.MethodVisitor#visitLocalVariable}.
+ * Method debug info. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLocalVariable}.
*
* @param name the name of a local variable.
* @param descriptor the type descriptor of this local variable.
@@ -1167,7 +1167,7 @@ public abstract void visitLocalVariable(
/**
* Local variable type annotation. See {@link
- * org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitTryCatchAnnotation}.
*
* @param typeRef a reference to the annotated type. The sort of this type reference must be
* {@link TypeReference#LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE}. See {@link
@@ -1197,7 +1197,7 @@ public Printer visitLocalVariableAnnotation(
}
/**
- * Method debug info. See {@link org.objectweb.asm.MethodVisitor#visitLineNumber}.
+ * Method debug info. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitLineNumber}.
*
* @param line a line number. This number refers to the source file from which the class was
* compiled.
@@ -1206,14 +1206,14 @@ public Printer visitLocalVariableAnnotation(
public abstract void visitLineNumber(int line, Label start);
/**
- * Method max stack and max locals. See {@link org.objectweb.asm.MethodVisitor#visitMaxs}.
+ * Method max stack and max locals. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitMaxs}.
*
* @param maxStack maximum stack size of the method.
* @param maxLocals maximum number of local variables for the method.
*/
public abstract void visitMaxs(int maxStack, int maxLocals);
- /** Method end. See {@link org.objectweb.asm.MethodVisitor#visitEnd}. */
+ /** Method end. See {@link jdk.internal.org.objectweb.asm.MethodVisitor#visitEnd}. */
public abstract void visitMethodEnd();
// -----------------------------------------------------------------------------------------------
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Textifier.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Textifier.java
similarity index 99%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Textifier.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Textifier.java
index 3de7b888299f..fe8a9c8bf318 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/Textifier.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/Textifier.java
@@ -57,7 +57,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.IOException;
import java.io.PrintWriter;
@@ -66,14 +66,14 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.TypePath;
-import org.objectweb.asm.TypeReference;
-import org.objectweb.asm.signature.SignatureReader;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
+import jdk.internal.org.objectweb.asm.signature.SignatureReader;
/**
* A {@link Printer} that prints a disassembled view of the classes it visits.
@@ -1588,7 +1588,7 @@ private void appendTypeReference(final int typeRef) {
*
* @param numTypes the number of stack map frame types in 'frameTypes'.
* @param frameTypes an array of stack map frame types, in the format described in {@link
- * org.objectweb.asm.MethodVisitor#visitFrame}.
+ * jdk.internal.org.objectweb.asm.MethodVisitor#visitFrame}.
*/
private void appendFrameTypes(final int numTypes, final Object[] frameTypes) {
for (int i = 0; i < numTypes; ++i) {
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TextifierSupport.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TextifierSupport.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TextifierSupport.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TextifierSupport.java
index 3d0d9063aecb..4ce8fe8031c0 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TextifierSupport.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TextifierSupport.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.Map;
-import org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Label;
/**
- * An {@link org.objectweb.asm.Attribute} that can print a readable representation of itself.
+ * An {@link jdk.internal.org.objectweb.asm.Attribute} that can print a readable representation of itself.
*
* @author Eugene Kuleshov
*/
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceAnnotationVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceAnnotationVisitor.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceAnnotationVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceAnnotationVisitor.java
index 88f82d4359dc..f487c69ca642 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceAnnotationVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceAnnotationVisitor.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* An {@link AnnotationVisitor} that prints the annotations it visits with a {@link Printer}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceClassVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java
similarity index 95%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceClassVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java
index 1505e30c2475..a6e58402db92 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceClassVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceClassVisitor.java
@@ -57,18 +57,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.io.PrintWriter;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link ClassVisitor} that prints the classes it visits with a {@link Printer}. This class
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceFieldVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceFieldVisitor.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceFieldVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceFieldVisitor.java
index 0598ea31d10b..1dd448c640f8 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceFieldVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceFieldVisitor.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.FieldVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link FieldVisitor} that prints the fields it visits with a {@link Printer}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceMethodVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceMethodVisitor.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceMethodVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceMethodVisitor.java
index 4463978a0910..06f96819d3ef 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceMethodVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceMethodVisitor.java
@@ -57,15 +57,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
-
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.TypePath;
+package jdk.internal.org.objectweb.asm.util;
+
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link MethodVisitor} that prints the methods it visits with a {@link Printer}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceModuleVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceModuleVisitor.java
similarity index 97%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceModuleVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceModuleVisitor.java
index 7c396fdf65b5..cbd0bf7aca74 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceModuleVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceModuleVisitor.java
@@ -57,10 +57,10 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.ModuleVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A {@link ModuleVisitor} that prints the fields it visits with a {@link Printer}.
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceRecordComponentVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceRecordComponentVisitor.java
similarity index 94%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceRecordComponentVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceRecordComponentVisitor.java
index 06ed71bdec4f..e40ce8c79cd6 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceRecordComponentVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceRecordComponentVisitor.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.RecordComponentVisitor;
-import org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
+import jdk.internal.org.objectweb.asm.TypePath;
/**
* A {@link RecordComponentVisitor} that prints the record components it visits with a {@link
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceSignatureVisitor.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceSignatureVisitor.java
similarity index 98%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceSignatureVisitor.java
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceSignatureVisitor.java
index 966978ad508a..ba8da75fdc4f 100644
--- a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/util/TraceSignatureVisitor.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/util/TraceSignatureVisitor.java
@@ -57,13 +57,13 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.objectweb.asm.util;
+package jdk.internal.org.objectweb.asm.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.signature.SignatureVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.signature.SignatureVisitor;
/**
* A {@link SignatureVisitor} that builds the Java generic type declaration corresponding to the
diff --git a/test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/version.txt b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/version.txt
similarity index 100%
rename from test/hotspot/jtreg/testlibrary/asm/org/objectweb/asm/version.txt
rename to src/java.base/share/classes/jdk/internal/org/objectweb/asm/version.txt
diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
index 22f40d9cead1..333d7d999039 100644
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -135,10 +135,12 @@
exports javax.security.auth.x500;
exports javax.security.cert;
-
// additional qualified exports may be inserted at build time
// see make/gensrc/GenModuleInfo.gmk
+ exports com.jetbrains.exported;
+ opens com.jetbrains.bootstrap;
+
exports com.sun.crypto.provider to
jdk.crypto.cryptoki;
exports sun.invoke.util to
diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
index 72de217a83ce..9cab3d6e9773 100644
--- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -48,6 +48,7 @@
import java.nio.channels.WritableByteChannel;
import java.util.Objects;
+import com.jetbrains.internal.IoOverNio;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.event.FileForceEvent;
@@ -138,6 +139,9 @@ private FileChannelImpl(FileDescriptor fd, String path, boolean readable,
this.writable = writable;
this.sync = sync;
this.direct = direct;
+ if (parent == null) {
+ parent = IoOverNio.PARENT_FOR_FILE_CHANNEL_IMPL.get();
+ }
this.parent = parent;
if (direct) {
assert path != null;
@@ -173,6 +177,24 @@ public void setUninterruptible() {
uninterruptible = true;
}
+ public void setInterruptible() {
+ uninterruptible = false;
+ }
+
+ @Override
+ public FileChannelImpl clone() {
+ FileChannelImpl result = new FileChannelImpl(fd, path, readable, writable, sync, direct, parent);
+ result.uninterruptible = uninterruptible;
+ if (!isOpen()) {
+ try {
+ result.close();
+ } catch (IOException _) {
+ // Ignored.
+ }
+ }
+ return result;
+ }
+
private void beginBlocking() {
if (!uninterruptible) begin();
}
@@ -1810,4 +1832,8 @@ void release(FileLockImpl fli) throws IOException {
assert fileLockTable != null;
fileLockTable.remove(fli);
}
+
+ public FileDescriptor getFD() {
+ return fd;
+ }
}
diff --git a/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java b/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
index 128694cb52d5..3a3953017cba 100644
--- a/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
+++ b/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
@@ -75,8 +75,22 @@ static UnixDomainSocketAddress checkAddress(SocketAddress sa) {
}
static byte[] getPathBytes(Path path) {
- FileSystemProvider provider = FileSystems.getDefault().provider();
- return ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path);
+ java.nio.file.FileSystem fs = path.getFileSystem();
+ FileSystemProvider provider = fs.provider();
+ if (fs == sun.nio.fs.DefaultFileSystemProvider.theFileSystem()) {
+ return ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path);
+ } else {
+ try {
+ java.lang.reflect.Method method = provider.getClass().getMethod("getSunPathForSocketFile", Path.class);
+ Object result = method.invoke(provider, path);
+ return (byte[]) result;
+ } catch (NoSuchMethodException | SecurityException e) {
+ // This should've been verified when UnixDomainSocketAddress was created
+ throw new Error("Can't find getSunPathForSocketFile(Path) in the non-default file system provider " + provider.getClass());
+ } catch (java.lang.reflect.InvocationTargetException | IllegalAccessException e) {
+ throw new RuntimeException("Can't invoke getSunPathForSocketFile(Path) from a non-default file system provider", e);
+ }
+ }
}
static FileDescriptor socket() throws IOException {
@@ -112,13 +126,11 @@ static UnixDomainSocketAddress generateTempName() throws IOException {
int rnd = random.nextInt(Integer.MAX_VALUE);
try {
final Path path = Path.of(dir, "socket_" + rnd);
- if (path.getFileSystem().provider() != sun.nio.fs.DefaultFileSystemProvider.instance()) {
- throw new UnsupportedOperationException(
- "Unix Domain Sockets not supported on non-default file system");
- }
return UnixDomainSocketAddress.of(path);
} catch (InvalidPathException e) {
throw new BindException("Invalid temporary directory");
+ } catch (IllegalArgumentException e) {
+ throw new UnsupportedOperationException("Unix Domain Sockets not supported on non-default file system without method getSunPathForSocketFile(Path)");
}
}
diff --git a/test/hotspot/jtreg/testlibrary/asm/asm.md b/src/java.base/share/legal/asm.md
similarity index 100%
rename from test/hotspot/jtreg/testlibrary/asm/asm.md
rename to src/java.base/share/legal/asm.md
diff --git a/src/java.base/share/native/libjava/NativeLibraries.c b/src/java.base/share/native/libjava/NativeLibraries.c
index b6624eccd1d1..f25abaa2d1b6 100644
--- a/src/java.base/share/native/libjava/NativeLibraries.c
+++ b/src/java.base/share/native/libjava/NativeLibraries.c
@@ -104,7 +104,6 @@ Java_jdk_internal_loader_NativeLibraries_load
(JNIEnv *env, jclass cls, jobject lib, jstring name,
jboolean isBuiltin, jboolean throwExceptionIfFail)
{
- const char *cname;
jint jniVersion;
jthrowable cause;
void * handle;
@@ -113,14 +112,15 @@ Java_jdk_internal_loader_NativeLibraries_load
if (!initIDs(env))
return JNI_FALSE;
- cname = JNU_GetStringPlatformChars(env, name, 0);
- if (cname == 0)
+ const char * utf8_name = GetStringUTF8Chars(env, name);
+ if (utf8_name == NULL)
return JNI_FALSE;
- handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname, throwExceptionIfFail);
+
+ handle = isBuiltin ? procHandle : JVM_LoadLibrary(utf8_name, throwExceptionIfFail);
if (handle) {
JNI_OnLoad_t JNI_OnLoad;
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
- isBuiltin ? cname : NULL,
+ isBuiltin ? utf8_name : NULL,
JNI_TRUE);
if (JNI_OnLoad) {
JavaVM *jvm;
@@ -145,7 +145,7 @@ Java_jdk_internal_loader_NativeLibraries_load
char msg[256];
jio_snprintf(msg, sizeof(msg),
"unsupported JNI version 0x%08X required by %s",
- jniVersion, cname);
+ jniVersion, utf8_name);
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
@@ -167,7 +167,7 @@ Java_jdk_internal_loader_NativeLibraries_load
loaded = JNI_TRUE;
done:
- JNU_ReleaseStringPlatformChars(env, name, cname);
+ ReleaseStringUTF8Chars(env, name, utf8_name);
return loaded;
}
diff --git a/src/java.base/share/native/libjava/RandomAccessFile.c b/src/java.base/share/native/libjava/RandomAccessFile.c
index 28b733a1c25f..bca27ed91bcc 100644
--- a/src/java.base/share/native/libjava/RandomAccessFile.c
+++ b/src/java.base/share/native/libjava/RandomAccessFile.c
@@ -90,7 +90,7 @@ Java_java_io_RandomAccessFile_writeBytes0(JNIEnv *env,
}
JNIEXPORT jlong JNICALL
-Java_java_io_RandomAccessFile_getFilePointer(JNIEnv *env, jobject this) {
+Java_java_io_RandomAccessFile_getFilePointer0(JNIEnv *env, jobject this) {
FD fd;
jlong ret;
diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c
index 7b038a6a9d59..8af3ac246bff 100644
--- a/src/java.base/share/native/libjava/System.c
+++ b/src/java.base/share/native/libjava/System.c
@@ -322,3 +322,27 @@ Java_java_lang_System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
return (*env)->NewString(env, chars, len);
}
+
+JNIEXPORT void JNICALL
+Java_java_lang_System__00024_00024jb_00024FullGC(JNIEnv *env, jclass ign)
+{
+ jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$FullGC");
+ if (cls != 0) {
+ // Throwing an exception by this name will trigger a full GC with
+ // a special cause indicating the need to collect as much as possible
+ // for testing purposes.
+ (*env)->ThrowNew(env, cls, NULL);
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_java_lang_System__00024_00024jb_00024ShrinkingGC(JNIEnv *env, jclass ign)
+{
+ jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$ShrinkingGC");
+ if (cls != 0) {
+ // Throwing an exception by this name will trigger a full GC with
+ // a special cause indicating the need to shrink the heap
+ (*env)->ThrowNew(env, cls, NULL);
+ }
+}
+
diff --git a/src/java.base/share/native/libjava/jni_util.c b/src/java.base/share/native/libjava/jni_util.c
index ce23d1604306..399636486e68 100644
--- a/src/java.base/share/native/libjava/jni_util.c
+++ b/src/java.base/share/native/libjava/jni_util.c
@@ -722,6 +722,35 @@ newStringUTF8(JNIEnv *env, const char *str)
return newSizedStringJava(env, str, len);
}
+static jobject
+getUTF8Charset(JNIEnv *env) {
+ static jobject utf8Charset = NULL;
+ if (utf8Charset == NULL) {
+ jstring enc = (*env)->NewStringUTF(env, "UTF-8");
+ if (enc == NULL) {
+ return NULL;
+ }
+ jboolean exc;
+ jvalue charset = JNU_CallStaticMethodByName(
+ env, &exc,
+ "java/nio/charset/Charset",
+ "forName",
+ "(Ljava/lang/String;)Ljava/nio/charset/Charset;",
+ enc);
+ if (exc) {
+ (*env)->ExceptionClear(env);
+ }
+ (*env)->DeleteLocalRef(env, enc);
+
+ if (!exc && charset.l != NULL) {
+ utf8Charset = (*env)->NewGlobalRef(env, charset.l);
+ (*env)->DeleteLocalRef(env, charset.l);
+ }
+ }
+
+ return utf8Charset;
+}
+
/* Initialize the fast encoding from the encoding name.
* Export InitializeEncoding so that the VM can initialize it if required.
*/
@@ -849,14 +878,26 @@ GetStringPlatformCharsStrict(JNIEnv *env, jstring jstr, jboolean *isCopy)
return getStringPlatformChars0(env, jstr, isCopy, JNI_TRUE);
}
-static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict) {
+/* Convert the given Java string into a null-terminated byte sequence according
+ * to the platform encoding (if needUTF8 is false) or to UTF-8 encoding (if
+ * needUTF8 is true).
+ */
+static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict, jboolean needUTF8) {
char *result = NULL;
jbyteArray hab = 0;
if ((*env)->EnsureLocalCapacity(env, 2) < 0)
return 0;
- hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, jnuCharset);
+ if (needUTF8) {
+ if (getUTF8Charset(env) == NULL) {
+ return NULL;
+ }
+ hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, getUTF8Charset(env));
+ } else {
+ hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, jnuCharset);
+ }
+
if (hab != 0) {
if (!(*env)->ExceptionCheck(env)) {
jint len = (*env)->GetArrayLength(env, hab);
@@ -882,6 +923,7 @@ static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict) {
}
(*env)->DeleteLocalRef(env, hab);
}
+
return result;
}
@@ -897,7 +939,8 @@ getStringUTF8(JNIEnv *env, jstring jstr, jboolean strict)
int ri;
jbyte coder = (*env)->GetByteField(env, jstr, String_coder_ID);
if (coder != java_lang_String_LATIN1) {
- return getStringBytes(env, jstr, strict);
+ const jboolean forceUTF8 = (fastEncoding != FAST_UTF_8);
+ return getStringBytes(env, jstr, strict, forceUTF8);
}
if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
return NULL;
@@ -945,6 +988,18 @@ getStringUTF8(JNIEnv *env, jstring jstr, jboolean strict)
return result;
}
+JNIEXPORT const char *
+GetStringUTF8Chars(JNIEnv *env, jstring jstr)
+{
+ return getStringUTF8(env, jstr, JNI_FALSE);
+}
+
+JNIEXPORT void
+ReleaseStringUTF8Chars(JNIEnv* env, jstring jstr, const char *str)
+{
+ free((void *)str);
+}
+
JNIEXPORT const char * JNICALL
JNU_GetStringPlatformChars(JNIEnv *env, jstring jstr, jboolean *isCopy)
{
@@ -976,7 +1031,7 @@ getStringPlatformChars0(JNIEnv *env, jstring jstr, jboolean *isCopy, jboolean st
JNU_ThrowInternalError(env, "platform encoding not initialized");
return 0;
} else
- return getStringBytes(env, jstr, strict);
+ return getStringBytes(env, jstr, strict, JNI_FALSE /* Need platform encoding */);
}
JNIEXPORT void JNICALL
@@ -1251,3 +1306,54 @@ JNU_GetStaticFieldByName(JNIEnv *env,
}
return result;
}
+
+JNIEXPORT void JNICALL
+JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg)
+{
+ jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$Assertion");
+ if (cls != 0) {
+ size_t len = strlen(msg) + strlen(file) + 64;
+ char *real_msg = malloc(len);
+ snprintf(real_msg, len, "%s (%s:%d)", msg, file, line);
+ // Throwing an exception by this name will trigger JVM fatal error
+ // with the given message
+ (*env)->ThrowNew(env, cls, real_msg);
+ }
+}
+
+JNIEXPORT void JNICALL
+JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...)
+{
+ jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$Event");
+ if (cls == 0) return;
+
+ va_list args;
+
+ va_start(args, fmt);
+ int len = vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+ if (len < 0) return;
+
+ int suffix_len = (int) strlen(file) + 64;
+ len += suffix_len;
+ char * real_msg = malloc(len);
+ if (real_msg == NULL) return;
+
+ va_start(args, fmt);
+ vsnprintf(real_msg, len, fmt, args);
+ va_end(args);
+
+ char * suffix = malloc(suffix_len);
+ if (suffix == NULL) {
+ free(real_msg);
+ return;
+ }
+
+ snprintf(suffix, suffix_len, " (%s:%d)", file, line);
+ strncat(real_msg, suffix, suffix_len);
+ free(suffix);
+
+ // Throwing an exception by this name will result in Events::log() call
+ (*env)->ThrowNew(env, cls, real_msg);
+ free(real_msg);
+}
\ No newline at end of file
diff --git a/src/java.base/share/native/libjava/jni_util.h b/src/java.base/share/native/libjava/jni_util.h
index 89414697328a..7ce8b8e9ea4f 100644
--- a/src/java.base/share/native/libjava/jni_util.h
+++ b/src/java.base/share/native/libjava/jni_util.h
@@ -108,6 +108,13 @@ GetStringPlatformChars(JNIEnv *env, jstring jstr, jboolean *isCopy);
JNIEXPORT const char *
GetStringPlatformCharsStrict(JNIEnv *env, jstring jstr, jboolean *isCopy);
+/* Convert the Java string to UTF-8 (not "modified UTF-8") */
+JNIEXPORT const char *
+GetStringUTF8Chars(JNIEnv *env, jstring jstr);
+
+JNIEXPORT void
+ReleaseStringUTF8Chars(JNIEnv* env, jstring jstr, const char *str);
+
JNIEXPORT jstring JNICALL
JNU_NewStringPlatform(JNIEnv *env, const char *str);
@@ -231,6 +238,15 @@ JNU_GetStaticFieldByName(JNIEnv *env,
const char *name,
const char *sig);
+/*
+ * Crashes the JVM with Internal Error providing the message and
+ * source location information in the fatal error log.
+ */
+JNIEXPORT void JNICALL
+JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg);
+
+JNIEXPORT void JNICALL
+JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...);
/************************************************************************
* Miscellaneous utilities used by the class libraries
@@ -304,6 +320,23 @@ JNU_GetStaticFieldByName(JNIEnv *env,
} while (0)
#endif /* __cplusplus */
+/*
+ * Crashes the JVM with Internal Error providing the message and
+ * source location information in the fatal error log iff
+ * 'cond' is not true.
+ */
+#define JNU_RUNTIME_ASSERT(env, cond, msg) \
+ do { \
+ if (!(cond)) { \
+ JNU_Fatal((env), __FILE__, __LINE__, (msg));\
+ } \
+ } while(0)
+
+#define JNU_LOG_EVENT(env, fmt, ...) \
+ do { \
+ JNU_LogEvent((env), __FILE__, __LINE__, (fmt), ##__VA_ARGS__); \
+ } while(0)
+
/************************************************************************
* Debugging utilities
*/
diff --git a/src/java.base/share/native/libnio/nio_util.c b/src/java.base/share/native/libnio/nio_util.c
index 2235f0a5998d..6bea39d9e622 100644
--- a/src/java.base/share/native/libnio/nio_util.c
+++ b/src/java.base/share/native/libnio/nio_util.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,15 @@
#include "jni.h"
#include "jvm.h"
#include "jni_util.h"
+#include "nio_util.h"
+
+JavaVM *jvm;
JNIEXPORT jint JNICALL
DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env;
+ jvm = vm;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) {
return JNI_EVERSION; /* JNI version not supported */
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java b/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java
index 1e04291d4589..3b396349df32 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java
@@ -197,8 +197,9 @@ protected static FileDescriptor open(int dfd,
if (flags.createNew) {
byte[] pathForSysCall = path.getByteArrayForSysCalls();
- // throw exception if file name is "." to avoid confusing error
- if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
+ // throw exception if file name is "." or "" to avoid confusing error
+ if ((pathForSysCall.length == 0) ||
+ (pathForSysCall[pathForSysCall.length-1] == '.') &&
(pathForSysCall.length == 1 ||
(pathForSysCall[pathForSysCall.length-2] == '/')))
{
diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixException.java b/src/java.base/unix/classes/sun/nio/fs/UnixException.java
index 9c90911f9cf0..67af98a7c1af 100644
--- a/src/java.base/unix/classes/sun/nio/fs/UnixException.java
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixException.java
@@ -25,6 +25,8 @@
package sun.nio.fs;
+import com.jetbrains.internal.IoToNioErrorMessageHolder;
+
import java.nio.file.*;
import java.io.IOException;
@@ -104,6 +106,7 @@ void rethrowAsIOException(UnixPath file, UnixPath other) throws IOException {
String a = (file == null) ? null : file.getPathForExceptionMessage();
String b = (other == null) ? null : other.getPathForExceptionMessage();
IOException x = translateToIOException(a, b);
+ IoToNioErrorMessageHolder.addMessage(x, errorString());
throw x;
}
@@ -112,6 +115,8 @@ void rethrowAsIOException(UnixPath file) throws IOException {
}
IOException asIOException(UnixPath file) {
- return translateToIOException(file.getPathForExceptionMessage(), null);
+ IOException x = translateToIOException(file.getPathForExceptionMessage(), null);
+ IoToNioErrorMessageHolder.addMessage(x, errorString());
+ return x;
}
}
diff --git a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java
index a814746d9601..0140866eb4ec 100644
--- a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java
+++ b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java
@@ -30,6 +30,7 @@
import java.security.*;
import java.util.Arrays;
+import com.jetbrains.internal.IoOverNio;
import sun.security.util.Debug;
/**
@@ -126,8 +127,14 @@ private static URL getEgdUrl() {
/**
* Create a RandomIO object for all I/O of this Variant type.
*/
+ @SuppressWarnings("try")
private static RandomIO initIO(final Variant v) {
+ try (var ignored = IoOverNio.disableInThisThread()) {
+ return initIOImpl(v);
+ }
+ }
+ private static RandomIO initIOImpl(final Variant v) {
File seedFile;
File nextFile;
diff --git a/src/java.base/unix/native/libjli/java_md.c b/src/java.base/unix/native/libjli/java_md.c
index 2c25a7668c39..828e9b5fafb3 100644
--- a/src/java.base/unix/native/libjli/java_md.c
+++ b/src/java.base/unix/native/libjli/java_md.c
@@ -27,6 +27,7 @@
#include "jvm_md.h"
#include
#include
+#include
#include
#include
#include
@@ -137,6 +138,17 @@
/* Store the name of the executable once computed */
static char *execname = NULL;
+typedef enum awt_toolkit {
+ TK_UNKNOWN = 0,
+ TK_X11 = 1,
+ TK_WAYLAND = 2
+} awt_toolkit;
+
+// TODO when wayland support will be fully implemented change to TK_UNKNOWN
+// currently wayland support is not Production-Ready ready so default awt toolkit is X11
+// wayland could be chosen manually via passing -Dawt.toolkit.name=WLToolkit argument
+static awt_toolkit _awt_toolkit = TK_X11;
+
/*
* execname accessor from other parts of platform dependent logic
*/
@@ -204,6 +216,39 @@ ContainsLibJVM(const char *env) {
return JNI_FALSE;
}
+static int
+HaveGLibCCompatLibrary(struct dl_phdr_info* info, size_t size, void* data)
+{
+ static const char * const GLIBC_COMPAT_LIBRARY_NAME = "libgcompat.so";
+
+ const char * const so_pathname = info->dlpi_name;
+ if (so_pathname != NULL && so_pathname[0] != 0) {
+ const char * const last_slash = JLI_StrRChr(so_pathname, '/');
+ const char * const so_basename = (last_slash != NULL) ? last_slash + 1 : so_pathname;
+ if (JLI_StrNCmp(so_basename, GLIBC_COMPAT_LIBRARY_NAME, JLI_StrLen(GLIBC_COMPAT_LIBRARY_NAME)) == 0) {
+ return JNI_TRUE;
+ }
+ }
+
+ return 0; /* also means continue to iterate */
+}
+
+static jboolean
+UsingMusl(void) {
+ const jlong start = CurrentTimeMicros();
+
+ const int found_gcompat = dl_iterate_phdr(HaveGLibCCompatLibrary, NULL);
+
+ if (JLI_IsTraceLauncher()) {
+ const jlong end = CurrentTimeMicros();
+ JLI_TraceLauncher("%ld micro seconds to check for the musl compatibility layer for glibc\n",
+ (long)(end - start));
+ }
+
+ return (found_gcompat != 0);
+}
+
+
/*
* Test whether the LD_LIBRARY_PATH environment variable needs to be set.
*/
@@ -226,6 +271,10 @@ RequiresSetenv(const char *jvmpath) {
return JNI_TRUE;
#endif
+ if (UsingMusl()) {
+ return JNI_TRUE;
+ }
+
llp = getenv("LD_LIBRARY_PATH");
/* no environment variable is a good environment variable */
if (llp == NULL) {
@@ -603,7 +652,6 @@ SetExecname(char **argv)
}
/* --- Splash Screen shared library support --- */
-static const char* SPLASHSCREEN_SO = JNI_LIB_NAME("splashscreen");
static void* hSplashLib = NULL;
void* SplashProcAddress(const char* name) {
@@ -619,8 +667,14 @@ void* SplashProcAddress(const char* name) {
JLI_ReportErrorMessage(LAUNCHER_ERROR1);
return NULL;
}
+#if defined(__linux__)
+ const char* splash_screen_so = _awt_toolkit == TK_WAYLAND ?
+ JNI_LIB_NAME("wlsplashscreen") : JNI_LIB_NAME("splashscreen");
+#else
+ const char* splash_screen_so = JNI_LIB_NAME("splashscreen");
+#endif
ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s",
- jdkRoot, SPLASHSCREEN_SO);
+ jdkRoot, splash_screen_so);
if (ret >= (int) sizeof(splashPath)) {
JLI_ReportErrorMessage(LAUNCHER_ERROR3);
@@ -708,11 +762,34 @@ CallJavaMainInNewThread(jlong stack_size, void* args) {
/* Coarse estimation of number of digits assuming the worst case is a 64-bit pid. */
#define MAX_PID_STR_SZ 20
+static char*
+getToolkitNameByEnv() {
+ if (_awt_toolkit == TK_UNKNOWN) {
+ char *xdg_session_type = getenv("XDG_SESSION_TYPE");
+ if (xdg_session_type != NULL && strcmp(xdg_session_type, "wayland") == 0) {
+ _awt_toolkit = TK_WAYLAND;
+ } else if (xdg_session_type != NULL && strcmp(xdg_session_type, "x11") == 0) {
+ _awt_toolkit = TK_X11;
+ } else if (getenv("DISPLAY") != NULL) {
+ _awt_toolkit = TK_X11;
+ } else if (getenv("WAYLAND_DISPLAY") != NULL) {
+ _awt_toolkit = TK_WAYLAND;
+ }
+ }
+ return _awt_toolkit == TK_WAYLAND ? "WLToolkit" : "XToolkit";
+}
+
int
JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
+ char *toolkit_name = getToolkitNameByEnv();
+ size_t toolkit_name_size = JLI_StrLen("-Dawt.toolkit.name=") + JLI_StrLen(toolkit_name) + 1;
+ char *toolkit_option = (char *)JLI_MemAlloc(toolkit_name_size);
+ snprintf(toolkit_option, toolkit_name_size, "-Dawt.toolkit.name=%s", toolkit_name);
+ AddOption(toolkit_option, NULL);
+
ShowSplashScreen();
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}
@@ -729,11 +806,16 @@ RegisterThread()
// stubbed out for windows and *nixes.
}
-/*
- * on unix, we return a false to indicate this option is not applicable
- */
jboolean
ProcessPlatformOption(const char *arg)
{
+ if (JLI_StrCCmp(arg, "-Dawt.toolkit.name=WLToolkit") == 0) {
+ _awt_toolkit = TK_WAYLAND;
+ return JNI_TRUE;
+ } else if (JLI_StrCCmp(arg, "-Dawt.toolkit.name=XToolkit") == 0) {
+ _awt_toolkit = TK_X11;
+ return JNI_TRUE;
+ }
+
return JNI_FALSE;
}
diff --git a/src/java.base/unix/native/libnio/ch/nio_util.h b/src/java.base/unix/native/libnio/ch/nio_util.h
index 6cd3c858a7bd..3ea52ba57371 100644
--- a/src/java.base/unix/native/libnio/ch/nio_util.h
+++ b/src/java.base/unix/native/libnio/ch/nio_util.h
@@ -48,6 +48,8 @@
#define MAX_UNIX_DOMAIN_PATH_LEN \
(int)(sizeof(((struct sockaddr_un *)0)->sun_path)-2)
+extern JavaVM *jvm;
+
/* NIO utility procedures */
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
index f1eff69210b0..7f377d74c828 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java
@@ -25,6 +25,8 @@
package sun.nio.fs;
+import com.jetbrains.internal.IoToNioErrorMessageHolder;
+
import java.nio.file.*;
import java.io.IOException;
@@ -94,6 +96,7 @@ private IOException translateToIOException(String file, String other) {
void rethrowAsIOException(String file) throws IOException {
IOException x = translateToIOException(file, null);
+ IoToNioErrorMessageHolder.addMessage(x, errorString());
throw x;
}
@@ -101,6 +104,7 @@ void rethrowAsIOException(WindowsPath file, WindowsPath other) throws IOExceptio
String a = (file == null) ? null : file.getPathForExceptionMessage();
String b = (other == null) ? null : other.getPathForExceptionMessage();
IOException x = translateToIOException(a, b);
+ IoToNioErrorMessageHolder.addMessage(x, errorString());
throw x;
}
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java b/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
index 6198fbfd5b59..6ca7c96ccc19 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
@@ -186,14 +186,9 @@ private static String normalize(StringBuilder sb, String path, int off) {
int len = path.length();
off = nextNonSlash(path, off, len);
int start = off;
- char lastC = 0;
while (off < len) {
char c = path.charAt(off);
if (isSlash(c)) {
- if (lastC == ' ')
- throw new InvalidPathException(path,
- "Trailing char <" + lastC + ">",
- off - 1);
sb.append(path, start, off);
off = nextNonSlash(path, off, len);
if (off != len) //no slash at the end of normalized path
@@ -204,15 +199,10 @@ private static String normalize(StringBuilder sb, String path, int off) {
throw new InvalidPathException(path,
"Illegal char <" + c + ">",
off);
- lastC = c;
off++;
}
}
if (start != off) {
- if (lastC == ' ')
- throw new InvalidPathException(path,
- "Trailing char <" + lastC + ">",
- off - 1);
sb.append(path, start, off);
}
return sb.toString();
diff --git a/src/java.base/windows/native/launcher/icons/awt.ico b/src/java.base/windows/native/launcher/icons/awt.ico
index 8d2c9571ed9e..b99c0c69965b 100644
Binary files a/src/java.base/windows/native/launcher/icons/awt.ico and b/src/java.base/windows/native/launcher/icons/awt.ico differ
diff --git a/src/java.base/windows/native/launcher/java.rc b/src/java.base/windows/native/launcher/java.rc
index 27c6465de210..6789a9a6091b 100644
--- a/src/java.base/windows/native/launcher/java.rc
+++ b/src/java.base/windows/native/launcher/java.rc
@@ -1,6 +1,5 @@
#include "version.rc"
-
-#define IDI_ICON 2000
+#include "java_rc.h"
/////////////////////////////////////////////////////////////////////////////
//
diff --git a/src/java.base/windows/native/launcher/java_rc.h b/src/java.base/windows/native/launcher/java_rc.h
new file mode 100644
index 000000000000..d3a1efe7536e
--- /dev/null
+++ b/src/java.base/windows/native/launcher/java_rc.h
@@ -0,0 +1,6 @@
+#ifndef _JAVA_RC_H_
+#define _JAVA_RC_H_
+
+#define IDI_ICON 2000
+
+#endif // _JAVA_RC_H_
diff --git a/src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c b/src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c
index 88eb3cbec2d6..1038da7b9524 100644
--- a/src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c
+++ b/src/java.desktop/aix/native/libawt_xawt/awt/awt_InputMethod.c
@@ -374,7 +374,7 @@ setXICWindowFocus(XIC ic, Window w)
#define INITIAL_LOOKUP_BUF_SIZE 512
Boolean
-awt_x11inputmethod_lookupString(XKeyPressedEvent *event, KeySym *keysymp)
+awt_x11inputmethod_lookupString(XKeyPressedEvent *event, KeySym *keysymp, Boolean /* not used */)
{
JNIEnv *env = GetJNIEnv();
X11InputMethodData *pX11IMData = NULL;
diff --git a/src/java.desktop/linux/native/libawt/awt/dbus_interface.c b/src/java.desktop/linux/native/libawt/awt/dbus_interface.c
new file mode 100644
index 000000000000..656988c28681
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt/awt/dbus_interface.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 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. 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.
+ */
+
+
+#include "dbus_interface.h"
+
+#include
+#include
+#include
+#include
+
+#include "jvm_md.h"
+
+#define DBUS_LIB JNI_LIB_NAME("dbus-1")
+#define DBUS_LIB_VERSIONED VERSIONED_JNI_LIB_NAME("dbus-1", "3")
+
+static bool isCurrentVersionSupported(DBusApi* dBusApi) {
+ int major = 0, minor = 0, micro = 0;
+ dBusApi->dbus_get_version(&major, &minor, µ);
+ return major == 1;
+}
+
+static bool DBusApi_init(DBusApi* dBusApi, void *libhandle) {
+ dBusApi->dbus_get_version = dlsym(libhandle, "dbus_get_version");
+ if (dBusApi->dbus_get_version == NULL || !isCurrentVersionSupported(dBusApi)) {
+ return false;
+ }
+
+ dBusApi->dbus_error_init = dlsym(libhandle, "dbus_error_init");
+ dBusApi->dbus_bus_get = dlsym(libhandle, "dbus_bus_get");
+ dBusApi->dbus_error_is_set = dlsym(libhandle, "dbus_error_is_set");
+ dBusApi->dbus_bus_request_name = dlsym(libhandle, "dbus_bus_request_name");
+ dBusApi->dbus_connection_flush = dlsym(libhandle, "dbus_connection_flush");
+ dBusApi->dbus_message_new_method_call = dlsym(libhandle, "dbus_message_new_method_call");
+ dBusApi->dbus_message_set_destination = dlsym(libhandle, "dbus_message_set_destination");
+ dBusApi->dbus_message_iter_init_append = dlsym(libhandle, "dbus_message_iter_init_append");
+ dBusApi->dbus_message_iter_append_basic = dlsym(libhandle, "dbus_message_iter_append_basic");
+ dBusApi->dbus_connection_send_with_reply_and_block = dlsym(libhandle, "dbus_connection_send_with_reply_and_block");
+ dBusApi->dbus_message_iter_init = dlsym(libhandle, "dbus_message_iter_init");
+ dBusApi->dbus_message_iter_get_arg_type = dlsym(libhandle, "dbus_message_iter_get_arg_type");
+ dBusApi->dbus_message_iter_get_basic = dlsym(libhandle, "dbus_message_iter_get_basic");
+ dBusApi->dbus_message_iter_recurse = dlsym(libhandle, "dbus_message_iter_recurse");
+ dBusApi->dbus_message_iter_next = dlsym(libhandle, "dbus_message_iter_next");
+ dBusApi->dbus_message_unref = dlsym(libhandle, "dbus_message_unref");
+ dBusApi->dbus_error_free = dlsym(libhandle, "dbus_error_free");
+
+ return dBusApi->dbus_error_init != NULL && dBusApi->dbus_bus_get != NULL && dBusApi->dbus_error_is_set != NULL &&
+ dBusApi->dbus_bus_request_name != NULL && dBusApi->dbus_connection_flush != NULL &&
+ dBusApi->dbus_message_set_destination != NULL && dBusApi->dbus_message_iter_init_append != NULL &&
+ dBusApi->dbus_message_iter_append_basic != NULL && dBusApi->dbus_connection_send_with_reply_and_block != NULL &&
+ dBusApi->dbus_message_iter_init != NULL && dBusApi->dbus_message_iter_get_arg_type != NULL &&
+ dBusApi->dbus_message_iter_get_basic != NULL && dBusApi->dbus_message_iter_recurse != NULL &&
+ dBusApi->dbus_message_iter_next != NULL && dBusApi->dbus_message_unref != NULL &&
+ dBusApi->dbus_message_new_method_call != NULL && dBusApi->dbus_error_free != NULL;
+}
+
+DBusApi* DBusApi_setupDBus(void *libhandle) {
+ DBusApi *dBusApi = (DBusApi*)malloc(sizeof(DBusApi));
+ if (dBusApi == NULL || !DBusApi_init(dBusApi, libhandle)) {
+ free(dBusApi);
+ dBusApi = NULL;
+ }
+
+ return dBusApi;
+}
+
+DBusApi* DBusApi_setupDBusDefault() {
+ void *dbus_libhandle = dlopen(DBUS_LIB, RTLD_LAZY | RTLD_LOCAL);
+ if (dbus_libhandle == NULL) {
+ dbus_libhandle = dlopen(DBUS_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL);
+ if (dbus_libhandle == NULL) {
+ return NULL;
+ }
+ }
+
+ return DBusApi_setupDBus(dbus_libhandle);
+}
\ No newline at end of file
diff --git a/src/java.desktop/linux/native/libawt/awt/dbus_interface.h b/src/java.desktop/linux/native/libawt/awt/dbus_interface.h
new file mode 100644
index 000000000000..62f9b6c3ae64
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt/awt/dbus_interface.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 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. 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.
+ */
+
+#ifndef JETBRAINSRUNTIME_DBUS_INTERFACE_H
+#define JETBRAINSRUNTIME_DBUS_INTERFACE_H
+
+#include
+
+typedef struct DBusApi {
+ void (*dbus_get_version)(int *major_version_p, int *minor_version_p, int *micro_version_p);
+
+ void (*dbus_error_init)(DBusError *error);
+
+ DBusConnection *(*dbus_bus_get)(DBusBusType type, DBusError *error);
+
+ dbus_bool_t (*dbus_error_is_set)(const DBusError *error);
+
+ void (*dbus_error_free)(DBusError *error);
+
+ int (*dbus_bus_request_name)(DBusConnection *connection, const char *name, unsigned int flags, DBusError *error);
+
+ void (*dbus_connection_flush)(DBusConnection *connection);
+
+ DBusMessage* (*dbus_message_new_method_call)(const char *bus_name, const char *path,
+ const char *iface, const char *method);
+
+ dbus_bool_t (*dbus_message_set_destination)(DBusMessage *message, const char *destination);
+
+ void (*dbus_message_iter_init_append)(DBusMessage *message, DBusMessageIter *iter);
+
+ dbus_bool_t (*dbus_message_iter_append_basic)(DBusMessageIter *iter, int type, const void *value);
+
+ DBusMessage *(*dbus_connection_send_with_reply_and_block)(DBusConnection *connection, DBusMessage *message,
+ int timeout_milliseconds, DBusError *error);
+
+ dbus_bool_t (*dbus_message_iter_init)(DBusMessage *message, DBusMessageIter *iter);
+
+ int (*dbus_message_iter_get_arg_type)(DBusMessageIter *iter);
+
+ void (*dbus_message_iter_get_basic)(DBusMessageIter *iter, void *value);
+
+ void (*dbus_message_iter_recurse)(DBusMessageIter *iter, DBusMessageIter *sub);
+
+ dbus_bool_t (*dbus_message_iter_next)(DBusMessageIter *iter);
+
+ void (*dbus_message_unref)(DBusMessage *message);
+} DBusApi;
+
+DBusApi* DBusApi_setupDBus(void *libhandle);
+DBusApi* DBusApi_setupDBusDefault();
+
+#endif //JETBRAINSRUNTIME_DBUS_INTERFACE_H
diff --git a/src/java.desktop/linux/native/libawt/awt/system_properties.c b/src/java.desktop/linux/native/libawt/awt/system_properties.c
new file mode 100644
index 000000000000..d6085a433c47
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt/awt/system_properties.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 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. 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.
+ */
+
+#include "system_properties.h"
+
+#ifdef DBUS_FOUND
+
+#include
+#include
+#include
+
+#define UNKNOWN_RESULT -1
+#define SETTING_INTERFACE "org.freedesktop.portal.Settings"
+#define SETTING_INTERFACE_METHOD "Read"
+#define DESKTOP_DESTINATION "org.freedesktop.portal.Desktop"
+#define DESKTOP_PATH "/org/freedesktop/portal/desktop"
+#define REPLY_TIMEOUT 150
+
+static DBusConnection *connection = NULL;
+static JNIEnv *env = NULL;
+static DBusApi *dBus = NULL;
+static DBusMessage *msg_freedesktop_appearance = NULL;
+static DBusMessage *msg_gnome_desktop = NULL;
+static bool initialized = false;
+static bool logEnabled = true;
+extern JavaVM *jvm;
+
+static void printError(const char* fmt, ...) {
+ if (!logEnabled) {
+ return;
+ }
+
+ env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
+ char* buf = (char*)malloc(1024);
+
+ if (env && buf) {
+ va_list vargs;
+ va_start(vargs, fmt);
+ vsnprintf(buf, 1024, fmt, vargs);
+ jstring text = JNU_NewStringPlatform(env, buf);
+ free(buf);
+ va_end(vargs);
+
+ jboolean ignoreException;
+ JNU_CallStaticMethodByName(env, &ignoreException, "sun/awt/UNIXToolkit", "printError",
+ "(Ljava/lang/String;)V", text);
+ }
+}
+
+static bool dbusCheckError(DBusError *err, const char *msg) {
+ bool is_error_set = dBus->dbus_error_is_set(err);
+ if (is_error_set) {
+ printError("DBus error: %s. %s\n", msg, err->message);
+ dBus->dbus_error_free(err);
+ }
+ return is_error_set;
+}
+
+// current implementation of object decomposition supports only
+// primitive types (including a recursive type wrapper)
+static bool decomposeDBusReply(void *val, DBusMessageIter *iter, int demand_type) {
+ int cur_type = dBus->dbus_message_iter_get_arg_type(iter);
+ switch (cur_type)
+ {
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_STRING:
+ {
+ if (cur_type != demand_type) {
+ return false;
+ }
+ dBus->dbus_message_iter_get_basic(iter, val);
+ return true;
+ }
+ case DBUS_TYPE_VARIANT:
+ {
+ DBusMessageIter unwrap_iter;
+ dBus->dbus_message_iter_recurse(iter, &unwrap_iter);
+ bool res = decomposeDBusReply(val, &unwrap_iter, demand_type);
+ // current implementation doesn't support types with multiple fields
+ if (dBus->dbus_message_iter_next(iter)) {
+ return false;
+ }
+ return res;
+ }
+ case DBUS_TYPE_INVALID:
+ default:
+ return false;
+ }
+}
+
+static DBusMessage *createDBusMessage(const char *messages[], int message_count) {
+ DBusMessage *msg = NULL;
+ DBusMessageIter msg_iter;
+
+ if ((msg = dBus->dbus_message_new_method_call(NULL, DESKTOP_PATH, SETTING_INTERFACE, SETTING_INTERFACE_METHOD)) == NULL) {
+ printError("DBus error: cannot allocate message\n");
+ goto cleanup;
+ }
+
+ if (!dBus->dbus_message_set_destination(msg, DESKTOP_DESTINATION)) {
+ printError("DBus error: cannot set destination\n");
+ goto cleanup;
+ }
+
+ dBus->dbus_message_iter_init_append(msg, &msg_iter);
+
+ for (int i = 0; i < message_count; i++) {
+ if (!dBus->dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &messages[i])) {
+ printError("DBus error: cannot append to message\n");
+ goto cleanup;
+ }
+ }
+
+ return msg;
+cleanup:
+ if (msg) {
+ dBus->dbus_message_unref(msg);
+ }
+ return NULL;
+}
+
+static bool sendDBusMessageWithReply(DBusMessage *msg, void *val, int demand_type) {
+ DBusError error;
+ DBusMessage *msg_reply = NULL;
+ DBusMessageIter msg_iter;
+ bool res = false;
+
+ dBus->dbus_error_init(&error);
+ if ((msg_reply = dBus->dbus_connection_send_with_reply_and_block(connection, msg, REPLY_TIMEOUT, &error)) == NULL) {
+ printError("DBus error: cannot get msg_reply or sent message. %s\n", dBus->dbus_error_is_set(&error) ? error.message : "");
+ goto cleanup;
+ }
+
+ if (!dBus->dbus_message_iter_init(msg_reply, &msg_iter)) {
+ printError("DBus error: cannot process message\n");
+ goto cleanup;
+ }
+
+ res = decomposeDBusReply(val, &msg_iter, demand_type);
+cleanup:
+ if (msg_reply) {
+ dBus->dbus_message_unref(msg_reply);
+ }
+ return res;
+}
+
+JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_isSystemDarkColorScheme() {
+ static int use_freedesktop_appearance = -1;
+ if (!initialized) {
+ return UNKNOWN_RESULT;
+ }
+
+ if (use_freedesktop_appearance == -1) {
+ unsigned int res = 0;
+ logEnabled = false;
+ use_freedesktop_appearance =
+ sendDBusMessageWithReply(msg_freedesktop_appearance, &res, DBUS_TYPE_UINT32);
+ logEnabled = true;
+ }
+
+ if (use_freedesktop_appearance) {
+ unsigned int res = 0;
+ if (!sendDBusMessageWithReply(msg_freedesktop_appearance, &res, DBUS_TYPE_UINT32)) {
+ return UNKNOWN_RESULT;
+ }
+ /* From org.freedesktop.portal color-scheme specs:
+ * 0: No preference, 1: Prefer dark appearance, 2: Prefer light appearance
+ */
+ return res == 1;
+ } else {
+ char *res = NULL;
+ if (!sendDBusMessageWithReply(msg_gnome_desktop, &res, DBUS_TYPE_STRING)) {
+ return UNKNOWN_RESULT;
+ }
+ return (res != NULL) ? strstr(res, "dark") != NULL : UNKNOWN_RESULT;
+ }
+}
+
+jboolean SystemProperties_setup(DBusApi *dBus_, JNIEnv *env_) {
+ env = env_;
+ dBus = dBus_;
+ DBusError err;
+ int ret;
+
+ dBus->dbus_error_init(&err);
+ if ((connection = dBus->dbus_bus_get(DBUS_BUS_SESSION, &err)) == NULL) {
+ printError("DBus error: connection is Null\n");
+ return JNI_FALSE;
+ }
+ if (dbusCheckError(&err, "connection error")) {
+ return JNI_FALSE;
+ }
+
+ ret = dBus->dbus_bus_request_name(connection, "dbus.JBR.server", DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER && ret != DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
+ printError("DBus error: Failed to acquire service name \n");
+ return JNI_FALSE;
+ }
+ if (dbusCheckError(&err, "error request 'dbus.JBR.server' name on the bus")) {
+ return JNI_FALSE;
+ }
+
+ dBus->dbus_connection_flush(connection);
+
+ const char *freedesktop_appearance_messages[] = {"org.freedesktop.appearance", "color-scheme"};
+ const char *gnome_desktop_messages[] = {"org.gnome.desktop.interface", "gtk-theme"};
+ msg_freedesktop_appearance = createDBusMessage(freedesktop_appearance_messages, 2);
+ msg_gnome_desktop = createDBusMessage(gnome_desktop_messages, 2);
+ if (msg_freedesktop_appearance == NULL || msg_gnome_desktop == NULL) {
+ return JNI_FALSE;
+ }
+
+ initialized = true;
+
+ return JNI_TRUE;
+}
+
+JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_dbusInit() {
+ DBusApi *dBus = DBusApi_setupDBusDefault();
+ if (dBus) {
+ return SystemProperties_setup(dBus, env);
+ }
+ return JNI_FALSE;
+}
+
+#else
+
+JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_isSystemDarkColorScheme() {
+ return -1;
+}
+
+JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_dbusInit() {
+ return JNI_FALSE;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/java.desktop/linux/native/libawt/awt/system_properties.h b/src/java.desktop/linux/native/libawt/awt/system_properties.h
new file mode 100644
index 000000000000..6166f255c707
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt/awt/system_properties.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 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. 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.
+ */
+
+#ifndef JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H
+#define JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H
+
+#include
+#include
+#include
+
+#ifdef DBUS_FOUND
+
+#include "dbus_interface.h"
+
+jboolean SystemProperties_setup(DBusApi *dBus_, JNIEnv *env_);
+void SystemProperties_pullEvent(void);
+
+#endif
+
+#endif //JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c
new file mode 100644
index 000000000000..eacb036f5971
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c
@@ -0,0 +1,48 @@
+/*
+ * 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. 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.
+ */
+
+#include "sun_swing_AccessibleAnnouncer.h"
+#include "OrcaAnnouncer.h"
+
+/*
+ * Class: sun_swing_AccessibleAnnouncer
+ * Method: nativeAnnounce
+ * Signature: (Ljavax/accessibility/Accessible;Ljava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_swing_AccessibleAnnouncer_nativeAnnounce
+(JNIEnv *env, jclass cls, jobject accessible, jstring str, jint priority)
+{
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+ if (OrcaAnnounce(env, str, priority) == 0)
+ {
+ return;
+ }
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Each announcer has failed or the build was made without any of them\n");
+#endif
+}
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h
new file mode 100644
index 000000000000..1c3861cf7d10
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h
@@ -0,0 +1,175 @@
+/*
+ * 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. 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.
+ */
+
+#ifndef ACCESSIBLEANNOUNCERJNIUTILS_H
+#define ACCESSIBLEANNOUNCERJNIUTILS_H
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include "jni.h"
+
+#define GET_AccessibleAnnouncerUtilities()\
+if (jc_AccessibleAnnouncerUtilities == NULL) {\
+jclass cls = (*env)->FindClass(env, "sun/awt/AccessibleAnnouncerUtilities");\
+if (cls == NULL) {\
+return;\
+}\
+jc_AccessibleAnnouncerUtilities = (*env)->NewGlobalRef(env, cls);\
+(*env)->DeleteLocalRef(env, cls);\
+}\
+
+#define GET_AccessibleAnnouncerUtilitiesReturn(ret)\
+if (jc_AccessibleAnnouncerUtilities == NULL) {\
+jclass cls = (*env)->FindClass(env, "sun/awt/AccessibleAnnouncerUtilities");\
+if (cls == NULL) {\
+return ret;\
+}\
+jc_AccessibleAnnouncerUtilities = (*env)->NewGlobalRef(env, cls);\
+(*env)->DeleteLocalRef(env, cls);\
+}\
+
+#define GET_getOrcaConf()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getOrcaConf == NULL) {\
+jsm_getOrcaConf = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getOrcaConf", "()Ljava/lang/Object;");\
+if (jsm_getOrcaConf == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getOrcaConfReturn(ret)\
+GET_AccessibleAnnouncerUtilitiesReturn(ret);\
+if (jsm_getOrcaConf == NULL) {\
+jsm_getOrcaConf = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getOrcaConf", "()Ljava/lang/Object;");\
+if (jsm_getOrcaConf == NULL) {\
+return ret;\
+}\
+}\
+
+#define GET_getSpeechServerInfo()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getSpeechServerInfo == NULL) {\
+jsm_getSpeechServerInfo = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getSpeechServerInfo", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getSpeechServerInfo == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getGain()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getGain == NULL) {\
+jsm_getGain = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getGain", "(Ljava/lang/Object;)D");\
+if (jsm_getGain == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getVariant()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getVariant == NULL) {\
+jsm_getVariant = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getVariant", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getVariant == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getDialect()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getDialect == NULL) {\
+jsm_getDialect = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getDialect", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getDialect == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getLang()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getLang == NULL) {\
+jsm_getLang = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getLang", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getLang == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getName()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getName == NULL) {\
+jsm_getName = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getName", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getName == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getAveragePitch()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getAveragePitch == NULL) {\
+jsm_getAveragePitch = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getAveragePitch", "(Ljava/lang/Object;)D");\
+if (jsm_getAveragePitch == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getRate()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getRate == NULL) {\
+jsm_getRate = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getRate", "(Ljava/lang/Object;)D");\
+if (jsm_getRate == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getActiveProfile()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getActiveProfile == NULL) {\
+jsm_getActiveProfile = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getActiveProfile", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getActiveProfile == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getVerbalizePunctuationStyle()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getVerbalizePunctuationStyle == NULL) {\
+jsm_getVerbalizePunctuationStyle = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getVerbalizePunctuationStyle", "(Ljava/lang/Object;)I");\
+if (jsm_getVerbalizePunctuationStyle == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getEnableSpeech(ret)\
+GET_AccessibleAnnouncerUtilitiesReturn(ret);\
+if (jsm_getEnableSpeech == NULL) {\
+jsm_getEnableSpeech = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getEnableSpeech", "(Ljava/lang/Object;)Z");\
+if (jsm_getEnableSpeech == NULL) {\
+return ret;\
+}\
+}\
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+
+#endif //ACCESSIBLEANNOUNCERJNIUTILS_H
+
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c
new file mode 100644
index 000000000000..acf24f40cf5b
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c
@@ -0,0 +1,303 @@
+/*
+ * 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. 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.
+ */
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include "sun_swing_AccessibleAnnouncer.h"
+#include "OrcaAnnouncer.h"
+#include "AccessibleAnnouncerJNIUtils.h"
+#include "jni_util.h"
+#include "debug_assert.h"
+
+static jclass jc_AccessibleAnnouncerUtilities = NULL;
+static jmethodID jsm_getOrcaConf = NULL;
+static jmethodID jsm_getSpeechServerInfo = NULL;
+static jmethodID jsm_getGain = NULL;
+static jmethodID jsm_getVariant = NULL;
+static jmethodID jsm_getDialect = NULL;
+static jmethodID jsm_getLang = NULL;
+static jmethodID jsm_getName = NULL;
+static jmethodID jsm_getAveragePitch = NULL;
+static jmethodID jsm_getRate = NULL;
+static jmethodID jsm_getEstablished = NULL;
+static jmethodID jsm_getActiveProfile = NULL;
+static jmethodID jsm_getVerbalizePunctuationStyle = NULL;
+static jmethodID jsm_getOnlySpeakDisplayedText = NULL;
+static jmethodID jsm_getEnableSpeech = NULL;
+
+int OrcaAnnounce(JNIEnv *env, jstring str, jint priority)
+{
+ DASSERT(env != NULL);
+ DASSERT(str != NULL)
+
+ jobject conf = OrcaGetConf(env);
+ if (conf == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read Orca configuration file\n");
+#endif
+ return -1;
+ }
+
+ if (OrcaGetEnableSpeech(env, conf) <= 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Speech is disable\n");
+#endif
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ SPDConnection *connection = spd_open("Java announcer", NULL, NULL, SPD_MODE_SINGLE);
+ if (connection == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Speech dispatcher connection is null\n");
+#endif
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ const char *msg = JNU_GetStringPlatformChars(env, str, NULL);
+ if (msg == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the announcing string");
+ }
+
+ spd_close(connection);
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ OrcaSetSpeechConf(env, connection, conf);
+ (*env)->DeleteLocalRef(env, conf);
+ int p = SPD_TEXT;
+ if (priority == sun_swing_AccessibleAnnouncer_ANNOUNCE_WITH_INTERRUPTING_CURRENT_OUTPUT)
+ {
+ p = SPD_MESSAGE;
+ }
+ int err = spd_say(connection, p, msg);
+ spd_close(connection);
+ JNU_ReleaseStringPlatformChars(env, str, msg);
+
+ if (err < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to say message\n");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+void OrcaSetSpeechConf(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ OrcaSetOutputModule(env, connection, conf);
+ OrcaSetSynthesisVoice(env, connection, conf);
+ OrcaSetLanguage(env, connection, conf);
+ OrcaSetPunctuation(env, connection, conf);
+ OrcaSetVoiceRate(env, connection, conf);
+ OrcaSetVoicePitch(env, connection, conf);
+ OrcaSetVolume(env, connection, conf);
+}
+
+void OrcaSetVolume(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getGain();
+ jdouble gain = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getGain, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (gain < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of gain from config\n");
+#endif
+ return;
+ }
+
+ int volume = (int)((gain - 5) * 20);
+ spd_set_volume(connection, volume);
+}
+
+void OrcaSetVoiceRate(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getRate();
+ jdouble rate = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getRate, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (rate < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of rate from config\n");
+#endif
+ return;
+ }
+
+ int iRate = (int)((rate - 50) * 2);
+ spd_set_voice_rate(connection, iRate);
+}
+
+void OrcaSetPunctuation(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getVerbalizePunctuationStyle();
+ jint punctuation = (*env)->CallStaticIntMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getVerbalizePunctuationStyle, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (punctuation < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of punctuation from config\n");
+#endif
+ return;
+ }
+
+ spd_set_punctuation(connection, punctuation);
+}
+
+void OrcaSetVoicePitch(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getAveragePitch();
+ jdouble pitch = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getAveragePitch, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (pitch < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of pitch from config\n");
+#endif
+ return;
+ }
+
+ int iPitch = (int)((pitch - 5) * 20);
+ spd_set_voice_pitch(connection, iPitch);
+}
+
+void OrcaSetOutputModule(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getSpeechServerInfo();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getSpeechServerInfo, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of speech server info from config\n");
+#endif
+ return;
+ }
+
+ const char *sintName = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (sintName == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the sintName string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_output_module(connection, sintName);
+ JNU_ReleaseStringPlatformChars(env, jStr, sintName);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+void OrcaSetLanguage(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getLang();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getLang, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of lang from config\n");
+#endif
+ return;
+ }
+
+ const char *lang = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (lang == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the lang string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_language(connection, lang);
+ JNU_ReleaseStringPlatformChars(env, jStr, lang);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+int OrcaGetEnableSpeech(JNIEnv *env, jobject conf)
+{
+ GET_getEnableSpeech(-1);
+ int es = (*env)->CallStaticBooleanMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getEnableSpeech, conf);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+ return es;
+}
+
+void OrcaSetSynthesisVoice(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getName();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getName, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of voice name from config\n");
+#endif
+ return;
+ }
+
+ const char *voiceName = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (voiceName == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the voiceName string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_synthesis_voice(connection, voiceName);
+ JNU_ReleaseStringPlatformChars(env, jStr, voiceName);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+jobject OrcaGetConf(JNIEnv *env)
+{
+ GET_getOrcaConfReturn(NULL);
+ jobject o = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getOrcaConf);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ return o;
+}
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h
new file mode 100644
index 000000000000..8eac261482cd
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h
@@ -0,0 +1,50 @@
+/*
+ * 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. 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.
+ */
+
+#ifndef ORCACONF_H
+#define ORCACONF_H
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include
+#include "jni.h"
+
+int OrcaAnnounce(JNIEnv *env, jstring str, jint priority);
+jobject OrcaGetConf(JNIEnv *env);
+void OrcaSetSpeechConf(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetLanguage(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetOutputModule(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetPunctuation(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetSynthesisVoice(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVoiceRate(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVoicePitch(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVolume(JNIEnv *env, SPDConnection *connection, jobject conf);
+int OrcaGetEnableSpeech(JNIEnv *env, jobject conf);
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#endif //ORCACONF_H
+
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/Application.java b/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
index 11c34ff7c5cf..f0c9af25654b 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
@@ -54,6 +54,7 @@
import sun.awt.AWTAccessor;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CPlatformWindow;
+import sun.util.logging.PlatformLogger;
/**
* The {@code Application} class allows you to integrate your Java application with the native Mac OS X environment.
@@ -74,6 +75,8 @@
* @since 1.4
*/
public class Application {
+ private static final PlatformLogger focusRequestLog = PlatformLogger.getLogger("jb.focus.requests");
+
private static native void nativeInitializeApplicationDelegate();
static Application sApplication = null;
@@ -287,6 +290,9 @@ public void disableSuddenTermination() {
* @since Java for Mac OS X 10.5 Update 6 - 1.6, 1.5
*/
public void requestForeground(final boolean allWindows) {
+ if (focusRequestLog.isLoggable(PlatformLogger.Level.FINE)) {
+ focusRequestLog.fine("requestForeground(" + (allWindows ? "allWindows" : "") + ")", new Throwable());
+ }
_AppMiscHandlers.requestActivation(allWindows);
}
@@ -415,4 +421,19 @@ public void requestToggleFullScreen(final Window window) {
((CPlatformWindow)platformWindow).toggleFullScreen();
}
+ public void requestEnterFullScreen(final Window window) {
+ final Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (!(peer instanceof LWWindowPeer)) return;
+ Object platformWindow = ((LWWindowPeer) peer).getPlatformWindow();
+ if (!(platformWindow instanceof CPlatformWindow)) return;
+ ((CPlatformWindow)platformWindow).enterFullScreenMode();
+ }
+
+ public void requestLeaveFullScreen(final Window window) {
+ final Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (!(peer instanceof LWWindowPeer)) return;
+ Object platformWindow = ((LWWindowPeer) peer).getPlatformWindow();
+ if (!(platformWindow instanceof CPlatformWindow)) return;
+ ((CPlatformWindow)platformWindow).exitFullScreenMode();
+ }
}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
index c1d36d8add2b..6f2e7fc764e0 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
@@ -67,8 +67,10 @@ static void removeFullScreenListenerFrom(final RootPaneContainer window, final F
}
static FullScreenHandler getHandlerFor(final RootPaneContainer window) {
- final Object value = window.getRootPane().getClientProperty(CLIENT_PROPERTY);
- if (value instanceof FullScreenHandler) return (FullScreenHandler)value;
+ if (window != null && window.getRootPane() != null) {
+ final Object value = window.getRootPane().getClientProperty(CLIENT_PROPERTY);
+ if (value instanceof FullScreenHandler) return (FullScreenHandler)value;
+ }
return null;
}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
index 248ae55f6d74..4ebd08b9cca0 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
@@ -27,6 +27,7 @@
import java.awt.EventQueue;
import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
import java.awt.desktop.AboutEvent;
import java.awt.desktop.AboutHandler;
import java.awt.desktop.AppForegroundEvent;
@@ -65,8 +66,12 @@
import java.util.Map;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
+import sun.java2d.SunGraphicsEnvironment;
+import sun.util.logging.PlatformLogger;
class _AppEventHandler {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(_AppEventHandler.class.getName());
+
private static final int NOTIFY_ABOUT = 1;
private static final int NOTIFY_PREFS = 2;
private static final int NOTIFY_OPEN_APP = 3;
@@ -83,6 +88,7 @@ class _AppEventHandler {
private static final int NOTIFY_SCREEN_WAKE = 14;
private static final int NOTIFY_SYSTEM_SLEEP = 15;
private static final int NOTIFY_SYSTEM_WAKE = 16;
+ private static final int NOTIFY_SCREEN_CHANGE_PARAMETERS = 17;
private static final int REGISTER_USER_SESSION = 1;
private static final int REGISTER_SCREEN_SLEEP = 2;
@@ -264,6 +270,16 @@ private static void handleNativeNotification(final int code) {
case NOTIFY_SYSTEM_WAKE:
instance.systemSleepDispatcher.dispatch(new _NativeEvent(Boolean.FALSE));
break;
+ case NOTIFY_SCREEN_CHANGE_PARAMETERS:
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("NOTIFY_SCREEN_CHANGE_PARAMETERS");
+ }
+ if (AppContext.getAppContext() != null) {
+ EventQueue.invokeLater(
+ () -> ((SunGraphicsEnvironment) GraphicsEnvironment.
+ getLocalGraphicsEnvironment()).displayParametersChanged());
+ }
+ break;
default:
System.err.println("EAWT unknown native notification: " + code);
break;
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
index 4514da90ca90..e3813cf9dbdd 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
@@ -43,6 +43,7 @@ final class GestureHandler {
@Native static final int ROTATE = 2;
@Native static final int MAGNIFY = 3;
@Native static final int SWIPE = 4;
+ @Native static final int PRESSURE = 5;
// installs a private instance of GestureHandler, if necessary
static void addGestureListenerTo(final JComponent component, final GestureListener listener) {
@@ -97,6 +98,9 @@ public void run() {
case SWIPE:
firstNotifier.recursivelyHandleSwipe(a, b, new SwipeEvent());
return;
+ case PRESSURE:
+ firstNotifier.recursivelyHandlePressure(new PressureEvent(a,b));
+ return;
}
}
});
@@ -107,6 +111,7 @@ public void run() {
final List rotaters = new LinkedList();
final List magnifiers = new LinkedList();
final List swipers = new LinkedList();
+ final List pressures = new LinkedList();
GestureHandler() { }
@@ -115,6 +120,7 @@ void addListener(final GestureListener listener) {
if (listener instanceof RotationListener) rotaters.add((RotationListener)listener);
if (listener instanceof MagnificationListener) magnifiers.add((MagnificationListener)listener);
if (listener instanceof SwipeListener) swipers.add((SwipeListener)listener);
+ if (listener instanceof PressureListener) pressures.add((PressureListener)listener);
}
void removeListener(final GestureListener listener) {
@@ -169,6 +175,16 @@ void recursivelyHandleMagnify(final MagnificationEvent e) {
if (next != null) next.recursivelyHandleMagnify(e);
}
+ void recursivelyHandlePressure(final PressureEvent e) {
+ for (final PressureListener listener : handler.pressures) {
+ listener.pressure(e);
+ if (e.isConsumed()) return;
+ }
+
+ final PerComponentNotifier next = getNextNotifierForComponent(component.getParent());
+ if (next != null) next.recursivelyHandlePressure(e);
+ }
+
void recursivelyHandleSwipe(final double x, final double y, final SwipeEvent e) {
for (final SwipeListener listener : handler.swipers) {
if (x < 0) listener.swipedLeft(e);
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java
new file mode 100644
index 000000000000..f3140cc3dfb0
--- /dev/null
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.apple.eawt.event;
+
+/**
+ * Event indicating a swipe was performed by the user.
+ *
+ * @see PressureListener
+ *
+ * @since Java for Mac OS X 10.10 Update 3, JDK 8
+ */
+public class PressureEvent extends GestureEvent {
+
+ public double getPressure() {
+ return pressure;
+ }
+
+ public double getStage() {
+ return stage;
+ }
+
+ private double pressure;
+ private double stage;
+
+ PressureEvent(double pressure, double stage) {
+ this.pressure = pressure;
+ this.stage = stage;
+ }
+}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java
new file mode 100644
index 000000000000..de6a646b0bb7
--- /dev/null
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.apple.eawt.event;
+
+/**
+ * Listener interface for receiving pressure events.
+ *
+ * @see PressureEvent
+ * @see GestureUtilities
+ *
+ * @since Java for Mac OS X 10.5 Update 7, Java for Mac OS X 10.6 Update 2
+ */
+public interface PressureListener extends GestureListener {
+ public void pressure(final PressureEvent e);
+}
diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
index 3c1b147d1eab..6e325187813a 100644
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
@@ -62,6 +62,7 @@ public void install(final JTextComponent c) {
public void deinstall(final JTextComponent c) {
c.removePropertyChangeListener(this);
super.deinstall(c);
+ mFocused = false;
}
@Override
diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
index 2d6d965134c1..ad9f59c6b6d9 100644
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
@@ -511,4 +511,23 @@ public static Color getSelectionInactiveForegroundColorUIResource() {
public static Color getSelectedControlColorUIResource() {
return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.SELECTED_CONTROL_TEXT_COLOR));
}
+
+ private static class EmptyImage {
+ static final BufferedImage INSTANCE;
+ static {
+ INSTANCE = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = INSTANCE.createGraphics();
+ g.setColor(new Color(0, 0, 0, 0));
+ g.fillRect(0, 0, 16, 16);
+ g.dispose();
+ }
+ }
+
+ // [tav] a workaround for JBR-1492
+ private static Image checkValidOrStub(Image image) {
+ if (image == null || image.getWidth(null) <= 0 || image.getHeight(null) <= 0) {
+ return EmptyImage.INSTANCE;
+ }
+ return image;
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
index bb44ca0f26d5..3a68566e452d 100644
--- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
+++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
@@ -40,12 +40,14 @@
import sun.java2d.MacOSFlags;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
+import sun.util.logging.PlatformLogger;
import static java.awt.peer.ComponentPeer.SET_BOUNDS;
public final class CGraphicsDevice extends GraphicsDevice
- implements DisplayChangedListener {
+ implements DisplayChangedListener, DisplayParametersChangedListener {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(CGraphicsDevice.class.getName());
/**
* CoreGraphics display ID. This identifier can become non-valid at any time
* therefore methods, which is using this id should be ready to it.
@@ -53,12 +55,12 @@ public final class CGraphicsDevice extends GraphicsDevice
private volatile int displayID;
private volatile double xResolution;
private volatile double yResolution;
+ private volatile boolean isMirroring;
private volatile Rectangle bounds;
private volatile int scale;
+ private volatile Insets screenInsets;
private GraphicsConfiguration config;
- private static boolean metalPipelineEnabled = false;
- private static boolean oglPipelineEnabled = false;
// Save/restore DisplayMode for the Full Screen mode
@@ -68,70 +70,34 @@ public final class CGraphicsDevice extends GraphicsDevice
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
this.initialMode = getDisplayMode();
+ StringBuilder errorMessage = new StringBuilder();
- if (MacOSFlags.isMetalEnabled()) {
- // Try to create MTLGraphicsConfig, if it fails,
- // try to create CGLGraphicsConfig as a fallback
- this.config = MTLGraphicsConfig.getConfig(this, displayID);
-
- if (this.config != null) {
- metalPipelineEnabled = true;
- } else {
- // Try falling back to OpenGL pipeline
- if (MacOSFlags.isMetalVerbose()) {
- System.out.println("Metal rendering pipeline" +
- " initialization failed,using OpenGL" +
- " rendering pipeline");
- }
-
- this.config = CGLGraphicsConfig.getConfig(this);
+ this.config = CGraphicsEnvironment.usingMetalPipeline() ?
+ MTLGraphicsConfig.getConfig(this, displayID, errorMessage) :
+ CGLGraphicsConfig.getConfig(this);
- if (this.config != null) {
- oglPipelineEnabled = true;
- }
+ if (this.config == null) {
+ if (MacOSFlags.isMetalVerbose() || MacOSFlags.isOGLVerbose()) {
+ System.out.println(MacOSFlags.getRenderPipelineName() +
+ " rendering pipeline initialization failed");
}
+ throw new IllegalStateException("Error - unable to initialize " +
+ MacOSFlags.getRenderPipelineName());
} else {
- // Try to create CGLGraphicsConfig, if it fails,
- // try to create MTLGraphicsConfig as a fallback
- this.config = CGLGraphicsConfig.getConfig(this);
-
- if (this.config != null) {
- oglPipelineEnabled = true;
- } else {
- // Try falling back to Metal pipeline
- if (MacOSFlags.isOGLVerbose()) {
- System.out.println("OpenGL rendering pipeline" +
- " initialization failed,using Metal" +
- " rendering pipeline");
- }
-
- this.config = MTLGraphicsConfig.getConfig(this, displayID);
-
- if (this.config != null) {
- metalPipelineEnabled = true;
- }
+ if (MacOSFlags.isMetalVerbose() || MacOSFlags.isOGLVerbose()) {
+ System.out.println(MacOSFlags.getRenderPipelineName() +
+ " pipeline enabled on screen " + displayID);
}
}
- if (!metalPipelineEnabled && !oglPipelineEnabled) {
- // This indicates fallback to other rendering pipeline also failed.
- // Should never reach here
- throw new InternalError("Error - unable to initialize any" +
- " rendering pipeline.");
- }
-
- if (metalPipelineEnabled && MacOSFlags.isMetalVerbose()) {
- System.out.println("Metal pipeline enabled on screen " + displayID);
- } else if (oglPipelineEnabled && MacOSFlags.isOGLVerbose()) {
- System.out.println("OpenGL pipeline enabled on screen " + displayID);
- }
-
- // initializes default device state, might be redundant step since we
- // call "displayChanged()" later anyway, but we do not want to leave the
- // device in an inconsistent state after construction
+ // [JBR] we don't call displayChanged after creating a device, so call it here.
displayChanged();
}
+ int getDisplayID() {
+ return displayID;
+ }
+
/**
* Return a list of all configurations.
*/
@@ -175,18 +141,16 @@ public double getYResolution() {
return yResolution;
}
+ public boolean isMirroring() {
+ return isMirroring;
+ }
+
Rectangle getBounds() {
return bounds.getBounds();
}
public Insets getScreenInsets() {
- // the insets are queried synchronously and are not cached
- // since there are no Quartz or Cocoa means to receive notifications
- // on insets changes (e.g. when the Dock is resized):
- // the existing CGDisplayReconfigurationCallBack is not notified
- // as well as the NSApplicationDidChangeScreenParametersNotification
- // is fired on the Dock location changes only
- return nativeGetScreenInsets(displayID);
+ return screenInsets;
}
public int getScaleFactor() {
@@ -208,12 +172,40 @@ public void invalidate(CGraphicsDevice device) {
public void displayChanged() {
xResolution = nativeGetXResolution(displayID);
yResolution = nativeGetYResolution(displayID);
+ isMirroring = nativeIsMirroring(displayID);
bounds = nativeGetBounds(displayID).getBounds(); //does integer rounding
+ screenInsets = nativeGetScreenInsets(displayID);
initScaleFactor();
resizeFSWindow(getFullScreenWindow(), bounds);
//TODO configs?
}
+ /**
+ * @return false if display parameters were changed, so we need to recreate the device.
+ */
+ boolean updateDevice() {
+ int s = scale;
+ double xr = xResolution, yr = yResolution;
+ boolean m = isMirroring;
+ var b = bounds;
+ displayChanged();
+ return s == scale && xr == xResolution && yr == yResolution && m == isMirroring && b.equals(bounds);
+ }
+
+ public void displayParametersChanged() {
+ Insets newScreenInsets = nativeGetScreenInsets(displayID);
+ if (!newScreenInsets.equals(screenInsets)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Screen insets for display(" + displayID + ") changed " +
+ "[top=" + screenInsets.top + ",left=" + screenInsets.left +
+ ",bottom=" + screenInsets.bottom + ",right=" + screenInsets.right +
+ "]->[top=" + newScreenInsets.top + ",left=" + newScreenInsets.left +
+ ",bottom=" + newScreenInsets.bottom + ",right=" + newScreenInsets.right +
+ "]");
+ }
+ screenInsets = newScreenInsets;
+ }
+ }
@Override
public void paletteChanged() {
// devices do not need to react to this event.
@@ -360,11 +352,8 @@ public DisplayMode[] getDisplayModes() {
}
}
- public static boolean usingMetalPipeline() {
- return metalPipelineEnabled;
- }
-
private void initScaleFactor() {
+ int _scale = scale;
if (SunGraphicsEnvironment.isUIScaleEnabled()) {
double debugScale = SunGraphicsEnvironment.getDebugScale();
scale = (int) (debugScale >= 1
@@ -373,6 +362,9 @@ private void initScaleFactor() {
} else {
scale = 1;
}
+ if (_scale != scale && logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("current scale = " + _scale + ", new scale = " + scale + " (" + this + ")");
+ }
}
private static native double nativeGetScaleFactor(int displayID);
@@ -389,6 +381,8 @@ private void initScaleFactor() {
private static native double nativeGetYResolution(int displayID);
+ private static native boolean nativeIsMirroring(int displayID);
+
private static native Insets nativeGetScreenInsets(int displayID);
private static native Rectangle2D nativeGetBounds(int displayID);
diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
index fe4431e30912..9a2096ec21c6 100644
--- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
+++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
@@ -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
@@ -30,16 +30,22 @@
import java.awt.GraphicsDevice;
import java.awt.HeadlessException;
import java.awt.Toolkit;
+import java.io.File;
+import java.lang.annotation.Native;
import java.lang.ref.WeakReference;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import sun.java2d.MacOSFlags;
import sun.java2d.MacosxSurfaceManagerFactory;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
+import sun.util.logging.PlatformLogger;
/**
* This is an implementation of a GraphicsEnvironment object for the default
@@ -51,6 +57,14 @@
*/
public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
+ private static final PlatformLogger logger =
+ PlatformLogger.getLogger(CGraphicsEnvironment.class.getName());
+
+ @Native private final static int MTL_SUPPORTED = 0;
+ @Native private final static int MTL_NO_DEVICE = 1;
+ @Native private final static int MTL_NO_SHADER_LIB = 2;
+ @Native private final static int MTL_ERROR = 3;
+
/**
* Fetch an array of all valid CoreGraphics display identifiers.
*/
@@ -67,9 +81,35 @@ public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
*/
public static void init() { }
+ @SuppressWarnings("removal")
+ private static final String mtlShadersLib = AccessController.doPrivileged(
+ (PrivilegedAction) () ->
+ System.getProperty("java.home", "") + File.separator +
+ "lib" + File.separator + "shaders.metallib");
+
+ private static native int initMetal(String shaderLib);
+
static {
// Load libraries and initialize the Toolkit.
Toolkit.getDefaultToolkit();
+ metalPipelineEnabled = false;
+ if (MacOSFlags.isMetalEnabled()) {
+ int res = initMetal(mtlShadersLib);
+ if (res != MTL_SUPPORTED) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Cannot initialize Metal: " +
+ switch (res) {
+ case MTL_ERROR -> "Unexpected error.";
+ case MTL_NO_DEVICE -> "No MTLDevice.";
+ case MTL_NO_SHADER_LIB -> "No Metal shader library.";
+ default -> "Unexpected error (" + res + ").";
+ });
+ }
+ } else {
+ metalPipelineEnabled = true;
+ }
+ }
+
// Install the correct surface manager factory.
SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory());
}
@@ -88,6 +128,12 @@ public static void init() { }
*/
private native void deregisterDisplayReconfiguration(long context);
+ private static boolean metalPipelineEnabled;
+
+ public static boolean usingMetalPipeline() {
+ return metalPipelineEnabled;
+ }
+
/** Available CoreGraphics displays. */
private final Map devices = new HashMap<>(5);
/**
@@ -113,6 +159,12 @@ public CGraphicsEnvironment() {
/* Populate the device table */
rebuildDevices();
+ if (LogDisplay.ENABLED) {
+ for (CGraphicsDevice gd : devices.values()) {
+ LogDisplay.ADDED.log(gd.getDisplayID(), gd.getBounds(), gd.getScaleFactor());
+ }
+ }
+
/* Register our display reconfiguration listener */
displayReconfigContext = registerDisplayReconfiguration();
if (displayReconfigContext == 0L) {
@@ -120,12 +172,17 @@ public CGraphicsEnvironment() {
}
}
+ public static String getMtlShadersLibPath() {
+ return mtlShadersLib;
+ }
+
/**
* Updates the list of devices and notify listeners.
*/
private void rebuildDevices() {
initDevices();
- displayChanged();
+ // Do not notify devices, this was already done in initDevices.
+ displayChanger.notifyListeners();
}
/**
@@ -134,13 +191,25 @@ private void rebuildDevices() {
* @param displayId CoreGraphics displayId
* @param removed true if displayId was removed, false otherwise.
*/
- void _displayReconfiguration(int displayId, boolean removed) {
+ void _displayReconfiguration(int displayId, int flags) {
+ // See CGDisplayChangeSummaryFlags
+ LogDisplay log = !LogDisplay.ENABLED ? null :
+ (flags & (1 << 4)) != 0 ? LogDisplay.ADDED :
+ (flags & (1 << 5)) != 0 ? LogDisplay.REMOVED : LogDisplay.CHANGED;
+ if (log == LogDisplay.REMOVED) {
+ CGraphicsDevice gd = devices.get(displayId);
+ log.log(displayId, gd != null ? gd.getBounds() : "UNKNOWN", gd != null ? gd.getScaleFactor() : Double.NaN);
+ }
// we ignore the passed parameters and check removed devices ourself
// Note that it is possible that this callback is called when the
// monitors are not added nor removed, but when the video card is
// switched to/from the discrete video card, so we should try to map the
// old to the new devices.
rebuildDevices();
+ if (log != null && log != LogDisplay.REMOVED) {
+ CGraphicsDevice gd = devices.get(displayId);
+ log.log(displayId, gd != null ? gd.getBounds() : "UNKNOWN", gd != null ? gd.getScaleFactor() : Double.NaN);
+ }
}
@Override
@@ -166,7 +235,14 @@ private synchronized void initDevices() {
// So, we initialize the main display first, then retrieve actual list
// of displays, and then recheck the main display again.
if (!old.containsKey(mainDisplayID)) {
- old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID));
+ try {
+ old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID));
+ } catch (IllegalStateException e) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to initialize graphics device for displayID=" +
+ mainDisplayID + " : " + e);
+ }
+ }
}
int[] displayIDs = getDisplayIDs();
@@ -175,8 +251,17 @@ private synchronized void initDevices() {
displayIDs = new int[]{mainDisplayID};
}
for (int id : displayIDs) {
- devices.put(id, old.containsKey(id) ? old.remove(id)
- : new CGraphicsDevice(id));
+ old.compute(id, (i, d) -> {
+ if (d != null && d.updateDevice()) {
+ // Device didn't change -> reuse
+ devices.put(i, d);
+ return null;
+ } else {
+ // Device changed -> create new
+ devices.put(i, new CGraphicsDevice(i));
+ return d;
+ }
+ });
}
// fetch the main display again, the old value might be outdated
mainDisplayID = getMainDisplayID();
@@ -212,9 +297,12 @@ private synchronized void initDevices() {
}
private CGraphicsDevice getSimilarDevice(CGraphicsDevice old) {
+ CGraphicsDevice sameId = devices.get(old.getDisplayID());
+ if (sameId != null) {
+ return sameId;
+ }
for (CGraphicsDevice device : devices.values()) {
if (device.getBounds().equals(old.getBounds())) {
- // for now we will use the bounds only
return device;
}
}
@@ -261,12 +349,15 @@ public Font[] getAllFonts() {
int numLogical = sLogicalFonts.length;
int numOtherFonts = superFonts.length;
- newFonts = new Font[numOtherFonts + numLogical];
- System.arraycopy(superFonts,0,newFonts,numLogical,numOtherFonts);
+ newFonts = new Font[numOtherFonts + numLogical * 4];
+ System.arraycopy(superFonts, 0, newFonts, numLogical * 4, numOtherFonts);
for (int i = 0; i < numLogical; i++)
{
- newFonts[i] = new Font(sLogicalFonts[i], Font.PLAIN, 1);
+ for (int j = 0; j < 4; j++)
+ {
+ newFonts[i * 4 + j] = new Font(sLogicalFonts[i], j, 1);
+ }
}
return newFonts;
}
diff --git a/src/java.desktop/macosx/classes/sun/awt/PlatformGraphicsInfo.java b/src/java.desktop/macosx/classes/sun/awt/PlatformGraphicsInfo.java
index 728ee39a092d..45d1ccf01910 100644
--- a/src/java.desktop/macosx/classes/sun/awt/PlatformGraphicsInfo.java
+++ b/src/java.desktop/macosx/classes/sun/awt/PlatformGraphicsInfo.java
@@ -65,4 +65,7 @@ public static String getDefaultHeadlessMessage() {
"but this program performed an operation which requires it.";
}
+ public static String getSplashScreenLib() {
+ return "splashscreen";
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
index a919fcbbd889..a1de2c2690af 100644
--- a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
+++ b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
@@ -28,6 +28,8 @@
import java.util.HashMap;
public class CCharToGlyphMapper extends CharToGlyphMapper {
+ private static final int UNMAPPED_CHAR = Integer.MIN_VALUE;
+
private static native int countGlyphs(final long nativeFontPtr);
private Cache cache = new Cache();
@@ -77,7 +79,8 @@ public synchronized boolean charsToGlyphsNS(int count,
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
continue;
- } else if (FontUtilities.isComplexCharCode(code)) {
+ } else if (FontUtilities.isComplexCharCode(code) ||
+ CharToGlyphMapper.isVariationSelector(code)) {
return true;
} else if (code >= 0x10000) {
i += 1; // Empty glyph slot after surrogate
@@ -88,9 +91,24 @@ public synchronized boolean charsToGlyphsNS(int count,
return false;
}
+ public synchronized int charToVariationGlyph(int unicode, int variationSelector) {
+ if (variationSelector == 0) {
+ return charToGlyph(unicode);
+ }
+ final char[] unicodeArray = new char[4];
+ final int[] glyphArray = new int[4];
+
+ int size = Character.toChars(unicode, unicodeArray, 0);
+ size += Character.toChars(variationSelector, unicodeArray, size);
+
+ nativeCharsToGlyphs(fFont.getNativeFontPtr(), size, unicodeArray, glyphArray);
+
+ return glyphArray[0];
+ }
+
public synchronized int charToGlyph(char unicode) {
int glyph = cache.get(unicode);
- if (glyph != 0) return glyph;
+ if (glyph != 0) return glyph == UNMAPPED_CHAR ? 0 : glyph;
if (FontUtilities.isDefaultIgnorable(unicode)) {
glyph = INVISIBLE_GLYPH_ID;
@@ -101,7 +119,7 @@ public synchronized int charToGlyph(char unicode) {
glyph = glyphArray[0];
}
- cache.put(unicode, glyph);
+ cache.put(unicode, glyph == 0 ? UNMAPPED_CHAR : glyph);
return glyph;
}
@@ -248,7 +266,7 @@ public synchronized void get(int count, char[] indices, int[] values)
final int value = get(code);
if (value != 0 && value != -1) {
- values[i] = value;
+ values[i] = value == UNMAPPED_CHAR ? 0 : value;
if (code >= 0x10000) {
values[i+1] = INVISIBLE_GLYPH_ID;
i++;
@@ -296,9 +314,10 @@ public synchronized void get(int count, char[] indices, int[] values)
low - LO_SURROGATE_START + 0x10000;
}
}
- values[i] = glyphCodes[m];
- put(code, values[i]);
- if (code >= 0x10000) {
+ values[i] = glyphCodes[m];
+ int glyphCode = values[i];
+ put(code, glyphCode == 0 ? UNMAPPED_CHAR : glyphCode);
+ if (code >= 0x10000) {
m++;
values[i + 1] = INVISIBLE_GLYPH_ID;
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java b/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java
new file mode 100644
index 000000000000..a26c55c35b80
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2023 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.font;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class CCompositeFont extends CompositeFont {
+ private final List fallbackFonts = new ArrayList<>();
+
+ public CCompositeFont(CFont font) {
+ super(new PhysicalFont[]{font});
+ mapper = new CCompositeGlyphMapper(this);
+ }
+
+ @Override
+ protected void initSlotMask() {
+ // List of fallback fonts can grow dynamically for CCompositeFont.
+ // Adding a new font to fallback list may require more bits
+ // to represent slot index, which will cause slotShift to increment,
+ // which in turn will invalidate all glyph codes returned earlier.
+ // This will cause rendering garbage when fallback list grows
+ // while rendering a chunk of text, so here we just set slotShift
+ // to fixed 8 bits and hope we never exceed it (just like before).
+ slotShift = 8;
+ slotMask = 0xff;
+ }
+
+ @Override
+ public synchronized int getNumSlots() {
+ return super.getNumSlots();
+ }
+
+ @Override
+ public CFont getSlotFont(int slot) {
+ if (slot == 0) return (CFont) super.getSlotFont(0);
+ synchronized (this) {
+ return fallbackFonts.get(slot - 1);
+ }
+ }
+
+ @Override
+ synchronized FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
+ return super.getStrike(desc, copy);
+ }
+
+ @Override
+ protected synchronized int getValidatedGlyphCode(int glyphCode) {
+ return super.getValidatedGlyphCode(glyphCode);
+ }
+
+ @Override
+ public boolean hasSupplementaryChars() {
+ return false;
+ }
+
+ @Override
+ public boolean useAAForPtSize(int ptsize) {
+ return true;
+ }
+
+ public synchronized int findSlot(String fontName) {
+ for (int slot = 0; slot < numSlots; slot++) {
+ CFont slotFont = getSlotFont(slot);
+ if (fontName.equals(slotFont.getNativeFontName())) {
+ return slot;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized int addSlot(CFont font) {
+ int slot = findSlot(font.getNativeFontName());
+ if (slot >= 0) return slot;
+ fallbackFonts.add(font);
+ lastFontStrike = new SoftReference<>(null);
+ strikeCache.clear();
+ return numSlots++;
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java b/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
index 1897c7af6420..a48fb61f7b07 100644
--- a/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
+++ b/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
@@ -25,131 +25,68 @@
package sun.font;
-public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
-
- private CompositeFont font;
- private CharToGlyphMapper[] slotMappers;
+import java.awt.*;
- public CCompositeGlyphMapper(CompositeFont compFont) {
+public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
+ public CCompositeGlyphMapper(CCompositeFont compFont) {
super(compFont);
- font = compFont;
- slotMappers = new CharToGlyphMapper[font.numSlots];
- missingGlyph = 0;
}
- private CharToGlyphMapper getSlotMapper(int slot) {
- CharToGlyphMapper mapper = slotMappers[slot];
- if (mapper == null) {
- mapper = font.getSlotFont(slot).getMapper();
- slotMappers[slot] = mapper;
+ @Override
+ public int charToVariationGlyph(int unicode, int variationSelector) {
+ if (variationSelector == 0) {
+ return charToGlyph(unicode);
+ } else {
+ int glyph = convertToGlyph(unicode, variationSelector);
+ if (glyph == missingGlyph) glyph = charToGlyph(unicode);
+ return glyph;
}
- return mapper;
}
- public boolean canDisplay(char ch) {
- int glyph = charToGlyph(ch);
- return glyph != missingGlyph;
+ @Override
+ protected int convertToGlyph(int unicode) {
+ int glyph = convertToGlyph(unicode, 0);
+ setCachedGlyphCode(unicode, glyph);
+ return glyph;
}
- private int convertToGlyph(int unicode) {
- for (int slot = 0; slot < font.numSlots; slot++) {
- CharToGlyphMapper mapper = getSlotMapper(slot);
- int glyphCode = mapper.charToGlyph(unicode);
- // The CFont Mappers will return a negative code
- // for fonts that will fill the glyph from fallbacks
- // - cascading font in OSX-speak. But we need to be
- // know here that only the codes > 0 are really present.
- if (glyphCode > 0) {
- glyphCode = compositeGlyphCode(slot, glyphCode);
- return glyphCode;
- }
+ @Override
+ protected int convertToGlyph(int unicode, int variationSelector) {
+ CCompositeFont compositeFont = (CCompositeFont) font;
+ CFont mainFont = (CFont) font.getSlotFont(0);
+ String[] fallbackFontInfo = new String[2];
+ int glyphCode = nativeCodePointToGlyph(mainFont.getNativeFontPtr(), unicode, variationSelector, fallbackFontInfo);
+ if (glyphCode == missingGlyph) {
+ return missingGlyph;
}
- return missingGlyph;
- }
-
- public int getNumGlyphs() {
- int numGlyphs = 0;
- for (int slot=0; slot<1 /*font.numSlots*/; slot++) {
- CharToGlyphMapper mapper = slotMappers[slot];
- if (mapper == null) {
- mapper = font.getSlotFont(slot).getMapper();
- slotMappers[slot] = mapper;
- }
- numGlyphs += mapper.getNumGlyphs();
+ String fallbackFontName = fallbackFontInfo[0];
+ String fallbackFontFamilyName = fallbackFontInfo[1];
+ if (fallbackFontName == null || fallbackFontFamilyName == null) {
+ return font.compositeGlyphCode(0, glyphCode);
}
- return numGlyphs;
- }
-
- public int charToGlyph(int unicode) {
- return convertToGlyph(unicode);
- }
-
- public int charToGlyph(char unicode) {
- return convertToGlyph(unicode);
- }
-
- public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
-
- for (int i=0; i= HI_SURROGATE_START &&
- code <= HI_SURROGATE_END && i < count - 1) {
- char low = unicodes[i + 1];
- if (low >= LO_SURROGATE_START &&
- low <= LO_SURROGATE_END) {
- code = (code - HI_SURROGATE_START) *
- 0x400 + low - LO_SURROGATE_START + 0x10000;
- glyphs[i + 1] = INVISIBLE_GLYPH_ID;
- }
+ int slot = compositeFont.findSlot(fallbackFontName);
+
+ if (slot < 0) {
+ Font2D fallbackFont = FontManagerFactory.getInstance().findFont2D(fallbackFontName,
+ Font.PLAIN, FontManager.NO_FALLBACK);
+ if (!(fallbackFont instanceof CFont) ||
+ !fallbackFontName.equals(((CFont) fallbackFont).getNativeFontName())) {
+ // Native font fallback mechanism can return "hidden" fonts - their names start with dot,
+ // and they are not returned in a list of fonts available in system, but they can still be used
+ // if requested explicitly.
+ fallbackFont = new CFont(fallbackFontName, fallbackFontFamilyName, null);
}
- glyphs[i] = convertToGlyph(code);
+ if (mainFont.isFakeItalic()) fallbackFont = ((CFont)fallbackFont).createItalicVariant(false);
- if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
- continue;
- }
- else if (FontUtilities.isComplexCharCode(code)) {
- return true;
- }
- else if (code >= 0x10000) {
- i += 1; // Empty glyph slot after surrogate
- continue;
- }
- }
-
- return false;
- }
-
- public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
- for (int i=0; i= HI_SURROGATE_START &&
- code <= HI_SURROGATE_END && i < count - 1) {
- char low = unicodes[i + 1];
-
- if (low >= LO_SURROGATE_START &&
- low <= LO_SURROGATE_END) {
- code = (code - HI_SURROGATE_START) *
- 0x400 + low - LO_SURROGATE_START + 0x10000;
-
- glyphs[i] = convertToGlyph(code);
- i += 1; // Empty glyph slot after surrogate
- glyphs[i] = INVISIBLE_GLYPH_ID;
- continue;
- }
- }
-
- glyphs[i] = convertToGlyph(code);
+ slot = compositeFont.addSlot((CFont) fallbackFont);
}
- }
- public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
- for (int i=0; i listOfString);
-
- private CompositeFont createCompositeFont() {
- ArrayList listOfString = new ArrayList();
- getCascadeList(nativeFontPtr, listOfString);
-
- // In some italic cases the standard Mac cascade list is missing Arabic.
- listOfString.add("GeezaPro");
- CFontManager fm = (CFontManager) FontManagerFactory.getInstance();
- int numFonts = 1 + listOfString.size();
- PhysicalFont[] fonts = new PhysicalFont[numFonts];
- fonts[0] = this;
- int idx = 1;
- if (FontUtilities.isLogging()) {
- FontUtilities.logInfo("Cascading list for " + this + " :");
- }
- for (String s : listOfString) {
- if (FontUtilities.isLogging()) {
- FontUtilities.logInfo("Fallback:" + s);
- }
- if (s.equals(".AppleSymbolsFB")) {
- // Don't know why we get the weird name above .. replace.
- s = "AppleSymbols";
- }
- Font2D f2d = fm.getOrCreateFallbackFont(s);
- if (f2d == null || f2d == this) {
- continue;
- }
- fonts[idx++] = (PhysicalFont)f2d;
- }
- if (idx < fonts.length) {
- PhysicalFont[] orig = fonts;
- fonts = new PhysicalFont[idx];
- System.arraycopy(orig, 0, fonts, 0, idx);
- }
- CompositeFont compFont = new CompositeFont(fonts);
- compFont.mapper = new CCompositeGlyphMapper(compFont);
- return compFont;
- }
-
private CompositeFont compFont;
public CompositeFont getCompositeFont2D() {
if (compFont == null) {
- compFont = createCompositeFont();
+ compFont = new CCompositeFont(this);
}
return compFont;
}
@@ -270,6 +230,19 @@ protected FontStrike createStrike(FontStrikeDesc desc) {
return new CStrike(this, desc);
}
+ boolean isFakeItalic() {
+ return isFakeItalic;
+ }
+
+ String getNativeFontName() {
+ return nativeFontName;
+ }
+
+ @Override
+ public String getTypographicSubfamilyName() {
+ return faceName == null ? super.getTypographicSubfamilyName() : faceName;
+ }
+
// sun.font.Font2D caches the last used strike,
// but does not check if the properties of the strike match the properties
// of the incoming java.awt.Font object (size, style, etc).
@@ -297,4 +270,8 @@ public String toString() {
", familyName: " + familyName + ", style: " + style +
" } aka: " + super.toString();
}
+
+ public Font2D createItalic() {
+ return this.createItalicVariant(true);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CFontManager.java b/src/java.desktop/macosx/classes/sun/font/CFontManager.java
index baa7643d8292..d0b96eec955a 100644
--- a/src/java.desktop/macosx/classes/sun/font/CFontManager.java
+++ b/src/java.desktop/macosx/classes/sun/font/CFontManager.java
@@ -27,25 +27,21 @@
import java.awt.*;
import java.io.File;
+import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
-import java.util.Map;
import java.util.TreeMap;
-import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
import javax.swing.plaf.FontUIResource;
import sun.awt.FontConfiguration;
import sun.awt.HeadlessToolkit;
-import sun.awt.util.ThreadGroupUtils;
import sun.lwawt.macosx.*;
public final class CFontManager extends SunFontManager {
private static Hashtable genericFonts = new Hashtable();
- private final Map fallbackFonts = new ConcurrentHashMap<>();
@Override
protected FontConfiguration createFontConfiguration() {
@@ -142,6 +138,19 @@ protected void addNativeFontFamilyNames(TreeMap familyNames, Loc
}
}
+ @Override
+ protected String getSystemFontVersion(TrueTypeFont bundledFont) {
+ String version = getNativeFontVersion(bundledFont.getPostscriptName());
+ return version != null ? TrueTypeFont.parseVersion(version) : "0";
+ }
+
+ @Override
+ protected void loadJREFonts(String[] fonts) {
+ for (String name : fonts) {
+ loadNativeDirFonts(name);
+ }
+ }
+
protected void registerFontsInDir(final String dirName, boolean useJavaRasterizer,
int fontRank, boolean defer, boolean resolveSymLinks) {
@@ -159,44 +168,14 @@ protected void registerFontsInDir(final String dirName, boolean useJavaRasterize
private native void loadNativeDirFonts(String fontPath);
private native void loadNativeFonts();
+ native String getNativeFontVersion(String psName);
- void registerFont(String fontName, String fontFamilyName) {
- final CFont font = new CFont(fontName, fontFamilyName);
-
+ void registerFont(String fontName, String fontFamilyName, String faceName) {
+ // Use different family for specific font faces
+ final CFont font = new CFont(fontName, jreFamilyMap.getOrDefault(fontName, fontFamilyName), faceName);
registerGenericFont(font);
}
- void registerItalicDerived() {
- FontFamily[] famArr = FontFamily.getAllFontFamilies();
- for (int i=0; i fontToFileMap, HashMap fontToFamilyNameMap,
HashMap> familyToFontListMap, Locale locale) {}
-
- Font2D getOrCreateFallbackFont(String fontName) {
- Font2D font2D = findFont2D(fontName, Font.PLAIN, FontManager.NO_FALLBACK);
- if (font2D != null || fontName.startsWith(".")) {
- return font2D;
- } else {
- // macOS doesn't list some system fonts in [NSFontManager availableFontFamilies] output,
- // so they are not registered in font manager as part of 'loadNativeFonts'.
- // These fonts are present in [NSFontManager availableFonts] output though,
- // and can be accessed in the same way as other system fonts.
- return fallbackFonts.computeIfAbsent(fontName, name -> new CFont(name, null));
- }
- }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java
index 323c6e86a724..ab2ad5d8b21c 100644
--- a/src/java.desktop/macosx/classes/sun/font/CStrike.java
+++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java
@@ -25,9 +25,15 @@
package sun.font;
+import sun.lwawt.macosx.concurrent.Dispatch;
+
import java.awt.Rectangle;
import java.awt.geom.*;
-import java.util.*;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import sun.lwawt.macosx.CThreading;
+import static sun.awt.SunHints.*;
+
public final class CStrike extends PhysicalStrike {
@@ -36,7 +42,9 @@ private static native long createNativeStrikePtr(long nativeFontPtr,
double[] glyphTx,
double[] invDevTxMatrix,
int aaHint,
- int fmHint);
+ int fmHint,
+ int subpixelResolutionX,
+ int subpixelResolutionY);
// Disposes the native strike
private static native void disposeNativeStrikePtr(long nativeStrikePtr);
@@ -60,6 +68,16 @@ private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
double x,
double y);
+ private static native void getNativeGlyphRenderData(long nativeStrikePtr,
+ int glyphCode,
+ double x,
+ double y,
+ GlyphRenderData result);
+
+ private static native void getNativeGlyphOutlineBounds(long nativeStrikePtr,
+ int glyphCode,
+ float [] rectData);
+
// returns the bounding rect for a glyph
private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
int glyphCode,
@@ -119,7 +137,9 @@ public long getNativeStrikePtr() {
}
nativeStrikePtr =
createNativeStrikePtr(nativeFont.getNativeFontPtr(),
- glyphTx, invDevTxMatrix, aaHint, fmHint);
+ glyphTx, invDevTxMatrix, aaHint, fmHint,
+ FontUtilities.subpixelResolution.width,
+ FontUtilities.subpixelResolution.height);
}
return nativeStrikePtr;
@@ -173,19 +193,9 @@ Point2D.Float getGlyphMetrics(final int glyphCode) {
}
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
- GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);
- Rectangle2D r2d = gp.getBounds2D();
- Rectangle2D.Float r2df;
- if (r2d instanceof Rectangle2D.Float) {
- r2df = (Rectangle2D.Float)r2d;
- } else {
- float x = (float)r2d.getX();
- float y = (float)r2d.getY();
- float w = (float)r2d.getWidth();
- float h = (float)r2d.getHeight();
- r2df = new Rectangle2D.Float(x, y, w, h);
- }
- return r2df;
+ final float [] rectData = new float[4];
+ getNativeGlyphOutlineBounds(getNativeStrikePtr(), glyphCode, rectData);
+ return new Rectangle2D.Float(rectData[0], rectData[1], rectData[2], rectData[3]);
}
// pt, result in device space
@@ -203,7 +213,15 @@ void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
return;
}
- result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);
+ boolean subpixel = desc.aaHint == INTVAL_TEXT_ANTIALIAS_ON &&
+ desc.fmHint == INTVAL_FRACTIONALMETRICS_ON;
+ float subpixelResolutionX = subpixel ? FontUtilities.subpixelResolution.width : 1;
+ float subpixelResolutionY = subpixel ? FontUtilities.subpixelResolution.height : 1;
+ // Before rendering, glyph positions are offset by 0.5 pixels, take into consideration
+ float x = ((int) (pt.x * subpixelResolutionX + 0.5f)) / subpixelResolutionX;
+ float y = ((int) (pt.y * subpixelResolutionY + 0.5f)) / subpixelResolutionY;
+
+ result.setRect(floatRect.x + x, floatRect.y + y, floatRect.width, floatRect.height);
}
private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {
@@ -214,9 +232,10 @@ GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y);
}
- // should implement, however not called though any path that is publicly exposed
- GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
- throw new Error("not implemented yet");
+ GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
+ GlyphRenderData result = new GlyphRenderData();
+ getNativeGlyphRenderData(getNativeStrikePtr(), glyphCode, x, y, result);
+ return result;
}
// called from the Sun2D renderer
@@ -355,7 +374,7 @@ private static class GlyphInfoCache extends CStrikeDisposer {
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
// rdar://problem/5204197
- private boolean disposed = false;
+ private final AtomicBoolean disposed = new AtomicBoolean(false);
private final long[] firstLayerCache;
private SparseBitShiftingTwoLayerArray secondLayerCache;
@@ -418,41 +437,50 @@ public synchronized void put(final int index, final long value) {
}
public synchronized void dispose() {
- // rdar://problem/5204197
- // Note that sun.font.Font2D.getStrike() actively disposes
- // cleared strikeRef. We need to check the disposed flag to
- // prevent double frees of native resources.
- if (disposed) {
- return;
- }
-
- super.dispose();
-
- // clean out the first array
- disposeLongArray(firstLayerCache);
-
- // clean out the two layer arrays
- if (secondLayerCache != null) {
- final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
- for (int i = 0; i < secondLayerLongArrayArray.length; i++) {
- final long[] longArray = secondLayerLongArrayArray[i];
- if (longArray != null) disposeLongArray(longArray);
- }
- }
+ final Runnable command = () -> {
+ // rdar://problem/5204197
+ // Note that sun.font.Font2D.getStrike() actively disposes
+ // cleared strikeRef. We need to check the disposed flag to
+ // prevent double frees of native resources.
+ if (disposed.compareAndSet(false, true)) {
+
+ super.dispose();
+
+ // clean out the first array
+ disposeLongArray(firstLayerCache);
+
+ // clean out the two layer arrays
+ if (secondLayerCache != null) {
+ final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
+ for (final long[] longArray : secondLayerLongArrayArray) {
+ if (longArray != null) disposeLongArray(longArray);
+ }
+ }
- // clean up everyone else
- if (generalCache != null) {
- for (long longValue : generalCache.values()) {
- if (longValue != -1 && longValue != 0) {
- removeGlyphInfoFromCache(longValue);
- StrikeCache.freeLongPointer(longValue);
+ // clean up everyone else
+ if (generalCache != null) {
+ for (long longValue : generalCache.values()) {
+ if (longValue != -1 && longValue != 0) {
+ removeGlyphInfoFromCache(longValue);
+ StrikeCache.freeLongPointer(longValue);
+ }
+ }
}
}
- }
-
- // rdar://problem/5204197
- // Finally, set the flag.
- disposed = true;
+ };
+
+ // Move disposal code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from Java2D
+ // disposal thread) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+ //
+ // If dispatch instance is not available, run the code on
+ // disposal thread as before
+
+ CThreading.executeOnAppKit(command);
}
private static void disposeLongArray(final long[] longArray) {
diff --git a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
index b0c8c788ec99..2e72b977626c 100644
--- a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
+++ b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
@@ -25,6 +25,11 @@
package sun.font;
+import sun.lwawt.macosx.concurrent.Dispatch;
+import sun.lwawt.macosx.CThreading;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
/*
* This keeps track of data that needs to be cleaned up once a
* strike is freed.
@@ -49,6 +54,7 @@
class CStrikeDisposer extends FontStrikeDisposer {
long pNativeScalerContext;
+ private final AtomicBoolean disposed = new AtomicBoolean(false);
public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
long pContext, int[] images)
@@ -76,12 +82,23 @@ public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc) {
}
public synchronized void dispose() {
- if (!disposed) {
- if (pNativeScalerContext != 0L) {
- freeNativeScalerContext(pNativeScalerContext);
+ final Runnable command = () -> {
+ if (disposed.compareAndSet(false, true)) {
+ if (pNativeScalerContext != 0L) {
+ freeNativeScalerContext(pNativeScalerContext);
+ }
+ super.dispose();
}
- super.dispose();
- }
+ };
+ // Move disposal code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from Java2D
+ // disposal thread) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+
+ CThreading.executeOnAppKit(command);
}
private native void freeNativeScalerContext(long pContext);
diff --git a/src/java.desktop/macosx/classes/sun/font/NativeFont.java b/src/java.desktop/macosx/classes/sun/font/NativeFont.java
index d3203da3980a..a24ab1221051 100644
--- a/src/java.desktop/macosx/classes/sun/font/NativeFont.java
+++ b/src/java.desktop/macosx/classes/sun/font/NativeFont.java
@@ -79,12 +79,6 @@ public GeneralPath getGlyphOutline(long pScalerContext,
return null;
}
- public GeneralPath getGlyphVectorOutline(long pScalerContext,
- int[] glyphs, int numGlyphs,
- float x, float y) {
- return null;
- }
-
long getGlyphImage(long pScalerContext, int glyphCode) {
return 0L;
@@ -104,4 +98,8 @@ Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
int glyphCode) {
return new Rectangle2D.Float(0f, 0f, 0f, 0f);
}
+
+ public SlotInfo getSlotInfoForGlyph(int glyphCode) {
+ return new SlotInfo(getDelegateFont());
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/NativeStrike.java b/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
index de5c3aca312d..3afaf2c150a3 100644
--- a/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
+++ b/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
@@ -79,7 +79,7 @@ GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
return null;
}
- GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
+ GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return null;
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java b/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
index f2fb23f0a610..2f086df31c60 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
@@ -40,6 +40,7 @@ public class MacOSFlags {
private static boolean oglEnabled;
private static boolean oglVerbose;
private static boolean metalEnabled;
+ private static boolean metalDisplaySyncEnabled;
private static boolean metalVerbose;
private enum PropertyState {ENABLED, DISABLED, UNSPECIFIED}
@@ -111,6 +112,9 @@ private static void initJavaFlags() {
metalEnabled = false;
}
+ metalDisplaySyncEnabled = metalEnabled &&
+ !"false".equals(System.getProperty("sun.java2d.metal.displaySync"));
+
oglVerbose = isBooleanPropTrueVerbose("sun.java2d.opengl");
metalVerbose = isBooleanPropTrueVerbose("sun.java2d.metal");
@@ -146,4 +150,12 @@ public static boolean isOGLEnabled() {
public static boolean isOGLVerbose() {
return oglVerbose;
}
+
+ public static boolean isMetalDisplaySyncEnabled() {
+ return metalDisplaySyncEnabled;
+ }
+
+ public static String getRenderPipelineName() {
+ return metalEnabled? "Metal" : oglEnabled ? "OpenGL" : "Unknown";
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java b/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
index 93fac3fb22a4..57ac05314617 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
@@ -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
@@ -25,9 +25,16 @@
package sun.java2d;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+
+import sun.awt.CGraphicsEnvironment;
import sun.awt.image.SunVolatileImage;
+import sun.awt.image.SurfaceManager;
+import sun.awt.image.TextureWrapperSurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
-import sun.awt.CGraphicsDevice;
+import sun.java2d.metal.MTLGraphicsConfig;
+import sun.java2d.metal.MTLSurfaceData;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
@@ -51,7 +58,18 @@ public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
- return CGraphicsDevice.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
+ return CGraphicsEnvironment.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
+
+ @Override
+ public SurfaceManager createTextureWrapperSurfaceManager(GraphicsConfiguration gc, Image image, long texture) {
+ SurfaceData sd;
+ if (gc instanceof MTLGraphicsConfig) {
+ sd = MTLSurfaceData.createData((MTLGraphicsConfig) gc, image, texture);
+ } else {
+ throw new UnsupportedOperationException("Unsupported GraphicsConfiguration");
+ }
+ return new TextureWrapperSurfaceManager(sd);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLContext.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLContext.java
index 516fe33fd4e0..3004246a441c 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLContext.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLContext.java
@@ -32,6 +32,7 @@
import java.lang.annotation.Native;
+import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.SET_SCRATCH_SURFACE;
/**
@@ -78,6 +79,20 @@ public static void setScratchSurface(long pConfigInfo) {
buf.putLong(pConfigInfo);
}
+ public static void disposeSurface(long pData) {
+ // assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
+
+ // invalidate the current context
+ currentContext = null;
+
+ // set the scratch context
+ MTLRenderQueue rq = MTLRenderQueue.getInstance();
+ RenderBuffer buf = rq.getBuffer();
+ rq.ensureCapacityAndAlignment(12, 4);
+ buf.putInt(DISPOSE_SURFACE);
+ buf.putLong(pData);
+ }
+
public static class MTLContextCaps extends ContextCapabilities {
/** Indicates that the context is doublebuffered. */
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
index 82adac72c03b..e2773bebcdc7 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
@@ -27,6 +27,7 @@
import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.java2d.Disposer;
@@ -57,7 +58,6 @@
import java.awt.image.DirectColorModel;
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
-import java.io.File;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
@@ -71,12 +71,6 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
{
private static ImageCapabilities imageCaps = new MTLImageCaps();
-
- private static final String mtlShadersLib =
- System.getProperty("java.home", "") + File.separator +
- "lib" + File.separator + "shaders.metallib";
-
-
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities mtlCaps;
@@ -84,7 +78,6 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
private final Object disposerReferent = new Object();
private final int maxTextureSize;
- private static native boolean tryLoadMetalLibrary(int displayID, String shaderLib);
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
/**
@@ -116,18 +109,14 @@ public SurfaceData createManagedSurface(int w, int h, int transparency) {
}
public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
- int displayID)
+ int displayID, StringBuilder errorMessage)
{
- if (!tryLoadMetalLibrary(displayID, mtlShadersLib)) {
- return null;
- }
-
long cfginfo = 0;
int textureSize = 0;
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
- cfginfo = getMTLConfigInfo(displayID, mtlShadersLib);
+ cfginfo = getMTLConfigInfo(displayID, CGraphicsEnvironment.getMtlShadersLibPath());
if (cfginfo != 0L) {
textureSize = nativeGetMaxTextureSize();
// TODO : This clamping code is same as in OpenGL.
@@ -140,6 +129,7 @@ public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
rq.unlock();
}
if (cfginfo == 0) {
+ errorMessage.append(" Cannot create MTLConfigInfo.");
return null;
}
@@ -149,6 +139,7 @@ public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE |
CAPS_EXT_BIOP_SHADER | CAPS_EXT_GRAD_SHADER,
null);
+
return new MTLGraphicsConfig(device, cfginfo, textureSize, caps);
}
@@ -300,6 +291,11 @@ public void flip(final LWComponentPeer, ?> peer, final Image backBuffer,
}
}
+ @Override
+ public void flush(LWComponentPeer, ?> peer) {
+ MTLSurfaceData.flushBuffer(peer.getPlatformWindow().getLayerPtr());
+ }
+
private static class MTLBufferCaps extends BufferCapabilities {
public MTLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
index 890c55376c78..03610982f2cf 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
@@ -25,16 +25,23 @@
package sun.java2d.metal;
+import sun.awt.AWTAccessor;
import sun.java2d.NullSurfaceData;
import sun.java2d.SurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CFLayer;
+import sun.util.logging.PlatformLogger;
+
+import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
+import java.awt.Window;
+
public class MTLLayer extends CFLayer {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(MTLLayer.class.getName());
- private native long nativeCreateLayer();
+ private native long nativeCreateLayer(boolean perfCountersEnabled);
private static native void nativeSetScale(long layerPtr, double scale);
// Pass the insets to native code to make adjustments in blitTexture
@@ -48,11 +55,22 @@ public class MTLLayer extends CFLayer {
public MTLLayer(LWWindowPeer peer) {
super(0, true);
- setPtr(nativeCreateLayer());
+ Window target = (peer != null) ? peer.getTarget() : null;
+ boolean perfCountersEnabled = (target != null) && AWTAccessor.getWindowAccessor().countersEnabled(target);
+
+ setPtr(nativeCreateLayer(perfCountersEnabled));
this.peer = peer;
+
+ MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("device = " + (gc != null ? gc.getDevice() : "null"));
+ }
+ if (gc != null) {
+ setScale(gc.getDevice().getScaleFactor());
+ }
}
- public SurfaceData replaceSurfaceData() {
+ public SurfaceData replaceSurfaceData(int scale) {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
@@ -62,7 +80,10 @@ public SurfaceData replaceSurfaceData() {
// and blits the buffer to the layer surface (in display callback)
MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
surfaceData = gc.createSurfaceData(this);
- setScale(gc.getDevice().getScaleFactor());
+ if (scale <= 0) {
+ scale = gc.getDevice().getScaleFactor();
+ }
+ setScale(scale);
if (peer != null) {
Insets insets = peer.getInsets();
execute(ptr -> nativeSetInsets(ptr, insets.top, insets.left));
@@ -100,6 +121,10 @@ public void dispose() {
private void setScale(final int _scale) {
if (scale != _scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
+ logger.fine("current scale = " + scale + ", new scale = " + _scale + " (device = " + (gc != null ? gc.getDevice() : "null") + ")");
+ }
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
@@ -124,4 +149,22 @@ private void drawInMTLContext() {
rq.unlock();
}
}
+
+ private void countNewFrame() {
+ // Called from the native code when this layer has been presented on screen
+ Component target = peer.getTarget();
+ if (target instanceof Window window) {
+ AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
+ }
+ }
+
+ private void countDroppedFrame() {
+ // Called from the native code when an attempt was made to present this layer
+ // on screen, but that attempt was not successful. This can happen, for example,
+ // when those attempts are too frequent.
+ Component target = peer.getTarget();
+ if (target instanceof Window window) {
+ AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLRenderQueue.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLRenderQueue.java
index 85f9023eadab..d4c782edac46 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLRenderQueue.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLRenderQueue.java
@@ -123,8 +123,7 @@ public void flushNow() {
try {
flusher.flushNow();
} catch (Exception e) {
- System.err.println("exception in flushNow:");
- e.printStackTrace();
+ logger.severe("MTLRenderQueue.flushNow: exception occurred: ", e);
}
}
@@ -133,8 +132,7 @@ public void flushAndInvokeNow(Runnable r) {
try {
flusher.flushAndInvokeNow(r);
} catch (Exception e) {
- System.err.println("exception in flushAndInvokeNow:");
- e.printStackTrace();
+ logger.severe("MTLRenderQueue.flushAndInvokeNow: exception occurred: ", e);
}
}
@@ -178,6 +176,7 @@ public synchronized void flushNow() {
try {
wait();
} catch (InterruptedException e) {
+ logger.fine("QueueFlusher.flushNow: interrupted");
}
}
@@ -220,6 +219,7 @@ public synchronized void run() {
}
}
} catch (InterruptedException e) {
+ logger.fine("QueueFlusher.run: interrupted");
}
}
try {
@@ -231,11 +231,11 @@ public synchronized void run() {
if (task != null) {
task.run();
}
- } catch (Error e) {
- error = e;
- } catch (Exception x) {
- System.err.println("exception in QueueFlusher:");
- x.printStackTrace();
+ } catch (Error err) {
+ logger.severe("QueueFlusher.run: error occurred: ", err);
+ error = err;
+ } catch (Exception e) {
+ logger.severe("QueueFlusher.run: exception occurred: ", e);
} finally {
if (timedOut) {
unlock();
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
index 8dd2557743e4..09eec6be8eec 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
@@ -50,9 +50,11 @@
import java.awt.image.ColorModel;
import java.awt.image.Raster;
+import java.util.concurrent.atomic.AtomicBoolean;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
+import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_MULTITEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
@@ -199,6 +201,10 @@ public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,
type);
}
+ public static MTLTextureWrapperSurfaceData createData(MTLGraphicsConfig gc, Image image, long pTexture) {
+ return new MTLTextureWrapperSurfaceData(gc, image, pTexture);
+ }
+
@Override
public double getDefaultScaleX() {
return scale;
@@ -218,9 +224,11 @@ public Rectangle getBounds() {
protected native boolean initTexture(long pData, boolean isOpaque, int width, int height);
+ protected native boolean initWithTexture(long pData, boolean isOpaque, long texturePtr);
+
protected native boolean initRTexture(long pData, boolean isOpaque, int width, int height);
- protected native boolean initFlipBackbuffer(long pData);
+ protected native boolean initFlipBackbuffer(long pData, boolean isOpaque, int width, int height);
@Override
public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
@@ -246,7 +254,7 @@ private void initSurfaceNow(int width, int height) {
break;
case FLIP_BACKBUFFER:
- success = initFlipBackbuffer(getNativeOps());
+ success = initFlipBackbuffer(getNativeOps(), isOpaque, width, height);
break;
default:
@@ -270,6 +278,7 @@ protected void initSurface(final int width, final int height) {
switch (type) {
case TEXTURE:
case RT_TEXTURE:
+ case FLIP_BACKBUFFER:
// need to make sure the context is current before
// creating the texture
MTLContext.setScratchSurface(graphicsConfig);
@@ -554,7 +563,7 @@ public static class MTLLayerSurfaceData extends MTLSurfaceData {
private MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
int width, int height) {
- super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);
+ super(layer, gc, gc.getColorModel(), FLIP_BACKBUFFER, width, height);
this.layer = layer;
initSurface(this.width, this.height);
}
@@ -621,22 +630,97 @@ public Object getDestination() {
* the native Dispose() method from the Disposer thread when the
* Java-level MTLSurfaceData object is about to go away.
*/
- public static void dispose(long pData, MTLGraphicsConfig gc) {
+ public static void dispose(long pData) {
+ MTLRenderQueue rq = MTLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ MTLContext.disposeSurface(pData);
+ // this call is expected to complete synchronously, so flush now
+ rq.flushNow();
+ } finally {
+ rq.unlock();
+ }
+ }
+
+ static void flushBuffer(long layer) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
- // make sure we have a current context before
- // disposing the native resources (e.g. texture object)
- MTLContext.setScratchSurface(gc);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
- buf.putInt(DISPOSE_SURFACE);
- buf.putLong(pData);
-
- // this call is expected to complete synchronously, so flush now
+ buf.putInt(FLUSH_BUFFER);
+ buf.putLong(layer);
rq.flushNow();
} finally {
rq.unlock();
}
}
+
+ /**
+ * Loads native image raster into surface.
+ *
+ * @param pRaster native pointer image raster with 8-bit RGBA color components packed into integer pixels.
+ * Note: The color data in this image is considered to be premultiplied with alpha.
+ * @param width width of image in pixels
+ * @param height height of image in pixels
+ * @param pRects native pointer to array of "dirty" rects, each rect is a sequence of four 32-bit integers: x, y, width, heigth
+ * Note: can be null (then whole image used)
+ * @param rectsCount count of "dirty" rects (if 0 then whole image used)
+ */
+ @Override
+ protected void loadNativeRaster(long pRaster, int width, int height, long pRects, int rectsCount) {
+ MTLRenderQueue rq = MTLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ MTLContext.setScratchSurface(getMTLGraphicsConfig());
+ rq.flushAndInvokeNow(() -> loadNativeRasterWithRects(getNativeOps(), pRaster, width, height, pRects, rectsCount));
+ } finally {
+ rq.unlock();
+ }
+ markDirty();
+ }
+
+ private static native boolean loadNativeRasterWithRects(long sdops, long pRaster, int width, int height, long pRects, int rectsCount);
+
+ /**
+ * Surface data for an existing texture
+ */
+ public static final class MTLTextureWrapperSurfaceData extends MTLSurfaceData {
+ private final Image myImage;
+
+ private MTLTextureWrapperSurfaceData(MTLGraphicsConfig gc, Image image, long pTexture) throws IllegalArgumentException {
+ super(null, gc, ColorModel.getRGBdefault(), RT_TEXTURE, /*width=*/ 0, /*height=*/ 0);
+ myImage = image;
+
+ MTLRenderQueue rq = MTLRenderQueue.getInstance();
+ AtomicBoolean success = new AtomicBoolean(false);
+
+ rq.lock();
+ try {
+ MTLContext.setScratchSurface(gc);
+ rq.flushAndInvokeNow(() -> success.set(initWithTexture(getNativeOps(), false, pTexture)));
+ } finally {
+ rq.unlock();
+ }
+
+ if (!success.get()) {
+ throw new IllegalArgumentException("Failed to init the surface data");
+ }
+ }
+
+ @Override
+ public SurfaceData getReplacement() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public Object getDestination() {
+ return myImage;
+ }
+
+ @Override
+ public Rectangle getBounds() {
+ return getNativeBounds();
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
index ee9b0aec3a00..1f268d19ebab 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
@@ -26,6 +26,7 @@
package sun.java2d.opengl;
import java.awt.AWTException;
+import java.awt.AWTError;
import java.awt.BufferCapabilities;
import java.awt.Component;
import java.awt.Graphics;
@@ -61,6 +62,9 @@
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT;
import static sun.java2d.opengl.OGLSurfaceData.FBOBJECT;
import static sun.java2d.opengl.OGLSurfaceData.TEXTURE;
+import sun.lwawt.macosx.CThreading;
+import java.security.PrivilegedAction;
+import java.util.concurrent.Callable;
public final class CGLGraphicsConfig extends CGraphicsConfig
implements OGLGraphicsConfig
@@ -113,44 +117,63 @@ public SurfaceData createManagedSurface(int w, int h, int transparency) {
OGLSurfaceData.TEXTURE);
}
+ @SuppressWarnings("removal")
public static CGLGraphicsConfig getConfig(CGraphicsDevice device)
{
if (!cglAvailable) {
return null;
}
- long cfginfo = 0;
- int textureSize = 0;
- final String[] ids = new String[1];
- OGLRenderQueue rq = OGLRenderQueue.getInstance();
- rq.lock();
- try {
- // getCGLConfigInfo() creates and destroys temporary
- // surfaces/contexts, so we should first invalidate the current
- // Java-level context and flush the queue...
- OGLContext.invalidateCurrentContext();
- cfginfo = getCGLConfigInfo();
- if (cfginfo != 0L) {
- textureSize = nativeGetMaxTextureSize();
- // 7160609: GL still fails to create a square texture of this
- // size. Half should be safe enough.
- // Explicitly not support a texture more than 2^14, see 8010999.
- textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
- OGLContext.setScratchSurface(cfginfo);
- rq.flushAndInvokeNow(() -> {
- ids[0] = OGLContext.getOGLIdString();
- });
+ // Move CGLGraphicsConfig creation code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from EDT) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+
+ Callable command = () -> {
+ long cfginfo = 0;
+ int textureSize = 0;
+ final String[] ids = new String[1];
+ OGLRenderQueue rq = OGLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ // getCGLConfigInfo() creates and destroys temporary
+ // surfaces/contexts, so we should first invalidate the current
+ // Java-level context and flush the queue...
+ OGLContext.invalidateCurrentContext();
+ cfginfo = getCGLConfigInfo();
+ if (cfginfo != 0L) {
+ textureSize = nativeGetMaxTextureSize();
+ // 7160609: GL still fails to create a square texture of this
+ // size. Half should be safe enough.
+ // Explicitly not support a texture more than 2^14, see 8010999.
+ textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
+ OGLContext.setScratchSurface(cfginfo);
+ rq.flushAndInvokeNow(() -> {
+ ids[0] = OGLContext.getOGLIdString();
+ });
+ }
+ } finally {
+ rq.unlock();
+ }
+ if (cfginfo == 0) {
+ return null;
}
- } finally {
- rq.unlock();
- }
- if (cfginfo == 0) {
- return null;
- }
- int oglCaps = getOGLCapabilities(cfginfo);
- ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
- return new CGLGraphicsConfig(device, cfginfo, textureSize, caps);
+ int oglCaps = getOGLCapabilities(cfginfo);
+ ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
+ return new CGLGraphicsConfig(device, cfginfo, textureSize, caps);
+ };
+
+ return java.security.AccessController.doPrivileged(
+ (PrivilegedAction) () -> {
+ try {
+ return CThreading.executeOnAppKit(command);
+ } catch (Throwable throwable) {
+ throw new AWTError(throwable.getMessage());
+ }
+ });
}
public static boolean isCGLAvailable() {
@@ -319,6 +342,11 @@ public void flip(final LWComponentPeer, ?> peer, final Image backBuffer,
}
}
+ @Override
+ public void flush(LWComponentPeer, ?> peer) {
+
+ }
+
private static class CGLBufferCaps extends BufferCapabilities {
public CGLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
diff --git a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
index eb6629a0ae93..9f82e1b75055 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
@@ -31,8 +31,10 @@
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.lwawt.macosx.CFLayer;
+import sun.util.logging.PlatformLogger;
public class CGLLayer extends CFLayer {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(CGLLayer.class.getName());
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
@@ -46,9 +48,17 @@ public CGLLayer(LWWindowPeer peer) {
setPtr(nativeCreateLayer());
this.peer = peer;
+
+ CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("device = " + (gc != null ? gc.getDevice() : "null"));
+ }
+ if (gc != null) {
+ setScale(gc.getDevice().getScaleFactor());
+ }
}
- public SurfaceData replaceSurfaceData() {
+ public SurfaceData replaceSurfaceData(int scale) {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
@@ -57,8 +67,15 @@ public SurfaceData replaceSurfaceData() {
// the layer redirects all painting to the buffer's graphics
// and blits the buffer to the layer surface (in drawInCGLContext callback)
CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ if (gc == null) {
+ surfaceData = NullSurfaceData.theInstance;
+ return surfaceData;
+ }
surfaceData = gc.createSurfaceData(this);
- setScale(gc.getDevice().getScaleFactor());
+ if (scale <= 0) {
+ scale = gc.getDevice().getScaleFactor();
+ }
+ setScale(scale);
// the layer holds a reference to the buffer, which in
// turn has a reference back to this layer
if (surfaceData instanceof CGLSurfaceData) {
@@ -90,8 +107,12 @@ public void dispose() {
super.dispose();
}
- private void setScale(final int _scale) {
+ private void setScale(int _scale) {
if (scale != _scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ logger.fine("current scale = " + scale + ", new scale = " + _scale + " (device = " + (gc != null ? gc.getDevice() : "null") + ")");
+ }
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
@@ -107,7 +128,7 @@ private void drawInCGLContext() {
OGLRenderQueue rq = OGLRenderQueue.getInstance();
rq.lock();
try {
- execute(ptr -> blitTexture(ptr));
+ blitTexture(getPointer());
} finally {
rq.unlock();
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
index 27fca3d2c70e..c7a0813ffe84 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
@@ -66,12 +66,13 @@
import com.sun.java.swing.SwingUtilities3;
import sun.awt.AWTAccessor;
-import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.awt.PaintEventDispatcher;
import sun.awt.RepaintArea;
import sun.awt.SunToolkit;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
+import sun.java2d.MacOSFlags;
import sun.java2d.SunGraphics2D;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
@@ -917,7 +918,7 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
getTarget(), lightweightChild, temporary,
- focusedWindowChangeAllowed, time, cause);
+ focusedWindowChangeAllowed, time, cause, true);
switch (result) {
case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
return false;
@@ -942,21 +943,16 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
// however that is the shared code and this particular problem's reproducibility has
// platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
// current release. TODO: consider fixing it in the shared code.
- if (!focusedWindowChangeAllowed) {
- LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
- LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
-
- if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
- "decoratedPeer is inactive: " + decoratedPeer);
- }
- LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
- return false;
+ if (!focusedWindowChangeAllowed && !parentPeer.getPlatformWindow().isActive()) {
+ if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
+ focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
+ "parentPeer is inactive: " + parentPeer);
}
+ LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
+ return false;
}
- boolean res = parentPeer.requestWindowFocus(cause);
+ boolean res = !focusedWindowChangeAllowed || parentPeer.requestWindowFocus(cause);
// If parent window can be made focused and has been made focused (synchronously)
// then we can proceed with children, otherwise we retreat
if (!res || !parentWindow.isFocused()) {
@@ -971,9 +967,7 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
- getTarget(), temporary,
- focusedWindowChangeAllowed,
- time, cause, focusOwner);
+ getTarget(), true, cause, focusOwner);
case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
return true;
@@ -1295,6 +1289,10 @@ private void handleJavaPaintEvent() {
// if (!isLayouting && !paintPending) {
if (!isLayouting()) {
targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint());
+ if (MacOSFlags.isMetalEnabled() && !MacOSFlags.isMetalDisplaySyncEnabled()) {
+ SunToolkit.executeOnEventHandlerThread(getTarget(),
+ () -> getWindowPeerOrSelf().updateWindow());
+ }
}
}
@@ -1322,7 +1320,7 @@ public Point windowToLocal(int x, int y, LWWindowPeer wp) {
public Point windowToLocal(Point p, LWWindowPeer wp) {
LWComponentPeer, ?> cp = this;
- while (cp != wp) {
+ while (cp != null && cp != wp) {
Rectangle cpb = cp.getBounds();
p.x -= cpb.x;
p.y -= cpb.y;
@@ -1406,7 +1404,7 @@ protected final void paintPeer(final Graphics g) {
}
protected static final void flushOnscreenGraphics(){
- RenderQueue rq = CGraphicsDevice.usingMetalPipeline() ?
+ RenderQueue rq = CGraphicsEnvironment.usingMetalPipeline() ?
MTLRenderQueue.getInstance() : OGLRenderQueue.getInstance();
rq.lock();
try {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
index d556ea759952..e989bde846ab 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
@@ -89,6 +89,11 @@ void assertOperationSupported(int numBuffers, BufferCapabilities caps)
void flip(LWComponentPeer, ?> peer, Image backBuffer, int x1, int y1,
int x2, int y2, BufferCapabilities.FlipContents flipAction);
+ /**
+ * Performs the native flush operation for the given peer
+ */
+ void flush(LWComponentPeer, ?> peer);
+
/**
* Creates a new hidden-acceleration image of the given width and height
* that is associated with the target Component.
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
index a507b75a9048..1982f120cdca 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
@@ -25,8 +25,7 @@
package sun.lwawt;
-import java.awt.Point;
-import java.awt.Window;
+import java.awt.*;
import java.awt.peer.MouseInfoPeer;
@@ -41,8 +40,21 @@ public int fillPointWithCoords(Point point) {
Point cursorPos = cursorManager.getCursorPosition();
point.x = cursorPos.x;
point.y = cursorPos.y;
- // TODO: multiscreen
- return 0;
+ GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getScreenDevices();
+ int nearestScreen = 0, nearestScreenDistance = Integer.MAX_VALUE;
+ for (int i = 0; i < gds.length; i++) {
+ Rectangle bounds = gds[i].getDefaultConfiguration().getBounds();
+ int dx = Math.max(Math.min(point.x, bounds.x + bounds.width), bounds.x) - point.x;
+ int dy = Math.max(Math.min(point.y, bounds.y + bounds.height), bounds.y) - point.y;
+ int dist = dx*dx + dy*dy;
+ if (dist == 0) return i;
+ if (dist < nearestScreenDistance) {
+ nearestScreen = i;
+ nearestScreenDistance = dist;
+ }
+ }
+ return nearestScreen;
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
index 287de9e18013..63cda878a19e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
@@ -29,18 +29,23 @@
import java.awt.List;
import java.awt.datatransfer.*;
import java.awt.dnd.DropTarget;
-import java.awt.image.*;
import java.awt.peer.*;
import java.util.*;
import sun.awt.*;
+import sun.java2d.MacOSFlags;
import sun.print.*;
import sun.awt.util.ThreadGroupUtils;
import static sun.lwawt.LWWindowPeer.PeerType;
+import sun.util.logging.PlatformLogger;
+
+
public abstract class LWToolkit extends SunToolkit implements Runnable {
+ private static final PlatformLogger log = PlatformLogger.getLogger(LWToolkit.class.getName());
+
private static final int STATE_NONE = 0;
private static final int STATE_INIT = 1;
private static final int STATE_MESSAGELOOP = 2;
@@ -141,8 +146,8 @@ private void waitForRunState(int state) {
synchronized (this) {
wait();
}
- } catch (InterruptedException z) {
- // TODO: log
+ } catch (InterruptedException ie) {
+ log.fine("LWToolkit.run: interrupted");
break;
}
}
@@ -162,10 +167,8 @@ public final void run() {
break;
}
}
- } catch (Throwable t) {
- // TODO: log
- System.err.println("Exception on the toolkit thread");
- t.printStackTrace(System.err);
+ } catch (Throwable th) {
+ log.severe("LWToolkit.run: failure", th);
}
}
//XXX: if that's a secondary loop, jump back to the STATE_MESSAGELOOP
@@ -359,6 +362,11 @@ public final boolean isTaskbarSupported() {
return true;
}
+ @Override
+ public boolean needUpdateWindowAfterPaint() {
+ return MacOSFlags.isMetalEnabled() && !MacOSFlags.isMetalDisplaySyncEnabled();
+ }
+
@Override
public final KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() {
return LWKeyboardFocusManagerPeer.getInstance();
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
index 054e74dcef25..b39990e7af7f 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
@@ -39,7 +39,6 @@
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
-import java.awt.KeyboardFocusManager;
import java.awt.MenuBar;
import java.awt.Point;
import java.awt.Rectangle;
@@ -59,12 +58,12 @@
import java.awt.peer.WindowPeer;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import javax.swing.JComponent;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
-import sun.awt.AppContext;
import sun.awt.CGraphicsDevice;
import sun.awt.DisplayChangedListener;
import sun.awt.ExtendedKeyCodes;
@@ -79,6 +78,7 @@
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.pipe.Region;
+import sun.lwawt.macosx.CPlatformWindow;
import sun.util.logging.PlatformLogger;
public class LWWindowPeer
@@ -95,6 +95,7 @@ public enum PeerType {
}
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer");
+ private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.LWWindowPeer");
private final PlatformWindow platformWindow;
@@ -155,6 +156,8 @@ public enum PeerType {
*/
private LWWindowPeer blocker;
+ Jbr7481MouseEnteredExitedFix jbr7481MouseEnteredExitedFix = null;
+
public LWWindowPeer(Window target, PlatformComponent platformComponent,
PlatformWindow platformWindow, PeerType peerType)
{
@@ -281,23 +284,6 @@ protected void setVisibleImpl(final boolean visible) {
super.setVisibleImpl(visible);
// TODO: update graphicsConfig, see 4868278
platformWindow.setVisible(visible);
- if (isSimpleWindow()) {
- KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
- if (visible) {
- if (!getTarget().isAutoRequestFocus()) {
- return;
- } else {
- requestWindowFocus(FocusEvent.Cause.ACTIVATION);
- }
- // Focus the owner in case this window is focused.
- } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) {
- // Transfer focus to the owner.
- LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this);
- if (owner != null) {
- owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION);
- }
- }
- }
}
@Override
@@ -503,6 +489,7 @@ private void updateOpaque() {
@Override
public void updateWindow() {
+ getLWGC().flush(this);
}
public final boolean isTextured() {
@@ -701,7 +688,16 @@ public void notifyReshape(int x, int y, int w, int h) {
setBounds(x, y, w, h, SET_BOUNDS, false, false);
// Second, update the graphics config and surface data
- final boolean isNewDevice = updateGraphicsDevice();
+ GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ if (newGraphicsDevice == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to update graphics device: " +
+ "platform window graphics device is null");
+ }
+ return;
+ }
+ boolean isNewDevice = updateGraphicsDevice(newGraphicsDevice);
+
if (isNewDevice && !isMaximizedBoundsSet()) {
setPlatformMaximizedBounds(getDefaultMaximizedBounds());
}
@@ -717,7 +713,11 @@ public void notifyReshape(int x, int y, int w, int h) {
}
if (tResized || pResized || invalid || isNewDevice) {
handleResize(w, h, true);
- repaintPeer();
+ // Changing window size fires up the execution of the current method on AppKit thread and as result it could
+ // leads to race condition with paint event on EDT. Running repaintPeer on EDT eliminates such issue.
+ SunToolkit.executeOnEventHandlerThread(getTarget(), () -> {
+ repaintPeer();
+ });
}
}
@@ -778,6 +778,18 @@ public void notifyNCMouseDown() {
*/
@Override
public void notifyMouseEvent(int id, long when, int button,
+ int x, int y, int absX, int absY,
+ int modifiers, int clickCount, boolean popupTrigger,
+ byte[] bdata) {
+ if (Jbr7481MouseEnteredExitedFix.isEnabled) {
+ mouseEnteredExitedBugWorkaround().apply(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ else {
+ doNotifyMouseEvent(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ }
+
+ void doNotifyMouseEvent(int id, long when, int button,
int x, int y, int absX, int absY,
int modifiers, int clickCount, boolean popupTrigger,
byte[] bdata)
@@ -926,6 +938,13 @@ public void notifyMouseEvent(int id, long when, int button,
notifyUpdateCursor();
}
+ private Jbr7481MouseEnteredExitedFix mouseEnteredExitedBugWorkaround() {
+ if (jbr7481MouseEnteredExitedFix == null) {
+ jbr7481MouseEnteredExitedFix = new Jbr7481MouseEnteredExitedFix(this);
+ }
+ return jbr7481MouseEnteredExitedFix;
+ }
+
private void generateMouseEnterExitEventsForComponents(long when,
int button, int x, int y, int screenX, int screenY,
int modifiers, int clickCount, boolean popupTrigger,
@@ -1006,7 +1025,7 @@ public void notifyMouseWheelEvent(long when, int x, int y, int absX,
*/
@Override
public void notifyKeyEvent(int id, long when, int modifiers,
- int keyCode, char keyChar, int keyLocation, int extendedKeyCode)
+ int keyCode, char keyChar, int keyLocation, Map properties)
{
LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
@@ -1020,13 +1039,10 @@ public void notifyKeyEvent(int id, long when, int modifiers,
KeyEvent keyEvent = new KeyEvent(focusOwner, id, when, modifiers,
keyCode, keyChar, keyLocation);
- if (extendedKeyCode >= 0) {
- AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent, extendedKeyCode);
- } else {
- AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
- (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
- : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
- }
+ AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
+ (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
+ : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
+ AWTAccessor.getKeyEventAccessor().setJBRExtraProperties(keyEvent, properties);
postEvent(keyEvent);
}
@@ -1098,8 +1114,7 @@ private boolean setGraphicsConfig(GraphicsConfiguration gc) {
/**
* Returns true if the GraphicsDevice has been changed, false otherwise.
*/
- public boolean updateGraphicsDevice() {
- GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ public boolean updateGraphicsDevice(GraphicsDevice newGraphicsDevice) {
synchronized (getStateLock()) {
if (graphicsDevice == newGraphicsDevice) {
return false;
@@ -1121,16 +1136,29 @@ public void run() {
@Override
public final void displayChanged() {
- if (updateGraphicsDevice()) {
+ GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ if (newGraphicsDevice == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to update graphics device: " +
+ "platform window graphics device is null");
+ }
+ return;
+ }
+ boolean isNewDevice = updateGraphicsDevice(newGraphicsDevice);
+
+ if (isNewDevice) {
updateMinimumSize();
if (!isMaximizedBoundsSet()) {
setPlatformMaximizedBounds(getDefaultMaximizedBounds());
}
}
- // Replace surface unconditionally, because internal state of the
- // GraphicsDevice could be changed.
- replaceSurfaceData();
- repaintPeer();
+
+ SunToolkit.executeOnEventHandlerThread(getTarget(), () -> {
+ // Replace surface unconditionally, because internal state of the
+ // GraphicsDevice could be changed.
+ replaceSurfaceData();
+ repaintPeer();
+ });
}
@Override
@@ -1171,6 +1199,9 @@ private void replaceSurfaceData(final boolean blit) {
}
}
flushOnscreenGraphics();
+ if (((LWToolkit) Toolkit.getDefaultToolkit()).needUpdateWindowAfterPaint()) {
+ updateWindow();
+ }
}
private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) {
@@ -1236,53 +1267,6 @@ public boolean requestWindowFocus(FocusEvent.Cause cause) {
return false;
}
- AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget());
- KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor()
- .getCurrentKeyboardFocusManager(targetAppContext);
- Window currentActive = kfm.getActiveWindow();
-
-
- Window opposite = LWKeyboardFocusManagerPeer.getInstance().
- getCurrentFocusedWindow();
-
- // Make the owner active window.
- if (isSimpleWindow()) {
- LWWindowPeer owner = getOwnerFrameDialog(this);
-
- // If owner is not natively active, request native
- // activation on it w/o sending events up to java.
- if (owner != null && !owner.platformWindow.isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("requesting native focus to the owner " + owner);
- }
- LWWindowPeer currentActivePeer = currentActive == null ? null :
- (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(
- currentActive);
-
- // Ensure the opposite is natively active and suppress sending events.
- if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("the opposite is " + currentActivePeer);
- }
- currentActivePeer.skipNextFocusChange = true;
- }
- owner.skipNextFocusChange = true;
-
- owner.platformWindow.requestWindowFocus();
- }
-
- // DKFM will synthesize all the focus/activation events correctly.
- changeFocusedWindow(true, opposite);
- return true;
-
- // In case the toplevel is active but not focused, change focus directly,
- // as requesting native focus on it will not have effect.
- } else if (getTarget() == currentActive && !getTarget().hasFocus()) {
-
- changeFocusedWindow(true, opposite);
- return true;
- }
-
return platformWindow.requestWindowFocus();
}
@@ -1357,7 +1341,8 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
// - when the opposite (gaining focus) window is an owned/owner window.
// - for a simple window in any case.
if (!becomesFocused &&
- (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)))
+ (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)) &&
+ (opposite == null || getOwnerFrameDialog(AWTAccessor.getComponentAccessor().getPeer(opposite)) != this))
{
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("ungrabbing on " + grabbingWindow);
@@ -1379,7 +1364,7 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis());
// TODO: wrap in SequencedEvent
- postEvent(windowEvent);
+ SunToolkit.postPriorityEvent(windowEvent);
}
/*
@@ -1458,4 +1443,135 @@ public PeerType getPeerType() {
public String toString() {
return super.toString() + " [target is " + getTarget() + "]";
}
+
+ /**
+ * [tav] Used externally.
+ */
+ @Override
+ public long getWindowHandle() {
+ final long[] handle = new long[1];
+ PlatformWindow window = getPlatformWindow();
+ if (window instanceof CPlatformWindow) {
+ ((CPlatformWindow)window).execute(ptr -> handle[0] = ptr);
+ }
+ return handle[0];
+ }
+
+ static class Jbr7481MouseEnteredExitedFix {
+ static final boolean isEnabled;
+
+ static {
+ boolean isEnabledLocal = false;
+
+ try {
+ isEnabledLocal = Boolean.parseBoolean(System.getProperty("awt.mac.enableMouseEnteredExitedWorkaround", "true"));
+ } catch (Exception ignored) {
+ }
+
+ isEnabled = isEnabledLocal;
+ }
+
+ private final LWWindowPeer peer;
+ long when;
+ int x;
+ int y;
+ int absX;
+ int absY;
+ int modifiers;
+
+ Jbr7481MouseEnteredExitedFix(LWWindowPeer peer) {
+ this.peer = peer;
+ }
+
+ void apply(
+ int id, long when, int button,
+ int x, int y, int absX, int absY,
+ int modifiers, int clickCount, boolean popupTrigger,
+ byte[] bdata
+ ) {
+ this.when = when;
+ this.x = x;
+ this.y = y;
+ this.absX = absX;
+ this.absY = absY;
+ this.modifiers = modifiers;
+ switch (id) {
+ case MouseEvent.MOUSE_ENTERED -> {
+ var currentPeerWorkaround = getCurrentPeerWorkaroundOrNull();
+ // First, send a "mouse exited" to the current window, if any,
+ // to maintain a sensible "exited, entered" order.
+ if (currentPeerWorkaround != null && currentPeerWorkaround != this) {
+ currentPeerWorkaround.sendMouseExited();
+ }
+ // Then forward the "mouse entered" to this window, regardless of whether it's already current.
+ // Repeated "mouse entered" are allowed and may be even needed somewhere deep inside this call.
+ peer.doNotifyMouseEvent(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ case MouseEvent.MOUSE_EXITED -> {
+ var currentPeerWorkaround = getCurrentPeerWorkaroundOrNull();
+ // An "exited" event often arrives too late. Such events may cause the current window to get lost.
+ // And since we've already sent a "mouse exited" when entering the current window, we don't need another one.
+ if (currentPeerWorkaround == this) {
+ peer.doNotifyMouseEvent(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ }
+ case MouseEvent.MOUSE_MOVED -> {
+ var currentPeerWorkaround = getCurrentPeerWorkaroundOrNull();
+ if (currentPeerWorkaround != this) {
+ // Inconsistency detected: either the events arrived out of order or never did.
+ // First, send an "exited" event to the current window, if any.
+ if (currentPeerWorkaround != null) {
+ currentPeerWorkaround.sendMouseExited();
+ }
+ // Next, send a fake "mouse entered" to the new window.
+ sendMouseEntered();
+ }
+ peer.doNotifyMouseEvent(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ default -> {
+ peer.doNotifyMouseEvent(id, when, button, x, y, absX, absY, modifiers, clickCount, popupTrigger, bdata);
+ }
+ }
+ }
+
+ private static Jbr7481MouseEnteredExitedFix getCurrentPeerWorkaroundOrNull() {
+ var currentPeer = getCurrentWindowPeer();
+ return currentPeer == null ? null : currentPeer.jbr7481MouseEnteredExitedFix;
+ }
+
+ private static LWWindowPeer getCurrentWindowPeer() {
+ return LWWindowPeer.getWindowUnderCursor();
+ }
+
+ private void sendMouseEntered() {
+ peer.doNotifyMouseEvent(
+ MouseEvent.MOUSE_ENTERED, when, MouseEvent.NOBUTTON,
+ x, y, absX, absY,
+ modifiers, 0, false,
+ null
+ );
+ }
+
+ private void sendMouseExited() {
+ peer.doNotifyMouseEvent(
+ MouseEvent.MOUSE_EXITED, when, MouseEvent.NOBUTTON,
+ x, y, absX, absY,
+ modifiers, 0, false,
+ null
+ );
+ }
+
+ @Override
+ public String toString() {
+ return "Jbr7481MouseEnteredExitedFix{" +
+ "peer=" + peer +
+ ", when=" + when +
+ ", x=" + x +
+ ", y=" + y +
+ ", absX=" + absX +
+ ", absY=" + absY +
+ ", modifiers=" + modifiers +
+ '}';
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java b/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
index aea8ff2e6a93..902de12ee94c 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
@@ -26,6 +26,7 @@
package sun.lwawt;
import java.awt.Rectangle;
+import java.util.Map;
public interface PlatformEventNotifier {
void notifyIconify(boolean iconify);
@@ -61,5 +62,9 @@ void notifyMouseWheelEvent(long when, int x, int y, final int absX,
* Called by the delegate when a key is pressed.
*/
void notifyKeyEvent(int id, long when, int modifiers,
- int keyCode, char keyChar, int keyLocation, int extendedKeyCode);
+ int keyCode, char keyChar, int keyLocation, Map properties);
+
+ default void notifyKeyEvent(int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation) {
+ notifyKeyEvent(id, when, modifiers, keyCode, keyChar, keyLocation, null);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
index 417750507aad..b8899cd2b091 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
@@ -37,6 +37,7 @@
import java.beans.PropertyChangeListener;
import java.lang.annotation.Native;
import java.lang.reflect.InvocationTargetException;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -44,6 +45,8 @@
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.Arrays;
+import java.util.function.Function;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleAction;
@@ -57,6 +60,7 @@
import javax.accessibility.AccessibleText;
import javax.accessibility.AccessibleValue;
import javax.swing.Icon;
+import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
@@ -72,6 +76,7 @@
class CAccessibility implements PropertyChangeListener {
private static Set ignoredRoles;
+ private static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
static {
loadAWTLibrary();
@@ -79,8 +84,8 @@ class CAccessibility implements PropertyChangeListener {
@SuppressWarnings("restricted")
private static void loadAWTLibrary() {
- // Need to load the native library for this code.
- System.loadLibrary("awt");
+ // Need to load the native library for this code.
+ System.loadLibrary("awt");
}
static CAccessibility sAccessibility;
@@ -122,14 +127,14 @@ public void propertyChange(final PropertyChangeEvent evt) {
private native void focusChanged();
static T invokeAndWait(final Callable callable, final Component c) {
- return invokeAndWait(callable, c, null);
+ return invokeAndWait(callable, c, (T)null);
}
static T invokeAndWait(final Callable callable, final Component c, final T defValue) {
T value = null;
if (c != null) {
try {
- value = LWCToolkit.invokeAndWait(callable, c);
+ value = LWCToolkit.invokeAndWait(callable, c, INVOKE_TIMEOUT_SECONDS);
} catch (final Exception e) { e.printStackTrace(); }
}
@@ -755,7 +760,7 @@ private static Object[] getChildrenAndRolesImpl(Accessible a, Component c, int w
// This method is called from the native
// Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level
- private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) {
+ private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
if (a == null) return null;
return invokeAndWait(new Callable() {
public Object[] call() throws Exception {
@@ -766,7 +771,7 @@ public Object[] call() throws Exception {
parentStack.add(a);
ArrayList indexses = new ArrayList();
Integer index = 0;
- int currentLevel = level;
+ int currentLevel = 0;
while (!parentStack.isEmpty()) {
Accessible p = parentStack.get(parentStack.size() - 1);
if (!childrenOfParent.containsKey(p)) {
@@ -1075,4 +1080,18 @@ public Boolean call() throws Exception {
}
}, c);
}
+
+ private static boolean isComboBoxEditable(Accessible a, Component c) {
+ if (a == null) return false;
+
+ return invokeAndWait(new Callable() {
+ public Boolean call() throws Exception {
+ Accessible sa = CAccessible.getSwingAccessible(a);
+ if (sa instanceof JComboBox> comboBox) {
+ return comboBox.isEditable();
+ }
+ return false;
+ }
+ }, c, false);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
index babe899296f1..1d6f426e69de 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
@@ -25,15 +25,15 @@
package sun.lwawt.macosx;
-import java.awt.Component;
+import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Objects;
+import java.security.PrivilegedAction;
+import java.util.concurrent.atomic.AtomicBoolean;
-import javax.accessibility.Accessible;
-import javax.accessibility.AccessibleContext;
-import javax.swing.JSpinner;
-import javax.swing.JTabbedPane;
+import javax.accessibility.*;
+import javax.swing.*;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
@@ -44,12 +44,28 @@
import static javax.accessibility.AccessibleContext.ACCESSIBLE_NAME_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_VALUE_PROPERTY;
-import javax.accessibility.AccessibleRole;
-import javax.accessibility.AccessibleState;
import sun.awt.AWTAccessor;
class CAccessible extends CFRetainedResource implements Accessible {
+ private static Timer timer = null;
+ private final static int SELECTED_CHILDREN_MILLISECONDS_DEFAULT = 100;
+ private static int SELECTED_CHILDREN_MILLISECONDS;
+
+ static {
+ @SuppressWarnings("removal") int scms = java.security.AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Integer run() {
+ return Integer.getInteger("sun.lwawt.macosx.CAccessible.selectedChildrenMilliSeconds",
+ SELECTED_CHILDREN_MILLISECONDS_DEFAULT);
+ }
+ });
+ SELECTED_CHILDREN_MILLISECONDS = scms >= 0 ? scms : SELECTED_CHILDREN_MILLISECONDS_DEFAULT;
+
+ EVENTS_COALESCING_ENABLED = Boolean.parseBoolean(
+ System.getProperty("sun.lwawt.macosx.CAccessible.eventsCoalescingEnabled", "true")
+ );
+ }
public static CAccessible getCAccessible(final Accessible a) {
if (a == null) return null;
@@ -73,10 +89,14 @@ public static CAccessible getCAccessible(final Accessible a) {
private static native void menuOpened(long ptr);
private static native void menuClosed(long ptr);
private static native void menuItemSelected(long ptr);
- private static native void treeNodeExpanded(long ptr);
- private static native void treeNodeCollapsed(long ptr);
+ // JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeExpanded()} instead
+ private native void treeNodeExpanded(long ptr);
+ // JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeCollapsed()} instead
+ private native void treeNodeCollapsed(long ptr);
private static native void selectedCellsChanged(long ptr);
private static native void tableContentCacheClear(long ptr);
+ private static native void updateZoomCaretFocus(long ptr);
+ private static native void updateZoomFocus(long ptr);
private Accessible accessible;
@@ -101,7 +121,7 @@ protected synchronized void dispose() {
@Override
public AccessibleContext getAccessibleContext() {
- return accessible.getAccessibleContext();
+ return accessible != null ? accessible.getAccessibleContext() : null;
}
public void addNotificationListeners(Component c) {
@@ -111,35 +131,173 @@ public void addNotificationListeners(Component c) {
}
}
+
+ // =================================================== JBR-7659 ===================================================
+
+ // Since macOS 15 applications running a lot of nested CFRunLoop s has been crashing.
+ // In AWT case, a new nested CFRunLoop is run whenever macOS asks some a11y info about a UI component.
+ // macOS usually does that when AWT notifies it about changes in UI components.
+ // So, if there happen N changes of UI components in a row, the AppKit thread may have N nested CFRunLoops.
+ // If N is too high (>= ~700), the application will crash.
+ // In JBR-7659 the problem is observed with UI tree expansion/collapsing events, but AFAIU in general it may happen
+ // with any kind of UI change events.
+ // As for now we're covering only those 2 kinds of events to make sure if the solution is effective enough.
+ // The proposed solution is to make sure there is not more than one event of each kind in the AppKit queue
+ // for the same UI component (i.e. for the same CAccessible).
+
+ /** Is a way to disable the fix */
+ private static final boolean EVENTS_COALESCING_ENABLED;
+
+ /**
+ * The variables indicate whether there's an "event" posted by
+ * {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} onto the AppKit thread, but not processed by it yet.
+ */
+ private final AtomicBoolean isProcessingTreeNodeExpandedEvent = new AtomicBoolean(false),
+ isProcessingTreeNodeCollapsedEvent = new AtomicBoolean(false);
+ /**
+ * The variables indicate whether there was an attempt to post another
+ * {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} while there was already one
+ * on the AppKit thread (no matter if it's still in the queue or is being processed).
+ * It's necessary to make sure there won't be unobserved changes of the component.
+ */
+ private final AtomicBoolean hasDelayedTreeNodeExpandedEvent = new AtomicBoolean(false),
+ hasDelayedTreeNodeCollapsedEvent = new AtomicBoolean(false);
+
+ private void postCoalescedTreeNodeExpanded() {
+ postCoalescedEventImpl(
+ isProcessingTreeNodeExpandedEvent,
+ hasDelayedTreeNodeExpandedEvent,
+ this::treeNodeExpanded,
+ this
+ );
+ }
+
+ private void postCoalescedTreeNodeCollapsed() {
+ postCoalescedEventImpl(
+ isProcessingTreeNodeCollapsedEvent,
+ hasDelayedTreeNodeCollapsedEvent,
+ this::treeNodeCollapsed,
+ this
+ );
+ }
+
+ private static void postCoalescedEventImpl(
+ final AtomicBoolean isProcessingEventFlag,
+ final AtomicBoolean hasDelayedEventFlag,
+ final CFNativeAction eventPostingAction,
+ // a reference to this is passed instead of making the method non-static to make sure the implementation
+ // doesn't accidentally touch anything of the instance by mistake
+ final CAccessible self
+ ) {
+ if (!EVENTS_COALESCING_ENABLED) {
+ self.execute(eventPostingAction);
+ return;
+ }
+
+ assert EventQueue.isDispatchThread();
+
+ final var result = self.executeGet(ptr -> {
+ if (isProcessingEventFlag.compareAndSet(false, true)) {
+ hasDelayedEventFlag.set(false);
+
+ try {
+ eventPostingAction.run(ptr);
+ } catch (Exception err) {
+ isProcessingEventFlag.set(false);
+ throw err;
+ }
+ } else {
+ hasDelayedEventFlag.set(true);
+ }
+ return 1;
+ });
+
+ if (result != 1) {
+ // the routine hasn't been executed because there was no native resource (i.e. {@link #ptr} was 0)
+ isProcessingEventFlag.set(false);
+ hasDelayedEventFlag.set(false);
+ }
+ }
+
+ // Called from native
+ private void onProcessedTreeNodeExpandedEvent() {
+ onProcessedEventImpl(
+ isProcessingTreeNodeExpandedEvent,
+ hasDelayedTreeNodeExpandedEvent,
+ this::postCoalescedTreeNodeExpanded
+ );
+ }
+
+ // Called from native
+ private void onProcessedTreeNodeCollapsedEvent() {
+ onProcessedEventImpl(
+ isProcessingTreeNodeCollapsedEvent,
+ hasDelayedTreeNodeCollapsedEvent,
+ this::postCoalescedTreeNodeCollapsed
+ );
+ }
+
+ private static void onProcessedEventImpl(
+ final AtomicBoolean isProcessingEventFlag,
+ final AtomicBoolean hasDelayedEventFlag,
+ final Runnable postingCoalescedEventRoutine
+ ) {
+ if (!EVENTS_COALESCING_ENABLED) {
+ return;
+ }
+
+ isProcessingEventFlag.set(false);
+
+ if (hasDelayedEventFlag.compareAndSet(true, false)) {
+ // We shouldn't call postCoalesced<...> synchronously from here to allow the current CFRunLoop
+ // to finish, thus reducing the current number of nested CFRunLoop s.
+ EventQueue.invokeLater(postingCoalescedEventRoutine);
+ }
+ }
+
+ // =============================================== END of JBR-7659 ================================================
+
+
private class AXChangeNotifier implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent e) {
+ assert EventQueue.isDispatchThread();
String name = e.getPropertyName();
if ( ptr != 0 ) {
Object newValue = e.getNewValue();
Object oldValue = e.getOldValue();
if (name.equals(ACCESSIBLE_CARET_PROPERTY)) {
- selectedTextChanged(ptr);
+ execute(ptr -> selectedTextChanged(ptr));
+ // Notify macOS Accessibility Zoom to move focus to the new caret location.
+ execute(ptr -> updateZoomCaretFocus(ptr));
} else if (name.equals(ACCESSIBLE_TEXT_PROPERTY)) {
AccessibleContext thisAC = accessible.getAccessibleContext();
Accessible parentAccessible = thisAC.getAccessibleParent();
if (!(parentAccessible instanceof JSpinner.NumberEditor)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
}
} else if (name.equals(ACCESSIBLE_SELECTION_PROPERTY)) {
- selectionChanged(ptr);
+ if (timer != null) {
+ timer.stop();
+ }
+ timer = new Timer(SELECTED_CHILDREN_MILLISECONDS, actionEvent -> execute(ptr -> selectionChanged(ptr)));
+ timer.setRepeats(false);
+ timer.start();
+
+ // Notify the Accessibility Zoom to follow the new selection location.
+ execute(ptr -> updateZoomFocus(ptr));
} else if (name.equals(ACCESSIBLE_TABLE_MODEL_CHANGED)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
if (CAccessible.getSwingAccessible(CAccessible.this) != null) {
Accessible a = CAccessible.getSwingAccessible(CAccessible.this);
AccessibleContext ac = a.getAccessibleContext();
if ((ac != null) && (ac.getAccessibleRole() == AccessibleRole.TABLE)) {
- tableContentCacheClear(ptr);
+ execute(ptr -> tableContentCacheClear(ptr));
}
}
} else if (name.equals(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY)) {
- if (newValue instanceof AccessibleContext) {
+ if (newValue == null || newValue instanceof AccessibleContext) {
activeDescendant = (AccessibleContext)newValue;
if (newValue instanceof Accessible) {
Accessible a = (Accessible)newValue;
@@ -149,7 +307,7 @@ public void propertyChange(PropertyChangeEvent e) {
if (p != null) {
AccessibleContext pac = p.getAccessibleContext();
if ((pac != null) && (pac.getAccessibleRole() == AccessibleRole.TABLE)) {
- selectedCellsChanged(ptr);
+ execute(ptr -> selectedCellsChanged(ptr));
}
}
}
@@ -165,31 +323,56 @@ public void propertyChange(PropertyChangeEvent e) {
}
if (newValue == AccessibleState.EXPANDED) {
- treeNodeExpanded(ptr);
+ if (EVENTS_COALESCING_ENABLED) {
+ postCoalescedTreeNodeExpanded();
+ } else {
+ execute(ptr -> treeNodeExpanded(ptr));
+ }
} else if (newValue == AccessibleState.COLLAPSED) {
- treeNodeCollapsed(ptr);
+ if (EVENTS_COALESCING_ENABLED) {
+ postCoalescedTreeNodeCollapsed();
+ } else {
+ execute(ptr -> treeNodeCollapsed(ptr));
+ }
+ }
+
+ if (thisRole == AccessibleRole.COMBO_BOX) {
+ AccessibleStateSet as = thisAC.getAccessibleStateSet();
+ if (as != null)
+ if (as.contains(AccessibleState.EXPANDED)) {
+ if (timer != null) {
+ timer.stop();
+ }
+ timer = new Timer(SELECTED_CHILDREN_MILLISECONDS, actionEvent -> execute(ptr -> selectionChanged(ptr)));
+ timer.setRepeats(false);
+ timer.start();
+ if (as.contains(AccessibleState.COLLAPSED)) {
+ execute(ptr -> valueChanged(ptr));
+ }
+ }
}
if (thisRole == AccessibleRole.POPUP_MENU) {
- if ( newValue != null &&
- ((AccessibleState)newValue) == AccessibleState.VISIBLE ) {
- menuOpened(ptr);
- } else if ( oldValue != null &&
- ((AccessibleState)oldValue) == AccessibleState.VISIBLE ) {
- menuClosed(ptr);
+ if (newValue != null &&
+ ((AccessibleState) newValue) == AccessibleState.VISIBLE) {
+ execute(ptr -> menuOpened(ptr));
+ } else if (oldValue != null &&
+ ((AccessibleState) oldValue) == AccessibleState.VISIBLE) {
+ execute(ptr -> menuClosed(ptr));
+ execute(ptr -> unregisterFromCocoaAXSystem(ptr));
}
} else if (thisRole == AccessibleRole.MENU_ITEM ||
(thisRole == AccessibleRole.MENU)) {
- if ( newValue != null &&
- ((AccessibleState)newValue) == AccessibleState.FOCUSED ) {
- menuItemSelected(ptr);
+ if (newValue != null &&
+ ((AccessibleState) newValue) == AccessibleState.FOCUSED) {
+ execute(ptr -> menuItemSelected(ptr));
}
}
// Do send check box state changes to native side
if (thisRole == AccessibleRole.CHECK_BOX) {
if (!Objects.equals(newValue, oldValue)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
}
// Notify native side to handle check box style menuitem
@@ -221,14 +404,14 @@ public void propertyChange(PropertyChangeEvent e) {
} else if (name.equals(ACCESSIBLE_NAME_PROPERTY)) {
//for now trigger only for JTabbedPane.
if (e.getSource() instanceof JTabbedPane) {
- titleChanged(ptr);
+ execute(ptr -> titleChanged(ptr));
}
} else if (name.equals(ACCESSIBLE_VALUE_PROPERTY)) {
AccessibleRole thisRole = accessible.getAccessibleContext()
.getAccessibleRole();
if (thisRole == AccessibleRole.SLIDER ||
thisRole == AccessibleRole.PROGRESS_BAR) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
}
}
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
index cee23f2f96e3..73a28c65f133 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
@@ -29,6 +29,7 @@
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
+import java.awt.im.InputMethodRequests;
import java.util.concurrent.Callable;
import javax.accessibility.Accessible;
@@ -343,4 +344,66 @@ static int[] getRangeForLine(final Accessible a, final int lineIndex) {
return new int[] { line.getStartOffset(), line.getEndOffset() };
}
+
+ static double[] getCaretRectangle(final Accessible a, final Component c) {
+ final double[] ret = new double[4];
+
+ if (a == null) return null;
+
+ return CAccessibility.invokeAndWait(new Callable() {
+ public double[] call() throws Exception {
+ final Accessible sa = CAccessible.getSwingAccessible(a);
+ if (!(sa instanceof Component)) return null;
+
+ final Component component = (Component) sa;
+
+ final InputMethodRequests imr = component.getInputMethodRequests();
+ if (imr == null) return null;
+
+ final Rectangle caretRectangle = imr.getTextLocation(null);
+
+ if (caretRectangle == null) return null;
+
+ ret[0] = caretRectangle.getX();
+ ret[1] = caretRectangle.getY();
+ ret[2] = 1; // Use 1 as caret width.
+ ret[3] = caretRectangle.getHeight();
+
+ return ret;
+ }
+ }, c);
+ }
+
+ static void setText(final Accessible a, final Component c, final String newText) {
+ if (a == null) return;
+
+ CAccessibility.invokeLater(new Runnable() {
+ public void run() {
+ final AccessibleContext ac = a.getAccessibleContext();
+ if (ac == null) return;
+
+ final AccessibleEditableText aet = ac.getAccessibleEditableText();
+ if (aet == null) return;
+
+ aet.setTextContents(newText);
+ }
+ }, c);
+ }
+
+ static boolean isSetAccessibilityValueAllowed(final Accessible a, final Component c) {
+ if (a == null) return false;
+
+ return CAccessibility.invokeAndWait(new Callable() {
+ public Boolean call() throws Exception {
+ final AccessibleContext ac = a.getAccessibleContext();
+ if (ac == null || ac.getAccessibleEditableText() == null) return false;
+
+ final Accessible sa = CAccessible.getSwingAccessible(a);
+ if (sa instanceof JTextComponent textComponent) {
+ return textComponent.isEditable() && textComponent.isEnabled();
+ }
+ return false;
+ }
+ }, c, false);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
index e4abc5fe1a9c..4af7c3167f1f 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
@@ -45,6 +45,12 @@ public static CCursorManager getInstance() {
return theInstance;
}
+ private static void resetCurrentCursor() {
+ if (theInstance != null) {
+ theInstance.currentCursor = null;
+ }
+ }
+
private volatile Cursor currentCursor;
private CCursorManager() { }
@@ -61,6 +67,11 @@ protected void setCursor(final Cursor cursor) {
return;
}
currentCursor = cursor;
+ setCurrentCursor();
+ }
+
+ void setCurrentCursor() {
+ Cursor cursor = currentCursor;
if (cursor == null) {
nativeSetBuiltInCursor(Cursor.DEFAULT_CURSOR, null);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
index 049f8d0df32d..87ad9e075525 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
@@ -100,11 +100,10 @@ public void handleScrollEvent(double pluginX, double pluginY, int modifierFlags,
deltaY, NSEvent.SCROLL_PHASE_UNSUPPORTED);
}
- public void handleKeyEvent(int eventType, int modifierFlags, String characters,
- String charsIgnoringMods, boolean isRepeat, short keyCode,
+ public void handleKeyEvent(int eventType, int modifierFlags, String characters, boolean isRepeat, short keyCode,
boolean needsKeyTyped) {
- responder.handleKeyEvent(eventType, modifierFlags, characters, charsIgnoringMods,
- keyCode, needsKeyTyped, isRepeat);
+ responder.handleKeyEvent(eventType, modifierFlags, characters,
+ null, keyCode, needsKeyTyped, isRepeat);
}
public void handleInputEvent(String text) {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
index d88d33118717..ecb3ef6e6181 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
@@ -43,7 +43,11 @@ protected CFLayer(long ptr, boolean disposeOnAppKitThread) {
super(ptr, disposeOnAppKitThread);
}
- public abstract SurfaceData replaceSurfaceData();
+ public abstract SurfaceData replaceSurfaceData(int scale);
+
+ public SurfaceData replaceSurfaceData() {
+ return replaceSurfaceData(0);
+ }
@Override
public void dispose() {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
index 96879952dc31..b008ab1b2ff3 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
@@ -53,18 +53,34 @@
import java.io.FilenameFilter;
import java.util.List;
+import com.jetbrains.desktop.JBRFileDialog;
import sun.awt.AWTAccessor;
import sun.java2d.pipe.Region;
+import sun.lwawt.LWWindowPeer;
+import sun.util.logging.PlatformLogger;
+
+import static com.jetbrains.desktop.JBRFileDialog.*;
class CFileDialog implements FileDialogPeer {
+ private static final PlatformLogger log = PlatformLogger.getLogger("sun.lwawt.macosx.CFileDialog");
+
private class Task implements Runnable {
@Override
public void run() {
try {
boolean navigateApps = !Boolean.getBoolean("apple.awt.use-file-dialog-packages");
- boolean chooseDirectories = Boolean.getBoolean("apple.awt.fileDialogForDirectories");
+
+ JBRFileDialog jbrDialog = JBRFileDialog.get(target);
+ boolean createDirectories = (jbrDialog.getHints() & CREATE_DIRECTORIES_HINT) != 0;
+ boolean chooseDirectories = (jbrDialog.getHints() & SELECT_DIRECTORIES_HINT) != 0;
+ boolean chooseFiles = (jbrDialog.getHints() & SELECT_FILES_HINT) != 0;
+ if (!chooseDirectories && !chooseFiles) { // Fallback to default
+ boolean dir = Boolean.getBoolean("apple.awt.fileDialogForDirectories");
+ chooseFiles = !dir;
+ chooseDirectories = dir;
+ }
int dialogMode = target.getMode();
String title = target.getTitle();
@@ -72,12 +88,25 @@ public void run() {
title = " ";
}
- String[] userFileNames = nativeRunFileDialog(title,
+ Window owner = target.getOwner();
+
+ long ownerPtr = owner == null ?
+ 0L :
+ ((CPlatformWindow) ((LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner))
+ .getPlatformWindow()).executeGet(ptr -> ptr);
+
+ String[] userFileNames = nativeRunFileDialog(
+ ownerPtr,
+ title,
dialogMode,
+ target.getModalityType().ordinal(),
target.isMultipleMode(),
navigateApps,
chooseDirectories,
+ chooseFiles,
+ createDirectories,
target.getFilenameFilter() != null,
+ jbrDialog.fileFilterExtensions,
target.getDirectory(),
target.getFile());
@@ -85,7 +114,7 @@ public void run() {
String file = null;
File[] files = null;
- if (userFileNames != null) {
+ if (userFileNames != null && userFileNames.length > 0) {
// the dialog wasn't cancelled
int filesNumber = userFileNames.length;
files = new File[filesNumber];
@@ -146,6 +175,10 @@ public void setVisible(boolean visible) {
* If the dialog doesn't have a file filter return true.
*/
private boolean queryFilenameFilter(final String inFilename) {
+ if (!Boolean.getBoolean("awt.file.dialog.enable.filter")) {
+ return true;
+ }
+
boolean ret = false;
final FilenameFilter ff = target.getFilenameFilter();
@@ -155,14 +188,20 @@ private boolean queryFilenameFilter(final String inFilename) {
if (!fileObj.isDirectory()) {
File directoryObj = new File(fileObj.getParent());
String nameOnly = fileObj.getName();
- ret = ff.accept(directoryObj, nameOnly);
+ try {
+ ret = ff.accept(directoryObj, nameOnly);
+ } catch (Throwable e) {
+ log.warning("FilenameFilter call exception occurred", e);
+ }
}
return ret;
}
- private native String[] nativeRunFileDialog(String title, int mode,
+ private native String[] nativeRunFileDialog(
+ long ownerPtr, String title, int mode, int modality,
boolean multipleMode, boolean shouldNavigateApps,
- boolean canChooseDirectories, boolean hasFilenameFilter,
+ boolean canChooseDirectories, boolean canChooseFiles,
+ boolean canCreateDirectories, boolean hasFilenameFilter, String[] allowedFileTypes,
String directory, String file);
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
index 64d8a101d69f..45cea031b0c7 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
@@ -26,6 +26,7 @@
package sun.lwawt.macosx;
import java.awt.im.spi.*;
+import java.lang.reflect.Method;
import java.util.*;
import java.awt.*;
import java.awt.peer.*;
@@ -36,6 +37,7 @@
import java.lang.reflect.InvocationTargetException;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.text.JTextComponent;
import sun.awt.AWTAccessor;
@@ -45,10 +47,11 @@
import static sun.awt.AWTAccessor.ComponentAccessor;
public class CInputMethod extends InputMethodAdapter {
- private InputMethodContext fIMContext;
- private Component fAwtFocussedComponent;
+ private volatile InputMethodContext fIMContext;
+ private volatile Component fAwtFocussedComponent;
private LWComponentPeer, ?> fAwtFocussedComponentPeer;
private boolean isActive;
+ private boolean isTemporarilyDeactivated;
private static Map[] sHighlightStyles;
@@ -233,10 +236,13 @@ public void dispatchEvent(final AWTEvent event) {
*/
public void activate() {
isActive = true;
+ enableListening(true);
+ isTemporarilyDeactivated = false;
}
public void deactivate(boolean isTemporary) {
isActive = false;
+ isTemporarilyDeactivated = isTemporary;
}
/**
@@ -263,9 +269,13 @@ long getNativeViewPtr(LWComponentPeer, ?> peer) {
*/
public void removeNotify() {
if (fAwtFocussedComponentPeer != null) {
- nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+ nativeEndComposition(modelPtr, fAwtFocussedComponent);
+ nativeEnableListening(modelPtr, false);
+ nativeNotifyPeer(modelPtr, null);
}
+ fAwtFocussedComponent = null;
fAwtFocussedComponentPeer = null;
}
@@ -276,34 +286,37 @@ public void removeNotify() {
* to talk to when responding to key events.
*/
protected void setAWTFocussedComponent(Component component) {
- LWComponentPeer, ?> peer = null;
- long modelPtr = 0;
- CInputMethod imInstance = this;
+ if ((isTemporarilyDeactivated && component == null) || component == fAwtFocussedComponent) {
+ // Sometimes input happens for the natively unfocused window
+ // (e.g. in case of system emoji picker),
+ // so we don't reset last focused component on temporary focus lost.
+ return;
+ }
- // component will be null when we are told there's no focused component.
- // When that happens we need to notify the native architecture to stop generating IMEs
- if (component == null) {
- peer = fAwtFocussedComponentPeer;
- imInstance = null;
- } else {
- peer = getNearestNativePeer(component);
+ if (fAwtFocussedComponentPeer != null) {
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+ nativeNotifyPeer(modelPtr, null);
+ }
+
+ fAwtFocussedComponent = component;
+ fAwtFocussedComponentPeer = getNearestNativePeer(component);
+
+ if (fAwtFocussedComponentPeer != null) {
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+ CInputMethod imInstance = this;
// If we have a passive client, don't pass input method events to it.
if (component.getInputMethodRequests() == null) {
imInstance = null;
}
- }
-
- if (peer != null) {
- modelPtr = getNativeViewPtr(peer);
- // modelPtr refers to the ControlModel that either got or lost focus.
nativeNotifyPeer(modelPtr, imInstance);
}
+ }
- // Track the focused component and its nearest peer.
- fAwtFocussedComponent = component;
- fAwtFocussedComponentPeer = getNearestNativePeer(component);
+ @Override
+ protected void stopListening() {
+ enableListening(false);
}
/**
@@ -349,7 +362,7 @@ protected void setAWTFocussedComponent(Component component) {
*/
public void endComposition() {
if (fAwtFocussedComponentPeer != null)
- nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
+ nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer), fAwtFocussedComponent);
}
/**
@@ -411,6 +424,16 @@ public Object getControlObject() {
return null;
}
+ private void enableListening(boolean enable) {
+ if (fAwtFocussedComponentPeer != null) {
+ final long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+
+ if (modelPtr != 0) {
+ nativeEnableListening(modelPtr, enable);
+ }
+ }
+ }
+
// =========================== NSTextInput callbacks ===========================
// The 'marked text' that we get from Cocoa. We need to track this separately, since
// Java doesn't let us ask the IM context for it.
@@ -423,6 +446,8 @@ public Object getControlObject() {
* text view. This effectively wipes out any text in progress.
*/
private synchronized void insertText(String aString) {
+ if (fAwtFocussedComponent == null) return;
+
AttributedString attribString = new AttributedString(aString);
// Set locale information on the new string.
@@ -492,25 +517,29 @@ private void addAttribute (boolean isThickUnderline, boolean isGray, int start,
fCurrentText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, theHighlight, begin, end);
}
- /* Called from JNI to select the previously typed glyph during press and hold */
- private void selectPreviousGlyph() {
- if (fIMContext == null) return; // ???
+ private void selectRange(int selectionStart, int length) {
+ if (fIMContext == null || fAwtFocussedComponent == null) {
+ return;
+ }
+ final int selectionEnd = selectionStart + length;
try {
LWCToolkit.invokeLater(new Runnable() {
public void run() {
- final int offset = fIMContext.getInsertPositionOffset();
- if (offset < 1) return; // ???
-
if (fAwtFocussedComponent instanceof JTextComponent) {
- ((JTextComponent) fAwtFocussedComponent).select(offset - 1, offset);
+ ((JTextComponent) fAwtFocussedComponent).select(selectionStart, selectionEnd);
return;
}
if (fAwtFocussedComponent instanceof TextComponent) {
- ((TextComponent) fAwtFocussedComponent).select(offset - 1, offset);
+ ((TextComponent) fAwtFocussedComponent).select(selectionStart, selectionEnd);
return;
}
- // TODO: Ideally we want to disable press-and-hold in this case
+
+ var desc = (CInputMethodDescriptor)LWCToolkit.getLWCToolkit().getInputMethodAdapterDescriptor();
+ if (desc.textInputEventListener != null) {
+ var event = new JBRTextInputMacOS.SelectTextRangeEvent(fAwtFocussedComponent, selectionStart, length);
+ desc.textInputEventListener.handleSelectTextRangeEvent(event);
+ }
}
}, fAwtFocussedComponent);
} catch (Exception e) {
@@ -536,8 +565,7 @@ public void run() {
private void dispatchText(int selectStart, int selectLength, boolean pressAndHold) {
// Nothing to do if we have no text.
- if (fCurrentText == null)
- return;
+ if (fCurrentText == null || fAwtFocussedComponent == null) return;
TextHitInfo theCaret = (selectLength == 0 ? TextHitInfo.beforeOffset(selectStart) : null);
TextHitInfo visiblePosition = TextHitInfo.beforeOffset(0);
@@ -556,19 +584,21 @@ private void dispatchText(int selectStart, int selectLength, boolean pressAndHol
/**
* Frequent callbacks from NSTextInput. I think we're supposed to commit it here?
*/
- private synchronized void unmarkText() {
- if (fCurrentText == null)
- return;
+ private synchronized void unmarkText(Component component) {
+ if (component == null) {
+ component = fAwtFocussedComponent;
+ }
+ if (fCurrentText == null || component == null) return;
TextHitInfo theCaret = TextHitInfo.afterOffset(fCurrentTextLength);
TextHitInfo visiblePosition = theCaret;
- InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
+ InputMethodEvent event = new InputMethodEvent(component,
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
fCurrentText.getIterator(),
fCurrentTextLength,
theCaret,
visiblePosition);
- LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
+ LWCToolkit.postEvent(LWCToolkit.targetToAppContext(component), event);
fCurrentText = null;
fCurrentTextAsString = null;
fCurrentTextLength = 0;
@@ -586,46 +616,47 @@ private synchronized boolean hasMarkedText() {
private synchronized String attributedSubstringFromRange(final int locationIn, final int lengthIn) {
final String[] retString = new String[1];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(retString) {
- int location = locationIn;
- int length = lengthIn;
-
- if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {
- length = fIMContext.getCommittedTextLength() - location;
- }
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ invokeAndWaitNoThrow(new Runnable() {
+ public void run() {
+ synchronized (retString) {
+ int location = locationIn;
+ int length = lengthIn;
- AttributedCharacterIterator theIterator = null;
+ if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {
+ length = fIMContext.getCommittedTextLength() - location;
+ }
- if (fCurrentText == null) {
- theIterator = fIMContext.getCommittedText(location, location + length, null);
- } else {
- int insertSpot = fIMContext.getInsertPositionOffset();
+ AttributedCharacterIterator theIterator = null;
- if (location < insertSpot) {
+ if (fCurrentText == null) {
theIterator = fIMContext.getCommittedText(location, location + length, null);
- } else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {
- theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot +length);
- } else {
- theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);
+ } else {
+ int insertSpot = fIMContext.getInsertPositionOffset();
+
+ if (location < insertSpot) {
+ theIterator = fIMContext.getCommittedText(location, location + length, null);
+ } else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {
+ theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot + length);
+ } else {
+ theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);
+ }
}
- }
- // Get the characters from the iterator
- char[] selectedText = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];
- char current = theIterator.first();
- int index = 0;
- while (current != CharacterIterator.DONE) {
- selectedText[index++] = current;
- current = theIterator.next();
- }
+ // Get the characters from the iterator
+ char[] selectedText = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];
+ char current = theIterator.first();
+ int index = 0;
+ while (current != CharacterIterator.DONE) {
+ selectedText[index++] = current;
+ current = theIterator.next();
+ }
- retString[0] = new String(selectedText);
- }}
+ retString[0] = new String(selectedText);
+ }
+ }
}, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
-
+ }
synchronized(retString) { return retString[0]; }
}
@@ -638,8 +669,8 @@ public void run() { synchronized(retString) {
private synchronized int[] selectedRange() {
final int[] returnValue = new int[2];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ invokeAndWaitNoThrow(new Runnable() {
public void run() { synchronized(returnValue) {
AttributedCharacterIterator theIterator = fIMContext.getSelectedText(null);
if (theIterator == null) {
@@ -672,7 +703,7 @@ public void run() { synchronized(returnValue) {
}}
}, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
+ }
synchronized(returnValue) { return returnValue; }
}
@@ -684,20 +715,17 @@ public void run() { synchronized(returnValue) {
* return null.
*/
private synchronized int[] markedRange() {
- if (fCurrentText == null)
- return null;
+ if (fCurrentText == null || fAwtFocussedComponent == null) return null;
final int[] returnValue = new int[2];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(returnValue) {
- // The insert position is always after the composed text, so the range start is the
- // insert spot less the length of the composed text.
- returnValue[0] = fIMContext.getInsertPositionOffset();
- }}
- }, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
+ invokeAndWaitNoThrow(new Runnable() {
+ public void run() { synchronized(returnValue) {
+ // The insert position is always after the composed text, so the range start is the
+ // insert spot less the length of the composed text.
+ returnValue[0] = fIMContext.getInsertPositionOffset();
+ }}
+ }, fAwtFocussedComponent);
returnValue[1] = fCurrentTextLength;
synchronized(returnValue) { return returnValue; }
@@ -714,36 +742,38 @@ private synchronized int[] firstRectForCharacterRange(final int absoluteTextOffs
final int[] rect = new int[4];
try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(rect) {
- int insertOffset = fIMContext.getInsertPositionOffset();
- int composedTextOffset = absoluteTextOffset - insertOffset;
- if (composedTextOffset < 0) composedTextOffset = 0;
- Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));
- rect[0] = r.x;
- rect[1] = r.y;
- rect[2] = r.width;
- rect[3] = r.height;
-
- // This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores
- // the TextHitInfo passed to it and always returns the location of the insertion point, which is
- // at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri
- // follows the requested offset into the composed text.
- if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {
- Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));
-
- if (r.equals(r2)) {
- // FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate
- // window off the right edge of the component.
- String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);
- Graphics g = fAwtFocussedComponent.getGraphics();
- int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);
- rect[0] += xOffset;
- g.dispose();
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ FxInvoker.invoke(() -> {
+ synchronized (rect) {
+ int insertOffset = fIMContext.getInsertPositionOffset();
+ int composedTextOffset = absoluteTextOffset - insertOffset;
+ if (composedTextOffset < 0) composedTextOffset = 0;
+ Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));
+ rect[0] = r.x;
+ rect[1] = r.y;
+ rect[2] = r.width;
+ rect[3] = r.height;
+
+ // This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores
+ // the TextHitInfo passed to it and always returns the location of the insertion point, which is
+ // at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri
+ // follows the requested offset into the composed text.
+ if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {
+ Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));
+
+ if (r.equals(r2) && fCurrentTextAsString != null) {
+ // FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate
+ // window off the right edge of the component.
+ String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);
+ Graphics g = fAwtFocussedComponent.getGraphics();
+ int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);
+ rect[0] += xOffset;
+ g.dispose();
+ }
}
}
- }}
- }, fAwtFocussedComponent);
+ }, (sun.awt.im.InputContext)fIMContext, fAwtFocussedComponent);
+ }
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
synchronized(rect) { return rect; }
@@ -758,12 +788,14 @@ private synchronized int characterIndexForPoint(final int screenX, final int scr
final int[] insertPositionOffset = new int[1];
try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(offsetInfo) {
- offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
- insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
- }}
- }, fAwtFocussedComponent);
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ FxInvoker.invoke(() -> {
+ synchronized (offsetInfo) {
+ offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
+ insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
+ }
+ }, (sun.awt.im.InputContext)fIMContext, fAwtFocussedComponent);
+ }
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
// This bit of gymnastics ensures that the returned location is within the composed text.
@@ -799,9 +831,16 @@ public String getNativeInputMethodInfo()
// Note that if nativePeer isn't something that normally accepts keystrokes (i.e., a CPanel)
// these calls will be ignored.
private native void nativeNotifyPeer(long nativePeer, CInputMethod imInstance);
- private native void nativeEndComposition(long nativePeer);
+ private native void nativeEndComposition(long nativePeer, Component component);
private native void nativeHandleEvent(LWComponentPeer, ?> peer, AWTEvent event);
+ /*
+ * Passing false to the second parameter disables any interaction with
+ * the AppKit text input management subsystem (i.e. input methods, dead keys, maybe smth else)
+ * Passing true there enables it back
+ */
+ private native void nativeEnableListening(long nativePeerTarget, boolean enable);
+
// Returns the locale of the active input method.
static native Locale getNativeLocale();
@@ -813,4 +852,71 @@ public String getNativeInputMethodInfo()
// Initialize toolbox routines
static native void nativeInit();
+
+ private static class FxInvoker {
+ final static Method GET_CLIENT_COMPONENT_METHOD;
+ static Class> JFX_PANEL_CLASS;
+
+ static {
+ Method m = null;
+ try {
+ m = sun.awt.im.InputContext.class.getDeclaredMethod("getClientComponent");
+ if (m != null) m.setAccessible(true);
+ } catch (NoSuchMethodException ignore) {
+ }
+ GET_CLIENT_COMPONENT_METHOD = m;
+ }
+
+ static Component getClientComponent(sun.awt.im.InputContext ctx) {
+ if (GET_CLIENT_COMPONENT_METHOD != null) {
+ try {
+ return (Component)GET_CLIENT_COMPONENT_METHOD.invoke(ctx);
+ } catch (IllegalAccessException | InvocationTargetException ignore) {
+ }
+ }
+ return null;
+ }
+
+ static boolean instanceofJFXPanel(Component clientComponent) {
+ if (clientComponent != null) {
+ if (JFX_PANEL_CLASS == null) {
+ try {
+ // the class is not available in the current class loader context, use the client class loader
+ JFX_PANEL_CLASS = Class.forName("javafx.embed.swing.JFXPanel", false, clientComponent.getClass().getClassLoader());
+ } catch (ClassNotFoundException ignore) {
+ }
+ }
+ if (JFX_PANEL_CLASS != null) {
+ return JFX_PANEL_CLASS.isInstance(clientComponent);
+ }
+ }
+ return false;
+ }
+
+ // Executed on AppKit
+ static void invoke(Runnable runnable, sun.awt.im.InputContext inputContext, Component targetToAppContext) throws InvocationTargetException {
+ AtomicBoolean runOnAppKit = new AtomicBoolean(false);
+
+ // 1) Do not run secondary msg loop in this case.
+ // 2) Delegate runnable back to FX when applicable.
+ invokeAndWaitNoThrow(() -> {
+ runOnAppKit.set(instanceofJFXPanel(getClientComponent(inputContext)));
+ if (!runOnAppKit.get()) {
+ runnable.run();
+ }
+ }, targetToAppContext);
+
+ if (runOnAppKit.get()) {
+ runnable.run();
+ }
+ }
+ }
+
+ static void invokeAndWaitNoThrow(Runnable runnable, Component component) {
+ try {
+ LWCToolkit.invokeAndWait(runnable, component);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethodDescriptor.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethodDescriptor.java
index 062b2e2e59f2..4b4cfb3bd3ed 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethodDescriptor.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethodDescriptor.java
@@ -37,6 +37,7 @@
*/
public class CInputMethodDescriptor implements InputMethodDescriptor {
+ JBRTextInputMacOS.EventListener textInputEventListener;
static {
nativeInit();
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
index 3511d4aeb1d8..7fef2633d79a 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
@@ -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 @@ long createModel() {
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 final void addItem(MenuItem item) {
@Override
public final void delItem(final int index) {
- execute(ptr -> nativeDeleteItem(ptr, index));
+ AWTThreading.executeWaitToolkit(() -> execute(ptr -> nativeDeleteItem(ptr, index)));
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
index c4b2640efb84..0a21cbf00a0e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
@@ -33,6 +33,7 @@
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 @@ private boolean isSeparator() {
@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) {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
index a98b5529845d..51ac58a186ca 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
@@ -27,9 +27,11 @@
package sun.lwawt.macosx;
import java.awt.Insets;
+import java.util.Objects;
import sun.lwawt.PlatformComponent;
import sun.lwawt.PlatformWindow;
+import sun.lwawt.LWWindowPeer;
/**
* On OSX {@code CPlatformComponent} stores pointer to the native CAlayer which
@@ -53,6 +55,8 @@ public long getPointer() {
@Override
public void initialize(final PlatformWindow platformWindow) {
+ Objects.requireNonNull(platformWindow);
+
this.platformWindow = platformWindow;
setPtr(nativeCreateComponent(platformWindow.getLayerPtr()));
}
@@ -61,10 +65,13 @@ public void initialize(final PlatformWindow platformWindow) {
@Override
public void setBounds(final int x, final int y, final int w, final int h) {
- // translates values from the coordinate system of the top-level window
- // to the coordinate system of the content view
- final Insets insets = platformWindow.getPeer().getInsets();
- execute(ptr->nativeSetBounds(ptr, x - insets.left, y - insets.top, w, h));
+ if (platformWindow != null) {
+ // translates values from the coordinate system of the top-level window
+ // to the coordinate system of the content view
+ final LWWindowPeer peer = platformWindow.getPeer();
+ final Insets insets = (peer != null) ? peer.getInsets() : new Insets(0, 0, 0, 0);
+ execute(ptr -> nativeSetBounds(ptr, x - insets.left, y - insets.top, w, h));
+ }
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
index 8570ecaa97a8..da9cea1a0811 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
@@ -28,7 +28,7 @@
import java.awt.*;
import java.awt.event.FocusEvent;
-import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
@@ -55,7 +55,7 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override // PlatformWindow
public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) {
this.peer = peer;
- if (CGraphicsDevice.usingMetalPipeline()) {
+ if (CGraphicsEnvironment.usingMetalPipeline()) {
this.windowLayer = new MTLLayer(peer);
} else {
this.windowLayer = new CGLLayer(peer);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
index 0501bbfab032..71654bbd52e4 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
@@ -26,6 +26,7 @@
package sun.lwawt.macosx;
import sun.awt.SunToolkit;
+import sun.awt.event.KeyEventProcessing;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformEventNotifier;
@@ -34,7 +35,10 @@
import java.awt.event.InputEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.KeyEvent;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Translates NSEvents/NPCocoaEvents into AWT events.
@@ -46,6 +50,14 @@ final class CPlatformResponder {
private int lastKeyPressCode = KeyEvent.VK_UNDEFINED;
private final DeltaAccumulator deltaAccumulatorX = new DeltaAccumulator();
private final DeltaAccumulator deltaAccumulatorY = new DeltaAccumulator();
+ private boolean momentumStarted;
+ private int momentumX;
+ private int momentumY;
+ private int momentumModifiers;
+ private int lastDraggedAbsoluteX;
+ private int lastDraggedAbsoluteY;
+ private int lastDraggedRelativeX;
+ private int lastDraggedRelativeY;
CPlatformResponder(final PlatformEventNotifier eventNotifier,
final boolean isNpapiCallback) {
@@ -67,6 +79,18 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
int jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
NSEvent.nsToJavaEventType(eventType);
+ boolean dragged = jeventType == MouseEvent.MOUSE_DRAGGED;
+ if (dragged // ignore dragged event that does not change any location
+ && lastDraggedAbsoluteX == absX && lastDraggedRelativeX == x
+ && lastDraggedAbsoluteY == absY && lastDraggedRelativeY == y) return;
+
+ if (dragged || jeventType == MouseEvent.MOUSE_PRESSED) {
+ lastDraggedAbsoluteX = absX;
+ lastDraggedAbsoluteY = absY;
+ lastDraggedRelativeX = x;
+ lastDraggedRelativeY = y;
+ }
+
int jbuttonNumber = MouseEvent.NOBUTTON;
int jclickCount = 0;
@@ -79,16 +103,25 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
}
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
- if ((jeventType == MouseEvent.MOUSE_PRESSED) && (jbuttonNumber > MouseEvent.NOBUTTON)) {
- // 8294426: NSEvent.nsToJavaModifiers returns 0 on M2 MacBooks if the event is generated
- // via tapping (not pressing) on a trackpad
- // (System Preferences -> Trackpad -> Tap to click must be turned on).
- // So let's set the modifiers manually.
- jmodifiers |= MouseEvent.getMaskForButton(jbuttonNumber);
+ if (jbuttonNumber > MouseEvent.NOBUTTON) {
+ if ( (jeventType == MouseEvent.MOUSE_PRESSED) || (Jbr5762Fix.isEnabled && (jeventType == MouseEvent.MOUSE_DRAGGED)) ) {
+ // 8294426: NSEvent.nsToJavaModifiers returns 0 on M2 MacBooks if the event is generated
+ // via tapping (not pressing) on a trackpad
+ // (System Preferences -> Trackpad -> Tap to click must be turned on).
+ // So let's set the modifiers manually.
+ //
+ // JBR-5762: enforce modifiers for the pressed button of MOUSE_DRAGGED events as well.
+ jmodifiers |= MouseEvent.getMaskForButton(jbuttonNumber);
+ }
}
boolean jpopupTrigger = NSEvent.isPopupTrigger(jmodifiers, jeventType);
+ if (jeventType == MouseEvent.MOUSE_ENTERED || jeventType == MouseEvent.MOUSE_EXITED) {
+ // JBR-7484: AppKit resets the cursor we set previously on entered/exit events, so we re-set it.
+ CCursorManager.getInstance().setCurrentCursor();
+ }
+
eventNotifier.notifyMouseEvent(jeventType, System.currentTimeMillis(), jbuttonNumber,
x, y, absX, absY, jmodifiers, jclickCount,
jpopupTrigger, null);
@@ -97,11 +130,26 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
/**
* Handles scroll events.
*/
- void handleScrollEvent(final int x, final int y, final int absX,
+ void handleScrollEvent(int x, int y, final int absX,
final int absY, final int modifierFlags,
final double deltaX, final double deltaY,
final int scrollPhase) {
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
+
+ if (scrollPhase > NSEvent.SCROLL_PHASE_UNSUPPORTED) {
+ if (scrollPhase == NSEvent.SCROLL_PHASE_BEGAN) {
+ momentumStarted = false;
+ } else if (scrollPhase == NSEvent.SCROLL_PHASE_MOMENTUM_BEGAN) {
+ momentumStarted = true;
+ momentumX = x;
+ momentumY = y;
+ momentumModifiers = jmodifiers;
+ } else if (momentumStarted) {
+ x = momentumX;
+ y = momentumY;
+ jmodifiers = momentumModifiers;
+ }
+ }
final boolean isShift = (jmodifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
int roundDeltaX = deltaAccumulatorX.getRoundedDelta(deltaX, scrollPhase);
@@ -134,8 +182,17 @@ private void dispatchScrollEvent(final int x, final int y, final int absX,
/**
* Handles key events.
+ *
+ * @param eventType macOS event type ID: keyDown, keyUp or flagsChanged
+ * @param modifierFlags macOS modifier flags mask (NSEventModifierFlags)
+ * @param chars NSEvent's characters property
+ * @param actualChars If non-null, then this key should generate KEY_TYPED events
+ * corresponding to characters in this string. Only valid for keyDown events.
+ * @param keyCode macOS virtual key code of the key being pressed or released
+ * @param needsKeyTyped post KEY_TYPED events?
+ * @param needsKeyReleased post KEY_RELEASED events?
*/
- void handleKeyEvent(int eventType, int modifierFlags, String chars, String charsIgnoringModifiers,
+ void handleKeyEvent(int eventType, int modifierFlags, String chars, String actualChars,
short keyCode, boolean needsKeyTyped, boolean needsKeyReleased) {
boolean isFlagsChangedEvent =
isNpapiCallback ? (eventType == CocoaConstants.NPCocoaEventFlagsChanged) :
@@ -143,13 +200,13 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
int jeventType = KeyEvent.KEY_PRESSED;
int jkeyCode = KeyEvent.VK_UNDEFINED;
- int jextendedkeyCode = -1;
int jkeyLocation = KeyEvent.KEY_LOCATION_UNKNOWN;
- boolean postsTyped = false;
boolean spaceKeyTyped = false;
+ HashMap properties = new HashMap<>();
+
+ properties.put("RAW_KEYCODE", keyCode);
char testChar = KeyEvent.CHAR_UNDEFINED;
- boolean isDeadChar = (chars!= null && chars.length() == 0);
if (isFlagsChangedEvent) {
int[] in = new int[] {modifierFlags, keyCode};
@@ -160,62 +217,49 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
jkeyCode = out[0];
jkeyLocation = out[1];
jeventType = out[2];
+ properties.put("US_KEYCODE", jkeyCode);
+ properties.put("DEAD_KEYCODE", KeyEvent.VK_UNDEFINED);
+ properties.put("DEAD_KEYSTROKE", KeyEvent.VK_UNDEFINED);
+ properties.put("IS_TYPED", false);
+ properties.put("CHARACTERS", "");
} else {
if (chars != null && chars.length() > 0) {
- testChar = chars.charAt(0);
-
- //Check if String chars contains SPACE character.
+ // `chars` might contain more than one character, so why are we using the last one?
+ // It doesn't really matter actually! If the string contains more than one character,
+ // the only way that this character will be used is to construct the keyChar field of the KeyEvent object.
+ // That field is only guaranteed to be meaningful for KEY_TYPED events, so let's not overthink it.
+ // Please note: this character is NOT used to construct extended key codes, that happens
+ // inside the NSEvent.nsToJavaKeyInfo function.
+ testChar = chars.charAt(chars.length() - 1);
+
+ // Check if String chars contains SPACE character.
if (chars.trim().isEmpty()) {
spaceKeyTyped = true;
}
}
- char testCharIgnoringModifiers = charsIgnoringModifiers != null && charsIgnoringModifiers.length() > 0 ?
- charsIgnoringModifiers.charAt(0) : KeyEvent.CHAR_UNDEFINED;
-
- int[] in = new int[] {testCharIgnoringModifiers, isDeadChar ? 1 : 0, modifierFlags, keyCode};
- int[] out = new int[4]; // [jkeyCode, jkeyLocation, deadChar, extendedKeyCode]
-
- postsTyped = NSEvent.nsToJavaKeyInfo(in, out);
- if (!postsTyped) {
- testChar = KeyEvent.CHAR_UNDEFINED;
- }
-
- if(isDeadChar){
- testChar = (char) out[2];
- if(testChar == 0){
- return;
- }
- }
-
- // If Pinyin Simplified input method is selected, CAPS_LOCK key is supposed to switch
- // input to latin letters.
- // It is necessary to use testCharIgnoringModifiers instead of testChar for event
- // generation in such case to avoid uppercase letters in text components.
- LWCToolkit lwcToolkit = (LWCToolkit)Toolkit.getDefaultToolkit();
- if ((lwcToolkit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK) &&
- Locale.SIMPLIFIED_CHINESE.equals(lwcToolkit.getDefaultKeyboardLocale())) ||
- (LWCToolkit.isLocaleUSInternationalPC(lwcToolkit.getDefaultKeyboardLocale()) &&
- LWCToolkit.isCharModifierKeyInUSInternationalPC(testChar) &&
- (testChar != testCharIgnoringModifiers))) {
- testChar = testCharIgnoringModifiers;
- }
+ int[] in = new int[] {keyCode, modifierFlags, KeyEventProcessing.useNationalLayouts ? 1 : 0, KeyEventProcessing.reportDeadKeysAsNormal ? 1 : 0};
+ int[] out = new int[5]; // [jkeyCode, jkeyLocation, usKeyCode, deadKeyCode, deadKeyComboCode]
+ NSEvent.nsToJavaKeyInfo(in, out);
jkeyCode = out[0];
- jextendedkeyCode = out[3];
jkeyLocation = out[1];
jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
NSEvent.nsToJavaEventType(eventType);
- }
- char javaChar = NSEvent.nsToJavaChar(testChar, modifierFlags, spaceKeyTyped);
- // Some keys may generate a KEY_TYPED, but we can't determine
- // what that character is. That's likely a bug, but for now we
- // just check for CHAR_UNDEFINED.
- if (javaChar == KeyEvent.CHAR_UNDEFINED) {
- postsTyped = false;
+ properties.put("US_KEYCODE", out[2]);
+ properties.put("DEAD_KEYCODE", out[3]);
+ if (chars != null && chars.isEmpty()) {
+ properties.put("DEAD_KEYSTROKE", out[4]);
+ } else {
+ properties.put("DEAD_KEYSTROKE", KeyEvent.VK_UNDEFINED);
+ }
+ properties.put("CHARACTERS", actualChars == null ? "" : actualChars);
}
+ char javaChar = (testChar == KeyEvent.CHAR_UNDEFINED) ? KeyEvent.CHAR_UNDEFINED :
+ NSEvent.nsToJavaChar(testChar, modifierFlags, spaceKeyTyped);
+
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
long when = System.currentTimeMillis();
@@ -223,32 +267,33 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
lastKeyPressCode = jkeyCode;
}
eventNotifier.notifyKeyEvent(jeventType, when, jmodifiers,
- jkeyCode, javaChar, jkeyLocation, jextendedkeyCode);
-
- // Current browser may be sending input events, so don't
- // post the KEY_TYPED here.
- postsTyped &= needsKeyTyped;
+ jkeyCode, javaChar, jkeyLocation, properties);
// That's the reaction on the PRESSED (not RELEASED) event as it comes to
// appear in MacOSX.
// Modifier keys (shift, etc) don't want to send TYPED events.
// On the other hand we don't want to generate keyTyped events
// for clipboard related shortcuts like Meta + [CVX]
- if (jeventType == KeyEvent.KEY_PRESSED && postsTyped &&
+ if (jeventType == KeyEvent.KEY_PRESSED && needsKeyTyped && javaChar != KeyEvent.CHAR_UNDEFINED &&
(jmodifiers & KeyEvent.META_DOWN_MASK) == 0) {
- // Enter and Space keys finish the input method processing,
- // KEY_TYPED and KEY_RELEASED events for them are synthesized in handleInputEvent.
- if (needsKeyReleased && (jkeyCode == KeyEvent.VK_ENTER || jkeyCode == KeyEvent.VK_SPACE)) {
- return;
+ if (actualChars == null) {
+ // Either macOS didn't send us anything in insertText: to type,
+ // or this event was not generated in AWTView.m. Let's fall back to using javaChar
+ // since we still need to generate KEY_TYPED events, for instance for Ctrl+ combinations.
+ actualChars = String.valueOf(javaChar);
+ }
+
+ for (char ch : actualChars.toCharArray()) {
+ eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers,
+ KeyEvent.VK_UNDEFINED, ch,
+ KeyEvent.KEY_LOCATION_UNKNOWN);
}
- eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers,
- KeyEvent.VK_UNDEFINED, javaChar,
- KeyEvent.KEY_LOCATION_UNKNOWN, jextendedkeyCode);
- //If events come from Firefox, released events should also be generated.
+
+ // If events come from Firefox, released events should also be generated.
if (needsKeyReleased) {
eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED, when, jmodifiers,
jkeyCode, javaChar,
- KeyEvent.KEY_LOCATION_UNKNOWN, jextendedkeyCode);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
}
}
}
@@ -262,13 +307,13 @@ void handleInputEvent(String text) {
eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED,
System.currentTimeMillis(),
0, KeyEvent.VK_UNDEFINED, c,
- KeyEvent.KEY_LOCATION_UNKNOWN, -1);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
index++;
}
eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED,
System.currentTimeMillis(),
0, lastKeyPressCode, c,
- KeyEvent.KEY_LOCATION_UNKNOWN, -1);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
}
}
@@ -314,4 +359,19 @@ else if (scrollPhase == NSEvent.SCROLL_PHASE_MOMENTUM_BEGAN) {
return roundDelta;
}
}
+
+ static class Jbr5762Fix {
+ static final boolean isEnabled;
+
+ static {
+ boolean isEnabledLocal = false;
+
+ try {
+ isEnabledLocal = Boolean.parseBoolean(System.getProperty("awt.mac.enforceMouseModifiersForMouseDragged", "true"));
+ } catch (Exception ignored) {
+ }
+
+ isEnabled = isEnabledLocal;
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
index b79f7c3ecf89..ddf85c410a29 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
@@ -35,14 +35,16 @@
import java.util.concurrent.atomic.AtomicReference;
import sun.awt.CGraphicsEnvironment;
-import sun.awt.CGraphicsDevice;
import sun.java2d.metal.MTLLayer;
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
+import sun.util.logging.PlatformLogger;
public class CPlatformView extends CFRetainedResource {
+ private static final PlatformLogger logger =
+ PlatformLogger.getLogger(CPlatformView.class.getName());
private native long nativeCreateView(int x, int y, int width, int height, long windowLayerPtr);
private static native void nativeSetAutoResizable(long awtView, boolean toResize);
private static native int nativeGetNSViewDisplayID(long awtView);
@@ -61,8 +63,7 @@ public CPlatformView() {
public void initialize(LWWindowPeer peer, CPlatformResponder responder) {
initializeBase(peer, responder);
- this.windowLayer = CGraphicsDevice.usingMetalPipeline()? createMTLLayer() : createCGLayer();
-
+ this.windowLayer = CGraphicsEnvironment.usingMetalPipeline()? createMTLLayer() : createCGLayer();
setPtr(nativeCreateView(0, 0, 0, 0, getWindowLayerPtr()));
}
@@ -199,8 +200,8 @@ private void deliverMouseEvent(final NSEvent event) {
}
private void deliverKeyEvent(NSEvent event) {
- responder.handleKeyEvent(event.getType(), event.getModifierFlags(), event.getCharacters(),
- event.getCharactersIgnoringModifiers(), event.getKeyCode(), true, false);
+ responder.handleKeyEvent(event.getType(), event.getModifierFlags(), event.getCharacters(), event.getActualCharacters(),
+ event.getKeyCode(), true, false);
}
/**
@@ -210,4 +211,13 @@ private void deliverKeyEvent(NSEvent event) {
private void deliverWindowDidExposeEvent() {
peer.notifyExpose(peer.getSize());
}
+
+ private void deliverChangeBackingProperties(float scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Changed backing properties, scale = " + scale);
+ }
+ if (scale > 0 && windowLayer != null) {
+ windowLayer.replaceSurfaceData(Math.round(scale));
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
index 615fcec9a77d..a407075119ad 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
@@ -30,11 +30,14 @@
import java.awt.DefaultKeyboardFocusManager;
import java.awt.Dialog;
import java.awt.Dialog.ModalityType;
+import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
import java.awt.Insets;
+import java.awt.KeyboardFocusManager;
import java.awt.MenuBar;
import java.awt.Point;
import java.awt.Rectangle;
@@ -43,13 +46,16 @@
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
+import java.awt.peer.ComponentPeer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -59,11 +65,16 @@
import com.apple.laf.ClientPropertyApplicator;
import com.apple.laf.ClientPropertyApplicator.Property;
+import com.jetbrains.exported.JBRApi;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.AWTAccessor.WindowAccessor;
+import sun.awt.AWTThreading;
+import sun.awt.CGraphicsDevice;
+import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.lwawt.LWKeyboardFocusManagerPeer;
+import sun.lwawt.LWComponentPeer;
import sun.lwawt.LWLightweightFramePeer;
import sun.lwawt.LWToolkit;
import sun.lwawt.LWWindowPeer;
@@ -74,6 +85,7 @@
public class CPlatformWindow extends CFRetainedResource implements PlatformWindow {
private native long nativeCreateNSWindow(long nsViewPtr,long ownerPtr, long styleBits, double x, double y, double w, double h);
private static native void nativeSetNSWindowStyleBits(long nsWindowPtr, int mask, int data);
+ private static native void nativeSetNSWindowAppearance(long nsWindowPtr, String appearanceName);
private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr);
private static native Insets nativeGetNSWindowInsets(long nsWindowPtr);
private static native void nativeSetNSWindowBounds(long nsWindowPtr, double x, double y, double w, double h);
@@ -82,7 +94,8 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
double x, double y, double w, double h);
private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
private static native void nativePushNSWindowToBack(long nsWindowPtr);
- private static native void nativePushNSWindowToFront(long nsWindowPtr);
+ private static native void nativePushNSWindowToFront(long nsWindowPtr, boolean wait);
+ private static native void nativeHideWindow(long nsWindowPtr, boolean wait);
private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title);
private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr);
private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage);
@@ -95,6 +108,11 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
private static native void nativeEnterFullScreenMode(long nsWindowPtr);
private static native void nativeExitFullScreenMode(long nsWindowPtr);
static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
+ private static native void nativeRaiseLevel(long nsWindowPtr, boolean popup, boolean onlyIfParentIsActive);
+ private static native boolean nativeDelayShowing(long nsWindowPtr);
+ private static native void nativeUpdateCustomTitleBar(long nsWindowPtr);
+ private static native void nativeCallDeliverMoveResizeEvent(long nsWindowPtr);
+ private static native void nativeSetRoundedCorners(long nsWindowPrt, float radius, int borderWidth, int borderColor);
// Logger to report issues happened during execution but that do not affect functionality
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
@@ -126,6 +144,8 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
public static final String WINDOW_FULL_CONTENT = "apple.awt.fullWindowContent";
public static final String WINDOW_TRANSPARENT_TITLE_BAR = "apple.awt.transparentTitleBar";
public static final String WINDOW_TITLE_VISIBLE = "apple.awt.windowTitleVisible";
+ public static final String WINDOW_APPEARANCE = "apple.awt.windowAppearance";
+ public static final String WINDOW_CORNER_RADIUS = "apple.awt.windowCornerRadius";
// This system property is named as jdk.* because it is not specific to AWT
// and it is also used in JavaFX
@@ -266,6 +286,18 @@ public void applyProperty(final CPlatformWindow c, final Object value) {
public void applyProperty(final CPlatformWindow c, final Object value) {
c.setStyleBits(TITLE_VISIBLE, value == null ? true : Boolean.parseBoolean(value.toString()));
}
+ },
+ new Property(WINDOW_APPEARANCE) {
+ public void applyProperty(final CPlatformWindow c, final Object value) {
+ if (value != null && (value instanceof String)) {
+ c.execute(ptr -> nativeSetNSWindowAppearance(ptr, (String) value));
+ }
+ }
+ },
+ new Property(WINDOW_CORNER_RADIUS) {
+ public void applyProperty(final CPlatformWindow c, final Object value) {
+ c.setRoundedCorners(value);
+ }
}
}) {
public CPlatformWindow convertJComponentToTarget(final JRootPane p) {
@@ -346,17 +378,32 @@ public void initialize(Window _target, LWWindowPeer _peer, PlatformWindow _owner
if (owner != null) {
hasOwnerPtr = 0L != owner.executeGet(ownerPtr -> {
- ref.set(nativeCreateNSWindow(viewPtr, ownerPtr, styleBits,
- bounds.x, bounds.y,
- bounds.width, bounds.height));
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("createNSWindow: owner=" + Long.toHexString(ownerPtr)
+ + ", styleBits=" + Integer.toHexString(styleBits)
+ + ", bounds=" + bounds);
+ }
+ long windowPtr = createNSWindow(viewPtr, ownerPtr, styleBits,
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("window created: " + Long.toHexString(windowPtr));
+ }
+ ref.set(windowPtr);
return 1;
});
}
if (!hasOwnerPtr) {
- ref.set(nativeCreateNSWindow(viewPtr, 0,
- styleBits, bounds.x, bounds.y,
- bounds.width, bounds.height));
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("createNSWindow: styleBits=" + Integer.toHexString(styleBits)
+ + ", bounds=" + bounds);
+ }
+ long windowPtr = createNSWindow(viewPtr, 0, styleBits,
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("window created: " + Long.toHexString(windowPtr));
+ }
+ ref.set(windowPtr);
}
});
setPtr(ref.get());
@@ -393,18 +440,15 @@ CPlatformView createContentView() {
protected int getInitialStyleBits() {
// defaults style bits
- int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
+ int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
- if (isNativelyFocusableWindow()) {
- styleBits = SET(styleBits, SHOULD_BECOME_KEY, true);
- styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true);
- }
+ styleBits |= getFocusableStyleBits();
final boolean isFrame = (target instanceof Frame);
final boolean isDialog = (target instanceof Dialog);
final boolean isPopup = (target.getType() == Window.Type.POPUP);
- if (isDialog) {
- styleBits = SET(styleBits, MINIMIZABLE, false);
+ if (isFrame) {
+ styleBits = SET(styleBits, MINIMIZABLE, true);
}
// Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always is undecorated.
@@ -532,9 +576,19 @@ protected int getInitialStyleBits() {
return styleBits;
}
- // this is the counter-point to -[CWindow _nativeSetStyleBit:]
private void setStyleBits(final int mask, final boolean value) {
- execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value ? mask : 0));
+ setStyleBits(mask, value ? mask : 0);
+ }
+
+ // this is the counter-point to -[CWindow _nativeSetStyleBit:]
+ private void setStyleBits(final int mask, final int value) {
+ execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("nativeSetNSWindowStyleBits: window=" + Long.toHexString(ptr)
+ + ", mask=" + Integer.toHexString(mask) + ", value=" + Integer.toHexString(value));
+ }
+ nativeSetNSWindowStyleBits(ptr, mask, value);
+ });
}
private native void _toggleFullScreenMode(final long model);
@@ -564,8 +618,8 @@ public void dispose() {
@Override // PlatformWindow
public FontMetrics getFontMetrics(Font f) {
- // TODO: not implemented
- (new RuntimeException("unimplemented")).printStackTrace();
+ logger.severe("CPlatformWindow.getFontMetrics: exception occurred: ",
+ new RuntimeException("unimplemented"));
return null;
}
@@ -599,6 +653,18 @@ public SurfaceData replaceSurfaceData() {
return contentView.replaceSurfaceData();
}
+ public void displayChanged(boolean profileOnly) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine(profileOnly ? "DISPLAY_PROFILE_CHANGED" : "DISPLAY_CHANGED");
+ }
+
+ if (peer != null && !profileOnly) {
+ EventQueue.invokeLater(
+ () -> ((SunGraphicsEnvironment) GraphicsEnvironment.
+ getLocalGraphicsEnvironment()).displayChanged());
+ }
+ }
+
@Override // PlatformWindow
public void setBounds(int x, int y, int w, int h) {
execute(ptr -> nativeSetNSWindowBounds(ptr, x, y, w, h));
@@ -651,6 +717,16 @@ public boolean isVisible() {
return this.visible;
}
+ private static LWWindowPeer getBlockerFor(Window window) {
+ if (window != null) {
+ ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (peer instanceof LWWindowPeer) {
+ return ((LWWindowPeer)peer).getBlocker();
+ }
+ }
+ return null;
+ }
+
@Override // PlatformWindow
public void setVisible(boolean visible) {
// Configure stuff
@@ -701,52 +777,58 @@ public void setVisible(boolean visible) {
}
}
+ this.visible = visible;
+
// Actually show or hide the window
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
- if (blocker == null || !visible) {
+ if (!visible) {
+ execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativeHideWindow(ptr, wait)));
+ } else if (delayShowing()) {
+ if (blocker == null) {
+ Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
+ LWWindowPeer focusedWindowBlocker = getBlockerFor(focusedWindow);
+ if (focusedWindowBlocker == peer) {
+ // try to switch to target space if we're adding a modal dialog
+ // that would block currently focused window
+ owner.execute(CWrapper.NSWindow::orderFront);
+ }
+ }
+ } else if (blocker == null) {
// If it ain't blocked, or is being hidden, go regular way
- if (visible) {
- contentView.execute(viewPtr -> {
- execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
- viewPtr));
- });
+ contentView.execute(viewPtr -> {
+ execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
+ viewPtr));
+ });
- boolean isPopup = (target.getType() == Window.Type.POPUP);
- execute(ptr -> {
- if (isPopup) {
- // Popups in applets don't activate applet's process
- CWrapper.NSWindow.orderFrontRegardless(ptr);
- } else {
- CWrapper.NSWindow.orderFront(ptr);
- }
+ boolean isPopup = (target.getType() == Window.Type.POPUP);
+ execute(ptr -> {
+ if (isPopup) {
+ // Popups in applets don't activate applet's process
+ CWrapper.NSWindow.orderFrontRegardless(ptr);
+ } else {
+ CWrapper.NSWindow.orderFront(ptr);
+ }
- boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
- if (!isKeyWindow) {
- CWrapper.NSWindow.makeKeyWindow(ptr);
- }
+ boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
+ if (!isKeyWindow) {
+ logger.fine("setVisible: makeKeyWindow");
+ CWrapper.NSWindow.makeKeyWindow(ptr);
+ }
- if (owner != null
- && owner.getPeer() instanceof LWLightweightFramePeer) {
- LWLightweightFramePeer peer =
- (LWLightweightFramePeer) owner.getPeer();
-
- long ownerWindowPtr = peer.getOverriddenWindowHandle();
- if (ownerWindowPtr != 0) {
- //Place window above JavaFX stage
- CWrapper.NSWindow.addChildWindow(
- ownerWindowPtr, ptr,
- CWrapper.NSWindow.NSWindowAbove);
- }
+ if (owner != null
+ && owner.getPeer() instanceof LWLightweightFramePeer) {
+ LWLightweightFramePeer peer =
+ (LWLightweightFramePeer) owner.getPeer();
+
+ long ownerWindowPtr = peer.getOverriddenWindowHandle();
+ if (ownerWindowPtr != 0) {
+ //Place window above JavaFX stage
+ CWrapper.NSWindow.addChildWindow(
+ ownerWindowPtr, ptr,
+ CWrapper.NSWindow.NSWindowAbove);
}
- });
- } else {
- execute(ptr->{
- // immediately hide the window
- CWrapper.NSWindow.orderOut(ptr);
- // process the close
- CWrapper.NSWindow.close(ptr);
- });
- }
+ }
+ });
} else {
// otherwise, put it in a proper z-order
CPlatformWindow bw
@@ -759,7 +841,6 @@ public void setVisible(boolean visible) {
});
});
}
- this.visible = visible;
nativeSynthesizeMouseEnteredExitedEvents();
@@ -769,7 +850,7 @@ public void setVisible(boolean visible) {
// Manage parent-child relationship when showing
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
- if (visible) {
+ if (visible && !delayShowing()) {
// Order myself above my parent
if (owner != null && owner.isVisible()) {
owner.execute(ownerPtr -> {
@@ -801,7 +882,10 @@ public void setVisible(boolean visible) {
// Deal with the blocker of the window being shown
if (blocker != null && visible) {
// Make sure the blocker is above its siblings
- ((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings();
+ CPlatformWindow blockerWindow = (CPlatformWindow) blocker.getPlatformWindow();
+ if (!blockerWindow.delayShowing()) {
+ blockerWindow.orderAboveSiblings();
+ }
}
}
@@ -836,6 +920,7 @@ public void toBack() {
@Override // PlatformWindow
public void toFront() {
+ if (delayShowing()) return;
LWCToolkit lwcToolkit = (LWCToolkit) Toolkit.getDefaultToolkit();
Window w = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
@@ -845,7 +930,7 @@ public void toFront() {
lwcToolkit.activateApplicationIgnoringOtherApps();
}
updateFocusabilityForAutoRequestFocus(false);
- execute(CPlatformWindow::nativePushNSWindowToFront);
+ execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativePushNSWindowToFront(ptr, wait)));
updateFocusabilityForAutoRequestFocus(true);
}
@@ -889,6 +974,7 @@ public boolean rejectFocusRequest(FocusEvent.Cause cause) {
@Override
public boolean requestWindowFocus() {
+ if (delayShowing()) return false;
execute(ptr -> {
if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) {
CWrapper.NSWindow.makeMainWindow(ptr);
@@ -907,10 +993,28 @@ public boolean isActive() {
return ref.get();
}
+ private boolean isTabbedWindow() {
+ AtomicBoolean ref = new AtomicBoolean();
+ execute(ptr -> {
+ ref.set(CWrapper.NSWindow.isTabbedWindow(ptr));
+ });
+ return ref.get();
+ }
+
+ // We want a window to be always shown at the same space as its owning window.
+ // But macOS doesn't have an API to control the target space for a window -
+ // it's always shown at the active space. So if the target space isn't active now,
+ // the only way to achieve our goal seems to be delaying the appearance of the
+ // window till the target space becomes active.
+ private boolean delayShowing() {
+ AtomicBoolean ref = new AtomicBoolean(false);
+ execute(ptr -> ref.set(nativeDelayShowing(ptr)));
+ return ref.get();
+ }
+
@Override
public void updateFocusableWindowState() {
- final boolean isFocusable = isNativelyFocusableWindow();
- setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
+ setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, getFocusableStyleBits()); // set both bits at once
}
@Override
@@ -1051,13 +1155,17 @@ public void setModalBlocked(boolean blocked) {
execute(ptr -> nativeSynthesizeMouseEnteredExitedEvents(ptr, CocoaConstants.NSEventTypeMouseExited));
}
- execute(ptr -> nativeSetEnabled(ptr, !blocked));
+ execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("nativeSetEnabled: window=" + Long.toHexString(ptr) + ", enabled=" + !blocked);
+ }
+ nativeSetEnabled(ptr, !blocked);
+ });
Window currFocus = LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow();
if (!blocked && (target == currFocus)) {
requestWindowFocus();
}
- checkBlockingAndOrder();
}
public final void invalidateShadow() {
@@ -1105,21 +1213,138 @@ public long getLayerPtr() {
return contentView.getWindowLayerPtr();
}
+ private final static int INVOKE_LATER_DISABLED = 0;
+ private final static int INVOKE_LATER_AUTO = 1;
+ private final static int INVOKE_LATER_ENABLED = 2;
+
+ private final static int INVOKE_LATER_FLUSH_BUFFERS = getInvokeLaterMode();
+
+ private static int getInvokeLaterMode() {
+ final String invokeLaterKey = "awt.mac.flushBuffers.invokeLater";
+ final String invokeLaterArg = System.getProperty(invokeLaterKey);
+ final int result;
+ if (invokeLaterArg == null) {
+ // default = 'auto':
+ result = INVOKE_LATER_AUTO;
+ } else {
+ switch (invokeLaterArg.toLowerCase()) {
+ default:
+ case "auto":
+ result = INVOKE_LATER_AUTO;
+ break;
+ case "false":
+ result = INVOKE_LATER_DISABLED;
+ break;
+ case "true":
+ result = INVOKE_LATER_ENABLED;
+ break;
+ }
+ logger.info("CPlatformWindow: property \"{0}={1}\", using invokeLater={2}.",
+ invokeLaterKey, invokeLaterArg,
+ (result == INVOKE_LATER_DISABLED) ? "false"
+ : ((result == INVOKE_LATER_AUTO) ? "auto" : "true"));
+ }
+ return result;
+ }
+
+ private final static int INVOKE_LATER_COUNT = 5;
+ /** per window counter of remaining invokeLater calls */
+ private final AtomicInteger invokeLaterCount = new AtomicInteger();
+
+ // Specific class needed to get obvious stack traces:
+ private final class EmptyRunnable implements Runnable {
+ @Override
+ public void run() {
+ // Posting an empty to flush the EventQueue without blocking the main thread
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("CPlatformWindow.flushBuffers: run() invoked on {0}",
+ getIdentifier(target));
+ }
+ }
+ };
+ private final EmptyRunnable emptyTask = new EmptyRunnable();
+
void flushBuffers() {
+ // only 1 usage by deliverMoveResizeEvent():
if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) {
+ // use the system property 'awt.mac.flushBuffers.invokeLater' to true/auto (default: auto)
+ // to avoid deadlocks caused by the LWCToolkit.invokeAndWait() call below:
+ boolean useInvokeLater;
+
+ switch (INVOKE_LATER_FLUSH_BUFFERS) {
+ case INVOKE_LATER_DISABLED:
+ useInvokeLater = false;
+ break;
+ default:
+ case INVOKE_LATER_AUTO:
+ useInvokeLater = false;
+ if (peer != null) {
+ final GraphicsDevice device = peer.getGraphicsConfiguration().getDevice();
+ if (device instanceof CGraphicsDevice) {
+ // JBR-5497: avoid deadlock in mirroring mode (laptop + external screen):
+ useInvokeLater = ((CGraphicsDevice)device).isMirroring();
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("CPlatformWindow.flushBuffers: CGraphicsDevice.isMirroring = {0}",
+ useInvokeLater);
+ }
+ }
+ }
+ // JBR-5497: keep few more invokeLater() when computer returns from sleep or displayChanged()
+ // to avoid deadlocks until solved definitely:
+ if (useInvokeLater) {
+ // reset to max count:
+ invokeLaterCount.set(INVOKE_LATER_COUNT);
+ } else {
+ final int prev = invokeLaterCount.get();
+ if (prev > 0) {
+ invokeLaterCount.compareAndSet(prev, prev - 1);
+ useInvokeLater = true;
+ }
+ }
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("CPlatformWindow.flushBuffers: useInvokeLater = {0} (count = {1})",
+ useInvokeLater, invokeLaterCount.get());
+ }
+ break;
+ case INVOKE_LATER_ENABLED:
+ useInvokeLater = true;
+ break;
+ }
try {
- LWCToolkit.invokeAndWait(new Runnable() {
- @Override
- public void run() {
- //Posting an empty to flush the EventQueue without blocking the main thread
+ // check invokeAndWait: KO (operations require AWTLock and main thread)
+ // => use invokeLater as it is an empty event to force refresh ASAP
+ if (useInvokeLater) {
+ LWCToolkit.invokeLater(emptyTask, target);
+ } else {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("CPlatformWindow.flushBuffers: enter " +
+ "LWCToolkit.invokeAndWait(emptyTask) on target = {0}",
+ getIdentifier(target));
+ }
+
+ LWCToolkit.invokeAndWait(emptyTask, target);
+
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("CPlatformWindow.flushBuffers: exit " +
+ "LWCToolkit.invokeAndWait(emptyTask) on target = {0}",
+ getIdentifier(target));
}
- }, target);
- } catch (InvocationTargetException e) {
- e.printStackTrace();
+ }
+ } catch (InvocationTargetException ite) {
+ logger.severe("CPlatformWindow.flushBuffers: exception occurred: ", ite);
}
}
}
+ private static String getIdentifier(Window t) {
+ if (t == null) {
+ return "null";
+ }
+ return t.getClass().getName()
+ + "['" + Objects.toString(t.getName(), "")
+ + "' @" + Integer.toHexString(System.identityHashCode(t)) + ']';
+ }
+
/**
* Helper method to get a pointer to the native view from the PlatformWindow.
*/
@@ -1137,16 +1362,14 @@ static long getNativeViewPtr(PlatformWindow platformWindow) {
* Callbacks from the AWTWindow and AWTView objc classes.
*************************************************************/
private void deliverWindowFocusEvent(boolean gained, CPlatformWindow opposite){
- // Fix for 7150349: ignore "gained" notifications when the app is inactive.
- if (gained && !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()) {
- focusLogger.fine("the app is inactive, so the notification is ignored");
- return;
- }
-
LWWindowPeer oppositePeer = (opposite == null)? null : opposite.getPeer();
responder.handleWindowFocusEvent(gained, oppositePeer);
}
+ public void doDeliverMoveResizeEvent() {
+ execute(ptr -> nativeCallDeliverMoveResizeEvent(ptr));
+ }
+
protected void deliverMoveResizeEvent(int x, int y, int width, int height,
boolean byUser) {
AtomicBoolean ref = new AtomicBoolean();
@@ -1206,21 +1429,13 @@ private void deliverNCMouseDown() {
}
}
- /*
- * Our focus model is synthetic and only non-simple window
- * may become natively focusable window.
- */
- private boolean isNativelyFocusableWindow() {
- if (peer == null) {
- return false;
- }
-
- return !peer.isSimpleWindow() && target.getFocusableWindowState();
- }
-
- private boolean isBlocked() {
- LWWindowPeer blocker = (peer != null) ? peer.getBlocker() : null;
- return (blocker != null);
+ // returns a combination of SHOULD_BECOME_KEY/SHOULD_BECOME_MAIN relevant for the current window
+ private int getFocusableStyleBits() {
+ return (peer == null || target == null || !target.isFocusableWindow())
+ ? 0
+ : peer.isSimpleWindow()
+ ? SHOULD_BECOME_KEY
+ : SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN;
}
/*
@@ -1229,30 +1444,39 @@ private boolean isBlocked() {
* circumstances.
*/
private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) {
- if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return;
- setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
+ if (target.isAutoRequestFocus()) return;
+ int focusableStyleBits = getFocusableStyleBits();
+ if (focusableStyleBits == 0) return;
+ setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN,
+ isFocusable ? focusableStyleBits : 0); // set both bits at once
}
- private boolean checkBlockingAndOrder() {
+ private void checkBlockingAndOrder() {
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
if (blocker == null) {
- return false;
+ // If it's not blocked, make sure it's above its siblings
+ orderAboveSiblings();
+ return;
}
if (blocker instanceof CPrinterDialogPeer) {
- return true;
+ return;
}
CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow();
- pWindow.orderAboveSiblings();
+ if (!pWindow.delayShowing()) {
+ pWindow.orderAboveSiblings();
- pWindow.execute(ptr -> {
- CWrapper.NSWindow.orderFrontRegardless(ptr);
- CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
- CWrapper.NSWindow.makeMainWindow(ptr);
- });
- return true;
+ pWindow.execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Focus blocker " + Long.toHexString(ptr));
+ }
+ CWrapper.NSWindow.orderFrontRegardless(ptr);
+ CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
+ CWrapper.NSWindow.makeMainWindow(ptr);
+ });
+ }
}
private boolean isIconified() {
@@ -1290,7 +1514,9 @@ private void orderAboveSiblings() {
// which is going to become 'main window', are placed above their siblings.
CPlatformWindow rootOwner = getRootOwner();
if (rootOwner.isVisible() && !rootOwner.isIconified() && !rootOwner.isActive()) {
- rootOwner.execute(CWrapper.NSWindow::orderFront);
+ if (rootOwner != this || !isTabbedWindow()) {
+ rootOwner.execute(CWrapper.NSWindow::orderFrontIfOnActiveSpace);
+ }
}
// Do not order child windows of iconified owner.
@@ -1314,7 +1540,7 @@ private void orderAboveSiblingsImpl(Window[] windows) {
if (p instanceof LWWindowPeer) {
CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
iconified = isIconified();
- if (pw != null && pw.isVisible() && !iconified) {
+ if (pw != null && pw.isVisible() && !iconified && !pw.delayShowing()) {
// If the window is one of ancestors of 'main window' or is going to become main by itself,
// the window should be ordered above its siblings; otherwise the window is just ordered
// above its nearest parent.
@@ -1326,7 +1552,7 @@ private void orderAboveSiblingsImpl(Window[] windows) {
}
pwUnder.execute(underPtr -> {
pw.execute(ptr -> {
- CWrapper.NSWindow.orderWindow(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
+ CWrapper.NSWindow.orderWindowIfOnActiveSpace(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
});
});
pwUnder = pw;
@@ -1350,10 +1576,10 @@ private void orderAboveSiblingsImpl(Window[] windows) {
}
protected void applyWindowLevel(Window target) {
- if (target.isAlwaysOnTop() && target.getType() != Window.Type.POPUP) {
- execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSFloatingWindowLevel));
- } else if (target.getType() == Window.Type.POPUP) {
- execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSPopUpMenuWindowLevel));
+ boolean popup = target.getType() == Window.Type.POPUP;
+ boolean alwaysOnTop = target.isAlwaysOnTop();
+ if (popup || alwaysOnTop || owner != null) {
+ execute(ptr -> nativeRaiseLevel(ptr, popup, !popup && !alwaysOnTop));
}
}
@@ -1371,6 +1597,18 @@ private boolean isSimpleWindowOwnedByEmbeddedFrame() {
}
return false;
}
+
+ private long createNSWindow(long nsViewPtr,
+ long ownerPtr,
+ long styleBits,
+ double x,
+ double y,
+ double w,
+ double h) {
+ return AWTThreading.executeWaitToolkit(() ->
+ nativeCreateNSWindow(nsViewPtr, ownerPtr, styleBits, x, y, w, h));
+ }
+
// ----------------------------------------------------------------------
// NATIVE CALLBACKS
// ----------------------------------------------------------------------
@@ -1381,9 +1619,7 @@ private void windowWillMiniaturize() {
private void windowDidBecomeMain() {
lastBecomeMainTime = System.currentTimeMillis();
- if (checkBlockingAndOrder()) return;
- // If it's not blocked, make sure it's above its siblings
- orderAboveSiblings();
+ checkBlockingAndOrder();
}
private void windowWillEnterFullScreen() {
@@ -1403,4 +1639,37 @@ private void windowDidExitFullScreen() {
isInFullScreen = false;
isFullScreenAnimationOn = false;
}
+
+ @JBRApi.Provides("java.awt.Window.CustomTitleBarPeer#update")
+ private static void updateCustomTitleBar(ComponentPeer peer) {
+ if (peer instanceof LWWindowPeer lwwp &&
+ lwwp.getPlatformWindow() instanceof CPlatformWindow cpw) {
+ cpw.execute(CPlatformWindow::nativeUpdateCustomTitleBar);
+ }
+ }
+
+ @JBRApi.Provides("RoundedCornersManager")
+ private static void setRoundedCorners(Window window, Object params) {
+ Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (peer instanceof CPlatformWindow) {
+ ((CPlatformWindow)peer).setRoundedCorners(params);
+ } else if (window instanceof RootPaneContainer) {
+ JRootPane rootpane = ((RootPaneContainer)window).getRootPane();
+ if (rootpane != null) {
+ rootpane.putClientProperty(WINDOW_CORNER_RADIUS, params);
+ }
+ }
+ }
+
+ private void setRoundedCorners(Object params) {
+ if (params instanceof Float) {
+ execute(ptr -> nativeSetRoundedCorners(ptr, (float) params, 0, 0));
+ } else if (params instanceof Object[]) {
+ Object[] values = (Object[]) params;
+ if (values.length == 3 && values[0] instanceof Float && values[1] instanceof Integer && values[2] instanceof Color) {
+ Color color = (Color) values[2];
+ execute(ptr -> nativeSetRoundedCorners(ptr, (float) values[0], (int) values[1], color.getRGB()));
+ }
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
index a336f4278128..7b319521b32e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
@@ -30,6 +30,7 @@
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.print.*;
+import java.io.File;
import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;
@@ -188,6 +189,15 @@ protected void setAttributes(PrintRequestAttributeSet attributes) throws Printer
if (attributes == null) {
return;
}
+ if (getPrintService() == null && isPrintToFile) {
+ Destination destination = (Destination)attributes.get(Destination.class);
+ if (destination != null) {
+ try {
+ destinationAttr = "" + new File(destination.getURI().getSchemeSpecificPart());
+ } catch (Exception e) {
+ }
+ }
+ }
Attribute attr = attributes.get(Media.class);
if (attr instanceof CustomMediaTray) {
CustomMediaTray customTray = (CustomMediaTray) attr;
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
index 5dba06cb612b..808998e4c2e0 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
@@ -34,6 +34,7 @@
final class CRobot implements RobotPeer {
private static final int MOUSE_LOCATION_UNKNOWN = -1;
+ private static final int DEFAULT_SAFE_DELAY_MILLIS = 50;
private final CGraphicsDevice fDevice;
private int mouseLastX = MOUSE_LOCATION_UNKNOWN;
@@ -50,7 +51,12 @@ final class CRobot implements RobotPeer {
*/
CRobot(CGraphicsDevice d) {
fDevice = d;
- initRobot();
+ int safeDelayMillis = Integer.getInteger(
+ "sun.awt.osx.RobotSafeDelayMillis", DEFAULT_SAFE_DELAY_MILLIS);
+ if (safeDelayMillis < 0) {
+ safeDelayMillis = DEFAULT_SAFE_DELAY_MILLIS;
+ }
+ initRobot(safeDelayMillis);
}
/**
@@ -187,7 +193,7 @@ public int getRGBPixel(int x, int y) {
return c;
}
- private native void initRobot();
+ private native void initRobot(int safeDelayMillis);
private native void mouseEvent(int lastX, int lastY, int buttonsState,
boolean isButtonsDownState,
boolean isMouseMove);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
index 6aa36453ac0f..e54e5be9f10c 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
@@ -81,32 +81,26 @@ public void drawString(final SunGraphics2D sg2d, final String s, final double x,
}
}
- private boolean hasSlotData(GlyphVector gv) {
+ private boolean hasSlotData(GlyphVector gv, Font2D font2D) {
final int length = gv.getNumGlyphs();
- for (int i = 0; i < length; i++) {
- if ((gv.getGlyphCode(i) & CompositeGlyphMapper.SLOTMASK) != 0) {
- return true;
+ if (length > 0) {
+ int slotMask = font2D
+ .getSlotInfoForGlyph(gv.getGlyphCode(0))
+ .getSlotMask();
+ for (int i = 0; i < length; i++) {
+ int glyphCode = gv.getGlyphCode(i);
+ if ((glyphCode & slotMask) != 0) {
+ return true;
+ }
}
}
return false;
}
- private Font getSlotFont(Font font, int slot) {
- Font2D f2d = FontUtilities.getFont2D(font);
- if (f2d instanceof CFont) {
- CompositeFont cf = ((CFont)f2d).getCompositeFont2D();
- PhysicalFont pf = cf.getSlotFont(slot);
- Font f = new Font(pf.getFontName(null),
- font.getStyle(), font.getSize());
- return f;
- }
- return null;
- }
-
- private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector gV, int start, int count) {
+ private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector gV, int start, int count, int slotShift) {
int[] glyphs = new int[count];
for (int i = 0; i < count; i++) {
- glyphs[i] = gV.getGlyphCode(start+i) & CompositeGlyphMapper.GLYPHMASK;
+ glyphs[i] = gV.getGlyphCode(start+i) >>> slotShift;
}
// Positions should be null to recalculate by native methods,
// if GV was segmented.
@@ -119,11 +113,11 @@ private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector g
return sgv;
}
- private int getLengthOfSameSlot(final GlyphVector gV, final int targetSlot, final int start, final int length) {
+ private int getLengthOfSameSlot(final GlyphVector gV, final int targetSlot, final int slotMask,
+ final int start, final int length) {
int count = 1;
for (; start + count < length; count++) {
- int slot = (gV.getGlyphCode(start + count) &
- CompositeGlyphMapper.SLOTMASK) >> 24;
+ int slot = gV.getGlyphCode(start + count) & slotMask;
if (targetSlot != slot) {
break;
}
@@ -143,25 +137,33 @@ private void drawGlyphVectorImpl(final SunGraphics2D sg2d, final GlyphVector gV,
public void drawGlyphVector(final SunGraphics2D sg2d, final GlyphVector gV, final float x, final float y) {
final Font prevFont = sg2d.getFont();
- sg2d.setFont(gV.getFont());
+ Font gvFont = gV.getFont();
+ Font2D f2d = FontUtilities.getFont2D(gvFont);
+ if (f2d instanceof FontSubstitution fs) {
+ f2d = fs.getCompositeFont2D();
+ }
- if (hasSlotData(gV)) {
+ if (hasSlotData(gV, f2d)) {
final int length = gV.getNumGlyphs();
float[] positions = gV.getGlyphPositions(0, length, null);
int start = 0;
while (start < length) {
- int slot = (gV.getGlyphCode(start) &
- CompositeGlyphMapper.SLOTMASK) >> 24;
- sg2d.setFont(getSlotFont(gV.getFont(), slot));
- int count = getLengthOfSameSlot(gV, slot, start, length);
+ int glyphCode = gV.getGlyphCode(start);
+ Font2D.SlotInfo slotInfo = f2d.getSlotInfoForGlyph(glyphCode);
+ int slotMask = slotInfo.getSlotMask();
+ int slot = glyphCode & slotMask;
+ sg2d.setFont(new Font(slotInfo.font.getFontName(null),
+ gvFont.getStyle(), gvFont.getSize()));
+ int count = getLengthOfSameSlot(gV, slot, slotMask, start, length);
GlyphVector rangeGV = getGlyphVectorWithRange(sg2d.getFont(),
- gV, start, count);
+ gV, start, count, slotInfo.slotShift);
drawGlyphVectorImpl(sg2d, rangeGV,
x + positions[start * 2],
y + positions[start * 2 + 1]);
start += count;
}
} else {
+ sg2d.setFont(gvFont);
drawGlyphVectorImpl(sg2d, gV, x, y);
}
sg2d.setFont(prevFont);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java
new file mode 100644
index 000000000000..d7ab98a8fc8c
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.lwawt.macosx;
+
+import sun.lwawt.macosx.concurrent.Dispatch;
+
+import java.awt.EventQueue;
+import java.awt.AWTError;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+@SuppressWarnings("removal")
+public class CThreading {
+ static String APPKIT_THREAD_NAME = "AWT-AppKit";
+
+ static boolean isEventQueue() {
+ return EventQueue.isDispatchThread();
+ }
+
+ private static native boolean isMainThread();
+
+ public static boolean isAppKit() {
+ if (APPKIT_THREAD_NAME.equals(Thread.currentThread().getName())) return true;
+
+ if (isMainThread()) {
+ Thread.currentThread().setName(APPKIT_THREAD_NAME);
+ return true;
+ }
+ return false;
+ }
+
+
+ static boolean assertEventQueue() {
+ final boolean isEventQueue = isEventQueue();
+ assert isEventQueue : "Threading violation: not EventQueue thread";
+ return isEventQueue;
+ }
+
+ static boolean assertNotEventQueue() {
+ final boolean isNotEventQueue = isEventQueue();
+ assert isNotEventQueue : "Threading violation: EventQueue thread";
+ return isNotEventQueue;
+ }
+
+ static boolean assertAppKit() {
+ final boolean isAppKitThread = isAppKit();
+ assert isAppKitThread : "Threading violation: not AppKit thread";
+ return isAppKitThread;
+ }
+
+ static boolean assertNotAppKit() {
+ final boolean isNotAppKitThread = !isAppKit();
+ assert isNotAppKitThread : "Threading violation: AppKit thread";
+ return isNotAppKitThread;
+ }
+
+ public static V executeOnAppKit(final Callable command) throws Throwable {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch == null) {
+ throw new AWTError("Could not get Dispatch object");
+ }
+
+ Callable commandWithTNameFix = () -> {
+ if (!APPKIT_THREAD_NAME.equals(Thread.currentThread().getName())) {
+ Thread.currentThread().setName(APPKIT_THREAD_NAME);
+ }
+
+ return command.call();
+ };
+
+ FutureTask future = new FutureTask<>(commandWithTNameFix);
+
+ dispatch.getNonBlockingMainQueueExecutor().execute(future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ throw new AWTError(e.getMessage());
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ } else
+ return command.call();
+ }
+
+ public static V privilegedExecuteOnAppKit(Callable command)
+ throws Exception {
+ try {
+ return java.security.AccessController.doPrivileged(
+ (PrivilegedExceptionAction) () -> {
+ //noinspection TryWithIdenticalCatches
+ try {
+ return executeOnAppKit(command);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Throwable throwable) {
+ throw new Exception(throwable);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ }
+ }
+
+ public static void executeOnAppKit(Runnable command) {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch != null) {
+ dispatch.getNonBlockingMainQueueExecutor().execute(command);
+ }
+ else {
+ throw new AWTError("Could not get Dispatch object");
+ }
+ } else
+ command.run();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
index 516b8de15506..1baecf34555b 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
@@ -25,6 +25,8 @@
package sun.lwawt.macosx;
+import sun.awt.AWTThreading;
+
final class CWrapper {
private CWrapper() { }
@@ -45,22 +47,34 @@ static final class NSWindow {
// 'level' is one of the keys defined above
static native void setLevel(long window, int level);
- static native void makeKeyAndOrderFront(long window);
- static native void makeKeyWindow(long window);
+ static void makeKeyAndOrderFront(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeMakeKeyAndOrderFront(window, wait));
+ }
+ static void makeKeyWindow(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeMakeKeyWindow(window, wait));
+ }
static native void makeMainWindow(long window);
static native boolean canBecomeMainWindow(long window);
static native boolean isKeyWindow(long window);
static native void orderFront(long window);
+ static native void orderFrontIfOnActiveSpace(long window);
static native void orderFrontRegardless(long window);
static native void orderWindow(long window, int ordered, long relativeTo);
+ static native void orderWindowIfOnActiveSpace(long window, int ordered, long relativeTo);
/**
* Removes the window from the screen.
*
* @param window the pointer of the NSWindow
*/
- static native void orderOut(long window);
+ static void orderOut(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeOrderOut(window, wait));
+ }
+
+ private static native void nativeOrderOut(long window, boolean wait);
+ private static native void nativeMakeKeyWindow(long window, boolean wait);
+ private static native void nativeMakeKeyAndOrderFront(long window, boolean wait);
/**
* Removes the window from the screen and releases it. According to
@@ -94,6 +108,8 @@ static final class NSWindow {
static native void zoom(long window);
static native void makeFirstResponder(long window, long responder);
+
+ static native boolean isTabbedWindow(long window);
}
static final class NSView {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRKeyboardMacOS.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRKeyboardMacOS.java
new file mode 100644
index 000000000000..d0efebf8c4a4
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRKeyboardMacOS.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx;
+
+import com.jetbrains.desktop.JBRKeyboard;
+import com.jetbrains.exported.JBRApi;
+
+import java.util.List;
+
+public class JBRKeyboardMacOS extends JBRKeyboard {
+ @Override
+ public String getCurrentKeyboardLayout() {
+ // ensure LWCToolkit is initialized
+ LWCToolkit.getLWCToolkit();
+
+ return LWCToolkit.getKeyboardLayoutId();
+ }
+
+ @Override
+ public List getEnabledKeyboardLayouts() {
+ // ensure LWCToolkit is initialized
+ LWCToolkit.getLWCToolkit();
+
+ return LWCToolkit.getKeyboardLayoutList(false);
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRSystemShortcutsMacOS.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRSystemShortcutsMacOS.java
new file mode 100644
index 000000000000..cf5655cc47f5
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRSystemShortcutsMacOS.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2024 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx;
+
+import com.jetbrains.exported.JBRApi;
+
+import java.awt.*;
+import java.awt.desktop.SystemHotkey;
+import java.awt.event.InputEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@JBRApi.Service
+@JBRApi.Provides("SystemShortcuts")
+public class JBRSystemShortcutsMacOS {
+ @JBRApi.Provides("SystemShortcuts.Shortcut")
+ public static class Shortcut {
+ private final int keyCode;
+ private final char keyChar;
+ private final int modifiers;
+ private final String id;
+ private final String description;
+
+ public Shortcut(int keyCode, char keyChar, int modifiers, String id, String description) {
+ this.keyCode = keyCode;
+ this.keyChar = keyChar;
+ this.modifiers = modifiers;
+ this.id = id;
+ this.description = description;
+ }
+
+ public int getKeyCode() {
+ return keyCode;
+ }
+
+ public char getKeyChar() {
+ return keyChar;
+ }
+
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Shortcut shortcut)) return false;
+ return keyCode == shortcut.keyCode && keyChar == shortcut.keyChar && modifiers == shortcut.modifiers && Objects.equals(id, shortcut.id) && Objects.equals(description, shortcut.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(keyCode, keyChar, modifiers, id, description);
+ }
+
+ @Override
+ public String toString() {
+ return "Shortcut{" +
+ "keyCode=" + keyCode +
+ ", keyChar=" + (int)keyChar +
+ ", modifiers=" + modifiers +
+ ", id='" + id + '\'' +
+ ", description='" + description + '\'' +
+ '}';
+ }
+ }
+
+ @JBRApi.Provided("SystemShortcuts.ChangeEventListener")
+ public interface ChangeEventListener {
+ void handleSystemShortcutsChangeEvent();
+ }
+
+ public Shortcut[] querySystemShortcuts() {
+ var hotkeys = SystemHotkey.readSystemHotkeys();
+ var result = new Shortcut[hotkeys.size()];
+ for (int i = 0; i < hotkeys.size(); ++i) {
+ var hotkey = hotkeys.get(i);
+ result[i] = new Shortcut(
+ hotkey.getKeyCode(),
+ hotkey.getKeyChar(),
+ hotkey.getModifiers(),
+ hotkey.getId(),
+ hotkey.getDescription()
+ );
+ }
+
+ return result;
+ }
+
+ public void setChangeListener(ChangeEventListener listener) {
+ SystemHotkey.setChangeEventHandler(listener::handleSystemShortcutsChangeEvent);
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRTextInputMacOS.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRTextInputMacOS.java
new file mode 100644
index 000000000000..5a18cec2c145
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/JBRTextInputMacOS.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx;
+
+import com.jetbrains.exported.JBRApi;
+
+import java.awt.*;
+
+@JBRApi.Service
+@JBRApi.Provides("TextInput")
+public class JBRTextInputMacOS {
+ private EventListener listener;
+
+ JBRTextInputMacOS() {
+ var toolkit = Toolkit.getDefaultToolkit();
+ if (toolkit instanceof LWCToolkit) {
+ var desc = (CInputMethodDescriptor) ((LWCToolkit) toolkit).getInputMethodAdapterDescriptor();
+ desc.textInputEventListener = new EventListener() {
+ public void handleSelectTextRangeEvent(SelectTextRangeEvent event) {
+ // This listener is called on the EDT
+ synchronized (JBRTextInputMacOS.this) {
+ if (listener != null) {
+ listener.handleSelectTextRangeEvent(event);
+ }
+ }
+ }
+ };
+ }
+ }
+
+ @JBRApi.Provides("TextInput.SelectTextRangeEvent")
+ public static class SelectTextRangeEvent {
+ private final Object source;
+ private final int begin;
+ private final int length;
+
+ public SelectTextRangeEvent(Object source, int begin, int length) {
+ this.source = source;
+ this.begin = begin;
+ this.length = length;
+ }
+
+ public Object getSource() {
+ return source;
+ }
+
+ public int getBegin() {
+ return begin;
+ }
+
+ public int getLength() {
+ return length;
+ }
+ }
+
+ @JBRApi.Provided("TextInput.EventListener")
+ public interface EventListener {
+ void handleSelectTextRangeEvent(SelectTextRangeEvent event);
+ }
+
+ public synchronized void setGlobalEventListener(EventListener listener) {
+ this.listener = listener;
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
index 87b697cd7206..c02e9163f767 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
@@ -85,25 +85,15 @@
import java.awt.peer.TrayIconPeer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
-import java.net.MalformedURLException;
import java.net.URL;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.Objects;
-import java.util.ResourceBundle;
+import java.util.*;
import java.util.concurrent.Callable;
-
+import java.util.concurrent.CompletableFuture;
+import java.net.MalformedURLException;
import javax.swing.UIManager;
import com.apple.laf.AquaMenuBarUI;
-import sun.awt.AWTAccessor;
-import sun.awt.AppContext;
-import sun.awt.CGraphicsDevice;
-import sun.awt.LightweightFrame;
-import sun.awt.PlatformGraphicsInfo;
-import sun.awt.SunToolkit;
+import sun.awt.*;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.dnd.SunDragSourceContextPeer;
import sun.awt.util.ThreadGroupUtils;
@@ -117,6 +107,7 @@
import sun.lwawt.PlatformComponent;
import sun.lwawt.PlatformDropTarget;
import sun.lwawt.PlatformWindow;
+import sun.util.logging.PlatformLogger;
@SuppressWarnings("serial") // JDK implementation class
final class NamedCursor extends Cursor {
@@ -140,6 +131,67 @@ public final class LWCToolkit extends LWToolkit {
private static native void initAppkit(ThreadGroup appKitThreadGroup, boolean headless);
private static CInputMethodDescriptor sInputMethodDescriptor;
+ private static native boolean switchKeyboardLayoutNative(String layoutName);
+
+ private static native String getKeyboardLayoutNativeId();
+
+ private static native String[] getKeyboardLayoutListNative(boolean includeAll);
+
+ private static native boolean setKeyboardLayoutEnabledNative(String layoutName, boolean enabled);
+
+ public static void switchKeyboardLayout (String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!switchKeyboardLayoutNative(layoutName)) {
+ throw new RuntimeException("Couldn't switch layout to " + layoutName);
+ }
+ }
+
+ public static String getKeyboardLayoutId () {
+ return getKeyboardLayoutNativeId();
+ }
+
+ public static List getKeyboardLayoutList(boolean includeAll) {
+ String[] result = getKeyboardLayoutListNative(includeAll);
+ if (result == null) {
+ return new ArrayList<>();
+ }
+ return Arrays.asList(result);
+ }
+
+ public static void enableKeyboardLayout(String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!setKeyboardLayoutEnabledNative(layoutName, true)) {
+ throw new RuntimeException("Couldn't enable layout " + layoutName);
+ }
+ }
+
+ public static void disableKeyboardLayout(String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!setKeyboardLayoutEnabledNative(layoutName, false)) {
+ throw new RuntimeException("Couldn't disable layout " + layoutName);
+ }
+ }
+
+ public static boolean isKeyboardLayoutEnabled(String layoutName) {
+ return getKeyboardLayoutList(false).contains(layoutName);
+ }
+
+ public static boolean isKeyboardLayoutInstalled(String layoutName) {
+ return getKeyboardLayoutList(true).contains(layoutName);
+ }
+
+ // 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.
+ private static final boolean DISPOSE_INVOCATION_ON_EDT_FREE =
+ Boolean.getBoolean("sun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree");
+
static {
System.err.flush();
@@ -179,10 +231,11 @@ private static void loadLibrary() {
= !Boolean.parseBoolean(
System.getProperty("javafx.embed.singleThread", "false"));
+ private static final PlatformLogger log = PlatformLogger.getLogger(LWCToolkit.class.getName());
+
public LWCToolkit() {
final String extraButtons = "sun.awt.enableExtraMouseButtons";
- areExtraMouseButtonsEnabled =
- Boolean.parseBoolean(System.getProperty(extraButtons, "true"));
+ areExtraMouseButtonsEnabled = Boolean.parseBoolean(System.getProperty(extraButtons, "true"));
//set system property if not yet assigned
System.setProperty(extraButtons, "" + areExtraMouseButtonsEnabled);
initAppkit(ThreadGroupUtils.getRootThreadGroup(),
@@ -474,15 +527,16 @@ public int getScreenResolution() throws HeadlessException {
public Insets getScreenInsets(final GraphicsConfiguration gc) {
GraphicsDevice gd = gc.getDevice();
if (!(gd instanceof CGraphicsDevice)) {
- return super.getScreenInsets(gc);
+ return AWTThreading.executeWaitToolkit(() -> super.getScreenInsets(gc));
}
- return ((CGraphicsDevice)gd).getScreenInsets();
+ CGraphicsDevice cgd = (CGraphicsDevice) gd;
+ return cgd.getScreenInsets();
}
@Override
public void sync() {
// flush the rendering pipeline
- if (CGraphicsDevice.usingMetalPipeline()) {
+ if (CGraphicsEnvironment.usingMetalPipeline()) {
MTLRenderQueue.sync();
} else {
OGLRenderQueue.sync();
@@ -502,6 +556,8 @@ public RobotPeer createRobot(GraphicsDevice screen) throws AWTException {
private native boolean isCapsLockOn();
+ private static native boolean setCapsLockState(boolean on);
+
/*
* NOTE: Among the keys this method is supposed to check,
* only Caps Lock works as a true locking key with OS X.
@@ -528,6 +584,25 @@ public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationExcept
}
}
+ @Override
+ public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException {
+ switch (keyCode) {
+ case KeyEvent.VK_NUM_LOCK:
+ case KeyEvent.VK_SCROLL_LOCK:
+ case KeyEvent.VK_KANA_LOCK:
+ throw new UnsupportedOperationException("Toolkit.setLockingKeyState");
+
+ case KeyEvent.VK_CAPS_LOCK:
+ if (!setCapsLockState(on)) {
+ throw new RuntimeException("failed to set caps lock state");
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState");
+ }
+ }
+
//Is it allowed to generate events assigned to extra mouse buttons.
//Set to true by default.
private static boolean areExtraMouseButtonsEnabled = true;
@@ -639,19 +714,31 @@ public static boolean doEquals(final Object a, final Object b, Component c) {
if (a == b) return true;
final boolean[] ret = new boolean[1];
-
- try { invokeAndWait(new Runnable() { @Override
- public void run() { synchronized(ret) {
- ret[0] = a.equals(b);
- }}}, c); } catch (Exception e) { e.printStackTrace(); }
+ try {
+ // check invokeAndWait: OK (operations look not requiring locks or main thread):
+ invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ synchronized(ret) {
+ ret[0] = a.equals(b);
+ }
+ }
+ }, c);
+ } catch (Exception e) {
+ log.severe("LWCToolkit.doEquals: exception occurred: ", e);
+ }
synchronized(ret) { return ret[0]; }
}
public static T invokeAndWait(final Callable callable,
Component component) throws Exception {
+ return invokeAndWait(callable, component, -1);
+ }
+
+ public static T invokeAndWait(final Callable callable, Component component, int timeoutSeconds) throws Exception {
final CallableWrapper wrapper = new CallableWrapper<>(callable);
- invokeAndWait(wrapper, component);
+ invokeAndWait(wrapper, component, timeoutSeconds);
return wrapper.getResult();
}
@@ -677,40 +764,97 @@ public T getResult() throws Exception {
if (e != null) throw e;
return object;
}
+
+ @Override
+ public String toString() {
+ return "CallableWrapper{" + callable + "}";
+ }
}
/**
- * Kicks an event over to the appropriate event queue and waits for it to
+ * Kicks an event over to the appropriate eventqueue and waits for it to
* finish To avoid deadlocking, we manually run the NSRunLoop while waiting
* Any selector invoked using ThreadUtilities performOnMainThread will be
* processed in doAWTRunLoop The InvocationEvent will call
* LWCToolkit.stopAWTRunLoop() when finished, which will stop our manual
- * run loop. Does not dispatch native events while in the loop
+ * runloop Does not dispatch native events while in the loop
*/
public static void invokeAndWait(Runnable runnable, Component component)
- throws InvocationTargetException {
- Objects.requireNonNull(component, "Null component provided to invokeAndWait");
+ throws InvocationTargetException
+ {
+ invokeAndWait(runnable, component, -1);
+ }
+
+ public static void invokeAndWait(Runnable runnable, Component component, int timeoutSeconds)
+ throws InvocationTargetException
+ {
+ invokeAndWait(runnable, component, false, timeoutSeconds);
+ }
+
+ public static void invokeAndWait(Runnable runnable, Component component, boolean processEvents, int timeoutSeconds)
+ throws InvocationTargetException
+ {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("invokeAndWait started: {0}", runnable);
+ }
+
+ 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 =
+ AWTThreading.createAndTrackInvocationEvent(component, runnable, true);
long mediator = createAWTRunLoopMediator();
- InvocationEvent invocationEvent =
- new InvocationEvent(component,
- runnable,
- () -> {
- if (mediator != 0) {
- stopAWTRunLoop(mediator);
- }
- },
- true);
+ invocationEvent.onDone(() -> stopAWTRunLoop(mediator));
- AppContext appContext = SunToolkit.targetToAppContext(component);
- SunToolkit.postEvent(appContext, invocationEvent);
- // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
- SunToolkit.flushPendingEvents(appContext);
- doAWTRunLoop(mediator, false);
+ if (component != null) {
+ AppContext appContext = SunToolkit.targetToAppContext(component);
+ SunToolkit.postEvent(appContext, invocationEvent);
+
+ // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
+ SunToolkit.flushPendingEvents(appContext);
+ }
+ else {
+ // This should be the equivalent to EventQueue.invokeAndWait
+ ((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
+ }
+
+ if (DISPOSE_INVOCATION_ON_EDT_FREE) {
+ CompletableFuture 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 (!doAWTRunLoop(mediator, processEvents, timeoutSeconds)) {
+ invocationEvent.dispose("InvocationEvent has timed out");
+ }
+
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("invokeAndWait finished: {0}", runnable);
+ }
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");
@@ -725,6 +869,10 @@ public static void invokeLater(Runnable event, Component component)
checkException(invocationEvent);
}
+ EventQueue getSystemEventQueueForInvokeAndWait() {
+ return getSystemEventQueueImpl();
+ }
+
/**
* Checks if exception occurred while {@code InvocationEvent} was processed and rethrows it as
* an {@code InvocationTargetException}
@@ -749,6 +897,11 @@ private static void checkException(InvocationEvent event) throws InvocationTarge
*/
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
@@ -803,23 +956,6 @@ public Locale getDefaultKeyboardLocale() {
return locale;
}
- public static boolean isLocaleUSInternationalPC(Locale locale) {
- return (locale != null ?
- locale.toString().equals("_US_UserDefined_15000") : false);
- }
-
- public static boolean isCharModifierKeyInUSInternationalPC(char ch) {
- // 5 characters: APOSTROPHE, QUOTATION MARK, ACCENT GRAVE, SMALL TILDE,
- // CIRCUMFLEX ACCENT
- final char[] modifierKeys = {'\'', '"', '`', '\u02DC', '\u02C6'};
- for (char modKey : modifierKeys) {
- if (modKey == ch) {
- return true;
- }
- }
- return false;
- }
-
@Override
public InputMethodDescriptor getInputMethodAdapterDescriptor() {
if (sInputMethodDescriptor == null)
@@ -904,9 +1040,25 @@ public boolean canPopupOverlapTaskBar() {
* if false - all events come after exit form the nested loop
*/
static void doAWTRunLoop(long mediator, boolean processEvents) {
- doAWTRunLoopImpl(mediator, processEvents, inAWT);
+ doAWTRunLoop(mediator, processEvents, -1);
+ }
+
+ /**
+ * Starts run-loop with the provided timeout. Use (-1) for the infinite value.
+ */
+ static boolean doAWTRunLoop(long mediator, boolean processEvents, int timeoutSeconds) {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("doAWTRunLoop: enter: mediator: " + mediator + " processEvents: "+ processEvents + " timeoutSeconds: " + timeoutSeconds);
+ }
+ try {
+ return doAWTRunLoopImpl(mediator, processEvents, inAWT, timeoutSeconds);
+ } finally {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("doAWTRunLoop: exit: mediator: " + mediator + " processEvents: "+ processEvents + " timeoutSeconds: " + timeoutSeconds);
+ }
+ }
}
- private static native void doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT);
+ private static native boolean doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT, int timeoutSeconds);
static native void stopAWTRunLoop(long mediator);
private native boolean nativeSyncQueue(long timeout);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
index f3fcfdfb43cf..888c3f8e4784 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
@@ -56,15 +56,15 @@ final class NSEvent {
// Key event information
private short keyCode;
private String characters;
- private String charactersIgnoringModifiers;
+ private String actualCharacters;
// Called from native
- NSEvent(int type, int modifierFlags, short keyCode, String characters, String charactersIgnoringModifiers) {
+ NSEvent(int type, int modifierFlags, short keyCode, String characters, String actualCharacters) {
this.type = type;
this.modifierFlags = modifierFlags;
this.keyCode = keyCode;
this.characters = characters;
- this.charactersIgnoringModifiers = charactersIgnoringModifiers;
+ this.actualCharacters = actualCharacters;
}
// Called from native
@@ -132,20 +132,20 @@ short getKeyCode() {
return keyCode;
}
- String getCharactersIgnoringModifiers() {
- return charactersIgnoringModifiers;
- }
-
String getCharacters() {
return characters;
}
+ String getActualCharacters() {
+ return actualCharacters;
+ }
+
@Override
public String toString() {
return "NSEvent[" + getType() + " ," + getModifierFlags() + " ,"
+ getClickCount() + " ," + getButtonNumber() + " ," + getX() + " ,"
+ getY() + " ," + getAbsX() + " ," + getAbsY()+ " ," + getKeyCode() + " ,"
- + getCharacters() + " ," + getCharactersIgnoringModifiers() + "]";
+ + getCharacters() + " ," + getActualCharacters() + "]";
}
/*
@@ -256,7 +256,7 @@ static int nsToJavaEventType(int nsEventType) {
/*
* Converts NSEvent key info to AWT key info.
*/
- static native boolean nsToJavaKeyInfo(int[] in, int[] out);
+ static native void nsToJavaKeyInfo(int[] in, int[] out);
/*
* Converts NSEvent key modifiers to AWT key info.
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java
new file mode 100644
index 000000000000..b0b3d2acfb34
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.*;
+
+/**
+ * Factory for {@link Executor}s and {@link ExecutorService}s backed by
+ * libdispatch.
+ *
+ * Access is controlled through the Dispatch.getInstance() method, because
+ * performed tasks occur on threads owned by libdispatch. These threads are
+ * not owned by any particular AppContext or have any specific context
+ * classloader installed.
+ *
+ * @since Java for Mac OS X 10.6 Update 2
+ */
+@SuppressWarnings("removal")
+public final class Dispatch {
+ /**
+ * The priorities of the three default asynchronous queues.
+ */
+ public enum Priority {
+ LOW(-2), NORMAL(0), HIGH(2); // values from
+
+ final int nativePriority;
+ Priority(final int nativePriority) { this.nativePriority = nativePriority; }
+ };
+
+ final static Dispatch instance = new Dispatch();
+
+ /**
+ * Factory method returns an instnace of Dispatch if supported by the
+ * underlying operating system, and if the caller's security manager
+ * permits "canInvokeInSystemThreadGroup".
+ *
+ * @return a factory instance of Dispatch, or null if not available
+ */
+ public static Dispatch getInstance() {
+ checkSecurity();
+ if (!LibDispatchNative.nativeIsDispatchSupported()) return null;
+
+ return instance;
+ }
+
+ private static void checkSecurity() {
+ final SecurityManager security = System.getSecurityManager();
+ if (security != null) security.checkPermission(new RuntimePermission("canInvokeInSystemThreadGroup"));
+ }
+
+ private Dispatch() { }
+
+ /**
+ * Creates an {@link Executor} that performs tasks asynchronously. The {@link Executor}
+ * cannot be shutdown, and enqueued {@link Runnable}s cannot be canceled. Passing null
+ * returns the {@link Priority.NORMAL} {@link Executor}.
+ *
+ * @param priority - the priority of the returned {@link Executor}
+ * @return an asynchronous {@link Executor}
+ */
+ public Executor getAsyncExecutor(Priority priority) {
+ if (priority == null) priority = Priority.NORMAL;
+ final long nativeQueue = LibDispatchNative.nativeCreateConcurrentQueue(priority.nativePriority);
+ if (nativeQueue == 0L) return null;
+ return new LibDispatchConcurrentQueue(nativeQueue);
+ }
+
+ int queueIndex = 0;
+ /**
+ * Creates an {@link ExecutorService} that performs tasks synchronously in FIFO order.
+ * Useful to protect a resource against concurrent modification, in lieu of a lock.
+ * Passing null returns an {@link ExecutorService} with a uniquely labeled queue.
+ *
+ * @param label - a label to name the queue, shown in several debugging tools
+ * @return a synchronous {@link ExecutorService}
+ */
+ public ExecutorService createSerialExecutor(String label) {
+ if (label == null) label = "";
+ if (label.length() > 256) label = label.substring(0, 256);
+ String queueName = "com.apple.java.concurrent.";
+ if ("".equals(label)) {
+ synchronized (this) {
+ queueName += queueIndex++;
+ }
+ } else {
+ queueName += label;
+ }
+
+ final long nativeQueue = LibDispatchNative.nativeCreateSerialQueue(queueName);
+ if (nativeQueue == 0) return null;
+ return new LibDispatchSerialQueue(nativeQueue);
+ }
+
+ Executor nonBlockingMainQueue = null;
+ /**
+ * Returns an {@link Executor} that performs the provided Runnables on the main queue of the process.
+ * Runnables submitted to this {@link Executor} will not run until the AWT is started or another native toolkit is running a CFRunLoop or NSRunLoop on the main thread.
+ *
+ * Submitting a Runnable to this {@link Executor} does not wait for the Runnable to complete.
+ * @return an asynchronous {@link Executor} that is backed by the main queue
+ */
+ public synchronized Executor getNonBlockingMainQueueExecutor() {
+ if (nonBlockingMainQueue != null) return nonBlockingMainQueue;
+ return nonBlockingMainQueue = new LibDispatchMainQueue.ASync();
+ }
+
+ Executor blockingMainQueue = null;
+ /**
+ * Returns an {@link Executor} that performs the provided Runnables on the main queue of the process.
+ * Runnables submitted to this {@link Executor} will not run until the AWT is started or another native toolkit is running a CFRunLoop or NSRunLoop on the main thread.
+ *
+ * Submitting a Runnable to this {@link Executor} will block until the Runnable has completed.
+ * @return an {@link Executor} that is backed by the main queue
+ */
+ public synchronized Executor getBlockingMainQueueExecutor() {
+ if (blockingMainQueue != null) return blockingMainQueue;
+ return blockingMainQueue = new LibDispatchMainQueue.Sync();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java
new file mode 100644
index 000000000000..9ed6046c6668
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.Executor;
+
+class LibDispatchConcurrentQueue extends LibDispatchQueue implements Executor {
+ LibDispatchConcurrentQueue(final long queuePtr) {
+ super(queuePtr);
+ }
+
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteAsync(ptr, task);
+ }
+
+ @Override
+ protected synchronized void dispose() {
+ // should not dispose the default concurrent queues
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java
new file mode 100644
index 000000000000..d935372e8403
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.Executor;
+
+abstract class LibDispatchMainQueue extends LibDispatchQueue implements Executor {
+ public LibDispatchMainQueue() {
+ super(LibDispatchNative.nativeGetMainQueue());
+ }
+
+ @Override
+ protected synchronized void dispose() {
+ // should not dispose the main queue
+ }
+
+ static class Sync extends LibDispatchMainQueue {
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteSync(ptr, task);
+ }
+ }
+
+ static class ASync extends LibDispatchMainQueue {
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteAsync(ptr, task);
+ }
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java
new file mode 100644
index 000000000000..ea2a18783bed
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+@SuppressWarnings("removal")
+final class LibDispatchNative {
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ @SuppressWarnings("restricted")
+ public Void run() {
+ System.loadLibrary("osx");
+ return null;
+ }
+ });
+ }
+
+ static native boolean nativeIsDispatchSupported();
+ static native long nativeGetMainQueue();
+ static native long nativeCreateConcurrentQueue(int priority);
+ static native long nativeCreateSerialQueue(String name);
+ static native void nativeReleaseQueue(long nativeQueue);
+ static native void nativeExecuteAsync(long nativeQueue, Runnable task);
+ static native void nativeExecuteSync(long nativeQueue, Runnable task);
+
+ private LibDispatchNative() { }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java
new file mode 100644
index 000000000000..01362574c012
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+class LibDispatchQueue extends LibDispatchRetainedResource {
+ LibDispatchQueue(final long queuePtr) {
+ super(queuePtr);
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java
new file mode 100644
index 000000000000..5cffc70cdf63
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+class LibDispatchRetainedResource {
+ protected long ptr;
+
+ protected LibDispatchRetainedResource(final long ptr) {
+ this.ptr = ptr;
+ }
+
+ protected synchronized void dispose() {
+ if (ptr != 0) LibDispatchNative.nativeReleaseQueue(ptr);
+ ptr = 0;
+ }
+
+ @Override
+ @SuppressWarnings("removal")
+ protected void finalize() throws Throwable {
+ dispose();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java
new file mode 100644
index 000000000000..2b5569edac02
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.List;
+import java.util.concurrent.*;
+
+class LibDispatchSerialQueue extends AbstractExecutorService {
+ static final int RUNNING = 0;
+ static final int SHUTDOWN = 1;
+// static final int STOP = 2; // not supported by GCD
+ static final int TERMINATED = 3;
+
+ final Object lock = new Object();
+ LibDispatchQueue nativeQueueWrapper;
+ volatile int runState;
+
+ LibDispatchSerialQueue(final long queuePtr) {
+ nativeQueueWrapper = new LibDispatchQueue(queuePtr);
+ }
+
+ @Override
+ public void execute(final Runnable task) {
+ if (nativeQueueWrapper == null) return;
+ LibDispatchNative.nativeExecuteAsync(nativeQueueWrapper.ptr, task);
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return runState != RUNNING;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return runState == TERMINATED;
+ }
+
+ @Override
+ public void shutdown() {
+ synchronized (lock) {
+ if (runState != RUNNING) return;
+
+ runState = SHUTDOWN;
+ execute(new Runnable() {
+ public void run() {
+ synchronized (lock) {
+ runState = TERMINATED;
+ lock.notifyAll(); // for the benefit of awaitTermination()
+ }
+ }
+ });
+ nativeQueueWrapper = null;
+ }
+ }
+
+ @Override
+ public List shutdownNow() {
+ shutdown();
+ return null;
+ }
+
+ @Override
+ public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
+ if (runState == TERMINATED) return true;
+
+ final long millis = unit.toMillis(timeout);
+ if (millis <= 0) return false;
+
+ synchronized (lock) {
+ if (runState == TERMINATED) return true;
+ lock.wait(timeout);
+ if (runState == TERMINATED) return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
index 3c7cbe086ee5..5e4e35ed66cb 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
@@ -26,13 +26,25 @@
#ifndef __AWTEVENT_H
#define __AWTEVENT_H
+#import
+
jlong UTC(NSEvent *event);
void DeliverJavaKeyEvent(JNIEnv *env, NSEvent *event, jobject peer);
void DeliverJavaMouseEvent(JNIEnv *env, NSEvent *event, jobject peer);
void SendAdditionalJavaEvents(JNIEnv *env, NSEvent *nsEvent, jobject peer);
-jint GetJavaMouseModifiers(NSInteger button, NSUInteger modifierFlags);
+jint GetJavaMouseModifiers(NSUInteger modifierFlags);
jint NsKeyModifiersToJavaModifiers(NSUInteger nsFlags, BOOL isExtMods);
NSUInteger JavaModifiersToNsKeyModifiers(jint javaModifiers, BOOL isExtMods);
-unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers);
+unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers, BOOL spaceKeyTyped);
+
+struct KeyCodeTranslationResult {
+ unichar character;
+ BOOL isSuccess;
+ BOOL isDead;
+ BOOL isTyped;
+};
+
+TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts);
+struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode, unsigned nsMods);
#endif /* __AWTEVENT_H */
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
index d26def5694fb..29c2d9bc55c3 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
@@ -28,6 +28,7 @@
#import "LWCToolkit.h"
#import "JNIUtilities.h"
+#import "AWTEvent.h"
#import
#import
@@ -60,143 +61,166 @@
#define KL_STANDARD java_awt_event_KeyEvent_KEY_LOCATION_STANDARD
#define KL_NUMPAD java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD
#define KL_UNKNOWN java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN
-static struct _key
+#define KL_LEFT java_awt_event_KeyEvent_KEY_LOCATION_LEFT
+#define KL_RIGHT java_awt_event_KeyEvent_KEY_LOCATION_RIGHT
+
+struct KeyTableEntry
{
unsigned short keyCode;
- BOOL postsTyped;
+ unichar character;
+ BOOL variesBetweenLayouts;
jint javaKeyLocation;
jint javaKeyCode;
-}
-const keyTable[] =
+};
+
+static const struct KeyTableEntry unknownKeyEntry = {
+ 0xFFFF, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED
+};
+
+static const struct KeyTableEntry keyTable[] =
{
- {0x00, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A},
- {0x01, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S},
- {0x02, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D},
- {0x03, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F},
- {0x04, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H},
- {0x05, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G},
- {0x06, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z},
- {0x07, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X},
- {0x08, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C},
- {0x09, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V},
- {0x0A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
- {0x0B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B},
- {0x0C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q},
- {0x0D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W},
- {0x0E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E},
- {0x0F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R},
- {0x10, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y},
- {0x11, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T},
- {0x12, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1},
- {0x13, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2},
- {0x14, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3},
- {0x15, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4},
- {0x16, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6},
- {0x17, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5},
- {0x18, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS},
- {0x19, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9},
- {0x1A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7},
- {0x1B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS},
- {0x1C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8},
- {0x1D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0},
- {0x1E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
- {0x1F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O},
- {0x20, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U},
- {0x21, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
- {0x22, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I},
- {0x23, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P},
- {0x24, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER},
- {0x25, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L},
- {0x26, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J},
- {0x27, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE},
- {0x28, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K},
- {0x29, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON},
- {0x2A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH},
- {0x2B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA},
- {0x2C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH},
- {0x2D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N},
- {0x2E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M},
- {0x2F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD},
- {0x30, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB},
- {0x31, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE},
- {0x32, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
- {0x33, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE},
- {0x34, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
- {0x35, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE},
- {0x36, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x37, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_META}, // ****
- {0x38, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_SHIFT}, // ****
- {0x39, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK},
- {0x3A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_ALT}, // ****
- {0x3B, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_CONTROL}, // ****
- {0x3C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x3D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_ALT_GRAPH},
- {0x3E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x3F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // the 'fn' key on PowerBooks
- {0x40, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F17},
- {0x41, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DECIMAL},
- {0x42, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x43, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_MULTIPLY},
- {0x44, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x45, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ADD},
- {0x46, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x47, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_CLEAR},
- {0x48, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x49, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DIVIDE},
- {0x4C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
- {0x4D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_SUBTRACT},
- {0x4F, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F18},
- {0x50, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F19},
- {0x51, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_EQUALS},
- {0x52, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD0},
- {0x53, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD1},
- {0x54, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD2},
- {0x55, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD3},
- {0x56, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD4},
- {0x57, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD5},
- {0x58, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD6},
- {0x59, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD7},
- {0x5A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F20},
- {0x5B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD8},
- {0x5C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD9},
- {0x5D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, // This is a combo yen/backslash on JIS keyboards.
- {0x5E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_UNDERSCORE},
- {0x5F, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_COMMA},
- {0x60, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F5},
- {0x61, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F6},
- {0x62, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F7},
- {0x63, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F3},
- {0x64, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F8},
- {0x65, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F9},
- {0x66, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC},
- {0x67, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F11},
- {0x68, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA},
- {0x69, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F13},
- {0x6A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F16},
- {0x6B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F14},
- {0x6C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x6D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F10},
- {0x6E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x6F, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F12},
- {0x70, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x71, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F15},
- {0x72, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HELP},
- {0x73, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HOME},
- {0x74, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP},
- {0x75, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE},
- {0x76, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F4},
- {0x77, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_END},
- {0x78, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F2},
- {0x79, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN},
- {0x7A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F1},
- {0x7B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT},
- {0x7C, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT},
- {0x7D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN},
- {0x7E, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_UP},
- {0x7F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ // Do not reorder the key codes! They are in ascending numeric order without gaps (from 0x00 to 0x7F).
+ // Characters in the second column are not the characters that UCKeyTranslate returns with the specified key code.
+ // Instead, they are the NSEvent.characters sent by macOS when pressing the specified key on the US layout.
+ // They are currently only used to determine whether the NextAppWindow shortcut was pressed.
+ // They are not used for KEY_TYPED events or anything of this sort.
+ {kVK_ANSI_A, 'a', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A},
+ {kVK_ANSI_S, 's', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S},
+ {kVK_ANSI_D, 'd', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D},
+ {kVK_ANSI_F, 'f', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F},
+ {kVK_ANSI_H, 'h', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H},
+ {kVK_ANSI_G, 'g', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G},
+ {kVK_ANSI_Z, 'z', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z},
+ {kVK_ANSI_X, 'x', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X},
+ {kVK_ANSI_C, 'c', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C},
+ {kVK_ANSI_V, 'v', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V},
+ {kVK_ISO_Section, 0xA7, YES, KL_STANDARD, 0x1000000 + 0x00A7},
+ {kVK_ANSI_B, 'b', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B},
+ {kVK_ANSI_Q, 'q', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q},
+ {kVK_ANSI_W, 'w', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W},
+ {kVK_ANSI_E, 'e', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E},
+ {kVK_ANSI_R, 'r', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R},
+ {kVK_ANSI_Y, 'y', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y},
+ {kVK_ANSI_T, 't', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T},
+ {kVK_ANSI_1, '1', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1},
+ {kVK_ANSI_2, '2', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2},
+ {kVK_ANSI_3, '3', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3},
+ {kVK_ANSI_4, '4', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4},
+ {kVK_ANSI_6, '6', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6},
+ {kVK_ANSI_5, '5', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5},
+ {kVK_ANSI_Equal, '=', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS},
+ {kVK_ANSI_9, '9', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9},
+ {kVK_ANSI_7, '7', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7},
+ {kVK_ANSI_Minus, '-', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS},
+ {kVK_ANSI_8, '8', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8},
+ {kVK_ANSI_0, '0', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0},
+ {kVK_ANSI_RightBracket, ']', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ {kVK_ANSI_O, 'o', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O},
+ {kVK_ANSI_U, 'u', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U},
+ {kVK_ANSI_LeftBracket, '[', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {kVK_ANSI_I, 'i', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I},
+ {kVK_ANSI_P, 'p', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P},
+ {kVK_Return, NSCarriageReturnCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER},
+ {kVK_ANSI_L, 'l', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L},
+ {kVK_ANSI_J, 'j', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J},
+ {kVK_ANSI_Quote, '\'', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE},
+ {kVK_ANSI_K, 'k', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K},
+ {kVK_ANSI_Semicolon, ';', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON},
+ {kVK_ANSI_Backslash, '\\', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH},
+ {kVK_ANSI_Comma, ',', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA},
+ {kVK_ANSI_Slash, '/', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH},
+ {kVK_ANSI_N, 'n', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N},
+ {kVK_ANSI_M, 'm', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M},
+ {kVK_ANSI_Period, '.', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD},
+ {kVK_Tab, NSTabCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB},
+ {kVK_Space, ' ', NO, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE},
+ {kVK_ANSI_Grave, '`', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
+ {kVK_Delete, NSDeleteCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE},
+ {0x34, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_Escape, 0x1B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE},
+ {kVK_RightCommand, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_META},
+ {kVK_Command, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_META},
+ {kVK_Shift, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_SHIFT},
+ {kVK_CapsLock, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK},
+ {kVK_Option, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_ALT},
+ {kVK_Control, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_CONTROL},
+ {kVK_RightShift, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_SHIFT},
+ {kVK_RightOption, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_ALT},
+ {kVK_RightControl, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_CONTROL},
+ {kVK_Function, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_F17, NSF17FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F17},
+ {kVK_ANSI_KeypadDecimal, '.', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_DECIMAL},
+ {0x42, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadMultiply, '*', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_MULTIPLY},
+ {0x44, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadPlus, '+', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_ADD},
+ {0x46, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadClear, NSClearLineFunctionKey, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_CLEAR},
+ {kVK_VolumeUp, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_VolumeDown, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_Mute, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_ANSI_KeypadDivide, '/', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_DIVIDE},
+ {kVK_ANSI_KeypadEnter, NSEnterCharacter, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
+ {0x4D, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadMinus, '-', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_SUBTRACT},
+ {kVK_F18, NSF18FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F18},
+ {kVK_F19, NSF19FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F19},
+ {kVK_ANSI_KeypadEquals, '=', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_EQUALS},
+ {kVK_ANSI_Keypad0, '0', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD0},
+ {kVK_ANSI_Keypad1, '1', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD1},
+ {kVK_ANSI_Keypad2, '2', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD2},
+ {kVK_ANSI_Keypad3, '3', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD3},
+ {kVK_ANSI_Keypad4, '4', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD4},
+ {kVK_ANSI_Keypad5, '5', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD5},
+ {kVK_ANSI_Keypad6, '6', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD6},
+ {kVK_ANSI_Keypad7, '7', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD7},
+ {kVK_F20, NSF20FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F20},
+ {kVK_ANSI_Keypad8, '8', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD8},
+ {kVK_ANSI_Keypad9, '9', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD9},
+ {kVK_JIS_Yen, 0xA5, YES, KL_STANDARD, 0x1000000 + 0x00A5},
+ {kVK_JIS_Underscore, '_', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_UNDERSCORE},
+ {kVK_JIS_KeypadComma, ',', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_COMMA},
+ {kVK_F5, NSF5FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F5},
+ {kVK_F6, NSF6FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F6},
+ {kVK_F7, NSF7FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F7},
+ {kVK_F3, NSF3FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F3},
+ {kVK_F8, NSF8FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F8},
+ {kVK_F9, NSF9FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F9},
+ {kVK_JIS_Eisu, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC},
+ {kVK_F11, NSF11FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F11},
+ {kVK_JIS_Kana, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA},
+ {kVK_F13, NSF13FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F13},
+ {kVK_F16, NSF16FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F16},
+ {kVK_F14, NSF14FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F14},
+ {0x6C, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_F10, NSF10FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F10},
+ {0x6E, NSMenuFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CONTEXT_MENU},
+ {kVK_F12, NSF12FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F12},
+ {0x70, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_F15, NSF15FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F15},
+ {kVK_Help, NSHelpFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HELP},
+ {kVK_Home, NSHomeFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HOME},
+ {kVK_PageUp, NSPageUpFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP},
+ {kVK_ForwardDelete, NSDeleteFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE},
+ {kVK_F4, NSF4FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F4},
+ {kVK_End, NSEndFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_END},
+ {kVK_F2, NSF2FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F2},
+ {kVK_PageDown, NSPageDownFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN},
+ {kVK_F1, NSF1FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F1},
+ {kVK_LeftArrow, NSLeftArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT},
+ {kVK_RightArrow, NSRightArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT},
+ {kVK_DownArrow, NSDownArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN},
+ {kVK_UpArrow, NSUpArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_UP},
+ {0x7F, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+};
+
+static const struct KeyTableEntry keyTableJISOverride[] = {
+ {0x18, '^', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CIRCUMFLEX},
+ {0x1E, '[', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {0x21, '@', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_AT},
+ {0x27, ':', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COLON},
+ {0x2A, ']', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ // Some other keys are already handled in the previous table, no need to repeat them here
};
/*
@@ -212,6 +236,7 @@
};
static const struct CharToVKEntry charToDeadVKTable[] = {
{0x0060, java_awt_event_KeyEvent_VK_DEAD_GRAVE},
+ {0x0027, java_awt_event_KeyEvent_VK_DEAD_ACUTE},
{0x00B4, java_awt_event_KeyEvent_VK_DEAD_ACUTE},
{0x0384, java_awt_event_KeyEvent_VK_DEAD_ACUTE}, // Unicode "GREEK TONOS" -- Greek keyboard, semicolon key
{0x005E, java_awt_event_KeyEvent_VK_DEAD_CIRCUMFLEX},
@@ -221,6 +246,7 @@
{0x02D8, java_awt_event_KeyEvent_VK_DEAD_BREVE},
{0x02D9, java_awt_event_KeyEvent_VK_DEAD_ABOVEDOT},
{0x00A8, java_awt_event_KeyEvent_VK_DEAD_DIAERESIS},
+ {0x00B0, java_awt_event_KeyEvent_VK_DEAD_ABOVERING},
{0x02DA, java_awt_event_KeyEvent_VK_DEAD_ABOVERING},
{0x02DD, java_awt_event_KeyEvent_VK_DEAD_DOUBLEACUTE},
{0x02C7, java_awt_event_KeyEvent_VK_DEAD_CARON},
@@ -232,6 +258,58 @@
{0,0}
};
+// This table is analogous to a similar one found in sun.awt.ExtendedKeyCodes.
+// It governs translating the unicode codepoints into the proper VK_ values for keys that have one.
+// It only deals with punctuation characters and certain exceptional letters from extended latin set.
+// Also see test/jdk/jb/sun/awt/macos/InputMethodTest/KeyCodesTest.java
+static const struct CharToVKEntry extraCharToVKTable[] = {
+ {0x0021, java_awt_event_KeyEvent_VK_EXCLAMATION_MARK},
+ {0x0022, java_awt_event_KeyEvent_VK_QUOTEDBL},
+ {0x0023, java_awt_event_KeyEvent_VK_NUMBER_SIGN},
+ {0x0024, java_awt_event_KeyEvent_VK_DOLLAR},
+ {0x0026, java_awt_event_KeyEvent_VK_AMPERSAND},
+ {0x0027, java_awt_event_KeyEvent_VK_QUOTE},
+ {0x0028, java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS},
+ {0x0029, java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS},
+ {0x002A, java_awt_event_KeyEvent_VK_ASTERISK},
+ {0x002B, java_awt_event_KeyEvent_VK_PLUS},
+ {0x002C, java_awt_event_KeyEvent_VK_COMMA},
+ {0x002D, java_awt_event_KeyEvent_VK_MINUS},
+ {0x002E, java_awt_event_KeyEvent_VK_PERIOD},
+ {0x002F, java_awt_event_KeyEvent_VK_SLASH},
+ {0x003A, java_awt_event_KeyEvent_VK_COLON},
+ {0x003B, java_awt_event_KeyEvent_VK_SEMICOLON},
+ {0x003C, java_awt_event_KeyEvent_VK_LESS},
+ {0x003D, java_awt_event_KeyEvent_VK_EQUALS},
+ {0x003E, java_awt_event_KeyEvent_VK_GREATER},
+ {0x0040, java_awt_event_KeyEvent_VK_AT},
+ {0x005B, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {0x005C, java_awt_event_KeyEvent_VK_BACK_SLASH},
+ {0x005D, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ {0x005E, java_awt_event_KeyEvent_VK_CIRCUMFLEX},
+ {0x005F, java_awt_event_KeyEvent_VK_UNDERSCORE},
+ {0x0060, java_awt_event_KeyEvent_VK_BACK_QUOTE},
+ {0x007B, java_awt_event_KeyEvent_VK_BRACELEFT},
+ {0x007D, java_awt_event_KeyEvent_VK_BRACERIGHT},
+ {0x00A1, java_awt_event_KeyEvent_VK_INVERTED_EXCLAMATION_MARK},
+
+ // These are the extended latin characters which have a non-obvious key code.
+ // Their key codes are derived from the upper case instead of the lower case.
+ // It probably has to do with how these key codes are reported on Windows.
+ // Translating these characters to the key codes corresponding to their uppercase versions
+ // makes getExtendedKeyCodeForChar consistent with how these events are reported on macOS.
+ {0x00E4, 0x01000000+0x00C4},
+ {0x00E5, 0x01000000+0x00C5},
+ {0x00E6, 0x01000000+0x00C6},
+ {0x00E7, 0x01000000+0x00C7},
+ {0x00F1, 0x01000000+0x00D1},
+ {0x00F6, 0x01000000+0x00D6},
+ {0x00F8, 0x01000000+0x00D8},
+
+ {0x20AC, java_awt_event_KeyEvent_VK_EURO_SIGN},
+ {0, 0}
+};
+
// TODO: some constants below are part of CGS (private interfaces)...
// for now we will look at the raw key code to determine left/right status
// but not sure this is foolproof...
@@ -381,128 +459,399 @@ unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers, BOOL spaceKeyType
return nsChar;
}
-static unichar NsGetDeadKeyChar(unsigned short keyCode)
+static const struct KeyTableEntry* GetKeyTableEntryForKeyCode(unsigned short keyCode) {
+ static const size_t keyTableSize = sizeof(keyTable) / sizeof(struct KeyTableEntry);
+ static const size_t keyTableJISOverrideSize = sizeof(keyTableJISOverride) / sizeof(struct KeyTableEntry);
+ BOOL isJIS = KBGetLayoutType(LMGetKbdType()) == kKeyboardJIS;
+
+ const struct KeyTableEntry* usKey = &unknownKeyEntry;
+
+ if (keyCode < keyTableSize) {
+ usKey = &keyTable[keyCode];
+ }
+
+ if (isJIS) {
+ for (int i = 0; i < keyTableJISOverrideSize; ++i) {
+ if (keyTableJISOverride[i].keyCode == keyCode) {
+ usKey = &keyTableJISOverride[i];
+ break;
+ }
+ }
+ }
+
+ return usKey;
+}
+
+TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts) {
+ // TISCopyCurrentKeyboardLayoutInputSource() should always return a key layout
+ // that has valid unicode character data for use with the UCKeyTranslate() function.
+ // This is more robust than checking whether the current input source has key layout data
+ // and then falling back to the override input source if it doesn't. This is because some
+ // custom IMEs don't set the override input source properly.
+
+ TISInputSourceRef currentLayout = TISCopyCurrentKeyboardLayoutInputSource();
+ Boolean currentAscii = currentLayout == nil ? NO :
+ CFBooleanGetValue((CFBooleanRef) TISGetInputSourceProperty(currentLayout, kTISPropertyInputSourceIsASCIICapable));
+ TISInputSourceRef underlyingLayout = (!useNationalLayouts || currentAscii) ? currentLayout : nil;
+
+ if (currentLayout) {
+ if (underlyingLayout) {
+ CFAutorelease(currentLayout);
+ } else {
+ CFRelease(currentLayout);
+ }
+ }
+
+ return underlyingLayout;
+}
+
+static unsigned NSEventModifiersToCarbonModifierMask(unsigned mods) {
+ unsigned result = 0;
+
+ if (mods & NSEventModifierFlagCommand) {
+ result |= cmdKey;
+ }
+
+ if (mods & NSEventModifierFlagControl) {
+ result |= controlKey;
+ }
+
+ if (mods & NSEventModifierFlagOption) {
+ result |= optionKey;
+ }
+
+ if (mods & NSEventModifierFlagShift) {
+ result |= shiftKey;
+ }
+
+ if (mods & NSEventModifierFlagCapsLock) {
+ result |= alphaLock;
+ }
+
+ return result;
+}
+
+struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode, unsigned nsMods)
{
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (uchr == nil) { return 0; }
+ struct KeyCodeTranslationResult result = {
+ .character = (unichar)0,
+ .isSuccess = NO,
+ .isDead = NO,
+ .isTyped = NO
+ };
+
+ const struct KeyTableEntry* usKey = GetKeyTableEntryForKeyCode(keyCode);
+
+ if (usKey == &unknownKeyEntry) {
+ return result;
+ }
+
+ if (!usKey->variesBetweenLayouts) {
+ result.isSuccess = YES;
+ result.character = usKey->character;
+ if (result.character != 0 && !(result.character >= NSUpArrowFunctionKey && result.character <= NSModeSwitchFunctionKey)) {
+ result.isTyped = YES;
+ }
+ return result;
+ }
+
+ if (layout == nil) {
+ // use the US layout
+ result.isSuccess = YES;
+ if (usKey->character != 0) {
+ result.character = usKey->character;
+ result.isTyped = YES;
+ }
+ return result;
+ }
+
+ CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(layout, kTISPropertyUnicodeKeyLayoutData);
+ if (uchr == nil) {
+ return result;
+ }
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
- // Carbon modifiers should be used instead of NSEvent modifiers
- UInt32 modifierKeyState = (GetCurrentEventKeyModifiers() >> 8) & 0xFF;
-
- if (keyboardLayout) {
- UInt32 deadKeyState = 0;
- UniCharCount maxStringLength = 255;
- UniCharCount actualStringLength = 0;
- UniChar unicodeString[maxStringLength];
-
- // get the deadKeyState
- OSStatus status = UCKeyTranslate(keyboardLayout,
- keyCode, kUCKeyActionDown, modifierKeyState,
- LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
- &deadKeyState,
- maxStringLength,
- &actualStringLength, unicodeString);
-
- if (status == noErr && deadKeyState != 0) {
- // Press SPACE to get the dead key char
- status = UCKeyTranslate(keyboardLayout,
- kVK_Space, kUCKeyActionDown, 0,
- LMGetKbdType(), 0,
- &deadKeyState,
- maxStringLength,
- &actualStringLength, unicodeString);
-
- if (status == noErr && actualStringLength > 0) {
- return unicodeString[0];
- }
+ if (keyboardLayout == NULL) {
+ return result;
+ }
+
+ // UCKeyTranslate accepts Carbon modifier mask shifted by 8
+ unsigned mods = NSEventModifiersToCarbonModifierMask(nsMods) >> 8;
+
+ UInt32 deadKeyState = 0;
+ const UniCharCount maxStringLength = 255;
+ UniCharCount actualStringLength = 0;
+ UniChar unicodeString[maxStringLength];
+
+ // get the deadKeyState
+ OSStatus status = UCKeyTranslate(keyboardLayout,
+ keyCode, kUCKeyActionDown, mods,
+ LMGetKbdType(), 0,
+ &deadKeyState,
+ maxStringLength,
+ &actualStringLength, unicodeString);
+
+ if (status != noErr) {
+ return result;
+ }
+
+ if (deadKeyState == 0) {
+ result.isSuccess = YES;
+ result.isDead = NO;
+
+ if (actualStringLength > 0) {
+ result.isTyped = YES;
+
+ // This is the character that will determine the Java key code of this key.
+ // There are some keys (even on ASCII-capable key layouts) that produce more than one
+ // code point (not just more than one UTF-16 code unit mind you!). It's unclear how one
+ // would go around constructing an extended key code for these keys. Luckily, if we
+ // use the last code unit to construct the extended key codes, there won't be any collisions
+ // among the standard macOS ASCII-capable key layouts. That seems good enough to me.
+ result.character = unicodeString[actualStringLength - 1];
}
+
+ return result;
}
- return 0;
+
+ deadKeyState = 0;
+
+ // Extract the dead key non-combining character
+ status = UCKeyTranslate(keyboardLayout,
+ keyCode, kUCKeyActionDown, mods,
+ LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask,
+ &deadKeyState,
+ maxStringLength,
+ &actualStringLength, unicodeString);
+
+ if (status != noErr) {
+ return result;
+ }
+
+ result.isSuccess = YES;
+ result.isDead = YES;
+
+ if (actualStringLength > 0) {
+ result.character = unicodeString[actualStringLength - 1];
+ }
+
+ return result;
}
+static jint CharacterToDeadKeyCode(unichar ch) {
+ for (const struct CharToVKEntry *map = charToDeadVKTable; map->c != 0; ++map) {
+ if (ch == map->c) {
+ return map->javaKey;
+ }
+ }
+
+ // No builtin VK_DEAD_ constant for this dead key,
+ // nothing better to do than to fall back to the extended key code.
+ // This can happen on the following ascii-capable key layouts on the base layer:
+ // - Apache (com.apple.keylayout.Apache)
+ // - Chickasaw (com.apple.keylayout.Chickasaw)
+ // - Choctaw (com.apple.keylayout.Choctaw)
+ // - Navajo (com.apple.keylayout.Navajo)
+ // - Vietnamese (com.apple.keylayout.Vietnamese)
+ // Vietnamese layout is unique among these in that the "dead key" is actually a self-containing symbol,
+ // that can be modified by an accent typed after it. In essence, it's like a dead key in reverse:
+ // the user should first type the letter and only then the necessary accent.
+ // This way the key code would be what the user expects.
+
+ return 0x1000000 + ch;
+}
+
+struct JavaKeyTranslationResult {
+ jint keyCode;
+ jint keyLocation;
+
+ jint usKeyCode; // this is always the key position on the QWERTY keyboard
+
+ // if the base key is dead, then it's the corresponding dead key code, otherwise it's VK_UNDEFINED
+ jint deadKeyCode;
+
+ // if the key combo is dead, then it's the corresponding dead key code, otherwise it's VK_UNDEFINED
+ jint deadKeyComboCode;
+};
+
/*
* This is the function that uses the table above to take incoming
* NSEvent keyCodes and translate to the Java virtual key code.
*/
static void
-NsCharToJavaVirtualKeyCode(unichar ch, BOOL isDeadChar,
- NSUInteger flags, unsigned short key,
- jint *keyCode, jint *keyLocation, BOOL *postsTyped,
- unichar *deadChar, jint *extendedkeyCode)
+NsCharToJavaVirtualKeyCode(unsigned short key, unsigned mods, const BOOL useNationalLayouts,
+ const BOOL reportDeadKeysAsNormal,
+ struct JavaKeyTranslationResult* result)
{
- static size_t size = sizeof(keyTable) / sizeof(struct _key);
- NSInteger offset;
-
- if (isDeadChar) {
- unichar testDeadChar = NsGetDeadKeyChar(key);
- const struct CharToVKEntry *map;
- for (map = charToDeadVKTable; map->c != 0; ++map) {
- if (testDeadChar == map->c) {
- *keyCode = map->javaKey;
- *postsTyped = NO;
- // TODO: use UNKNOWN here?
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
- *deadChar = testDeadChar;
- return;
- }
- }
- // If we got here, we keep looking for a normal key.
- }
-
- if ([[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
- // key is an alphabetic character
- unichar lower;
- lower = tolower(ch);
- offset = lower - 'a';
- if (offset >= 0 && offset <= 25) {
- // checking for A-Z characters
- *postsTyped = YES;
- // do quick conversion
- *keyCode = java_awt_event_KeyEvent_VK_A + offset;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
+ // This is going to be a lengthy explanation about what it is that we need to achieve in this function.
+ // It took me quite a while to figure out myself, so hopefully it will be useful to others as well.
+ // I will describe the desired behavior when useNationalLayouts = true. Setting this parameter to false should
+ // ideally make the behavior identical to the one of OpenJDK, barring a few obvious bugfixes, like JBR-3860.
+ //
+ // For clarity here's what I mean by certain phrases:
+ // - Input source: what macOS calls "Keyboard layout input source", so excluding emoji pickers and handwriting
+ // - Key layout: Input source in the com.apple.keylayout namespace, i.e. a "simple" keyboard
+ // - IME: Input source in the com.apple.inputmethod namespace, i.e. a "complex" input method
+ // - Physical layout: A property of the physical keyboard device that has to do with the physical location of keys and their mapping to key codes
+ // - Underlying key layout: The key layout which actually translates the keys that the user presses to Java events.
+ // - Key code: A macOS virtual key code (the property `keyCode` on `NSEvent`)
+ // - Java key code: The values returned by `KeyEvent.getKeyCode()`
+ // - Key: Keyboard key without any modifiers
+ // - Combo: Keyboard key with modifiers
+ // - Dead key/combo: A key/combo that sets a dead key state when interpreted using UCKeyTranslate
+ //
+ // Whenever I refer to a key on the physical keyboard I will use the US layout.
+ //
+ // This function needs to determine the following:
+ // - Java key code
+ // - Java key location
+ //
+ // Obtaining the key location is fairly easy, since it can be looked up in the table by key code and physical layout type.
+ //
+ // To understand how to obtain the Java key code, let's take a look at the types of input sources that we want to handle:
+ // - Latin-based key layouts (ABC, German, French, Spanish, etc)
+ // - Non-latin-based key layouts (Arabic, Armenian, Russian, etc)
+ // - Latin-based IMEs (Pinyin, Cantonese - Phonetic, Japanese Romaji, etc.)
+ // - Non-latin-based IMEs (Korean, Zhuyin, Japanese Kana, etc.)
+ //
+ // These are possible physical layouts supported on macOS:
+ // - ANSI (North America, most of Asia and others)
+ // - ISO (Europe, Latin America, Middle East and others)
+ // - JIS (Japan)
+ //
+ // As a rule, any input source can be used on any physical layout.
+ // This might cause some key codes to correspond to different characters on the same input source.
+ //
+ // Basically we want the following behavior:
+ // - Latin-based key layouts should report their own keys unchanged.
+ // - Other input sources should report the key on their underlying key layout.
+ //
+ // Latin-based IMEs make it easy to determine the underlying key layout.
+ // macOS allows us to obtain a copy of the input source by calling TISCopyCurrentKeyboardLayoutInputSource().
+ // There's also the TISCopyInputMethodKeyboardLayoutOverride() function, but certain IMs (Google Japanese IME)
+ // don't set it properly.
+ //
+ // Non-latin-based key layouts and IMEs will use the US key layout as the underlying one.
+ // This is the behavior of native apps.
+ //
+ // Java has builtin key codes for most characters that can appear at the base layer of various key layouts.
+ // The rest are constructed like this: 0x01000000 + codePoint. All keys on builtin ASCII-capable layouts produce
+ // no surrogate pairs, but some of them can produce strings containing more than one code point. These need to be
+ // dealt with carefully as to avoid having different keys produce same Java key codes.
+ //
+ // Here's the various groups of named Java key codes that we need to handle:
+ // - Fixed keys that don't vary between input sources: VK_SPACE, VK_SHIFT, VK_NUMPAD0-VK_NUMPAD9, VK_F1-VK_F24, etc.
+ // - Dead keys: VK_DEAD_ACUTE, VK_DEAD_GRAVE, etc.
+ // - Punctuation: VK_PLUS, VK_SLASH, VK_SEMICOLON, etc.
+ // - Latin letters: VK_A-VK_Z
+ // - Numbers: VK_0-VK_9
+ //
+ // Fixed keys are hardcoded in keyTable and keyTableJISOverride.
+ //
+ // Dead keys need to be mapped into the corresponding VK_DEAD_ key codes in the same way the normal keys are mapped,
+ // that is using an underlying layout. This is done by using the UCKeyTranslate function together with charToDeadVKTable.
+ // It is possible to extract a (usually) non-combining dead key character by calling UCKeyTranslate
+ // with the kUCKeyTranslateNoDeadKeysMask option.
+ //
+ // Punctuation is hardcoded in extraCharToVKTable. Latin letters and numbers are dealt with separately.
+ //
+ // Bonus! What does it mean to have the "national layouts" disabled? In my opinion this simply means that
+ // the underlying key layout is the one that the user currently uses, or the override key layout for the input method
+ // that the user currently uses. I think this approach strikes the right balance between preserving compatibility
+ // with OpenJDK where it matters, while at the same time fixing a lot of annoying bugs.
+
+ // Find out which key does the key code correspond to in the US/ABC key layout.
+ // Need to take into account that the same virtual key code may correspond to
+ // different keys depending on the physical layout.
+
+ // memset the result to zero
+ *result = (struct JavaKeyTranslationResult){0};
+
+ const struct KeyTableEntry* usKey = GetKeyTableEntryForKeyCode(key);
+
+ // Determine the underlying layout.
+ // If underlyingLayout is nil then fall back to using the usKey.
+
+ TISInputSourceRef underlyingLayout = GetCurrentUnderlyingLayout(useNationalLayouts);
+ TISInputSourceRef activeLayout = useNationalLayouts ? GetCurrentUnderlyingLayout(NO) : underlyingLayout;
+
+ // Default to returning the US key data.
+ result->keyCode = result->usKeyCode = usKey->javaKeyCode;
+ result->keyLocation = usKey->javaKeyLocation;
+ result->deadKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
+ result->deadKeyComboCode = java_awt_event_KeyEvent_VK_UNDEFINED;
+
+ if (underlyingLayout == nil || activeLayout == nil || !usKey->variesBetweenLayouts) {
+ return;
+ }
+
+ // Translate the key + modifiers using the current key layout
+ struct KeyCodeTranslationResult translatedKeyCombo = TranslateKeyCodeUsingLayout(activeLayout, key, mods);
+
+ // If the key + modifiers produces a dead key, report it
+ if (translatedKeyCombo.isDead) {
+ result->deadKeyComboCode = CharacterToDeadKeyCode(translatedKeyCombo.character);
+ }
+
+ // Translate the key using the underlying key layout.
+ // We keep Cmd as a modifier, since there are some layouts, like Dvorak-QWERTY-Cmd, which change behavior when Cmd is pressed.
+ unsigned keyTranslationModsMask = mods & NSEventModifierFlagCommand;
+ struct KeyCodeTranslationResult translatedKey = TranslateKeyCodeUsingLayout(underlyingLayout, key, keyTranslationModsMask);
+
+ struct KeyCodeTranslationResult translatedKeyNoMods = translatedKey;
+ if (keyTranslationModsMask != 0) {
+ translatedKeyNoMods = TranslateKeyCodeUsingLayout(underlyingLayout, key, 0);
+ }
+
+ // Test whether this key is dead.
+ if (translatedKeyNoMods.isDead) {
+ result->deadKeyCode = CharacterToDeadKeyCode(translatedKeyNoMods.character);
+ }
+
+ if (!reportDeadKeysAsNormal && result->deadKeyCode) {
+ result->keyCode = result->deadKeyCode;
+ return;
+ }
+
+ unichar ch = 0;
+
+ if (translatedKey.isDead || translatedKey.isTyped) {
+ ch = translatedKey.character;
+ }
+
+ // Together with the following two checks (letters and digits) this table
+ // properly handles all keys that have corresponding VK_ codes.
+ // Unfortunately not all keys are like that. They are handled separately.
+
+ for (const struct CharToVKEntry *map = extraCharToVKTable; map->c != 0; ++map) {
+ if (map->c == ch) {
+ result->keyCode = map->javaKey;
return;
- } else {
- // checking for non-english characters
- // this value comes from ExtendedKeyCodes.java
- offset += 0x01000000;
- *postsTyped = YES;
- // do quick conversion
- // the keyCode is off by 32, so adding it here
- *extendedkeyCode = java_awt_event_KeyEvent_VK_A + offset + 32;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
}
}
- if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:ch]) {
+ if (ch >= 'a' && ch <= 'z') {
+ // key is a basic latin letter
+ result->keyCode = java_awt_event_KeyEvent_VK_A + ch - 'a';
+ return;
+ }
+
+ if (ch >= '0' && ch <= '9') {
// key is a digit
- offset = ch - '0';
- // make sure in range for decimal digits
- if (offset >= 0 && offset <= 9) {
- jboolean numpad = ((flags & NSNumericPadKeyMask) &&
- (key > 81 && key < 93));
- *postsTyped = YES;
- if (numpad) {
- *keyCode = offset + java_awt_event_KeyEvent_VK_NUMPAD0;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD;
- } else {
- *keyCode = offset + java_awt_event_KeyEvent_VK_0;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
- }
- return;
- }
+ // numpad digits are already handled, since they don't vary between layouts
+ result->keyCode = java_awt_event_KeyEvent_VK_0 + ch - '0';
+ return;
}
- if (key < size) {
- *postsTyped = keyTable[key].postsTyped;
- *keyCode = keyTable[key].javaKeyCode;
- *keyLocation = keyTable[key].javaKeyLocation;
- } else {
- // Should we report this? This means we've got a keyboard
- // we don't know about...
- *postsTyped = NO;
- *keyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
+ if (useNationalLayouts || [[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
+ // If useNationalLayouts = false, then we only convert the key codes for letters here.
+ // This is the default behavior in OpenJDK and I don't think it's a good idea to change that.
+
+ // Otherwise we also need to report characters other than letters.
+ // If we ended up in this branch, this means that the character doesn't have its own VK_ code.
+ // Apart from letters, this is the case for characters like the Section Sign (U+00A7)
+ // on the French keyboard (key ANSI_6) or Pound Sign (U+00A3) on the Italian – QZERTY keyboard (key ANSI_8).
+
+ result->keyCode = 0x01000000 + ch;
}
}
@@ -541,8 +890,6 @@ static unichar NsGetDeadKeyChar(unsigned short keyCode)
*javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
} else if (eventKeyCode == cur->rightKeyCode) {
*javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else if (cur->nsMask == NSAlternateKeyMask) {
- continue;
}
*javaKeyType = (cur->nsMask & nsFlags) ?
java_awt_event_KeyEvent_KEY_PRESSED :
@@ -673,47 +1020,42 @@ jlong UTC(NSEvent *event) {
/*
* Class: sun_lwawt_macosx_NSEvent
* Method: nsToJavaKeyInfo
- * Signature: ([I[I)Z
+ * Signature: ([I[I)V
*/
-JNIEXPORT jboolean JNICALL
+JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_NSEvent_nsToJavaKeyInfo
(JNIEnv *env, jclass cls, jintArray inData, jintArray outData)
{
- BOOL postsTyped = NO;
-
JNI_COCOA_ENTER(env);
jboolean copy = JNI_FALSE;
jint *data = (*env)->GetIntArrayElements(env, inData, ©);
- CHECK_NULL_RETURN(data, postsTyped);
+ CHECK_NULL(data);
- // in = [testChar, testDeadChar, modifierFlags, keyCode]
- jchar testChar = (jchar)data[0];
- BOOL isDeadChar = (data[1] != 0);
- jint modifierFlags = data[2];
- jshort keyCode = (jshort)data[3];
+ // in = [keyCode, useNationalLayouts]
+ jshort keyCode = (jshort)data[0];
+ unsigned mods = ((unsigned*)data)[1];
+ BOOL useNationalLayouts = (data[2] != 0);
+ BOOL reportDeadKeysAsNormal = (data[3] != 0);
- jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
- jint jextendedkeyCode = -1;
- jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
- jint testDeadChar = 0;
+ struct JavaKeyTranslationResult result = {0};
- NsCharToJavaVirtualKeyCode((unichar)testChar, isDeadChar,
- (NSUInteger)modifierFlags, (unsigned short)keyCode,
- &jkeyCode, &jkeyLocation, &postsTyped,
- (unichar *) &testDeadChar, &jextendedkeyCode);
+ NsCharToJavaVirtualKeyCode((unsigned short)keyCode,
+ mods,
+ useNationalLayouts,
+ reportDeadKeysAsNormal,
+ &result);
- // out = [jkeyCode, jkeyLocation, deadChar, jextendedkeyCode];
- (*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
- (*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
- (*env)->SetIntArrayRegion(env, outData, 2, 1, &testDeadChar);
- (*env)->SetIntArrayRegion(env, outData, 3, 1, &jextendedkeyCode);
+ // out = [jkeyCode, jkeyLocation, usKeyCode, deadKeyCode, deadKeyComboCode];
+ (*env)->SetIntArrayRegion(env, outData, 0, 1, &result.keyCode);
+ (*env)->SetIntArrayRegion(env, outData, 1, 1, &result.keyLocation);
+ (*env)->SetIntArrayRegion(env, outData, 2, 1, &result.usKeyCode);
+ (*env)->SetIntArrayRegion(env, outData, 3, 1, &result.deadKeyCode);
+ (*env)->SetIntArrayRegion(env, outData, 4, 1, &result.deadKeyComboCode);
(*env)->ReleaseIntArrayElements(env, inData, data, 0);
JNI_COCOA_EXIT(env);
-
- return postsTyped;
}
/*
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
index 9f64001212c5..19d9994016d5 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
@@ -43,12 +43,13 @@
// Input method data
jobject fInputMethodLOCKABLE;
+ BOOL fInputMethodInteractionEnabled;
BOOL fKeyEventsNeeded;
BOOL fProcessingKeystroke;
+ NSString* actualCharacters;
BOOL fEnablePressAndHold;
- BOOL fInPressAndHold;
- BOOL fPAHNeedsToSelect;
+ BOOL fIsPressAndHold;
id cglLayer; // is a sublayer of view.layer
@@ -66,6 +67,7 @@
// Input method-related events
- (void)setInputMethod:(jobject)inputMethod;
-- (void)abandonInput;
+- (void)abandonInput:(jobject) component;
+- (void)enableImInteraction:(BOOL)enabled;
@end
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
index d6dde548c0a3..1eacd9ff0295 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
@@ -26,21 +26,31 @@
#import "jni_util.h"
#import "CGLGraphicsConfig.h"
#import "AWTView.h"
+#import "AWTEvent.h"
#import "AWTWindow.h"
#import "a11y/CommonComponentAccessibility.h"
#import "JavaAccessibilityUtilities.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
+#import "jni_util.h"
+#import "PropertiesUtilities.h"
+#import "sun_lwawt_macosx_CPlatformWindow.h"
#import
+#define DEFAULT_FRAME_WIDTH 1504
+#define DEFAULT_FRAME_HEIGHT 846
+
// keyboard layout
static NSString *kbdLayout;
// Constant for keyman layouts
#define KEYMAN_LAYOUT "keyman"
+// workaround for JBR-6704
+static unichar lastCtrlCombo;
+
@interface AWTView()
@property (retain) CDropTarget *_dropTarget;
@property (retain) CDragSource *_dragSource;
@@ -48,18 +58,19 @@ @interface AWTView()
-(void) deliverResize: (NSRect) rect;
-(void) resetTrackingArea;
-(void) deliverJavaKeyEventHelper: (NSEvent*) event;
--(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint;
-(NSMutableString *) parseString : (id) complexString;
@end
// Uncomment this line to see fprintfs of each InputMethod API being called on this View
//#define IM_DEBUG TRUE
-//#define EXTRA_DEBUG
+//#define LOG_KEY_EVENTS
static BOOL shouldUsePressAndHold() {
return YES;
}
+extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString * chars);
+
@implementation AWTView
@synthesize _dropTarget;
@@ -79,12 +90,12 @@ - (id) initWithRect: (NSRect) rect
m_cPlatformView = cPlatformView;
fInputMethodLOCKABLE = NULL;
+ fInputMethodInteractionEnabled = YES;
fKeyEventsNeeded = NO;
fProcessingKeystroke = NO;
+ lastCtrlCombo = 0;
fEnablePressAndHold = shouldUsePressAndHold();
- fInPressAndHold = NO;
- fPAHNeedsToSelect = NO;
mouseIsOver = NO;
[self resetTrackingArea];
@@ -144,6 +155,26 @@ - (void) viewDidMoveToWindow {
}
}
+- (void)setFrame:(NSRect)newFrame {
+ if (isnan(newFrame.origin.x)) {
+ newFrame.origin.x = 0;
+ }
+
+ if (isnan(newFrame.origin.y)) {
+ newFrame.origin.y = 0;
+ }
+
+ if (isnan(newFrame.size.width)) {
+ newFrame.size.width = DEFAULT_FRAME_WIDTH;
+ }
+
+ if (isnan(newFrame.size.height)) {
+ newFrame.size.height = DEFAULT_FRAME_HEIGHT;
+ }
+
+ [super setFrame:newFrame];
+}
+
- (BOOL) acceptsFirstMouse: (NSEvent *)event {
return YES;
}
@@ -173,25 +204,41 @@ - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize {
* MouseEvents support
*/
+- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event {
+ return [self isWindowDisabled];
+}
+
- (void) mouseDown: (NSEvent *)event {
- NSInputManager *inputManager = [NSInputManager currentInputManager];
- if ([inputManager wantsToHandleMouseEvents]) {
+ if ([self isWindowDisabled]) {
+ [NSApp preventWindowOrdering];
+ [NSApp activateIgnoringOtherApps:YES];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *checkForInputManagerInMouseDown =
+ [PropertiesUtilities
+ javaSystemPropertyForKey:@"apple.awt.checkForInputManagerInMouseDown"
+ withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:checkForInputManagerInMouseDown]) {
+ NSInputManager *inputManager = [NSInputManager currentInputManager];
+ if ([inputManager wantsToHandleMouseEvents]) {
#if IM_DEBUG
- NSLog(@"-> IM wants to handle event");
+ NSLog(@"-> IM wants to handle event");
#endif
- if (![inputManager handleMouseEvent:event]) {
- [self deliverJavaMouseEvent: event];
- } else {
+ if ([inputManager handleMouseEvent:event]) {
#if IM_DEBUG
- NSLog(@"-> Event was handled.");
+ NSLog(@"-> Event was handled.");
+ return;
#endif
- }
- } else {
+ }
+ } else {
#if IM_DEBUG
- NSLog(@"-> IM does not want to handle event");
+ NSLog(@"-> IM does not want to handle event");
#endif
- [self deliverJavaMouseEvent: event];
+ }
}
+
+ [self deliverJavaMouseEvent:event];
}
- (void) mouseUp: (NSEvent *)event {
@@ -221,10 +268,19 @@ - (void) mouseMoved: (NSEvent *)event {
NSPoint localPoint = [self convertPoint: eventLocation fromView: nil];
if ([self mouse: localPoint inRect: [self bounds]]) {
- [self deliverJavaMouseEvent: event];
- } else {
- [[self nextResponder] mouseDown:event];
+ NSWindow* eventWindow = [event window];
+ NSPoint screenPoint = (eventWindow == nil)
+ ? eventLocation
+ // SDK we build against is too old - it doesn't have convertPointToScreen
+ : [eventWindow convertRectToScreen:NSMakeRect(eventLocation.x, eventLocation.y, 0, 0)].origin;
+ // macOS can report mouseMoved events to a window even if it's not showing currently (see JBR-2702)
+ // so we're performing an additional check here
+ if (self.window.windowNumber == [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:0]) {
+ [self deliverJavaMouseEvent: event];
+ return;
+ }
}
+ [[self nextResponder] mouseMoved:event];
}
- (void) mouseDragged: (NSEvent *)event {
@@ -260,70 +316,186 @@ - (void) scrollWheel: (NSEvent*) event {
* KeyEvents support
*/
+#ifdef LOG_KEY_EVENTS
+static void debugPrintNSString(const char* name, NSString* s) {
+ if (s == nil) {
+ fprintf(stderr, "\t%s: nil\n", name);
+ return;
+ }
+ const char* utf8 = [s UTF8String];
+ int codeUnits = [s length];
+ int bytes = strlen(utf8);
+ fprintf(stderr, "\t%s: [utf8 = \"", name);
+ for (const unsigned char* c = (const unsigned char*)utf8; *c; ++c) {
+ if (*c >= 0x20 && *c <= 0x7e) {
+ fputc(*c, stderr);
+ } else {
+ fprintf(stderr, "\\x%02x", *c);
+ }
+ }
+ fprintf(stderr, "\", utf16 = \"");
+ for (int i = 0; i < codeUnits; ++i) {
+ int c = (int)[s characterAtIndex:i];
+ if (c >= 0x20 && c <= 0x7e) {
+ fputc(c, stderr);
+ } else {
+ fprintf(stderr, "\\u%04x", c);
+ }
+ }
+ fprintf(stderr, "\", bytes = %d, codeUnits = %d]\n", bytes, codeUnits);
+}
+
+static void debugPrintNSEvent(NSEvent* event, const char* comment) {
+ NSEventType type = [event type];
+ if (type == NSEventTypeKeyDown) {
+ fprintf(stderr, "[AWTView.m] keyDown in %s\n", comment);
+ } else if (type == NSEventTypeKeyUp) {
+ fprintf(stderr, "[AWTView.m] keyUp in %s\n", comment);
+ } else if (type == NSEventTypeFlagsChanged) {
+ fprintf(stderr, "[AWTView.m] flagsChanged in %s\n", comment);
+ } else {
+ fprintf(stderr, "[AWTView.m] unknown event %d in %s\n", (int)type, comment);
+ return;
+ }
+ if (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp) {
+ debugPrintNSString("characters", [event characters]);
+ debugPrintNSString("charactersIgnoringModifiers", [event charactersIgnoringModifiers]);
+ fprintf(stderr, "\tkeyCode: %d (0x%02x)\n", [event keyCode], [event keyCode]);
+ }
+ fprintf(stderr, "\tmodifierFlags: 0x%08x\n", (unsigned)[event modifierFlags]);
+ TISInputSourceRef is = TISCopyCurrentKeyboardLayoutInputSource();
+ fprintf(stderr, "\tTISCopyCurrentKeyboardLayoutInputSource: %s\n", is == nil ? "(nil)" : [(NSString*) TISGetInputSourceProperty(is, kTISPropertyInputSourceID) UTF8String]);
+ fprintf(stderr, "\twillBeHandledByComplexInputMethod: %s\n", [event willBeHandledByComplexInputMethod] ? "true" : "false");
+ CFRelease(is);
+}
+#endif
+
- (void) keyDown: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "keyDown");
+#endif
+ // Check for willBeHandledByComplexInputMethod here, because interpretKeyEvents might invalidate that field
+ fIsPressAndHold = fEnablePressAndHold && [event willBeHandledByComplexInputMethod] && fInputMethodLOCKABLE;
+
fProcessingKeystroke = YES;
- fKeyEventsNeeded = ![(NSString *)kbdLayout containsString:@KEYMAN_LAYOUT];
+ fKeyEventsNeeded = ![(NSString *)kbdLayout containsString:@KEYMAN_LAYOUT] && !fIsPressAndHold;
+
+ NSString *eventCharacters = [event characters];
+ unsigned mods = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
+
+ if (([event modifierFlags] & NSControlKeyMask) && [eventCharacters length] == 1) {
+ lastCtrlCombo = [eventCharacters characterAtIndex:0];
+ } else {
+ lastCtrlCombo = 0;
+ }
// Allow TSM to look at the event and potentially send back NSTextInputClient messages.
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+ if (fInputMethodInteractionEnabled == YES) {
+ [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+ }
- if (fEnablePressAndHold && [event willBeHandledByComplexInputMethod] &&
- fInputMethodLOCKABLE)
+ if (fIsPressAndHold)
{
+ fIsPressAndHold = NO;
+ BOOL skipProcessingCancelKeys = NO;
fProcessingKeystroke = NO;
- if (!fInPressAndHold) {
- fInPressAndHold = YES;
- fPAHNeedsToSelect = YES;
- } else {
- // Abandon input to reset IM and unblock input after canceling
- // input accented symbols
-
- switch([event keyCode]) {
- case kVK_Escape:
- case kVK_Delete:
- case kVK_Return:
- case kVK_ForwardDelete:
- case kVK_PageUp:
- case kVK_PageDown:
- case kVK_DownArrow:
- case kVK_UpArrow:
- case kVK_Home:
- case kVK_End:
- [self abandonInput];
- break;
- }
+ // Abandon input to reset IM and unblock input after canceling
+ // input accented symbols
+
+ switch([event keyCode]) {
+ case kVK_Return:
+ case kVK_Escape:
+ case kVK_PageUp:
+ case kVK_PageDown:
+ case kVK_DownArrow:
+ case kVK_UpArrow:
+ case kVK_Home:
+ case kVK_End:
+ skipProcessingCancelKeys = YES;
+
+ case kVK_ForwardDelete:
+ case kVK_Delete:
+ // Abandon input to reset IM and unblock input after
+ // canceling input accented symbols
+#ifdef LOG_KEY_EVENTS
+ fprintf(stderr, "[AWTView.m] Abandoning input in the keyDown event\n");
+#endif
+ [self abandonInput:nil];
+ break;
+ }
+ if (skipProcessingCancelKeys) {
+#ifdef LOG_KEY_EVENTS
+ fprintf(stderr, "[AWTView.m] Skipping the keyDown event: isPressAndHold && skipProcessingCancelKeys\n");
+#endif
+ return;
}
- return;
}
- NSString *eventCharacters = [event characters];
BOOL isDeadKey = (eventCharacters != nil && [eventCharacters length] == 0);
if ((![self hasMarkedText] && fKeyEventsNeeded) || isDeadKey) {
[self deliverJavaKeyEventHelper: event];
}
+#ifdef LOG_KEY_EVENTS
+ else {
+ fprintf(stderr, "[AWTView.m] Skipping the keyDown event: ([self hasMarkedText] || !fKeyEventsNeeded) && !isDeadKey\n");
+ fprintf(stderr, "[AWTView.m] hasMarkedText: %s\n", [self hasMarkedText] ? "YES" : "NO");
+ fprintf(stderr, "[AWTView.m] fKeyEventsNeeded: %s\n", fKeyEventsNeeded ? "YES" : "NO");
+ fprintf(stderr, "[AWTView.m] isDeadKey: %s\n", isDeadKey ? "YES" : "NO");
+ }
+#endif
+
+ if (actualCharacters != nil) {
+ [actualCharacters release];
+ actualCharacters = nil;
+ }
fProcessingKeystroke = NO;
}
- (void) keyUp: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "keyUp");
+#endif
[self deliverJavaKeyEventHelper: event];
}
- (void) flagsChanged: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "flagsChanged");
+#endif
[self deliverJavaKeyEventHelper: event];
}
- (BOOL) performKeyEquivalent: (NSEvent *) event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "performKeyEquivalent");
+#endif
// if IM is active key events should be ignored
- if (![self hasMarkedText] && !fInPressAndHold) {
+ if (![self hasMarkedText] && !fIsPressAndHold && ![event willBeHandledByComplexInputMethod]) {
[self deliverJavaKeyEventHelper: event];
}
+ const NSUInteger modFlags =
+ [event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
+
+ // Workaround for JBR-3544
+ // When tabbing mode is on (jdk.allowMacOSTabbedWindows=true) and "Ctrl Opt N" / "Cmd Opt N" is pressed,
+ // macOS first sends it, and immediately then sends "Ctrl N" / "Cmd N".
+ // The workaround is to "eat" (by returning TRUE) the "Ctrl Opt N" / "Cmd Opt N",
+ // so macOS won't send its "fallback" version ("Ctrl N" / "Cmd N").
+ if ([event keyCode] == kVK_ANSI_N) {
+ const NSUInteger ctrlOpt = (NSControlKeyMask | NSAlternateKeyMask);
+ const NSUInteger cmdOpt = (NSCommandKeyMask | NSAlternateKeyMask);
+
+ if ((modFlags == ctrlOpt) || (modFlags == cmdOpt)) {
+ [[NSApp mainMenu] performKeyEquivalent: event]; // just in case (as in the workaround for 8020209 below)
+ return YES;
+ }
+ }
+
// Workaround for 8020209: special case for "Cmd =" and "Cmd ."
// because Cocoa calls performKeyEquivalent twice for these keystrokes
- NSUInteger modFlags = [event modifierFlags] &
- (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
if (modFlags == NSCommandKeyMask) {
NSString *eventChars = [event charactersIgnoringModifiers];
if ([eventChars length] == 1) {
@@ -336,6 +508,23 @@ - (BOOL) performKeyEquivalent: (NSEvent *) event {
}
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *captureNextAppWinKey = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.captureNextAppWinKey"
+ withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:captureNextAppWinKey]) {
+ NSUInteger deviceIndependentModifierFlagsMask =
+ [event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
+ // Why translate the key code here and not just use event.characters?
+ // The default macOS shortcut for NextWindowInApplication is Cmd+Backtick. Pressing Cmd+Dead Grave also works
+ // for layouts that have the backtick as a dead key. Unfortunately, some (but notably not all) of these layouts
+ // consider Cmd+Dead Grave to also be a dead key, which means that event.characters will be an empty string.
+ // Explicitly translating the key code with a proper underlying key layout fixes this.
+ struct KeyCodeTranslationResult translationResult = TranslateKeyCodeUsingLayout(GetCurrentUnderlyingLayout(YES), [event keyCode], 0);
+ if (translationResult.isSuccess && translationResult.character) {
+ return isSystemShortcut_NextWindowInApplication(deviceIndependentModifierFlagsMask, [event keyCode], [NSString stringWithCharacters:&translationResult.character length:1]) ? YES : NO;
+ }
+ }
+
return NO;
}
@@ -343,14 +532,17 @@ - (BOOL) performKeyEquivalent: (NSEvent *) event {
* Utility methods and accessors
*/
--(void) deliverJavaMouseEvent: (NSEvent *) event {
- BOOL isEnabled = YES;
+-(BOOL) isWindowDisabled {
NSWindow* window = [self window];
if ([window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]]) {
- isEnabled = [(AWTWindow*)[window delegate] isEnabled];
+ return ![(AWTWindow*)[window delegate] isEnabled];
+ } else {
+ return NO;
}
+}
- if (!isEnabled) {
+-(void) deliverJavaMouseEvent: (NSEvent *) event {
+ if ([self isWindowDisabled]) {
return;
}
@@ -451,6 +643,9 @@ -(void) deliverJavaKeyEventHelper: (NSEvent *) event {
static NSEvent* sLastKeyEvent = nil;
if (event == sLastKeyEvent) {
// The event is repeatedly delivered by keyDown: after performKeyEquivalent:
+#ifdef LOG_KEY_EVENTS
+ fprintf(stderr, "[AWTView.m] deliverJavaKeyEventHelper: ignoring duplicate events\n");
+#endif
return;
}
[sLastKeyEvent release];
@@ -460,20 +655,22 @@ -(void) deliverJavaKeyEventHelper: (NSEvent *) event {
JNIEnv *env = [ThreadUtilities getJNIEnv];
jstring characters = NULL;
- jstring charactersIgnoringModifiers = NULL;
if ([event type] != NSEventTypeFlagsChanged) {
characters = NSStringToJavaString(env, [event characters]);
- charactersIgnoringModifiers = NSStringToJavaString(env, [event charactersIgnoringModifiers]);
+ }
+ jstring jActualCharacters = NULL;
+ if (actualCharacters != nil) {
+ jActualCharacters = NSStringToJavaString(env, actualCharacters);
}
DECLARE_CLASS(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
DECLARE_METHOD(jctor_NSEvent, jc_NSEvent, "", "(IISLjava/lang/String;Ljava/lang/String;)V");
jobject jEvent = (*env)->NewObject(env, jc_NSEvent, jctor_NSEvent,
- [event type],
- [event modifierFlags],
- [event keyCode],
- characters,
- charactersIgnoringModifiers);
+ [event type],
+ [event modifierFlags],
+ [event keyCode],
+ characters,
+ jActualCharacters);
CHECK_NULL(jEvent);
DECLARE_CLASS(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
@@ -488,6 +685,9 @@ -(void) deliverJavaKeyEventHelper: (NSEvent *) event {
if (characters != NULL) {
(*env)->DeleteLocalRef(env, characters);
}
+ if (jActualCharacters != NULL) {
+ (*env)->DeleteLocalRef(env, jActualCharacters);
+ }
(*env)->DeleteLocalRef(env, jEvent);
}
@@ -526,21 +726,6 @@ - (void) drawRect:(NSRect)dirtyRect {
}
}
--(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint {
- if ((codePoint == 0x0024) || (codePoint == 0x00A3) ||
- (codePoint == 0x00A5) ||
- ((codePoint >= 0x900) && (codePoint <= 0x97F)) ||
- ((codePoint >= 0x20A3) && (codePoint <= 0x20BF)) ||
- ((codePoint >= 0x3000) && (codePoint <= 0x303F)) ||
- ((codePoint >= 0xFF00) && (codePoint <= 0xFFEF))) {
- // Code point is in 'CJK Symbols and Punctuation' or
- // 'Halfwidth and Fullwidth Forms' Unicode block or
- // currency symbols unicode or Devanagari script
- return YES;
- }
- return NO;
-}
-
-(NSMutableString *) parseString : (id) complexString {
if ([complexString isKindOfClass:[NSString class]]) {
return [complexString mutableCopy];
@@ -593,6 +778,10 @@ + (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible
- (id)getAxData:(JNIEnv*)env
{
+ NSString *a11yEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"sun.awt.mac.a11y.enabled" withEnv:env];
+ if ([@"false" isCaseInsensitiveLike:a11yEnabledProp]) {
+ return nil;
+ }
jobject jcomponent = [self awtComponent:env];
id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
(*env)->DeleteLocalRef(env, jcomponent);
@@ -681,7 +870,7 @@ - (NSData *)accessibleSelectedTextAsRTFD
- (BOOL)replaceAccessibleTextSelection:(NSString *)text
{
id focused = [self accessibilityFocusedUIElement];
- if (![focused respondsToSelector:@selector(setAccessibilitySelectedText)]) return NO;
+ if (![focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) return NO;
[focused setAccessibilitySelectedText:text];
return YES;
}
@@ -758,9 +947,11 @@ - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
CDragSource *dragSource = self._dragSource;
NSDragOperation dragOp = NSDragOperationNone;
- if (dragSource != nil) {
+ if (dragSource != nil)
dragOp = [dragSource draggingSourceOperationMaskForLocal:flag];
- }
+ else if ([super respondsToSelector:@selector(draggingSourceOperationMaskForLocal:)])
+ dragOp = [super draggingSourceOperationMaskForLocal:flag];
+
return dragOp;
}
@@ -770,9 +961,11 @@ - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
CDragSource *dragSource = self._dragSource;
NSArray* array = nil;
- if (dragSource != nil) {
+ if (dragSource != nil)
array = [dragSource namesOfPromisedFilesDroppedAtDestination:dropDestination];
- }
+ else if ([super respondsToSelector:@selector(namesOfPromisedFilesDroppedAtDestination:)])
+ array = [super namesOfPromisedFilesDroppedAtDestination:dropDestination];
+
return array;
}
@@ -812,9 +1005,11 @@ - (BOOL)ignoreModifierKeysWhileDragging
CDragSource *dragSource = self._dragSource;
BOOL result = FALSE;
- if (dragSource != nil) {
+ if (dragSource != nil)
result = [dragSource ignoreModifierKeysWhileDragging];
- }
+ else if ([super respondsToSelector:@selector(ignoreModifierKeysWhileDragging)])
+ result = [super ignoreModifierKeysWhileDragging];
+
return result;
}
@@ -828,9 +1023,11 @@ - (NSDragOperation)draggingEntered:(id )sender
CDropTarget *dropTarget = self._dropTarget;
NSDragOperation dragOp = NSDragOperationNone;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
dragOp = [dropTarget draggingEntered:sender];
- }
+ else if ([super respondsToSelector:@selector(draggingEntered:)])
+ dragOp = [super draggingEntered:sender];
+
return dragOp;
}
@@ -840,9 +1037,11 @@ - (NSDragOperation)draggingUpdated:(id )sender
CDropTarget *dropTarget = self._dropTarget;
NSDragOperation dragOp = NSDragOperationNone;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
dragOp = [dropTarget draggingUpdated:sender];
- }
+ else if ([super respondsToSelector:@selector(draggingUpdated:)])
+ dragOp = [super draggingUpdated:sender];
+
return dragOp;
}
@@ -862,9 +1061,11 @@ - (BOOL)prepareForDragOperation:(id )sender
CDropTarget *dropTarget = self._dropTarget;
BOOL result = FALSE;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
result = [dropTarget prepareForDragOperation:sender];
- }
+ else if ([super respondsToSelector:@selector(prepareForDragOperation:)])
+ result = [super prepareForDragOperation:sender];
+
return result;
}
@@ -874,9 +1075,11 @@ - (BOOL)performDragOperation:(id )sender
CDropTarget *dropTarget = self._dropTarget;
BOOL result = FALSE;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
result = [dropTarget performDragOperation:sender];
- }
+ else if ([super respondsToSelector:@selector(performDragOperation:)])
+ result = [super performDragOperation:sender];
+
return result;
}
@@ -913,83 +1116,110 @@ - (void)draggingEnded:(id )sender
#define GET_CIM_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod", ret);
+- (NSInteger) windowLevel
+{
+#ifdef IM_DEBUG
+ fprintf(stderr, "AWTView InputMethod Selector Called : [windowLevel]\n");
+#endif // IM_DEBUG
+
+ NSWindow* const ownerWindow = [self window];
+ if (ownerWindow == nil) {
+ return NSNormalWindowLevel;
+ }
+
+ const NSWindowLevel ownerWindowLevel = [ownerWindow level];
+ if ( (ownerWindowLevel != NSNormalWindowLevel) && (ownerWindowLevel != NSFloatingWindowLevel) ) {
+ // the window level has been overridden, let's believe it
+ return ownerWindowLevel;
+ }
+
+ AWTWindow* const delegate = (AWTWindow*)[ownerWindow delegate];
+ if (delegate == nil) {
+ return ownerWindowLevel;
+ }
+
+ const jint styleBits = [delegate styleBits];
+
+ const BOOL isPopup = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_POPUP) != 0 );
+ if (isPopup) {
+ return NSPopUpMenuWindowLevel;
+ }
+
+ const BOOL isModal = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_MODAL) != 0 );
+ if (isModal) {
+ return NSFloatingWindowLevel;
+ }
+
+ return ownerWindowLevel;
+}
+
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
{
#ifdef IM_DEBUG
- fprintf(stderr, "AWTView InputMethod Selector Called : [insertText]: %s\n", [aString UTF8String]);
+ fprintf(stderr,
+ "AWTView InputMethod Selector Called : [insertText]: %s, replacementRange: location=%lu, length=%lu\n",
+ [aString UTF8String], replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
- if (fInputMethodLOCKABLE == NULL) {
+ NSMutableString * useString = [self parseString:aString];
+
+ // See JBR-6704
+ if (lastCtrlCombo && !fProcessingKeystroke && [useString length] == 1 && [useString characterAtIndex:0] == lastCtrlCombo) {
+ lastCtrlCombo = 0;
return;
}
- // Insert happens at the end of PAH
- fInPressAndHold = NO;
+ if (fInputMethodLOCKABLE == NULL) {
+ return;
+ }
// insertText gets called when the user commits text generated from an input method. It also gets
// called during ordinary input as well. We only need to send an input method event when we have marked
// text, or 'text in progress'. We also need to send the event if we get an insert text out of the blue!
// (i.e., when the user uses the Character palette or Inkwell), or when the string to insert is a complex
// Unicode value.
+ BOOL usingComplexIM = [self hasMarkedText] || !fProcessingKeystroke;
- NSMutableString * useString = [self parseString:aString];
+#ifdef IM_DEBUG
NSUInteger utf16Length = [useString lengthOfBytesUsingEncoding:NSUTF16StringEncoding];
NSUInteger utf8Length = [useString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- BOOL aStringIsComplex = NO;
-
- unichar codePoint = [useString characterAtIndex:0];
-#ifdef IM_DEBUG
NSLog(@"insertText kbdlayout %@ ",(NSString *)kbdLayout);
NSLog(@"utf8Length %lu utf16Length %lu", (unsigned long)utf8Length, (unsigned long)utf16Length);
- NSLog(@"codePoint %x", codePoint);
+ NSLog(@"hasMarkedText: %s, fProcessingKeyStroke: %s", [self hasMarkedText] ? "YES" : "NO", fProcessingKeystroke ? "YES" : "NO");
#endif // IM_DEBUG
- if ((utf16Length > 2) ||
- ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:codePoint]) ||
- [(NSString *)kbdLayout containsString:@KEYMAN_LAYOUT]) {
-#ifdef IM_DEBUG
- NSLog(@"string complex ");
-#endif
- aStringIsComplex = YES;
- }
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
- if ([self hasMarkedText] || !fProcessingKeystroke || aStringIsComplex) {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CIM_CLASS();
- GET_CIM_CLASS();
- DECLARE_METHOD(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
- // We need to select the previous glyph so that it is overwritten.
- if (fPAHNeedsToSelect) {
- (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
- CHECK_EXCEPTION();
- fPAHNeedsToSelect = NO;
- }
+ if (replacementRange.length > 0) {
+ DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
+ (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
+ replacementRange.length);
+ CHECK_EXCEPTION();
+ }
+ if (usingComplexIM) {
DECLARE_METHOD(jm_insertText, jc_CInputMethod, "insertText", "(Ljava/lang/String;)V");
- jstring insertedText = NSStringToJavaString(env, useString);
+ jstring insertedText = NSStringToJavaString(env, useString);
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_insertText, insertedText);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, insertedText);
-
- // The input method event will create psuedo-key events for each character in the committed string.
- // We also don't want to send the character that triggered the insertText, usually a return. [3337563]
fKeyEventsNeeded = NO;
- }
- else {
- // Need to set back the fKeyEventsNeeded flag so that the string following the
- // marked text is not ignored by keyDown
- if ([useString length] > 0) {
- fKeyEventsNeeded = YES;
+ } else {
+ if (actualCharacters != nil) {
+ [actualCharacters release];
}
+ actualCharacters = [useString copy];
+ fKeyEventsNeeded = YES;
}
- fPAHNeedsToSelect = NO;
// Abandon input to reset IM and unblock input after entering accented
// symbols
- [self abandonInput];
+ [self abandonInput:nil];
}
+ (void)keyboardInputSourceChanged:(NSNotification *)notification
@@ -1023,7 +1253,9 @@ - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replace
NSAttributedString *attrString = (isAttributedString ? (NSAttributedString *)aString : nil);
NSString *incomingString = (isAttributedString ? [aString string] : aString);
#ifdef IM_DEBUG
- fprintf(stderr, "AWTView InputMethod Selector Called : [setMarkedText] \"%s\", loc=%lu, length=%lu\n", [incomingString UTF8String], (unsigned long)selectionRange.location, (unsigned long)selectionRange.length);
+ fprintf(stderr, "AWTView InputMethod Selector Called :[setMarkedText] \"%s\","
+ "selectionRange(%lu, %lu), replacementRange(%lu, %lu)\n", [incomingString UTF8String],
+ selectionRange.location, selectionRange.length, replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
@@ -1031,6 +1263,14 @@ - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replace
DECLARE_METHOD(jm_addAttribute, jc_CInputMethod, "addAttribute", "(ZZII)V");
DECLARE_METHOD(jm_dispatchText, jc_CInputMethod, "dispatchText", "(IIZ)V");
+ if (replacementRange.length > 0) {
+ DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
+ (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
+ replacementRange.length);
+ CHECK_EXCEPTION();
+ }
+
+
// NSInputContext already did the analysis of the TSM event and created attributes indicating
// the underlining and color that should be done to the string. We need to look at the underline
// style and color to determine what kind of Java highlighting needs to be done.
@@ -1066,14 +1306,6 @@ - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replace
}
}
- DECLARE_METHOD(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
- // We need to select the previous glyph so that it is overwritten.
- if (fPAHNeedsToSelect) {
- (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
- CHECK_EXCEPTION();
- fPAHNeedsToSelect = NO;
- }
-
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_dispatchText,
selectionRange.location, selectionRange.length, JNI_FALSE);
CHECK_EXCEPTION();
@@ -1083,7 +1315,11 @@ - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replace
}
}
-- (void) unmarkText
+- (void) unmarkText {
+ [self unmarkText:nil];
+}
+
+- (void) unmarkText:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [unmarkText]\n");
@@ -1096,8 +1332,8 @@ - (void) unmarkText
// unmarkText cancels any input in progress and commits it to the text field.
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
- DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "()V");
- (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText);
+ DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "(Ljava/awt/Component;)V");
+ (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText, component);
CHECK_EXCEPTION();
}
@@ -1157,6 +1393,9 @@ - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)theRange a
jobject theString = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_substringFromRange, theRange.location, theRange.length);
CHECK_EXCEPTION_NULL_RETURN(theString, nil);
+ if (!theString) {
+ return NULL;
+ }
id result = [[[NSAttributedString alloc] initWithString:JavaStringToNSString(env, theString)] autorelease];
#ifdef IM_DEBUG
NSLog(@"attributedSubstringFromRange returning \"%@\"", result);
@@ -1355,20 +1594,52 @@ - (void)setInputMethod:(jobject)inputMethod
object:nil];
}
-- (void)abandonInput
+- (void)abandonInput:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [abandonInput]\n");
#endif // IM_DEBUG
[ThreadUtilities performOnMainThread:@selector(markedTextAbandoned:) on:[NSInputManager currentInputManager] withObject:self waitUntilDone:YES];
- [self unmarkText];
+ [self unmarkText:component];
}
-/******************************** END NSTextInputClient Protocol ********************************/
+-(void)enableImInteraction:(BOOL)enabled
+{
+#ifdef IM_DEBUG
+ fprintf(stderr, "AWTView InputMethod Selector Called : [enableImInteraction:%d]\n", enabled);
+#endif // IM_DEBUG
+ AWT_ASSERT_APPKIT_THREAD;
+ fInputMethodInteractionEnabled = (enabled == YES) ? YES : NO;
+}
+/******************************** END NSTextInputClient Protocol ********************************/
+
+- (void)viewDidChangeBackingProperties {
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ if ((*env)->IsSameObject(env, m_cPlatformView, NULL)) {
+ return;
+ }
+ static double debugScale = -2.0;
+ if (debugScale == -2.0) { // default debugScale value in SGE is -1.0
+ debugScale = JNU_CallStaticMethodByName(env, NULL, "sun/java2d/SunGraphicsEnvironment",
+ "getDebugScale", "()D").d;
+ }
+
+ if (self.window.backingScaleFactor > 0 && debugScale < 0) {
+ if (self.layer.contentsScale != self.window.backingScaleFactor) {
+ self.layer.contentsScale = self.window.backingScaleFactor;
+ DECLARE_CLASS(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
+ DECLARE_METHOD(deliverChangeBackingProperties, jc_CPlatformView, "deliverChangeBackingProperties", "(F)V");
+ jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
+ (*env)->CallVoidMethod(env, jlocal, deliverChangeBackingProperties, self.window.backingScaleFactor);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, jlocal);
+ }
+ }
+}
@end // AWTView
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
index 7d6fb4f3ebcf..ab33c7d8adef 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, 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,6 +32,7 @@
#import "LWCToolkit.h"
@class AWTView;
+@class AWTWindowZoomButtonMouseResponder;
@interface AWTWindow : NSObject {
@private
@@ -46,7 +47,14 @@
jint preFullScreenLevel;
NSRect standardFrame;
BOOL isMinimizing;
- BOOL keyNotificationRecd;
+ BOOL isJustCreated;
+ NSWindowTabbingMode javaWindowTabbingMode;
+ BOOL isEnterFullScreen;
+ CGFloat _customTitleBarHeight;
+ BOOL hideTabController;
+ NSView *_fullScreenButtons;
+ NSView *_fullScreenOriginalButtons;
+
}
// An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -62,7 +70,17 @@
@property (nonatomic) jint preFullScreenLevel;
@property (nonatomic) NSRect standardFrame;
@property (nonatomic) BOOL isMinimizing;
-@property (nonatomic) BOOL keyNotificationRecd;
+@property (nonatomic) BOOL isJustCreated;
+@property (nonatomic) NSWindowTabbingMode javaWindowTabbingMode;
+@property (nonatomic) BOOL isEnterFullScreen;
+@property (nonatomic, retain) NSNumber *currentDisplayID;
+@property (nonatomic, readonly) CGFloat customTitleBarHeight;
+@property (nonatomic) BOOL customTitleBarControlsVisible;
+@property (nonatomic, retain) NSMutableArray *customTitleBarConstraints;
+@property (nonatomic, retain) NSLayoutConstraint *customTitleBarHeightConstraint;
+@property (nonatomic, retain) NSMutableArray *customTitleBarButtonCenterXConstraints;
+@property (nonatomic) BOOL hideTabController;
+@property (nonatomic, retain) AWTWindowZoomButtonMouseResponder* zoomButtonMouseResponder;
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow
ownerWindow:owner
@@ -72,8 +90,11 @@
- (BOOL) isTopmostWindowUnderMouse;
+- (NSWindowTabbingMode) getJavaWindowTabbingMode;
+
// NSWindow overrides delegate methods
- (BOOL) canBecomeKeyWindow;
+- (void) becomeKeyWindow;
- (BOOL) canBecomeMainWindow;
- (BOOL) worksWhenModal;
- (void)sendEvent:(NSEvent *)event;
@@ -83,7 +104,9 @@
@end
-@interface AWTWindow_Normal : NSWindow
+@interface AWTWindow_Normal : NSWindow {
+ @private BOOL _ignoreMove;
+}
- (id) initWithDelegate:(AWTWindow *)delegate
frameRect:(NSRect)rect
styleMask:(NSUInteger)styleMask
@@ -97,4 +120,24 @@
contentView:(NSView *)view;
@end
+@interface AWTWindowDragView : NSView
+@property (nonatomic) jobject javaPlatformWindow;
+- (id) initWithPlatformWindow:(jobject)javaPlatformWindow;
+@end
+
+@interface AWTButtonsView : NSView {
+ @private BOOL _showButtons;
+ NSColor* _color;
+}
+
+
+- (void)configureColors;
+
+@end
+
+@interface AWTWindowZoomButtonMouseResponder : NSResponder
+- (id) initWithWindow:(NSWindow*)window;
+@end
+
+
#endif _AWTWINDOW_H
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
index 6aba7d55856d..bec6262d8921 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
@@ -23,8 +23,10 @@
* questions.
*/
+#include
#import
+#include
#import "sun_lwawt_macosx_CPlatformWindow.h"
#import "com_apple_eawt_event_GestureHandler.h"
#import "com_apple_eawt_FullScreenHandler.h"
@@ -34,7 +36,10 @@
#import "AWTView.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
+#import "NSApplicationAWT.h"
#import "JNIUtilities.h"
+#import "PropertiesUtilities.h"
+#include "Trace.h"
#define MASK(KEY) \
(sun_lwawt_macosx_CPlatformWindow_ ## KEY)
@@ -52,6 +57,35 @@
#define GET_CPLATFORM_WINDOW_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CPlatformWindow, "sun/lwawt/macosx/CPlatformWindow", ret);
+BOOL isColorMatchingEnabled() {
+ static int colorMatchingEnabled = -1;
+ if (colorMatchingEnabled == -1) {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ if (env == NULL) return NO;
+ NSString *colorMatchingEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"sun.java2d.osx.colorMatching"
+ withEnv:env];
+ if (colorMatchingEnabledProp == nil) {
+ // fallback on the former metal property:
+ colorMatchingEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"sun.java2d.metal.colorMatching"
+ withEnv:env];
+ }
+ colorMatchingEnabled = [@"false" isCaseInsensitiveLike:colorMatchingEnabledProp] ? NO : YES;
+ J2dRlsTraceLn1(J2D_TRACE_INFO, "AWTWindow_isColorMatchingEnabled: %d", colorMatchingEnabled);
+ }
+ return (BOOL)colorMatchingEnabled;
+}
+
+@interface NSTitlebarAccessoryViewController (Private)
+- (void)_setHidden:(BOOL)h animated:(BOOL)a;
+@end
+
+@interface NSWindow (Private)
+- (void)_setTabBarAccessoryViewController:(id)controller;
+- (void)setIgnoreMove:(BOOL)value;
+- (BOOL)isIgnoreMove;
+- (void)_adjustWindowToScreen;
+@end
+
// Cocoa windowDidBecomeKey/windowDidResignKey notifications
// doesn't provide information about "opposite" window, so we
// have to do a bit of tracking. This variable points to a window
@@ -65,6 +99,11 @@
// It would be NSZeroPoint if 'Location by Platform' is not used.
static NSPoint lastTopLeftPoint;
+static BOOL ignoreResizeWindowDuringAnotherWindowEnd = NO;
+
+static BOOL fullScreenTransitionInProgress = NO;
+static BOOL orderingScheduled = NO;
+
// --------------------------------------------------------------
// NSWindow/NSPanel descendants implementation
#define AWT_NS_WINDOW_IMPLEMENTATION \
@@ -85,7 +124,15 @@ - (id) initWithDelegate:(AWTWindow *)delegate \
[self setInitialFirstResponder:view]; \
[self setReleasedWhenClosed:NO]; \
[self setPreservesContentDuringLiveResize:YES]; \
- \
+ [[NSNotificationCenter defaultCenter] addObserver:self \
+ selector:@selector(windowDidChangeScreen) \
+ name:NSWindowDidChangeScreenNotification object:self]; \
+ [[NSNotificationCenter defaultCenter] addObserver:self \
+ selector:@selector(windowDidChangeProfile) \
+ name:NSWindowDidChangeScreenProfileNotification \
+ object:self]; \
+ [self addObserver:self forKeyPath:@"visible" \
+ options:NSKeyValueObservingOptionNew context:nil]; \
return self; \
} \
\
@@ -102,14 +149,83 @@ - (BOOL) worksWhenModal { \
return [(AWTWindow*)[self delegate] worksWhenModal]; \
} \
\
+- (void)cursorUpdate:(NSEvent *)event { \
+ /* Prevent cursor updates from OS side */ \
+} \
+ \
- (void)sendEvent:(NSEvent *)event { \
[(AWTWindow*)[self delegate] sendEvent:event]; \
[super sendEvent:event]; \
-}
+} \
+ \
+- (void)becomeKeyWindow { \
+ [super becomeKeyWindow]; \
+ [(AWTWindow*)[self delegate] becomeKeyWindow]; \
+} \
+ \
+- (NSWindowTabbingMode)tabbingMode { \
+ return ((AWTWindow*)[self delegate]).javaWindowTabbingMode; \
+} \
+ \
+- (void)observeValueForKeyPath:(NSString *)keyPath \
+ ofObject:(id)object \
+ change:(NSDictionary *)change \
+ context:(void *)context { \
+ if ([keyPath isEqualToString:@"visible"]) { \
+ BOOL isVisible = \
+ [[change objectForKey:NSKeyValueChangeNewKey] \
+ boolValue]; \
+ if (isVisible) { \
+ [(AWTWindow*)[self delegate] \
+ _windowDidBecomeVisible]; \
+ } \
+ } \
+} \
+ \
+- (void)windowDidChangeScreen { \
+ [(AWTWindow*)[self delegate] _displayChanged:NO]; \
+} \
+ \
+- (void)windowDidChangeProfile { \
+ [(AWTWindow*)[self delegate] _displayChanged:YES]; \
+} \
+ \
+- (void)dealloc { \
+ [[NSNotificationCenter defaultCenter] removeObserver:self \
+ name:NSWindowDidChangeScreenNotification object:self]; \
+ [[NSNotificationCenter defaultCenter] removeObserver:self \
+ name:NSWindowDidChangeScreenProfileNotification \
+ object:self]; \
+ [self removeObserver:self forKeyPath:@"visible"]; \
+ [super dealloc]; \
+} \
@implementation AWTWindow_Normal
AWT_NS_WINDOW_IMPLEMENTATION
+// suppress exception (actually assertion) from [NSWindow _changeJustMain]
+// workaround for https://youtrack.jetbrains.com/issue/JBR-2562
+- (void)_changeJustMain {
+ @try {
+ // NOTE: we can't use [super _changeJustMain] directly because of the warning ('may not perform to selector')
+ // And [super performSelector:@selector(_changeJustMain)] will invoke this method (not a base method).
+ // So do it with objc-runtime.h (see stackoverflow.com/questions/14635024/using-objc-msgsendsuper-to-invoke-a-class-method)
+ Class superClass = [self superclass];
+ struct objc_super mySuper = {
+ self,
+ class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
+ ? object_getClass(superClass) //if we are a Class, we need to send our metaclass (our Class's Class)
+ : superClass //if we are an instance, we need to send our Class (which we already have)
+ };
+ void (*_objc_msgSendSuper)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
+ (*_objc_msgSendSuper)(&mySuper, @selector(_changeJustMain));
+ } @catch (NSException *ex) {
+ NSLog(@"WARNING: suppressed exception from _changeJustMain (workaround for JBR-2562)");
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+ [NSApplicationAWT logException:ex forProcess:processInfo];
+ }
+}
+
// Gesture support
- (void)postGesture:(NSEvent *)event as:(jint)type a:(jdouble)a b:(jdouble)b {
AWT_ASSERT_APPKIT_THREAD;
@@ -139,21 +255,35 @@ - (void)postGesture:(NSEvent *)event as:(jint)type a:(jdouble)a b:(jdouble)b {
}
}
-- (void)beginGestureWithEvent:(NSEvent *)event {
- [self postGesture:event
- as:com_apple_eawt_event_GestureHandler_PHASE
- a:-1.0
- b:0.0];
-}
-
-- (void)endGestureWithEvent:(NSEvent *)event {
- [self postGesture:event
- as:com_apple_eawt_event_GestureHandler_PHASE
- a:1.0
- b:0.0];
+- (BOOL)postPhaseEvent:(NSEvent *)event {
+ // Consider changing API to reflect MacOS api
+ // Gesture event should come with phase field
+ // PhaseEvent should be removed
+ const unsigned int NSEventPhaseBegan = 0x1 << 0;
+ const unsigned int NSEventPhaseEnded = 0x1 << 3;
+ const unsigned int NSEventPhaseCancelled = 0x1 << 4;
+
+ if (event.phase == NSEventPhaseBegan) {
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PHASE
+ a:-1.0
+ b:0.0];
+ return true;
+ } else if (event.phase == NSEventPhaseEnded ||
+ event.phase == NSEventPhaseCancelled) {
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PHASE
+ a:1.0
+ b:0.0];
+ return true;
+ }
+ return false;
}
- (void)magnifyWithEvent:(NSEvent *)event {
+ if ([self postPhaseEvent:event]) {
+ return;
+ }
[self postGesture:event
as:com_apple_eawt_event_GestureHandler_MAGNIFY
a:[event magnification]
@@ -161,6 +291,9 @@ - (void)magnifyWithEvent:(NSEvent *)event {
}
- (void)rotateWithEvent:(NSEvent *)event {
+ if ([self postPhaseEvent:event]) {
+ return;
+ }
[self postGesture:event
as:com_apple_eawt_event_GestureHandler_ROTATE
a:[event rotation]
@@ -174,6 +307,120 @@ - (void)swipeWithEvent:(NSEvent *)event {
b:[event deltaY]];
}
+- (void)pressureChangeWithEvent:(NSEvent *)event {
+ float pressure = event.pressure;
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PRESSURE
+ a:pressure
+ b:(([event respondsToSelector:@selector(stage)]) ? ((NSInteger)[event stage]) : -1)
+ ];
+}
+
+- (void)moveTabToNewWindow:(id)sender {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ [super moveTabToNewWindow:sender];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, ((AWTWindow *)self.delegate).javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // extract the target AWT Window object out of the CPlatformWindow
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS(jc_Window, "java/awt/Window");
+ DECLARE_METHOD(jm_runMoveTabToNewWindowCallback, jc_Window, "runMoveTabToNewWindowCallback", "()V");
+ (*env)->CallVoidMethod(env, awtWindow, jm_runMoveTabToNewWindowCallback);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+
+#ifdef DEBUG
+ NSLog(@"=== Move Tab to new Window ===");
+#endif
+}
+
+// Call over Foundation from Java
+- (CGFloat) getTabBarVisibleAndHeight {
+ if (@available(macOS 10.13, *)) {
+ id tabGroup = [self tabGroup];
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar: %@ ===", tabGroup);
+#endif
+ if ([tabGroup isTabBarVisible]) {
+ if ([tabGroup respondsToSelector:@selector(_tabBar)]) { // private member
+ CGFloat height = [[tabGroup _tabBar] frame].size.height;
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar visible: %f ===", height);
+#endif
+ return height;
+ }
+#ifdef DEBUG
+ NSLog(@"=== NsWindow.tabGroup._tabBar not found ===");
+#endif
+ return -1; // if we don't get height return -1 and use default value in java without change native code
+ }
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar not visible ===");
+#endif
+ } else {
+#ifdef DEBUG
+ NSLog(@"=== Window tabGroup not supported before macOS 10.13 ===");
+#endif
+ }
+ return 0;
+}
+
+- (void)orderOut:(id)sender {
+ ignoreResizeWindowDuringAnotherWindowEnd = YES;
+ [super orderOut:sender];
+}
+
+- (void)_setTabBarAccessoryViewController:(id)_controller {
+ if (((AWTWindow *)self.delegate).hideTabController) {
+ NSTitlebarAccessoryViewController* controller = [[NSTitlebarAccessoryViewController alloc] init];
+ controller.view = [[NSView alloc] init];
+ [controller.view setFrame:NSMakeRect(0, 0, 0, 0)];
+ [controller _setHidden:YES animated:NO];
+
+ [super _setTabBarAccessoryViewController:controller];
+ } else {
+ [super _setTabBarAccessoryViewController:_controller];
+ }
+}
+
+- (BOOL)isNativeSelected {
+ if (@available(macOS 10.13, *)) {
+ return [[self tabGroup] selectedWindow] == self;
+ }
+ return NO;
+}
+
+- (void)setIgnoreMove:(BOOL)value {
+ _ignoreMove = value;
+ self.movable = !value;
+}
+
+- (BOOL)isIgnoreMove {
+ return _ignoreMove;
+}
+
+- (void)_adjustWindowToScreen {
+ if (_ignoreMove) {
+ self.movable = YES;
+ }
+
+ [super _adjustWindowToScreen];
+ [(AWTWindow *)self.delegate updateFullScreenButtons];
+
+ if (_ignoreMove) {
+ self.movable = NO;
+ }
+}
+
@end
@implementation AWTWindow_Panel
AWT_NS_WINDOW_IMPLEMENTATION
@@ -195,7 +442,10 @@ @implementation AWTWindow
@synthesize preFullScreenLevel;
@synthesize standardFrame;
@synthesize isMinimizing;
-@synthesize keyNotificationRecd;
+@synthesize isJustCreated;
+@synthesize javaWindowTabbingMode;
+@synthesize isEnterFullScreen;
+@synthesize hideTabController;
- (void) updateMinMaxSize:(BOOL)resizable {
if (resizable) {
@@ -226,11 +476,22 @@ + (NSUInteger) styleMaskForStyleBits:(jint)styleBits {
if (IS(styleBits, UTILITY)) type |= NSWindowStyleMaskUtilityWindow;
if (IS(styleBits, HUD)) type |= NSWindowStyleMaskHUDWindow;
if (IS(styleBits, SHEET)) type |= NSWindowStyleMaskDocModalWindow;
- if (IS(styleBits, NONACTIVATING)) type |= NSWindowStyleMaskNonactivatingPanel;
return type;
}
++ (jint) affectedStyleMaskForCustomTitleBar {
+ return MASK(FULL_WINDOW_CONTENT) | MASK(TRANSPARENT_TITLE_BAR) | MASK(TITLE_VISIBLE);
+}
+
++ (jint) overrideStyleBits:(jint)styleBits customTitleBarEnabled:(BOOL)customTitleBarEnabled fullscreen:(BOOL)fullscreen {
+ if (customTitleBarEnabled) {
+ styleBits |= MASK(FULL_WINDOW_CONTENT) | MASK(TRANSPARENT_TITLE_BAR);
+ if (!fullscreen) styleBits &= ~MASK(TITLE_VISIBLE);
+ }
+ return styleBits;
+}
+
// updates _METHOD_PROP_BITMASK based properties on the window
- (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
if (IS(mask, RESIZABLE)) {
@@ -269,9 +530,11 @@ - (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
if (IS(mask, FULLSCREENABLE) && [self.nsWindow respondsToSelector:@selector(toggleFullScreen:)]) {
if (IS(bits, FULLSCREENABLE)) {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior |
+ NSWindowCollectionBehaviorFullScreenPrimary;
} else {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
+ self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior &
+ ~NSWindowCollectionBehaviorFullScreenPrimary;
}
}
@@ -280,7 +543,7 @@ - (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
}
if (IS(mask, TITLE_VISIBLE) && [self.nsWindow respondsToSelector:@selector(setTitleVisibility:)]) {
- [self.nsWindow setTitleVisibility:(IS(bits, TITLE_VISIBLE)) ? NSWindowTitleVisible :NSWindowTitleHidden];
+ [self.nsWindow setTitleVisibility:(IS(bits, TITLE_VISIBLE) ? NSWindowTitleVisible : NSWindowTitleHidden)];
}
}
@@ -293,10 +556,23 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
{
AWT_ASSERT_APPKIT_THREAD;
+ self = [super init];
+ if (self == nil) return nil; // no hope
+ self.javaPlatformWindow = platformWindow;
+
NSUInteger newBits = bits;
if (IS(bits, SHEET) && owner == nil) {
newBits = bits & ~NSWindowStyleMaskDocModalWindow;
}
+
+ _customTitleBarHeight = -1.0f; // Negative means uninitialized
+ self.customTitleBarControlsVisible = YES;
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
+ // Force properties if custom title bar is enabled, but store original value in self.styleBits.
+ newBits = [AWTWindow overrideStyleBits:newBits customTitleBarEnabled:self.isCustomTitleBarEnabled fullscreen:false];
+
NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
NSRect contentRect = rect; //[NSWindow contentRectForFrameRect:rect styleMask:styleMask];
@@ -307,12 +583,7 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
contentRect.size.height = 1.0;
}
- self = [super init];
-
- if (self == nil) return nil; // no hope
-
if (IS(bits, UTILITY) ||
- IS(bits, NONACTIVATING) ||
IS(bits, HUD) ||
IS(bits, HIDES_ON_DEACTIVATE) ||
IS(bits, SHEET))
@@ -334,22 +605,36 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
if (self.nsWindow == nil) return nil; // no hope either
[self.nsWindow release]; // the property retains the object already
- self.keyNotificationRecd = NO;
+ if (isColorMatchingEnabled()) {
+ // Supported by both OpenGL & Metal pipelines
+ // Tell the system we have an sRGB backing store
+ // to ensure colors are displayed correctly on screen with non-SRGB profile:
+ [self.nsWindow setColorSpace: [NSColorSpace sRGBColorSpace]];
+ }
+
self.isEnabled = YES;
self.isMinimizing = NO;
- self.javaPlatformWindow = platformWindow;
self.styleBits = bits;
self.ownerWindow = owner;
- [self setPropertiesForStyleBits:styleBits mask:MASK(_METHOD_PROP_BITMASK)];
-
- if (IS(self.styleBits, IS_POPUP)) {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
- }
+ [self setPropertiesForStyleBits:newBits mask:MASK(_METHOD_PROP_BITMASK)];
if (IS(bits, SHEET) && owner != nil) {
[self.nsWindow setStyleMask: NSWindowStyleMaskDocModalWindow];
}
+ self.isJustCreated = YES;
+
+ self.javaWindowTabbingMode = [self getJavaWindowTabbingMode];
+ self.nsWindow.collectionBehavior = NSWindowCollectionBehaviorManaged;
+ self.isEnterFullScreen = NO;
+
+ [self configureJavaWindowTabbingIdentifier];
+
+ if (self.isCustomTitleBarEnabled && !self.isFullScreen) {
+ [self setUpCustomTitleBar];
+ }
+
+ self.currentDisplayID = nil;
return self;
}
@@ -357,67 +642,101 @@ + (BOOL) isAWTWindow:(NSWindow *)window {
return [window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]];
}
-// Retrieves the list of possible window layers (levels)
-+ (NSArray*) getWindowLayers {
- static NSArray *windowLayers;
- static dispatch_once_t token;
+// returns id for the topmost window under mouse
++ (NSInteger) getTopmostWindowUnderMouseID {
+ return [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:kCGNullWindowID];
+}
- // Initialize the list of possible window layers
- dispatch_once(&token, ^{
- // The layers are ordered from front to back, (i.e. the toppest one is the first)
- windowLayers = [NSArray arrayWithObjects:
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGPopUpMenuWindowLevelKey)],
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGFloatingWindowLevelKey)],
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGNormalWindowLevelKey)],
- nil
- ];
- [windowLayers retain];
- });
- return windowLayers;
+// checks that this window is under the mouse cursor and this point is not overlapped by others windows
+- (BOOL) isTopmostWindowUnderMouse {
+ return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID];
}
-// returns id for the topmost window under mouse
-+ (NSInteger) getTopmostWindowUnderMouseID {
- NSInteger result = -1;
+- (void) configureJavaWindowTabbingIdentifier {
+ AWT_ASSERT_APPKIT_THREAD;
- NSArray *windowLayers = [AWTWindow getWindowLayers];
- // Looking for the window under mouse starting from the toppest layer
- for (NSNumber *layer in windowLayers) {
- result = [AWTWindow getTopmostWindowUnderMouseIDImpl:[layer integerValue]];
- if (result != -1) {
- break;
- }
+ self.hideTabController = NO;
+
+ if (self.javaWindowTabbingMode != NSWindowTabbingModeAutomatic) {
+ return;
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow == NULL) {
+ return;
}
- return result;
-}
-+ (NSInteger) getTopmostWindowUnderMouseIDImpl:(NSInteger)windowLayer {
- NSInteger result = -1;
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+
+ if (awtWindow != NULL) {
+ DECLARE_CLASS(jc_RootPaneContainer, "javax/swing/RootPaneContainer");
+ if ((*env)->IsInstanceOf(env, awtWindow, jc_RootPaneContainer)) {
+ DECLARE_METHOD(jm_getRootPane, jc_RootPaneContainer, "getRootPane", "()Ljavax/swing/JRootPane;");
+ jobject rootPane = (*env)->CallObjectMethod(env, awtWindow, jm_getRootPane);
+ CHECK_EXCEPTION();
- NSRect screenRect = [[NSScreen mainScreen] frame];
- NSPoint nsMouseLocation = [NSEvent mouseLocation];
- CGPoint cgMouseLocation = CGPointMake(nsMouseLocation.x, screenRect.size.height - nsMouseLocation.y);
+ if (rootPane != NULL) {
+ DECLARE_CLASS(jc_JComponent, "javax/swing/JComponent");
+ DECLARE_METHOD(jm_getClientProperty, jc_JComponent, "getClientProperty", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ jstring jKey = NSStringToJavaString(env, @"JavaWindowTabbingIdentifier");
+ jobject jValue = (*env)->CallObjectMethod(env, rootPane, jm_getClientProperty, jKey);
+ CHECK_EXCEPTION();
+
+ if (jValue != NULL) {
+ DECLARE_CLASS(jc_String, "java/lang/String");
+ if ((*env)->IsInstanceOf(env, jValue, jc_String)) {
+ NSString *winId = JavaStringToNSString(env, (jstring)jValue);
+ [self.nsWindow setTabbingIdentifier:winId];
+ if ([winId characterAtIndex:0] == '+') {
+ self.hideTabController = YES;
+ [self.nsWindow _setTabBarAccessoryViewController:nil];
+ }
+ }
- NSMutableArray *windows = (NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
+ (*env)->DeleteLocalRef(env, jValue);
+ }
- for (NSDictionary *window in windows) {
- NSInteger layer = [[window objectForKey:(id)kCGWindowLayer] integerValue];
- if (layer == windowLayer) {
- CGRect rect;
- CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:(id)kCGWindowBounds], &rect);
- if (CGRectContainsPoint(rect, cgMouseLocation)) {
- result = [[window objectForKey:(id)kCGWindowNumber] integerValue];
- break;
+ (*env)->DeleteLocalRef(env, jKey);
+ (*env)->DeleteLocalRef(env, rootPane);
}
}
+
+ (*env)->DeleteLocalRef(env, awtWindow);
}
- [windows release];
- return result;
+
+ (*env)->DeleteLocalRef(env, platformWindow);
}
-// checks that this window is under the mouse cursor and this point is not overlapped by others windows
-- (BOOL) isTopmostWindowUnderMouse {
- return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID];
+- (NSWindowTabbingMode) getJavaWindowTabbingMode {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ BOOL result = NO;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // extract the target AWT Window object out of the CPlatformWindow
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(NSWindowTabbingModeDisallowed);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", NSWindowTabbingModeDisallowed);
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", NSWindowTabbingModeDisallowed);
+ DECLARE_METHOD_RETURN(jm_hasTabbingMode, jc_Window, "hasTabbingMode", "()Z", NSWindowTabbingModeDisallowed);
+ result = (*env)->CallBooleanMethod(env, awtWindow, jm_hasTabbingMode) == JNI_TRUE ? YES : NO;
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+
+#ifdef DEBUG
+ NSLog(@"=== getJavaWindowTabbingMode: %d ===", result);
+#endif
+
+ return result ? NSWindowTabbingModeAutomatic : NSWindowTabbingModeDisallowed;
}
+ (AWTWindow *) getTopmostWindowUnderMouse {
@@ -490,26 +809,14 @@ - (void) dealloc {
self.javaPlatformWindow = nil;
self.nsWindow = nil;
self.ownerWindow = nil;
+ self.currentDisplayID = nil;
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
+ self.zoomButtonMouseResponder = nil;
[super dealloc];
}
-// Tests whether window is blocked by modal dialog/window
-- (BOOL) isBlocked {
- BOOL isBlocked = NO;
-
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS_RETURN(isBlocked);
- DECLARE_METHOD_RETURN(jm_isBlocked, jc_CPlatformWindow, "isBlocked", "()Z", isBlocked);
- isBlocked = (*env)->CallBooleanMethod(env, platformWindow, jm_isBlocked) == JNI_TRUE ? YES : NO;
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
-
- return isBlocked;
-}
-
// Test whether window is simple window and owned by embedded frame
- (BOOL) isSimpleWindowOwnedByEmbeddedFrame {
BOOL isSimpleWindowOwnedByEmbeddedFrame = NO;
@@ -549,55 +856,115 @@ + (BOOL) isJavaPlatformWindowVisible:(NSWindow *)window {
return isVisible;
}
-// Orders window children based on the current focus state
-- (void) orderChildWindows:(BOOL)focus {
-AWT_ASSERT_APPKIT_THREAD;
+- (BOOL) delayShowing {
+ AWT_ASSERT_APPKIT_THREAD;
- if (self.isMinimizing || [self isBlocked]) {
- // Do not perform any ordering, if iconify is in progress
- // or the window is blocked by a modal window
+ return ownerWindow != nil &&
+ ([ownerWindow delayShowing] || !ownerWindow.nsWindow.onActiveSpace) &&
+ !nsWindow.visible;
+}
+
+- (BOOL) checkBlockingAndOrder {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
+ DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()V", NO);
+ (*env)->CallVoidMethod(env, platformWindow, jm_checkBlockingAndOrder);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ return YES;
+}
+
++ (void)activeSpaceDidChange {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ if (fullScreenTransitionInProgress) {
+ orderingScheduled = YES;
return;
}
+ // show delayed windows
+ for (NSWindow *window in NSApp.windows) {
+ if ([AWTWindow isJavaPlatformWindowVisible:window] && !window.visible) {
+ AWTWindow *awtWindow = (AWTWindow *)[window delegate];
+ while (awtWindow.ownerWindow != nil) {
+ awtWindow = awtWindow.ownerWindow;
+ }
+ if (awtWindow.nsWindow.visible && awtWindow.nsWindow.onActiveSpace) {
+ [awtWindow checkBlockingAndOrder];
+ }
+ }
+ }
+}
+
+- (void) processVisibleChildren:(void(^)(AWTWindow*))action {
NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
NSWindow *window;
while ((window = [windowEnumerator nextObject]) != nil) {
if ([AWTWindow isJavaPlatformWindowVisible:window]) {
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
- AWTWindow *owner = awtWindow.ownerWindow;
- if (IS(awtWindow.styleBits, ALWAYS_ON_TOP)) {
- // Do not order 'always on top' windows
- continue;
- }
- while (awtWindow.ownerWindow != nil) {
- if (awtWindow.ownerWindow == self) {
- if (focus) {
- // Move the childWindow to floating level
- // so it will appear in front of its
- // parent which owns the focus
- [window setLevel:NSFloatingWindowLevel];
- } else {
- // Focus owner has changed, move the childWindow
- // back to normal window level
- [window setLevel:NSNormalWindowLevel];
- }
- // The childWindow should be displayed in front of
- // its nearest parentWindow
- [window orderWindow:NSWindowAbove relativeTo:[owner.nsWindow windowNumber]];
+ AWTWindow *parent = awtWindow.ownerWindow;
+ while (parent != nil) {
+ if (parent == self) {
+ action(awtWindow);
break;
}
- awtWindow = awtWindow.ownerWindow;
+ parent = parent.ownerWindow;
}
}
}
}
+// Orders window children based on the current focus state
+- (void) orderChildWindows:(BOOL)focus {
+AWT_ASSERT_APPKIT_THREAD;
+
+ if (self.isMinimizing) {
+ // Do not perform any ordering, if iconify is in progress
+ return;
+ }
+
+ [self processVisibleChildren:^void(AWTWindow* child){
+ // Do not order 'always on top' windows
+ if (!IS(child.styleBits, ALWAYS_ON_TOP)) {
+ NSWindow *window = child.nsWindow;
+ NSWindow *owner = child.ownerWindow.nsWindow;
+ if (focus) {
+ // Move the childWindow to floating level
+ // so it will appear in front of its
+ // parent which owns the focus
+ [window setLevel:NSFloatingWindowLevel];
+ } else {
+ // Focus owner has changed, move the childWindow
+ // back to normal window level
+ [window setLevel:NSNormalWindowLevel];
+ }
+ }
+ }];
+}
+
// NSWindow overrides
- (BOOL) canBecomeKeyWindow {
AWT_ASSERT_APPKIT_THREAD;
return self.isEnabled && (IS(self.styleBits, SHOULD_BECOME_KEY) || [self isSimpleWindowOwnedByEmbeddedFrame]);
}
+- (void) becomeKeyWindow {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ // Reset current cursor in CCursorManager such that any following mouse update event
+ // restores the correct cursor to the frame context specific one.
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ DECLARE_CLASS(jc_CCursorManager, "sun/lwawt/macosx/CCursorManager");
+ DECLARE_STATIC_METHOD(sjm_resetCurrentCursor, jc_CCursorManager, "resetCurrentCursor", "()V");
+ (*env)->CallStaticVoidMethod(env, jc_CCursorManager, sjm_resetCurrentCursor);
+ CHECK_EXCEPTION();
+}
+
- (BOOL) canBecomeMainWindow {
AWT_ASSERT_APPKIT_THREAD;
if (!self.isEnabled) {
@@ -606,15 +973,7 @@ - (BOOL) canBecomeMainWindow {
// We should bring up the modal dialog manually
[AWTToolkit eventCountPlusPlus];
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
- DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()Z", NO);
- (*env)->CallBooleanMethod(env, platformWindow, jm_checkBlockingAndOrder);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
+ if (![self checkBlockingAndOrder]) return NO;
}
return self.isEnabled && IS(self.styleBits, SHOULD_BECOME_MAIN);
@@ -628,20 +987,67 @@ - (BOOL) worksWhenModal {
// NSWindowDelegate methods
-- (void) _deliverMoveResizeEvent {
-AWT_ASSERT_APPKIT_THREAD;
+- (void)_windowDidBecomeVisible {
+ self.currentDisplayID = [AWTWindow getNSWindowDisplayID_AppKitThread:nsWindow];
+}
- // deliver the event if this is a user-initiated live resize or as a side-effect
- // of a Java initiated resize, because AppKit can override the bounds and force
- // the bounds of the window to avoid the Dock or remain on screen.
- [AWTToolkit eventCountPlusPlus];
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+- (void)_displayChanged:(BOOL)profileOnly {
+ AWT_ASSERT_APPKIT_THREAD;
+ if (!profileOnly) {
+ NSNumber* newDisplayID = [AWTWindow getNSWindowDisplayID_AppKitThread:nsWindow];
+ if (newDisplayID == nil) {
+ // Do not proceed with unset window display id
+ // to avoid receiving wrong boundary values
+ return;
+ }
+
+ if (self.currentDisplayID == nil) {
+ // Do not trigger notification at first initialization
+ self.currentDisplayID = newDisplayID;
+ return;
+ }
+
+ if ([self.currentDisplayID isEqualToNumber: newDisplayID]) {
+ return;
+ }
+ self.currentDisplayID = newDisplayID;
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
if (platformWindow == NULL) {
- // TODO: create generic AWT assert
+ NSLog(@"[AWTWindow _displayChanged]: platformWindow == NULL");
+ return;
}
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_displayChanged, jc_CPlatformWindow, "displayChanged", "(Z)V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_displayChanged, profileOnly);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+}
- NSRect frame = ConvertNSScreenRect(env, [self.nsWindow frame]);
+- (void) _deliverMoveResizeEvent {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ // deliver the event if this is a user-initiated live resize or as a side-effect
+ // of a Java initiated resize, because AppKit can override the bounds and force
+ // the bounds of the window to avoid the Dock or remain on screen.
+ [AWTToolkit eventCountPlusPlus];
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow == NULL) {
+ NSLog(@"[AWTWindow _deliverMoveResizeEvent]: platformWindow == NULL");
+ return;
+ }
+ NSRect frame;
+ @try {
+ frame = ConvertNSScreenRect(env, [self.nsWindow frame]);
+ } @catch (NSException *e) {
+ NSLog(@"WARNING: suppressed exception from ConvertNSScreenRect() in [AWTWindow _deliverMoveResizeEvent]");
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+ [NSApplicationAWT logException:e forProcess:processInfo];
+ return;
+ }
GET_CPLATFORM_WINDOW_CLASS();
DECLARE_METHOD(jm_deliverMoveResizeEvent, jc_CPlatformWindow, "deliverMoveResizeEvent", "(IIIIZ)V");
@@ -659,13 +1065,17 @@ - (void) _deliverMoveResizeEvent {
- (void)windowDidMove:(NSNotification *)notification {
AWT_ASSERT_APPKIT_THREAD;
-
[self _deliverMoveResizeEvent];
}
- (void)windowDidResize:(NSNotification *)notification {
AWT_ASSERT_APPKIT_THREAD;
-
+ if (self.isEnterFullScreen && ignoreResizeWindowDuringAnotherWindowEnd) {
+#ifdef DEBUG
+ NSLog(@"=== Native.windowDidResize: %@ | ignored in transition to fullscreen ===", self.nsWindow.title);
+#endif
+ return;
+ }
[self _deliverMoveResizeEvent];
}
@@ -689,24 +1099,14 @@ - (NSRect)windowWillUseStandardFrame:(NSWindow *)window
- (void) iconifyChildWindows:(BOOL)iconify {
AWT_ASSERT_APPKIT_THREAD;
- NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
- NSWindow *window;
- while ((window = [windowEnumerator nextObject]) != nil) {
- if ([AWTWindow isJavaPlatformWindowVisible:window]) {
- AWTWindow *awtWindow = (AWTWindow *)[window delegate];
- while (awtWindow.ownerWindow != nil) {
- if (awtWindow.ownerWindow == self) {
- if (iconify) {
- [window orderOut:window];
- } else {
- [window orderFront:window];
- }
- break;
- }
- awtWindow = awtWindow.ownerWindow;
- }
+ [self processVisibleChildren:^void(AWTWindow* child){
+ NSWindow *window = child.nsWindow;
+ if (iconify) {
+ [window orderOut:window];
+ } else {
+ [window orderFront:window];
}
- }
+ }];
}
- (void) _deliverIconify:(BOOL)iconify {
@@ -777,19 +1177,10 @@ - (void) windowDidBecomeMain: (NSNotification *) notification {
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
#ifdef DEBUG
- NSLog(@"became main: %d %@ %@ %d", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow], self.keyNotificationRecd);
+ NSLog(@"became main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- // if for some reason, no KEY notification is received but this main window is also a key window
- // then we need to execute the KEY notification functionality.
- if(self.keyNotificationRecd != YES && [self.nsWindow isKeyWindow]) {
- [self doWindowDidBecomeKey];
- }
- self.keyNotificationRecd = NO;
-
- if (![self.nsWindow isKeyWindow]) {
- [self activateWindowMenuBar];
- }
+ [self activateWindowMenuBar];
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
@@ -800,6 +1191,8 @@ - (void) windowDidBecomeMain: (NSNotification *) notification {
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, platformWindow);
}
+
+ [self orderChildWindows:YES];
}
- (void) windowDidBecomeKey: (NSNotification *) notification {
@@ -808,22 +1201,29 @@ - (void) windowDidBecomeKey: (NSNotification *) notification {
#ifdef DEBUG
NSLog(@"became key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- [self doWindowDidBecomeKey];
- self.keyNotificationRecd = YES;
-}
-
-- (void) doWindowDidBecomeKey {
-AWT_ASSERT_APPKIT_THREAD;
AWTWindow *opposite = [AWTWindow lastKeyWindow];
if (![self.nsWindow isMainWindow]) {
- [self activateWindowMenuBar];
+ [self makeRelevantAncestorMain];
}
[AWTWindow setLastKeyWindow:nil];
[self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
- [self orderChildWindows:YES];
+}
+
+- (void) makeRelevantAncestorMain {
+ NSWindow *nativeWindow;
+ AWTWindow *awtWindow = self;
+
+ do {
+ nativeWindow = awtWindow.nsWindow;
+ if ([nativeWindow canBecomeMainWindow]) {
+ [nativeWindow makeMainWindow];
+ break;
+ }
+ awtWindow = awtWindow.ownerWindow;
+ } while (awtWindow);
}
- (void) activateWindowMenuBar {
@@ -867,7 +1267,7 @@ - (void) windowDidResignKey: (NSNotification *) notification {
#ifdef DEBUG
NSLog(@"resigned key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- if (![self.nsWindow isMainWindow]) {
+ if (![self.nsWindow isMainWindow] || [NSApp keyWindow] == self.nsWindow) {
[self deactivateWindow];
}
}
@@ -881,6 +1281,9 @@ - (void) windowDidResignMain: (NSNotification *) notification {
if (![self.nsWindow isKeyWindow]) {
[self deactivateWindow];
}
+
+ [self.javaMenuBar deactivate];
+ [self orderChildWindows:NO];
}
- (void) deactivateWindow {
@@ -888,20 +1291,20 @@ - (void) deactivateWindow {
#ifdef DEBUG
NSLog(@"deactivating window: %@", [self.nsWindow title]);
#endif
- [self.javaMenuBar deactivate];
// the new key window
NSWindow *keyWindow = [NSApp keyWindow];
AWTWindow *opposite = nil;
if ([AWTWindow isAWTWindow: keyWindow]) {
- opposite = (AWTWindow *)[keyWindow delegate];
+ if (keyWindow != self.nsWindow) {
+ opposite = (AWTWindow *)[keyWindow delegate];
+ }
[AWTWindow setLastKeyWindow: self];
} else {
[AWTWindow setLastKeyWindow: nil];
}
[self _deliverWindowFocusEvent:NO oppositeWindow: opposite];
- [self orderChildWindows:NO];
}
- (BOOL)windowShouldClose:(id)sender {
@@ -938,167 +1341,851 @@ - (void)_notifyFullScreenOp:(jint)op withEnv:(JNIEnv *)env {
}
}
+// this is required to move owned windows to the full-screen space when owner goes to full-screen mode
+- (void)allowMovingChildrenBetweenSpaces:(BOOL)allow {
+ [self processVisibleChildren:^void(AWTWindow* child){
+ NSWindow *window = child.nsWindow;
+ NSWindowCollectionBehavior behavior = window.collectionBehavior;
+ behavior &= ~(NSWindowCollectionBehaviorManaged | NSWindowCollectionBehaviorTransient);
+ behavior |= allow ? NSWindowCollectionBehaviorTransient : NSWindowCollectionBehaviorManaged;
+ window.collectionBehavior = behavior;
+ }];
+}
+
+- (void) fullScreenTransitionStarted {
+ fullScreenTransitionInProgress = YES;
+}
+
+- (void) fullScreenTransitionFinished {
+ fullScreenTransitionInProgress = NO;
+ if (orderingScheduled) {
+ orderingScheduled = NO;
+ [self checkBlockingAndOrder];
+ }
+}
+
+- (CGFloat) customTitleBarHeight {
+ CGFloat h = _customTitleBarHeight;
+ if (h < 0.0f) {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(YES);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", 0.0f);
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", 0.0f);
+ DECLARE_METHOD_RETURN(jm_internalCustomTitleBarHeight, jc_Window, "internalCustomTitleBarHeight", "()F", 0.0f);
+ DECLARE_METHOD_RETURN(jm_internalCustomTitleBarControlsVisible, jc_Window, "internalCustomTitleBarControlsVisible", "()Z", 0.0f);
+
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (!platformWindow) return 0.0f;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ h = (CGFloat) (*env)->CallFloatMethod(env, target, jm_internalCustomTitleBarHeight);
+ if (!(*env)->ExceptionCheck(env)) {
+ self.customTitleBarControlsVisible = (BOOL) (*env)->CallBooleanMethod(env, target, jm_internalCustomTitleBarControlsVisible);
+ }
+ (*env)->DeleteLocalRef(env, target);
+ }
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ if (h < 0.0f) h = 0.0f;
+ _customTitleBarHeight = h;
+ }
+ return h;
+}
+
+- (BOOL) isCustomTitleBarEnabled {
+ CGFloat h = _customTitleBarHeight;
+ if (h < 0.0f) h = self.customTitleBarHeight;
+ return h > 0.0f;
+}
+
+- (void) updateCustomTitleBarInsets:(BOOL)hasControls {
+ CGFloat leftInset;
+ if (hasControls) {
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ leftInset = self.customTitleBarHeight + 2.0f * horizontalButtonOffset;
+ } else leftInset = 0.0f;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ DECLARE_CLASS(jc_Window, "java/awt/Window");
+ DECLARE_METHOD(jm_internalCustomTitleBarUpdateInsets, jc_Window, "internalCustomTitleBarUpdateInsets", "(FF)V");
+
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (!platformWindow) return;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ (*env)->CallVoidMethod(env, target, jm_internalCustomTitleBarUpdateInsets, (jfloat) leftInset, (jfloat) 0.0f);
+ (*env)->DeleteLocalRef(env, target);
+ }
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification {
+ [self fullScreenTransitionStarted];
+ [self allowMovingChildrenBetweenSpaces:YES];
+
+ self.isEnterFullScreen = YES;
+
+ if (self.isCustomTitleBarEnabled) {
+ [self resetCustomTitleBar];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowWillEnterFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_ENTER withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ self.isEnterFullScreen = YES;
+
+ if (self.isCustomTitleBarEnabled) {
+ [self forceHideCustomTitleBarTitle:NO];
+ [self updateCustomTitleBarInsets:NO];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *newFullScreenControls = [PropertiesUtilities
+ javaSystemPropertyForKey:@"apple.awt.newFullScreenControls" withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:newFullScreenControls]) {
+ [self setWindowFullScreenControls];
+ }
+ }
+ [self allowMovingChildrenBetweenSpaces:NO];
+ [self fullScreenTransitionFinished];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowDidEnterFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_ENTER withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)notification {
+ self.isEnterFullScreen = NO;
+
+ [self fullScreenTransitionStarted];
+
+ if (self.isCustomTitleBarEnabled) {
+ [self setWindowControlsHidden:YES];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+ [self forceHideCustomTitleBarTitle:YES];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
+ if (jm_windowWillExitFullScreen == NULL) {
+ GET_CPLATFORM_WINDOW_CLASS();
+ jm_windowWillExitFullScreen = (*env)->GetMethodID(env, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
+ }
+ CHECK_NULL(jm_windowWillExitFullScreen);
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowWillExitFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_EXIT withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ [self resetWindowFullScreenControls];
+
+ self.isEnterFullScreen = NO;
+
+ [self fullScreenTransitionFinished];
+
+ if (self.isCustomTitleBarEnabled) {
+ [self setUpCustomTitleBar];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowDidExitFullScreen, jc_CPlatformWindow, "windowDidExitFullScreen", "()V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowDidExitFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_EXIT withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+}
+
+- (void)sendEvent:(NSEvent *)event {
+ if ([self.nsWindow isIgnoreMove]) {
+ // https://mmazzarolo.com/blog/2022-04-16-drag-window-by-clicking-anywhere-on-macos/
+ // Allow dragging a window by clicking on any part of it while holding cmd + ctrl
+ // if NSWindowShouldDragOnGesture is on.
+ const NSEventModifierFlags windowDragModifierFlags = NSEventModifierFlagCommand & NSEventModifierFlagControl;
+ if ([event type] == NSLeftMouseDown &&
+ ([event modifierFlags] & windowDragModifierFlags) == windowDragModifierFlags &&
+ [[NSUserDefaults standardUserDefaults] boolForKey:@"NSWindowShouldDragOnGesture"]) {
+ self.nsWindow.movable = YES;
+ return;
+ }
+ if ([event type] == NSLeftMouseUp) {
+ self.nsWindow.movable = NO;
+ }
+ }
+ if ([event type] == NSLeftMouseDown ||
+ [event type] == NSRightMouseDown ||
+ [event type] == NSOtherMouseDown) {
+ NSPoint p = [NSEvent mouseLocation];
+ NSRect frame = [self.nsWindow frame];
+ NSRect contentRect = [self.nsWindow contentRectForFrameRect:frame];
+
+ // Check if the click happened in the non-client area (title bar)
+ // Also, non-client area includes the edges at left, right and botton of frame
+ if ((p.y >= (frame.origin.y + contentRect.size.height)) ||
+ (p.x >= (frame.origin.x + contentRect.size.width - 3)) ||
+ (fabs(frame.origin.x - p.x) < 3) ||
+ (fabs(frame.origin.y - p.y) < 3)) {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // Currently, no need to deliver the whole NSEvent.
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_deliverNCMouseDown, jc_CPlatformWindow, "deliverNCMouseDown", "()V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_deliverNCMouseDown);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ }
+ }
+}
+
+- (void)constrainSize:(NSSize*)size {
+ float minWidth = 0.f, minHeight = 0.f;
+
+ if (IS(self.styleBits, DECORATED)) {
+ NSRect frame = [self.nsWindow frame];
+ NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[self.nsWindow styleMask]];
+
+ float top = frame.size.height - contentRect.size.height;
+ float left = contentRect.origin.x - frame.origin.x;
+ float bottom = contentRect.origin.y - frame.origin.y;
+ float right = frame.size.width - (contentRect.size.width + left);
+
+ // Speculative estimation: 80 - enough for window decorations controls
+ minWidth += left + right + 80;
+ minHeight += top + bottom;
+ }
+
+ minWidth = MAX(1.f, minWidth);
+ minHeight = MAX(1.f, minHeight);
+
+ size->width = MAX(size->width, minWidth);
+ size->height = MAX(size->height, minHeight);
+}
+
+- (void) setEnabled: (BOOL)flag {
+ self.isEnabled = flag;
+
+ if (IS(self.styleBits, CLOSEABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowCloseButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, MINIMIZABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, ZOOMABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowZoomButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, RESIZABLE)) {
+ [self updateMinMaxSize:flag];
+ [self.nsWindow setShowsResizeIndicator:flag];
+ }
+}
+
++ (void) setLastKeyWindow:(AWTWindow *)window {
+ [window retain];
+ [lastKeyWindow release];
+ lastKeyWindow = window;
+}
+
++ (AWTWindow *) lastKeyWindow {
+ return lastKeyWindow;
+}
+
+static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
+
+- (CGFloat) customTitleBarButtonShrinkingFactor {
+ CGFloat minimumHeightWithoutShrinking = 28.0; // This is the smallest macOS title bar availabe with public APIs as of Monterey
+ CGFloat shrinkingFactor = fmin(self.customTitleBarHeight / minimumHeightWithoutShrinking, 1.0);
+ return shrinkingFactor;
+}
+
+- (void) setUpCustomTitleBar {
+ if (self.customTitleBarConstraints != nil) {
+ [self resetCustomTitleBar];
+ }
+ /**
+ * The view hierarchy normally looks as follows:
+ * NSThemeFrame
+ * ├─NSView (content view)
+ * └─NSTitlebarContainerView
+ * ├─_NSTitlebarDecorationView (only on Mojave 10.14 and newer)
+ * └─NSTitlebarView
+ * ├─NSVisualEffectView (only on Big Sur 11 and newer)
+ * ├─NSView (only on Big Sur and newer)
+ * ├─_NSThemeCloseWidget - Close
+ * ├─_NSThemeZoomWidget - Full Screen
+ * ├─_NSThemeWidget - Minimize (note the different order compared to their layout)
+ * └─AWTWindowDragView (we will create this)
+ *
+ * But the order and presence of decorations and effects has been unstable across different macOS versions,
+ * even patch upgrades, which is why the code below uses scans instead of indexed access
+ */
+ NSView* closeButtonView = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ NSView* zoomButtonView = [self.nsWindow standardWindowButton:NSWindowZoomButton];
+ NSView* miniaturizeButtonView = [self.nsWindow standardWindowButton:NSWindowMiniaturizeButton];
+ if (!closeButtonView || !zoomButtonView || !miniaturizeButtonView) {
+ NSLog(@"WARNING: setUpCustomTitleBar closeButtonView=%@, zoomButtonView=%@, miniaturizeButtonView=%@",
+ closeButtonView, zoomButtonView, miniaturizeButtonView);
+ return;
+ }
+ NSView* titlebar = closeButtonView.superview;
+ NSView* titlebarContainer = titlebar.superview;
+ NSView* themeFrame = titlebarContainer.superview;
+ if (!themeFrame) {
+ NSLog(@"WARNING: setUpCustomTitleBar titlebar=%@, titlebarContainer=%@, themeFrame=%@",
+ titlebar, titlebarContainer, themeFrame);
+ return;
+ }
+
+ self.customTitleBarConstraints = [[NSMutableArray alloc] init];
+ titlebarContainer.translatesAutoresizingMaskIntoConstraints = NO;
+ self.customTitleBarHeightConstraint = [titlebarContainer.heightAnchor constraintEqualToConstant:self.customTitleBarHeight];
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [titlebarContainer.leftAnchor constraintEqualToAnchor:themeFrame.leftAnchor],
+ [titlebarContainer.widthAnchor constraintEqualToAnchor:themeFrame.widthAnchor],
+ [titlebarContainer.topAnchor constraintEqualToAnchor:themeFrame.topAnchor],
+ self.customTitleBarHeightConstraint,
+ ]];
+
+ [self.nsWindow setIgnoreMove:YES];
+
+ self.zoomButtonMouseResponder = [[AWTWindowZoomButtonMouseResponder alloc] initWithWindow:self.nsWindow];
+ [self.zoomButtonMouseResponder release]; // property retains the object
+
+ AWTWindowDragView* windowDragView = [[AWTWindowDragView alloc] initWithPlatformWindow:self.javaPlatformWindow];
+ [titlebar addSubview:windowDragView positioned:NSWindowBelow relativeTo:closeButtonView];
+
+ [@[titlebar, windowDragView] enumerateObjectsUsingBlock:^(NSView* view, NSUInteger index, BOOL* stop)
+ {
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [view.leftAnchor constraintEqualToAnchor:titlebarContainer.leftAnchor],
+ [view.rightAnchor constraintEqualToAnchor:titlebarContainer.rightAnchor],
+ [view.topAnchor constraintEqualToAnchor:titlebarContainer.topAnchor],
+ [view.bottomAnchor constraintEqualToAnchor:titlebarContainer.bottomAnchor],
+ ]];
+ }];
+
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ self.customTitleBarButtonCenterXConstraints = [[NSMutableArray alloc] initWithCapacity:3];
+ [@[closeButtonView, miniaturizeButtonView, zoomButtonView] enumerateObjectsUsingBlock:^(NSView* button, NSUInteger index, BOOL* stop)
+ {
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ NSLayoutConstraint* buttonCenterXConstraint = [button.centerXAnchor constraintEqualToAnchor:titlebarContainer.leftAnchor
+ constant:(self.customTitleBarHeight / 2.0 + (index * horizontalButtonOffset))];
+ [self.customTitleBarButtonCenterXConstraints addObject:buttonCenterXConstraint];
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [button.widthAnchor constraintLessThanOrEqualToAnchor:titlebarContainer.heightAnchor multiplier:0.5],
+ // Those corrections are required to keep the icons perfectly round because macOS adds a constant 2 px in resulting height to their frame
+ [button.heightAnchor constraintEqualToAnchor: button.widthAnchor multiplier:14.0/12.0 constant:-2.0],
+ [button.centerYAnchor constraintEqualToAnchor:titlebarContainer.centerYAnchor],
+ buttonCenterXConstraint,
+ ]];
+ }];
+
+ [NSLayoutConstraint activateConstraints:self.customTitleBarConstraints];
+ // These properties are already retained, release them so that retainCount = 1
+ [self.customTitleBarConstraints release];
+ [self.customTitleBarButtonCenterXConstraints release];
+
+ [self setWindowControlsHidden:!self.customTitleBarControlsVisible];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+}
+
+- (void) updateCustomTitleBarConstraints {
+ self.customTitleBarHeightConstraint.constant = self.customTitleBarHeight;
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ [self.customTitleBarButtonCenterXConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint* buttonConstraint, NSUInteger index, BOOL *stop)
+ {
+ buttonConstraint.constant = (self.customTitleBarHeight / 2.0 + (index * horizontalButtonOffset));
+ }];
+ [self setWindowControlsHidden:!self.customTitleBarControlsVisible];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+
+ [self updateFullScreenButtons];
+}
+
+- (void) resetCustomTitleBar {
+ // See [setUpCustomTitleBar] for the view hierarchy we're working with
+ NSView* closeButtonView = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ NSView* titlebar = closeButtonView.superview;
+ NSView* titlebarContainer = titlebar.superview;
+ if (!titlebarContainer) {
+ NSLog(@"WARNING: resetCustomTitleBar closeButtonView=%@, titlebar=%@, titlebarContainer=%@",
+ closeButtonView, titlebar, titlebarContainer);
+ return;
+ }
+
+ [NSLayoutConstraint deactivateConstraints:self.customTitleBarConstraints];
+
+ AWTWindowDragView* windowDragView = nil;
+ for (NSView* view in titlebar.subviews) {
+ if ([view isMemberOfClass:[AWTWindowDragView class]]) {
+ windowDragView = view;
+ }
+ }
+
+ if (windowDragView != nil) {
+ [windowDragView removeFromSuperview];
+ }
+
+ titlebarContainer.translatesAutoresizingMaskIntoConstraints = YES;
+ titlebar.translatesAutoresizingMaskIntoConstraints = YES;
+
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
+
+ [self setWindowControlsHidden:NO];
+ [self updateCustomTitleBarInsets:NO];
+
+ [self.nsWindow setIgnoreMove:NO];
+}
+
+- (void) setWindowControlsHidden: (BOOL) hidden {
+ if (_fullScreenOriginalButtons != nil) {
+ [_fullScreenOriginalButtons.window.contentView setHidden:NO];
+ _fullScreenButtons.hidden = YES;
+ }
+
+ [self.nsWindow standardWindowButton:NSWindowCloseButton].hidden = hidden;
+ [self.nsWindow standardWindowButton:NSWindowZoomButton].hidden = hidden;
+ [self.nsWindow standardWindowButton:NSWindowMiniaturizeButton].hidden = hidden;
+}
+
+- (void) setWindowFullScreenControls {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *dfmMode = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.distraction.free.mode" withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:dfmMode]) {
+ return;
+ }
+
+ NSView* oldCloseButton = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ _fullScreenOriginalButtons = oldCloseButton.superview;
+
+ NSRect closeButtonRect = [oldCloseButton frame];
+ NSRect miniaturizeButtonRect = [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] frame];
+ NSRect zoomButtonRect = [[self.nsWindow standardWindowButton:NSWindowZoomButton] frame];
+
+ for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
+ if ([window isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
+ [window.contentView setHidden:YES];
+ }
+ }
+
+ _fullScreenButtons = [[AWTButtonsView alloc] init];
+ [self updateFullScreenButtons];
+ [_fullScreenButtons addTrackingArea:[[NSTrackingArea alloc] initWithRect:[_fullScreenButtons visibleRect]
+ options:(NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved)
+ owner:_fullScreenButtons userInfo:nil]];
+
+ NSUInteger masks = [self.nsWindow styleMask];
+
+ NSButton *closeButton = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:masks];
+ [closeButton setFrame:closeButtonRect];
+ [_fullScreenButtons addSubview:closeButton];
+
+ NSButton *miniaturizeButton = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:masks];
+ [miniaturizeButton setFrame:miniaturizeButtonRect];
+ [_fullScreenButtons addSubview:miniaturizeButton];
+
+ NSButton *zoomButton = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:masks];
+ [zoomButton setFrame:zoomButtonRect];
+ [_fullScreenButtons addSubview:zoomButton];
+
+ [self.nsWindow.contentView addSubview:_fullScreenButtons];
+
+ [self updateColors];
+}
+
+- (void)updateColors {
+ if (_fullScreenButtons != nil) {
+ [(AWTButtonsView *)_fullScreenButtons configureColors];
+ }
+}
+
+- (void)updateFullScreenButtons {
+ if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
+ return;
+ }
+
+ NSView *parent = self.nsWindow.contentView;
+ CGFloat w = 80;
+ CGFloat h = _fullScreenOriginalButtons.frame.size.height;
+ CGFloat x = 6;
+ CGFloat y = parent.frame.size.height - h - (self.customTitleBarHeight - h) / 2.0;
+
+ [_fullScreenButtons setFrame:NSMakeRect(x, y, w - x, h)];
+}
+
+- (void)updateFullScreenButtons: (BOOL) dfm {
+ if (dfm) {
+ if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
+ return;
+ }
+ [_fullScreenOriginalButtons.window.contentView setHidden:NO];
+ [self resetWindowFullScreenControls];
+ } else {
+ if (!self.isCustomTitleBarEnabled || _fullScreenButtons != nil) {
+ return;
+ }
+ [self setWindowFullScreenControls];
+ }
+}
+
+- (void) resetWindowFullScreenControls {
+ if (_fullScreenButtons != nil) {
+ [_fullScreenButtons removeFromSuperview];
+ _fullScreenButtons = nil;
+ _fullScreenOriginalButtons = nil;
+ }
+}
+
+- (BOOL) isFullScreen {
+ NSUInteger masks = [self.nsWindow styleMask];
+ return (masks & NSWindowStyleMaskFullScreen) != 0;
+}
+
+- (void) forceHideCustomTitleBarTitle: (BOOL) hide {
+ jint bits = self.styleBits;
+ if (hide) bits &= ~MASK(TITLE_VISIBLE);
+ [self setPropertiesForStyleBits:bits mask:MASK(TITLE_VISIBLE)];
+}
+
+- (void) updateCustomTitleBar {
+ _customTitleBarHeight = -1.0f; // Reset for lazy init
+ BOOL enabled = self.isCustomTitleBarEnabled;
+ BOOL fullscreen = self.isFullScreen;
+
+ jint mask = [AWTWindow affectedStyleMaskForCustomTitleBar];
+ jint newBits = [AWTWindow overrideStyleBits:self.styleBits customTitleBarEnabled:enabled fullscreen:fullscreen];
+ // Copied from nativeSetNSWindowStyleBits:
+ // The content view must be resized first, otherwise the window will be resized to fit the existing
+ // content view.
+ NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
+ if (!fullscreen) {
+ NSRect frame = [nsWindow frame];
+ NSRect screenContentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
+ NSRect contentFrame = NSMakeRect(screenContentRect.origin.x - frame.origin.x,
+ screenContentRect.origin.y - frame.origin.y,
+ screenContentRect.size.width,
+ screenContentRect.size.height);
+ nsWindow.contentView.frame = contentFrame;
+ }
+ // NSWindowStyleMaskFullScreen bit shouldn't be updated directly
+ [nsWindow setStyleMask:(((NSWindowStyleMask) styleMask) & ~NSWindowStyleMaskFullScreen |
+ nsWindow.styleMask & NSWindowStyleMaskFullScreen)];
+ // calls methods on NSWindow to change other properties, based on the mask
+ [self setPropertiesForStyleBits:newBits mask:mask];
+
+ if (!fullscreen && !self.nsWindow.miniaturized) {
+ [self _deliverMoveResizeEvent];
+ }
+
+ if (enabled != (self.customTitleBarConstraints != nil)) {
+ if (!fullscreen) {
+ if (self.isCustomTitleBarEnabled) {
+ [self setUpCustomTitleBar];
+ } else {
+ [self resetCustomTitleBar];
+ }
+ } else {
+ [self updateFullScreenButtons];
+ }
+ } else if (enabled) {
+ [self updateCustomTitleBarConstraints];
+ }
+}
+
+@end // AWTWindow
+
+@implementation AWTWindowZoomButtonMouseResponder {
+ NSWindow* _window;
+ NSTrackingArea* _trackingArea;
+}
+
+- (id) initWithWindow:(NSWindow*)window {
+ self = [super init];
+ if (self == nil) {
+ return nil;
+ }
+
+ if (![window isKindOfClass: [AWTWindow_Normal class]]) {
+ [self release];
+ return nil;
+ }
+
+ NSView* zoomButtonView = [window standardWindowButton:NSWindowZoomButton];
+ if (zoomButtonView == nil) {
+ [self release];
+ return nil;
+ }
+
+ _window = [window retain];
+ _trackingArea = [[NSTrackingArea alloc]
+ initWithRect:zoomButtonView.bounds
+ options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow
+ owner:self
+ userInfo:nil
+ ];
+ [zoomButtonView addTrackingArea:_trackingArea];
+
+ return self;
+}
+
+- (void)mouseEntered:(NSEvent*)event {
+ if ([_window isIgnoreMove]) {
+ // Enable moving the window while we're mousing over the "maximize" button so that
+ // macOS 15 window tiling actions can properly appear
+ _window.movable = YES;
+ }
+}
-- (void)windowWillEnterFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowWillEnterFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_ENTER withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+- (void)mouseExited:(NSEvent*)event {
+ if ([_window isIgnoreMove]) {
+ _window.movable = NO;
}
}
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
+- (void)dealloc {
+ [_window release];
+ [_trackingArea release];
+ [super dealloc];
+}
+
+@end
+
+@implementation AWTWindowDragView {
+ BOOL _dragging;
+}
+
+- (id) initWithPlatformWindow:(jobject)javaPlatformWindow {
+ self = [super init];
+ if (self == nil) return nil; // no hope
+
+ self.javaPlatformWindow = javaPlatformWindow;
+ return self;
+}
+
+- (BOOL) areCustomTitleBarNativeActionsAllowed {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(YES);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", YES);
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", YES);
+ DECLARE_FIELD_RETURN(jf_customTitleBarHitTest, jc_Window, "customTitleBarHitTest", "I", YES);
+
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowDidEnterFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_ENTER withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+ if (!platformWindow) return YES;
+ jint hitTest = java_awt_Window_CustomTitleBar_HIT_UNDEFINED;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ hitTest = (jint) (*env)->GetIntField(env, target, jf_customTitleBarHitTest);
+ (*env)->DeleteLocalRef(env, target);
}
- [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ return hitTest <= java_awt_Window_CustomTitleBar_HIT_TITLEBAR;
}
-- (void)windowWillExitFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
- if (jm_windowWillExitFullScreen == NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- jm_windowWillExitFullScreen = (*env)->GetMethodID(env, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
+- (BOOL) acceptsFirstMouse:(NSEvent *)event {
+ return YES;
+}
+
+- (BOOL) shouldDelayWindowOrderingForEvent:(NSEvent *)event {
+ return [[self.window contentView] shouldDelayWindowOrderingForEvent:event];
+}
+- (void) mouseDown: (NSEvent *)event {
+ _dragging = NO;
+ [[self.window contentView] mouseDown:event];
+}
+- (void) mouseUp: (NSEvent *)event {
+ [[self.window contentView] mouseUp:event];
+ if (event.clickCount == 2 && [self areCustomTitleBarNativeActionsAllowed]) {
+ NSString *action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"];
+ if (action != nil && [action caseInsensitiveCompare:@"Minimize"] == NSOrderedSame) {
+ [self.window performMiniaturize:nil];
+ } else if (action == nil || [action caseInsensitiveCompare:@"None"] != NSOrderedSame) { // action == "Maximize" (default)
+ [self.window performZoom:nil];
+ }
}
- CHECK_NULL(jm_windowWillExitFullScreen);
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowWillExitFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_EXIT withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+}
+- (void) rightMouseDown: (NSEvent *)event {
+ [[self.window contentView] rightMouseDown:event];
+}
+- (void) rightMouseUp: (NSEvent *)event {
+ [[self.window contentView] rightMouseUp:event];
+}
+- (void) otherMouseDown: (NSEvent *)event {
+ [[self.window contentView] otherMouseDown:event];
+}
+- (void) otherMouseUp: (NSEvent *)event {
+ [[self.window contentView] otherMouseUp:event];
+}
+- (void) mouseMoved: (NSEvent *)event {
+ [[self.window contentView] mouseMoved:event];
+}
+- (void) mouseDragged: (NSEvent *)event {
+ if (!_dragging) {
+ _dragging = YES;
+ if ([self areCustomTitleBarNativeActionsAllowed]) {
+ [self.window performWindowDragWithEvent:event];
+ return;
+ }
}
+ [[self.window contentView] mouseDragged:event];
+}
+- (void) rightMouseDragged: (NSEvent *)event {
+ [[self.window contentView] rightMouseDragged:event];
+}
+- (void) otherMouseDragged: (NSEvent *)event {
+ [[self.window contentView] otherMouseDragged:event];
+}
+- (void) mouseEntered: (NSEvent *)event {
+ [[self.window contentView] mouseEntered:event];
+}
+- (void) mouseExited: (NSEvent *)event {
+ [[self.window contentView] mouseExited:event];
+}
+- (void) scrollWheel: (NSEvent*) event {
+ [[self.window contentView] scrollWheel:event];
+}
+- (void) keyDown: (NSEvent *)event {
+ [[self.window contentView] keyDown:event];
+}
+- (void) keyUp: (NSEvent *)event {
+ [[self.window contentView] keyUp:event];
+}
+- (void) flagsChanged: (NSEvent *)event {
+ [[self.window contentView] flagsChanged:event];
}
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowDidExitFullScreen, jc_CPlatformWindow, "windowDidExitFullScreen", "()V");
- (*env)->CallVoidMethod(env, platformWindow, jm_windowDidExitFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_EXIT withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+@end
+
+@implementation AWTButtonsView
+
+- (void)dealloc {
+ [_color release];
+ [super dealloc];
}
-- (void)sendEvent:(NSEvent *)event {
- if ([event type] == NSEventTypeLeftMouseDown ||
- [event type] == NSEventTypeRightMouseDown ||
- [event type] == NSEventTypeOtherMouseDown) {
- if ([self isBlocked]) {
- // Move parent windows to front and make sure that a child window is displayed
- // in front of its nearest parent.
- if (self.ownerWindow != nil) {
- JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_orderAboveSiblings, jc_CPlatformWindow, "orderAboveSiblings", "()V");
- (*env)->CallVoidMethod(env,platformWindow, jm_orderAboveSiblings);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- }
- [self orderChildWindows:YES];
- }
+- (void)mouseEntered:(NSEvent *)theEvent {
+ [self updateButtons:YES];
+}
- NSPoint p = [NSEvent mouseLocation];
- NSRect frame = [self.nsWindow frame];
- NSRect contentRect = [self.nsWindow contentRectForFrameRect:frame];
-
- // Check if the click happened in the non-client area (title bar)
- // Also, non-client area includes the edges at left, right and botton of frame
- if ((p.y >= (frame.origin.y + contentRect.size.height)) ||
- (p.x >= (frame.origin.x + contentRect.size.width - 3)) ||
- (fabs(frame.origin.x - p.x) < 3) ||
- (fabs(frame.origin.y - p.y) < 3)) {
- JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- // Currently, no need to deliver the whole NSEvent.
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_deliverNCMouseDown, jc_CPlatformWindow, "deliverNCMouseDown", "()V");
- (*env)->CallVoidMethod(env, platformWindow, jm_deliverNCMouseDown);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- }
- }
+- (void)mouseExited:(NSEvent *)theEvent {
+ [self updateButtons:NO];
}
-- (void)constrainSize:(NSSize*)size {
- float minWidth = 0.f, minHeight = 0.f;
+- (void)configureColors {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *javaColor = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.newFullScreenControls.background" withEnv:env];
- if (IS(self.styleBits, DECORATED)) {
- NSRect frame = [self.nsWindow frame];
- NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[self.nsWindow styleMask]];
+ [_color release];
- float top = frame.size.height - contentRect.size.height;
- float left = contentRect.origin.x - frame.origin.x;
- float bottom = contentRect.origin.y - frame.origin.y;
- float right = frame.size.width - (contentRect.size.width + left);
+ if (javaColor == nil) {
+ _color = nil;
+ } else {
+ int rgb = [javaColor intValue];
- // Speculative estimation: 80 - enough for window decorations controls
- minWidth += left + right + 80;
- minHeight += top + bottom;
- }
+ CGFloat alpha = (((rgb >> 24) & 0xff) / 255.0);
+ CGFloat red = (((rgb >> 16) & 0xff) / 255.0);
+ CGFloat green = (((rgb >> 8) & 0xff) / 255.0);
+ CGFloat blue = (((rgb >> 0) & 0xff) / 255.0);
- minWidth = MAX(1.f, minWidth);
- minHeight = MAX(1.f, minHeight);
+ _color = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
+ [_color retain];
+ }
- size->width = MAX(size->width, minWidth);
- size->height = MAX(size->height, minHeight);
+ [self updateButtons:NO];
}
-- (void) setEnabled: (BOOL)flag {
- self.isEnabled = flag;
+- (void)updateButtons:(BOOL) flag {
+ _showButtons = flag;
- if (IS(self.styleBits, CLOSEABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowCloseButton] setEnabled: flag];
+ if (self.subviews.count == 3) {
+ [self updateButton:0 flag:flag]; // close
+ [self updateButton:1 flag:NO]; // miniaturize
+ [self updateButton:2 flag:flag]; // zoom
}
- if (IS(self.styleBits, MINIMIZABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled: flag];
+ [self setNeedsDisplay:YES];
+}
+
+- (void)updateButton: (int)index flag:(BOOL) flag {
+ NSButton *button = (NSButton*)self.subviews[index];
+ [button setHidden:!flag];
+ [button setHighlighted:flag];
+}
+
+- (void)drawRect: (NSRect)dirtyRect {
+ if (self.subviews.count != 3) {
+ return;
}
- if (IS(self.styleBits, ZOOMABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowZoomButton] setEnabled: flag];
+ if (_color == nil) {
+ [[NSColor whiteColor] setFill];
+ } else {
+ [_color setFill];
}
- if (IS(self.styleBits, RESIZABLE)) {
- [self updateMinMaxSize:flag];
- [self.nsWindow setShowsResizeIndicator:flag];
+ if (_showButtons) {
+ [self drawButton:1]; // miniaturize
+ } else {
+ for (int i = 0; i < 3; i++) {
+ [self drawButton:i];
+ }
}
}
-+ (void) setLastKeyWindow:(AWTWindow *)window {
- [window retain];
- [lastKeyWindow release];
- lastKeyWindow = window;
-}
+- (void)drawButton: (int)index {
+ NSButton *button = (NSButton*)self.subviews[index];
+ NSRect rect = button.frame;
+ CGFloat r = 12;
+ CGFloat x = rect.origin.x + (rect.size.width - r) / 2;
+ CGFloat y = rect.origin.y + (rect.size.height - r) / 2;
-+ (AWTWindow *) lastKeyWindow {
- return lastKeyWindow;
+ NSBezierPath* circlePath = [NSBezierPath bezierPath];
+ [circlePath appendBezierPathWithOvalInRect:CGRectMake(x, y, r, r)];
+ [circlePath fill];
}
-@end // AWTWindow
+@end
/*
* Class: sun_lwawt_macosx_CPlatformWindow
@@ -1122,7 +2209,7 @@ + (AWTWindow *) lastKeyWindow {
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeCreateNSWindow
- * Signature: (JJIIII)J
+ * Signature: (JJIDDDD)J
*/
JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCreateNSWindow
(JNIEnv *env, jobject obj, jlong contentViewPtr, jlong ownerPtr, jlong styleBits, jdouble x, jdouble y, jdouble w, jdouble h)
@@ -1135,6 +2222,17 @@ + (AWTWindow *) lastKeyWindow {
NSView *contentView = OBJC(contentViewPtr);
NSRect frameRect = NSMakeRect(x, y, w, h);
AWTWindow *owner = [OBJC(ownerPtr) delegate];
+
+ BOOL isIgnoreMouseEvents = NO;
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(0);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", 0);
+ jobject awtWindow = (*env)->GetObjectField(env, obj, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", 0);
+ DECLARE_METHOD_RETURN(jm_isIgnoreMouseEvents, jc_Window, "isIgnoreMouseEvents", "()Z", 0);
+ isIgnoreMouseEvents = (*env)->CallBooleanMethod(env, awtWindow, jm_isIgnoreMouseEvents) == JNI_TRUE ? YES : NO;
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
window = [[AWTWindow alloc] initWithPlatformWindow:platformWindow
@@ -1144,7 +2242,12 @@ + (AWTWindow *) lastKeyWindow {
contentView:contentView];
// the window is released is CPlatformWindow.nativeDispose()
- if (window) [window.nsWindow retain];
+ if (window) {
+ [window.nsWindow retain];
+ if (isIgnoreMouseEvents) {
+ [window.nsWindow setIgnoresMouseEvents:YES];
+ }
+ }
}];
JNI_COCOA_EXIT(env);
@@ -1168,9 +2271,13 @@ + (AWTWindow *) lastKeyWindow {
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ BOOL customTitleBarEnabled = window.isCustomTitleBarEnabled;
+ BOOL fullscreen = window.isFullScreen;
// scans the bit field, and only updates the values requested by the mask
// (this implicitly handles the _CALLBACK_PROP_BITMASK case, since those are passive reads)
- jint newBits = window.styleBits & ~mask | bits & mask;
+ jint actualBits = window.styleBits & ~mask | bits & mask;
+ // Force properties if custom title bar is enabled, but store original value in self.styleBits.
+ jint newBits = [AWTWindow overrideStyleBits:actualBits customTitleBarEnabled:customTitleBarEnabled fullscreen:fullscreen];
BOOL resized = NO;
@@ -1178,7 +2285,8 @@ + (AWTWindow *) lastKeyWindow {
// The content view must be resized first, otherwise the window will be resized to fit the existing
// content view.
if (IS(mask, FULL_WINDOW_CONTENT)) {
- if (IS(newBits, FULL_WINDOW_CONTENT) != IS(window.styleBits, FULL_WINDOW_CONTENT)) {
+ if ((IS(newBits, FULL_WINDOW_CONTENT) != IS(window.styleBits, FULL_WINDOW_CONTENT) ||
+ customTitleBarEnabled) && !fullscreen) {
NSRect frame = [nsWindow frame];
NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
NSRect screenContentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
@@ -1189,11 +2297,19 @@ + (AWTWindow *) lastKeyWindow {
nsWindow.contentView.frame = contentFrame;
resized = YES;
}
+ if (window.isJustCreated) {
+ // Perform Move/Resize event for just created windows
+ resized = YES;
+ window.isJustCreated = NO;
+ }
}
// 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];
+ NSWindowStyleMask curMask = nsWindow.styleMask;
+ // NSWindowStyleMaskFullScreen bit shouldn't be updated directly
+ [nsWindow setStyleMask:(styleMask & ~NSWindowStyleMaskFullScreen | curMask & NSWindowStyleMaskFullScreen)];
}
// calls methods on NSWindow to change other properties, based on the mask
@@ -1201,7 +2317,7 @@ + (AWTWindow *) lastKeyWindow {
[window setPropertiesForStyleBits:newBits mask:mask];
}
- window.styleBits = newBits;
+ window.styleBits = actualBits;
if (resized) {
[window _deliverMoveResizeEvent];
@@ -1211,6 +2327,34 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeSetNSWindowAppearance
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowAppearance
+ (JNIEnv *env, jclass clazz, jlong windowPtr, jstring appearanceName)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ // create a global-ref around the appearanceName, so it can be safely passed to Main thread
+ jobject appearanceNameRef= (*env)->NewGlobalRef(env, appearanceName);
+
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ // attach the dispatch thread to the JVM if necessary, and get an env
+ JNIEnv* blockEnv = [ThreadUtilities getJNIEnvUncached];
+ NSAppearance* appearance = [NSAppearance appearanceNamed:
+ JavaStringToNSString(blockEnv, appearanceNameRef)];
+ if (appearance != NULL) {
+ [nsWindow setAppearance:appearance];
+ }
+ (*blockEnv)->DeleteGlobalRef(blockEnv, appearanceNameRef);
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowMenuBar
@@ -1227,7 +2371,7 @@ + (AWTWindow *) lastKeyWindow {
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
- if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
+ if ([nsWindow isMainWindow]) {
[window.javaMenuBar deactivate];
}
@@ -1238,7 +2382,7 @@ + (AWTWindow *) lastKeyWindow {
actualMenuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
}
- if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
+ if ([nsWindow isMainWindow]) {
[CMenuBar activate:actualMenuBar modallyDisabled:NO];
}
}];
@@ -1440,15 +2584,15 @@ + (AWTWindow *) lastKeyWindow {
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativePushNSWindowToFront
- * Signature: (J)V
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowToFront
-(JNIEnv *env, jclass clazz, jlong windowPtr)
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
NSWindow *nsWindow = OBJC(windowPtr);
- [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ [ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
if (![nsWindow isKeyWindow]) {
[nsWindow makeKeyAndOrderFront:nsWindow];
@@ -1460,6 +2604,38 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeHideWindow
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeHideWindow
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
+ if (nsWindow.keyWindow) {
+ // When 'windowDidResignKey' is called during 'orderOut', current key window
+ // is reported as 'nil', so it's impossible to create WINDOW_FOCUS_LOST event
+ // with correct 'opposite' window.
+ // So, as a workaround, we perform focus transfer to a parent window explicitly here.
+ NSWindow *parentWindow = nsWindow;
+ while ((parentWindow = ((AWTWindow*)parentWindow.delegate).ownerWindow.nsWindow) != nil) {
+ if (parentWindow.canBecomeKeyWindow) {
+ [parentWindow makeKeyWindow];
+ break;
+ }
+ }
+ }
+ [nsWindow orderOut:nsWindow];
+ [nsWindow close];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowTitle
@@ -1471,8 +2647,8 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_ENTER(env);
NSWindow *nsWindow = OBJC(windowPtr);
- [nsWindow performSelectorOnMainThread:@selector(setTitle:)
- withObject:JavaStringToNSString(env, jtitle)
+ [ThreadUtilities performOnMainThread:@selector(setTitle:) on:nsWindow
+ withObject:JavaStringToNSString(env, jtitle)
waitUntilDone:NO];
JNI_COCOA_EXIT(env);
@@ -1621,6 +2797,21 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+// undocumented approach which avoids focus stealing
+// and can be used full screen switch is in progress for another window
+void enableFullScreenSpecial(NSWindow *nsWindow) {
+ NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] init];
+ [nsWindow encodeRestorableStateWithCoder:coder];
+ [coder encodeBool:YES forKey:@"NSIsFullScreen"];
+ NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:coder.encodedData];
+ decoder.requiresSecureCoding = YES;
+ decoder.decodingFailurePolicy = NSDecodingFailurePolicySetErrorAndReturn;
+ [nsWindow restoreStateWithCoder:decoder];
+ [decoder finishDecoding];
+ [decoder release];
+ [coder release];
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: _toggleFullScreenMode
@@ -1636,7 +2827,17 @@ + (AWTWindow *) lastKeyWindow {
if (![nsWindow respondsToSelector:toggleFullScreenSelector]) return;
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ static BOOL inProgress = NO;
+ if ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) != NSWindowStyleMaskFullScreen &&
+ (inProgress || !NSApp.active)) {
+ enableFullScreenSpecial(nsWindow);
+ if ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) return; // success
+ // otherwise fall back to standard approach
+ }
+ BOOL savedValue = inProgress;
+ inProgress = YES;
[nsWindow performSelector:toggleFullScreenSelector withObject:nil];
+ inProgress = savedValue;
}];
JNI_COCOA_EXIT(env);
@@ -1677,6 +2878,8 @@ + (AWTWindow *) lastKeyWindow {
[nsWindow setDelegate: nil];
[window release];
+
+ ignoreResizeWindowDuringAnotherWindowEnd = NO;
}];
JNI_COCOA_EXIT(env);
@@ -1738,3 +2941,111 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeRaiseLevel
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean popup, jboolean onlyIfParentIsActive)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ if (onlyIfParentIsActive) {
+ AWTWindow *parent = window;
+ do {
+ parent = parent.ownerWindow;
+ } while (parent != nil && !parent.nsWindow.isMainWindow);
+ if (parent == nil) {
+ return;
+ }
+ }
+ [nsWindow setLevel: popup ? NSPopUpMenuWindowLevel : NSFloatingWindowLevel];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeDelayShowing
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeDelayShowing
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ __block jboolean result = JNI_FALSE;
+
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ result = [window delayShowing];
+ }];
+
+ JNI_COCOA_EXIT(env);
+
+ return result;
+}
+
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeUpdateCustomTitleBar
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ [window updateCustomTitleBar];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCallDeliverMoveResizeEvent
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ [window _deliverMoveResizeEvent];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCorners
+(JNIEnv *env, jclass clazz, jlong windowPtr, jfloat radius, jint borderWidth, jint borderRgb)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *w = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ w.hasShadow = YES;
+ w.contentView.wantsLayer = YES;
+ w.contentView.layer.cornerRadius = radius;
+ w.contentView.layer.masksToBounds = YES;
+ w.contentView.layer.opaque = NO;
+
+ if (borderWidth > 0) {
+ CGFloat alpha = (((borderRgb >> 24) & 0xff) / 255.0);
+ CGFloat red = (((borderRgb >> 16) & 0xff) / 255.0);
+ CGFloat green = (((borderRgb >> 8) & 0xff) / 255.0);
+ CGFloat blue = (((borderRgb >> 0) & 0xff) / 255.0);
+ NSColor *color = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
+
+ w.contentView.layer.borderWidth = borderWidth;
+ w.contentView.layer.borderColor = color.CGColor;
+ }
+
+ w.backgroundColor = NSColor.clearColor;
+ w.opaque = NO;
+ // remove corner radius animation
+ [w.contentView.layer removeAllAnimations];
+ [w invalidateShadow];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
index 4d8f1c765e65..375267c40025 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
@@ -37,6 +37,7 @@
#import "ThreadUtilities.h"
#import "NSApplicationAWT.h"
#import "JNIUtilities.h"
+#import "AWTWindow.h"
#pragma mark App Menu helpers
@@ -266,6 +267,11 @@ - (id) init {
[ctr addObserver:clz selector:@selector(_appDidDeactivate) name:NSApplicationDidResignActiveNotification object:nil];
[ctr addObserver:clz selector:@selector(_appDidHide) name:NSApplicationDidHideNotification object:nil];
[ctr addObserver:clz selector:@selector(_appDidUnhide) name:NSApplicationDidUnhideNotification object:nil];
+ [ctr addObserver:clz selector:@selector(_didChangeScreenParameters) name:NSApplicationDidChangeScreenParametersNotification object:nil];
+ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:[AWTWindow class]
+ selector:@selector(activeSpaceDidChange)
+ name:NSWorkspaceActiveSpaceDidChangeNotification
+ object:nil];
return self;
}
@@ -379,16 +385,20 @@ - (NSApplicationPrintReply)application:(NSApplication *)application printFiles:(
return NSPrintingSuccess;
}
+#define TRACE_NOTIFY 0
+
// Open app handler, registered in -init
+ (void)_notifyJava:(jint)notificationType {
AWT_ASSERT_APPKIT_THREAD;
+ if (TRACE_NOTIFY) NSLog(@"ApplicationDelegate._notifyJava: enter (%d)", notificationType);
- //fprintf(stderr,"jm_handleOpenApplication\n");
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_APPEVENTHANDLER_CLASS();
DECLARE_STATIC_METHOD(jm_handleNativeNotification, sjc_AppEventHandler, "handleNativeNotification", "(I)V");
(*env)->CallStaticVoidMethod(env, sjc_AppEventHandler, jm_handleNativeNotification, notificationType);
CHECK_EXCEPTION();
+
+ if (TRACE_NOTIFY) NSLog(@"ApplicationDelegate._notifyJava: exit (%d)", notificationType);
}
// About menu handler
@@ -499,6 +509,10 @@ + (void)_registerForNotification:(NSNumber *)notificationTypeNum {
}
}
++ (void)_didChangeScreenParameters {
+ [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_CHANGE_PARAMETERS];
+}
+
// Retrieves the menu to be attached to the Dock icon (AppKit callback)
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
AWT_ASSERT_APPKIT_THREAD;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
index 984e80faa66c..04156b702ed1 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
@@ -24,6 +24,7 @@
*/
#import "CDataTransferer.h"
+#include "sun_lwawt_macosx_CDataTransferer.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import
@@ -293,13 +294,22 @@ - (BOOL) checkPasteboardWithoutNotification:(id)application {
}
NSUInteger dataSize = [clipData length];
+ const void *dataBuffer = [clipData bytes];
+ if (format == sun_lwawt_macosx_CDataTransferer_CF_STRING && dataSize >= 3) {
+ const unsigned char *bytesPtr = (const unsigned char *)dataBuffer;
+ if (bytesPtr[0] == 0xEF && bytesPtr[1] == 0xBB && bytesPtr[2] == 0xBF) {
+ // strip BOM from string content, like native applications do
+ dataSize -= 3;
+ dataBuffer = (const void *)(bytesPtr + 3);
+ }
+ }
+
returnValue = (*env)->NewByteArray(env, dataSize);
if (returnValue == NULL) {
return NULL;
}
if (dataSize != 0) {
- const void *dataBuffer = [clipData bytes];
(*env)->SetByteArrayRegion(env, returnValue, 0, dataSize, (jbyte *)dataBuffer);
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
index ddb8630fb67b..310c084346ab 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2024, 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
@@ -237,7 +237,7 @@ - (jbyteArray)convertData:(jlong)format
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);
@@ -593,7 +593,7 @@ - (void)drag
{
AWT_ASSERT_NOT_APPKIT_THREAD;
- [self performSelectorOnMainThread:@selector(doDrag) withObject:nil waitUntilDone:YES];
+ [ThreadUtilities performOnMainThread:@selector(doDrag) on:self withObject:nil waitUntilDone:YES];
}
/******************************** BEGIN NSDraggingSource Interface ********************************/
@@ -744,8 +744,14 @@ - (void) postDragExit {
// Also see +[CWindow convertAWTToCocoaScreenRect]
// NSScreen-to-JavaScreen mapping:
- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint)screenPoint {
- NSRect mainR = [[[NSScreen screens] objectAtIndex:0] frame];
- NSPoint point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));
+ NSScreen *primaryScreen = [[NSScreen screens] firstObject];
+ NSPoint point = NSMakePoint(0, 0);
+ if (primaryScreen == nil) {
+ return point;
+ }
+
+ NSRect mainR = [primaryScreen frame];
+ point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));
// Adjust the point with the drag image offset to get the real mouse hotspot:
// The point should remain in screen coordinates (as per DragSourceEvent.getLocation() documentation)
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
index 677c9715679e..43cc18b1416b 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
@@ -26,9 +26,14 @@
#import
@interface CFileDialog : NSObject {
+ NSWindow* fOwner;
+
// Should we query back to Java for a file filter?
jboolean fHasFileFilter;
+ // Allowed file types
+ NSArray *fFileTypes;
+
// sun.awt.CFileDialog
jobject fFileDialog;
@@ -45,6 +50,9 @@
// File dialog's mode
jint fMode;
+ // File dialog's modality type
+ jint fModality;
+
// Indicates whether the user can select multiple files
BOOL fMultipleMode;
@@ -54,27 +62,41 @@
// Can the dialog choose directories ?
BOOL fChooseDirectories;
+ // Can the dialog choose files ?
+ BOOL fChooseFiles;
+
+ // Can the dialog create directories ?
+ BOOL fCreateDirectories;
+
// Contains the absolute paths of the selected files as URLs
NSArray *fURLs;
+
+ // Condition to signal when dialog is closed
+ NSCondition *fCondition;
}
// Allocator
-- (id) initWithFilter:(jboolean)inHasFilter
+- (id) initWithOwner:(NSWindow*) owner
+ filter:(jboolean)inHasFilter
+ fileTypes:(NSArray *)inFileTypes
fileDialog:(jobject)inDialog
title:(NSString *)inTitle
directory:(NSString *)inPath
file:(NSString *)inFile
mode:(jint)inMode
+ modality:(jint)inModality
multipleMode:(BOOL)inMultipleMode
shouldNavigate:(BOOL)inNavigateApps
canChooseDirectories:(BOOL)inChooseDirectories
+ canChooseFiles:(BOOL)inChooseFiles
+ canCreateDirectories:(BOOL)inCreateDirectories
withEnv:(JNIEnv*)env;
// Invoked from the main thread
- (void) safeSaveOrLoad;
-// Get dialog return value
-- (BOOL) userClickedOK;
+// Wait for completion and get dialog return value
+- (NSModalResponse) wait;
// Returns the absolute paths of the selected files as URLs
- (NSArray *) URLs;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
index 3b895be8094f..2fb8fe26738d 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
@@ -25,29 +25,42 @@
#import
#import
+#import
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import "CFileDialog.h"
+#import "AWTWindow.h"
+#import "CMenuBar.h"
+#import "ApplicationDelegate.h"
#import "java_awt_FileDialog.h"
#import "sun_lwawt_macosx_CFileDialog.h"
@implementation CFileDialog
-- (id)initWithFilter:(jboolean)inHasFilter
+- (id)initWithOwner:(NSWindow*)owner
+ filter:(jboolean)inHasFilter
+ fileTypes:(NSArray *)inFileTypes
fileDialog:(jobject)inDialog
title:(NSString *)inTitle
directory:(NSString *)inPath
file:(NSString *)inFile
mode:(jint)inMode
+ modality:(jint)inModality
multipleMode:(BOOL)inMultipleMode
shouldNavigate:(BOOL)inNavigateApps
canChooseDirectories:(BOOL)inChooseDirectories
+ canChooseFiles:(BOOL)inChooseFiles
+canCreateDirectories:(BOOL)inCreateDirectories
withEnv:(JNIEnv*)env;
{
- if (self = [super init]) {
+ if (self = [super init]) {
+ fOwner = owner;
+ [fOwner retain];
fHasFileFilter = inHasFilter;
+ fFileTypes = inFileTypes;
+ [fFileTypes retain];
fFileDialog = (*env)->NewGlobalRef(env, inDialog);
fDirectory = inPath;
[fDirectory retain];
@@ -56,10 +69,14 @@ - (id)initWithFilter:(jboolean)inHasFilter
fTitle = inTitle;
[fTitle retain];
fMode = inMode;
+ fModality = inModality;
fMultipleMode = inMultipleMode;
fNavigateApps = inNavigateApps;
fChooseDirectories = inChooseDirectories;
- fPanelResult = NSCancelButton;
+ fChooseFiles = inChooseFiles;
+ fCreateDirectories = inCreateDirectories;
+ fPanelResult = NSModalResponseContinue;
+ fCondition = [[NSCondition alloc] init];
}
return self;
@@ -74,6 +91,9 @@ -(void) disposer {
}
-(void) dealloc {
+ [fFileTypes release];
+ fFileTypes = nil;
+
[fDirectory release];
fDirectory = nil;
@@ -86,6 +106,12 @@ -(void) dealloc {
[fURLs release];
fURLs = nil;
+ [fOwner release];
+ fOwner = nil;
+
+ [fCondition release];
+ fCondition = nil;
+
[super dealloc];
}
@@ -110,6 +136,25 @@ - (void)safeSaveOrLoad {
if (thePanel != nil) {
[thePanel setTitle:fTitle];
+ if (fFileTypes != nil) {
+ if (@available(macOS 11, *)) {
+ int nTypes = (int)[fFileTypes count];
+ NSMutableArray *contentTypes = [NSMutableArray arrayWithCapacity:nTypes];
+ for (int i = 0; i < nTypes; ++i) {
+ NSString *fileType = (NSString *)[fFileTypes objectAtIndex:i];
+ UTType *contentType = [UTType typeWithFilenameExtension:fileType conformingToType:UTTypeData];
+ if (contentType != nil) {
+ [contentTypes addObject:contentType];
+ } else if (fileType.length == 0) {
+ [contentTypes addObject:UTTypeUnixExecutable];
+ }
+ }
+ [thePanel setAllowedContentTypes:contentTypes];
+ } else {
+ [thePanel setAllowedFileTypes:fFileTypes];
+ }
+ }
+
if (fNavigateApps) {
[thePanel setTreatsFilePackagesAsDirectories:YES];
}
@@ -117,27 +162,131 @@ - (void)safeSaveOrLoad {
if (fMode == java_awt_FileDialog_LOAD) {
NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
[openPanel setAllowsMultipleSelection:fMultipleMode];
- [openPanel setCanChooseFiles:!fChooseDirectories];
+ [openPanel setCanChooseFiles:fChooseFiles];
[openPanel setCanChooseDirectories:fChooseDirectories];
- [openPanel setCanCreateDirectories:YES];
+ [openPanel setCanCreateDirectories:fCreateDirectories];
}
[thePanel setDelegate:self];
- fPanelResult = [thePanel runModalForDirectory:fDirectory file:fFile];
- [thePanel setDelegate:nil];
- if ([self userClickedOK]) {
- if (fMode == java_awt_FileDialog_LOAD) {
- NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
- fURLs = [openPanel URLs];
+ NSMenuItem *editMenuItem = nil;
+ NSMenu *menu = [NSApp mainMenu];
+ if (menu != nil) {
+ if (menu.numberOfItems > 0) {
+ NSMenu *submenu = [[menu itemAtIndex:0] submenu];
+ if (submenu != nil) {
+ menu = submenu;
+ }
+ }
+
+ editMenuItem = [self createEditMenu];
+ if (menu.numberOfItems > 0) {
+ [menu insertItem:editMenuItem atIndex:0];
} else {
- fURLs = [NSArray arrayWithObject:[thePanel URL]];
+ [menu addItem:editMenuItem];
}
- [fURLs retain];
+ [editMenuItem release];
}
+
+ if (fDirectory != nil) {
+ [thePanel setDirectoryURL:[NSURL fileURLWithPath:[fDirectory stringByExpandingTildeInPath]]];
+ }
+ if (fFile != nil) {
+ [thePanel setNameFieldStringValue:fFile];
+ }
+
+ AWTWindow *awtWindow = nil;
+ CMenuBar *menuBar = nil;
+ if (fOwner != nil) {
+ // Finds appropriate menubar in our hierarchy,
+ awtWindow = (AWTWindow *)fOwner.delegate;
+ while (awtWindow.ownerWindow != nil) {
+ awtWindow = awtWindow.ownerWindow;
+ }
+
+ BOOL isDisabled = NO;
+ if ([awtWindow.nsWindow isVisible]){
+ menuBar = awtWindow.javaMenuBar;
+ isDisabled = !awtWindow.isEnabled;
+ }
+
+ if (menuBar != nil) {
+ [CMenuBar activate:menuBar modallyDisabled:isDisabled];
+ }
+
+ if (@available(macOS 10.14, *)) {
+ [thePanel setAppearance:fOwner.appearance];
+ }
+ }
+
+ void (^completionHandler)(NSModalResponse) = ^(NSModalResponse result) {
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ if (menuBar != nil) {
+ [CMenuBar activate:menuBar modallyDisabled:NO];
+ }
+
+ if (editMenuItem != nil) {
+ [menu removeItem:editMenuItem];
+ }
+
+ if (result == NSModalResponseOK) {
+ if (fMode == java_awt_FileDialog_LOAD) {
+ NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
+ fURLs = [openPanel URLs];
+ } else {
+ fURLs = [NSArray arrayWithObject:[thePanel URL]];
+ }
+ [fURLs retain];
+ }
+
+ [thePanel setDelegate:nil];
+ [self disposer];
+
+ [fCondition lock];
+ fPanelResult = result;
+ [fCondition signal];
+ [fCondition unlock];
+ }];
+ };
+
+ if (fModality == 0) {
+ [thePanel beginWithCompletionHandler:completionHandler];
+ } else if (fModality == 1) {
+ [thePanel beginSheetModalForWindow:awtWindow.nsWindow completionHandler:completionHandler];
+ } else {
+ completionHandler([thePanel runModal]);
+ }
+
+ } else {
+ [self disposer];
}
+}
+
+- (NSMenuItem *) createEditMenu {
+ NSMenu *editMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
+
+ NSMenuItem *cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
+ [editMenu addItem:cutItem];
+ [cutItem release];
- [self disposer];
+ NSMenuItem *copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
+ [editMenu addItem:copyItem];
+ [copyItem release];
+
+ NSMenuItem *pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
+ [editMenu addItem:pasteItem];
+ [pasteItem release];
+
+ NSMenuItem *selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
+ [editMenu addItem:selectAllItem];
+ [selectAllItem release];
+
+ NSMenuItem *editMenuItem = [NSMenuItem new];
+ [editMenuItem setTitle:@"Edit"];
+ [editMenuItem setSubmenu:editMenu];
+ [editMenu release];
+
+ return editMenuItem;
}
- (BOOL) askFilenameFilter:(NSString *)filename {
@@ -174,8 +323,13 @@ - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
return shouldEnableFile;
}
-- (BOOL) userClickedOK {
- return fPanelResult == NSOKButton;
+- (NSModalResponse) wait {
+ [fCondition lock];
+ while (fPanelResult == NSModalResponseContinue) {
+ [fCondition wait];
+ }
+ [fCondition unlock];
+ return fPanelResult;
}
- (NSArray *)URLs {
@@ -186,14 +340,12 @@ - (NSArray *)URLs {
/*
* Class: sun_lwawt_macosx_CFileDialog
* Method: nativeRunFileDialog
- * Signature: (Ljava/lang/String;ILjava/io/FilenameFilter;
- * Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_lwawt_macosx_CFileDialog_nativeRunFileDialog
-(JNIEnv *env, jobject peer, jstring title, jint mode, jboolean multipleMode,
- jboolean navigateApps, jboolean chooseDirectories, jboolean hasFilter,
- jstring directory, jstring file)
+(JNIEnv *env, jobject peer, jlong ownerPtr, jstring title, jint mode, jint modality, jboolean multipleMode,
+ jboolean navigateApps, jboolean chooseDirectories, jboolean chooseFiles, jboolean createDirectories,
+ jboolean hasFilter, jobjectArray allowedFileTypes, jstring directory, jstring file)
{
jobjectArray returnValue = NULL;
@@ -203,15 +355,33 @@ - (NSArray *)URLs {
dialogTitle = @" ";
}
- CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithFilter:hasFilter
+ NSMutableArray *fileTypes = nil;
+ if (allowedFileTypes != nil) {
+ int nTypes = (*env)->GetArrayLength(env, allowedFileTypes);
+ if (nTypes > 0) {
+ fileTypes = [NSMutableArray arrayWithCapacity:nTypes];
+ for (int i = 0; i < nTypes; i++) {
+ jstring fileType = (jstring)(*env)->GetObjectArrayElement(env, allowedFileTypes, i);
+ [fileTypes addObject:JavaStringToNSString(env, fileType)];
+ (*env)->DeleteLocalRef(env, fileType);
+ }
+ }
+ }
+
+ CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithOwner:(NSWindow *)jlong_to_ptr(ownerPtr)
+ filter:hasFilter
+ fileTypes:fileTypes
fileDialog:peer
title:dialogTitle
directory:JavaStringToNSString(env, directory)
file:JavaStringToNSString(env, file)
mode:mode
+ modality:modality
multipleMode:multipleMode
shouldNavigate:navigateApps
canChooseDirectories:chooseDirectories
+ canChooseFiles:chooseFiles
+ canCreateDirectories:createDirectories
withEnv:env];
[ThreadUtilities performOnMainThread:@selector(safeSaveOrLoad)
@@ -219,7 +389,7 @@ - (NSArray *)URLs {
withObject:nil
waitUntilDone:YES];
- if ([dialogDelegate userClickedOK]) {
+ if ([dialogDelegate wait] == NSModalResponseOK) {
NSArray *urls = [dialogDelegate URLs];
jsize count = [urls count];
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
index 32164493f9f2..342e4ead00a9 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
@@ -39,7 +39,7 @@
/*
* Convert the mode string to the more convenient bits per pixel value
*/
-static int getBPPFromModeString(CFStringRef mode)
+int getBPPFromModeString(CFStringRef mode)
{
if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
// This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits
@@ -169,9 +169,11 @@ static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env) {
w = CGDisplayModeGetWidth(mode);
CFRelease(currentBPP);
}
+ uint32_t flags = CGDisplayModeGetIOFlags(mode);
+ BOOL isDisplayModeDefault = (flags & kDisplayModeDefaultFlag) ? YES : NO;
DECLARE_CLASS_RETURN(jc_DisplayMode, "java/awt/DisplayMode", ret);
- DECLARE_METHOD_RETURN(jc_DisplayMode_ctor, jc_DisplayMode, "", "(IIII)V", ret);
- ret = (*env)->NewObject(env, jc_DisplayMode, jc_DisplayMode_ctor, w, h, bpp, refrate);
+ DECLARE_METHOD_RETURN(jc_DisplayMode_ctor, jc_DisplayMode, "", "(IIIIZ)V", ret);
+ ret = (*env)->NewObject(env, jc_DisplayMode, jc_DisplayMode_ctor, w, h, bpp, refrate, (jboolean)isDisplayModeDefault);
CHECK_EXCEPTION();
JNI_COCOA_EXIT(env);
return ret;
@@ -212,6 +214,19 @@ static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env) {
return inches > 0 ? rect.size.height / inches : DEFAULT_DEVICE_DPI;
}
+/*
+ * Class: sun_awt_CGraphicsDevice
+ * Method: nativeIsMirroring
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_sun_awt_CGraphicsDevice_nativeIsMirroring
+ (JNIEnv *env, jclass class, jint displayID)
+{
+ return (CGDisplayIsInMirrorSet(displayID)
+ || CGDisplayIsInHWMirrorSet(displayID)) ? JNI_TRUE : JNI_FALSE;
+}
+
/*
* Class: sun_awt_CGraphicsDevice
* Method: nativeGetBounds
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
index 58338cbc6c13..3bac3a103d07 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, 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
@@ -23,6 +23,9 @@
* questions.
*/
+#import
+#import
+#import "sun_awt_CGraphicsEnvironment.h"
#import "AWT_debug.h"
#import "JNIUtilities.h"
@@ -119,9 +122,9 @@
if (graphicsEnv == NULL) return; // ref already GC'd
DECLARE_CLASS(jc_CGraphicsEnvironment, "sun/awt/CGraphicsEnvironment");
DECLARE_METHOD(jm_displayReconfiguration,
- jc_CGraphicsEnvironment, "_displayReconfiguration","(IZ)V");
+ jc_CGraphicsEnvironment, "_displayReconfiguration","(II)V");
(*env)->CallVoidMethod(env, graphicsEnv, jm_displayReconfiguration,
- (jint) display, (jboolean) flags & kCGDisplayRemoveFlag);
+ (jint) display, (jint) flags);
(*env)->DeleteLocalRef(env, graphicsEnv);
CHECK_EXCEPTION();
}];
@@ -179,3 +182,36 @@
JNI_COCOA_EXIT(env);
}
+
+JNIEXPORT jint JNICALL Java_sun_awt_CGraphicsEnvironment_initMetal
+ (JNIEnv *env, jclass mtlgc, jstring shadersLibName)
+{
+ __block jint ret = sun_awt_CGraphicsEnvironment_MTL_ERROR;
+
+ JNI_COCOA_ENTER(env);
+
+ __block NSString* path = NormalizedPathNSStringFromJavaString(env, shadersLibName);
+
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
+
+ id device = MTLCreateSystemDefaultDevice();
+ if (device != nil) {
+ NSError* error = nil;
+ id lib = [device newLibraryWithFile:path error:&error];
+ if (lib != nil) {
+ ret = sun_awt_CGraphicsEnvironment_MTL_SUPPORTED;
+ } else {
+ J2dRlsTraceLn(J2D_TRACE_ERROR, "CGraphicsEnvironment_initMetal - "
+ "Failed to load Metal shader library.");
+ ret = sun_awt_CGraphicsEnvironment_MTL_NO_SHADER_LIB;
+ }
+ } else {
+ J2dRlsTraceLn(J2D_TRACE_ERROR, "CGraphicsEnvironment_initMetal - "
+ "Failed to create MTLDevice.");
+ ret = sun_awt_CGraphicsEnvironment_MTL_NO_DEVICE;
+ }
+ }];
+
+ JNI_COCOA_EXIT(env);
+ return ret;
+}
\ No newline at end of file
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
index cb9de8155906..c98cb80e807c 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
@@ -118,13 +118,6 @@ + (void) _nativeNotifyPeerWithView:(AWTView *)view inputMethod:(jobject) inputMe
[view setInputMethod:inputMethod]; // inputMethod is a GlobalRef or null to disable.
}
-+ (void) _nativeEndComposition:(AWTView *)view {
- if (view == nil) return;
-
- [view abandonInput];
-}
-
-
@end
/*
@@ -186,19 +179,48 @@ + (void) _nativeEndComposition:(AWTView *)view {
/*
* Class: sun_lwawt_macosx_CInputMethod
* Method: nativeEndComposition
- * Signature: (J)V
+ * Signature: (JLjava/awt/Component;)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition
-(JNIEnv *env, jobject this, jlong nativePeer)
+(JNIEnv *env, jobject this, jlong nativePeer, jobject component)
+{
+JNI_COCOA_ENTER(env);
+ AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
+ if (!view) return;
+ jobject componentRef = (*env)->NewGlobalRef(env, component);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ [view abandonInput:componentRef];
+ if (componentRef) {
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ (*env)->DeleteGlobalRef(env, componentRef);
+ }
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CInputMethod
+ * Method: nativeEnableListening
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEnableListening
+ (JNIEnv *env, jobject this, jlong nativePeer, jboolean enable)
{
+
JNI_COCOA_ENTER(env);
- AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
- [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
- [CInputMethod _nativeEndComposition:view];
+ AWTView * const view = (AWTView *)jlong_to_ptr(nativePeer);
+ if (view == NULL) {
+ return;
+ }
+
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ [view enableImInteraction:(enable == JNI_TRUE ? YES : NO)];
}];
JNI_COCOA_EXIT(env);
+
}
/*
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
index 147af2b7a7f0..ced9bf47e6de 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
@@ -26,6 +26,7 @@
#import "JNIUtilities.h"
#import
+#import
#import "CRobotKeyCode.h"
#import "LWCToolkit.h"
@@ -63,6 +64,10 @@
static int gsEventNumber;
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);
@@ -99,17 +104,31 @@ static inline void autoDelay(BOOL isMove) {
[NSThread sleepForTimeInterval:delay];
}
}
- gNextKeyEventTime = [[NSDate date] timeIntervalSinceReferenceDate] + 0.050;
+ 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
- * Signature: (V)V
+ * Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CRobot_initRobot
-(JNIEnv *env, jobject peer)
+(JNIEnv *env, jobject peer, jint safeDelayMillis)
{
// Set things up to let our app act like a synthetic keyboard and mouse.
// Always set all states, in case Apple ever changes default behaviors.
@@ -138,6 +157,7 @@ static inline void autoDelay(BOOL isMove) {
gsClickCount = 0;
gsLastClickTime = 0;
gNextKeyEventTime = 0;
+ safeDelay = (NSTimeInterval)safeDelayMillis/1000;
gsEventNumber = ROBOT_EVENT_NUMBER_START;
gsButtonEventNumber = (int*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(int), gNumberOfButtons);
@@ -149,6 +169,8 @@ static inline void autoDelay(BOOL isMove) {
for (i = 0; i < gNumberOfButtons; ++i) {
gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START;
}
+
+ initKeyFlags();
}];
}
}
@@ -277,6 +299,20 @@ static inline void autoDelay(BOOL isMove) {
}];
}
+// 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
@@ -289,14 +325,39 @@ static inline void autoDelay(BOOL isMove) {
autoDelay(NO);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- CGKeyCode keyCode = GetCGKeyCode(javaKeyCode);
+ CGKeyCode keyCode;
+
+ if (javaKeyCode & 0x2000000) {
+ // This is a dirty, dirty hack and is only used in tests.
+ // This allows us to send macOS virtual key code directly, without first looking up a Java key code.
+ // It also allows the caller to set the physical keyboard layout directly.
+ // The key code that should be passed to Robot in this case is the following:
+ // 0x2000000 | keyCode | (physicalLayout << 8)
+ // where physicalLayout is:
+ // 0 - ANSI
+ // 1 - ISO
+ // 2 - JIS
+
+ UInt32 physicalLayout = (javaKeyCode >> 8) & 0xff;
+ UInt32 keyboardType;
+ if (physicalLayout == 1) {
+ keyboardType = 41; // ISO
+ } else if (physicalLayout == 2) {
+ keyboardType = 42; // JIS
+ } else {
+ keyboardType = 40; // ANSI
+ }
+ CGEventSourceSetKeyboardType(source, keyboardType);
+ keyCode = javaKeyCode & 0xff;
+ } else {
+ keyCode = GetCGKeyCode(javaKeyCode);
+ }
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, keyPressed);
if (event != NULL) {
- CGEventFlags flags = CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState);
- if ((flags & kCGEventFlagMaskSecondaryFn) != 0) {
- flags ^= kCGEventFlagMaskSecondaryFn;
- CGEventSetFlags(event, flags);
- }
+ // 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);
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
index d06b9e8ee461..a2b4f6c5734b 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
@@ -99,6 +99,7 @@ const static int OSX_kVK_Tab = 0x30;
const static int OSX_kVK_Space = 0x31;
const static int OSX_Delete = 0x33;
const static int OSX_Escape = 0x35;
+const static int OSX_RightCommand = 0x36;
const static int OSX_Command = 0x37;
const static int OSX_Shift = 0x38;
const static int OSX_CapsLock = 0x39;
@@ -126,6 +127,7 @@ const static int OSX_F13 = 0x69;
const static int OSX_F16 = 0x6A;
const static int OSX_F14 = 0x6B;
const static int OSX_F10 = 0x6D;
+const static int OSX_ContextualMenu = 0x6E;
const static int OSX_F12 = 0x6F;
const static int OSX_F15 = 0x71;
const static int OSX_Help = 0x72;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
index e8caf8ca5156..a1f9d2df1127 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
@@ -160,6 +160,13 @@ -(id) init {
[NSNumber numberWithInt : OSX_F19], [NSNumber numberWithInt : java_awt_event_KeyEvent_VK_F19],
[NSNumber numberWithInt : OSX_F20], [NSNumber numberWithInt : java_awt_event_KeyEvent_VK_F20],
+ [NSNumber numberWithInt : OSX_ContextualMenu], [NSNumber numberWithInt : java_awt_event_KeyEvent_VK_CONTEXT_MENU],
+
+ // There's no VK_ key code for the section key (\u00a7), found on the Standard QWERTY (ABC) ISO layout.
+ // For consistency, let's use the code returned by getExtendedKeyCodeForChar for this symbol.
+ // Also see src/java.desktop/share/classes/sun/awt/ExtendedKeyCodes.java
+ [NSNumber numberWithInt : OSX_kVK_ISO_Section], [NSNumber numberWithInt : 0x1000000 + 0x00A7],
+
nil];
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
index 3244438bc1bc..f20f3225d34b 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
@@ -30,12 +30,12 @@
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: makeKeyAndOrderFront
- * Signature: (J)V
+ * Method: nativeMakeKeyAndOrderFront
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyAndOrderFront
-(JNIEnv *env, jclass cls, jlong windowPtr)
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeMakeKeyAndOrderFront
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -43,19 +43,19 @@
[ThreadUtilities performOnMainThread:@selector(makeKeyAndOrderFront:)
on:window
withObject:nil
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: makeKeyWindow
- * Signature: (J)V
+ * Method: nativeMakeKeyWindow
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyWindow
-(JNIEnv *env, jclass cls, jlong windowPtr)
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeMakeKeyWindow
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -63,7 +63,7 @@
[ThreadUtilities performOnMainThread:@selector(makeKeyWindow)
on:window
withObject:nil
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
@@ -156,20 +156,39 @@
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: orderOut
+ * Method: orderFrontIfOnActiveSpace
* Signature: (J)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderOut
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderFrontIfOnActiveSpace
(JNIEnv *env, jclass cls, jlong windowPtr)
{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ if (window.onActiveSpace) [window orderFront:window];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: nativeOrderOut
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeOrderOut
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
+{
JNI_COCOA_ENTER(env);
NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
[ThreadUtilities performOnMainThread:@selector(orderOut:)
on:window
withObject:window
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
@@ -231,6 +250,26 @@
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: orderWindowIfOnActiveSpace
+ * Signature: (JIJ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderWindowIfOnActiveSpace
+(JNIEnv *env, jclass cls, jlong windowPtr, jint order, jlong relativeToPtr)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ NSWindow *relativeTo = (NSWindow *)jlong_to_ptr(relativeToPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ if (window.onActiveSpace) [window orderWindow:(NSWindowOrderingMode)order relativeTo:[relativeTo windowNumber]];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
// Used for CWrapper.NSWindow.setLevel() (and level() which isn't implemented yet)
static NSInteger LEVELS[sun_lwawt_macosx_CWrapper_NSWindow_MAX_WINDOW_LEVELS];
static void initLevels()
@@ -477,6 +516,31 @@ static void initLevels()
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: isTabbedWindow
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_isTabbedWindow
+(JNIEnv *env, jclass cls, jlong windowPtr)
+{
+ __block jboolean isTabbedWindow = JNI_FALSE;
+
+JNI_COCOA_ENTER(env);
+
+ if (@available(macOS 10.13, *)) {
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ isTabbedWindow = [[[window tabGroup] windows] count] > 1;
+ }];
+ }
+
+JNI_COCOA_EXIT(env);
+
+ return isTabbedWindow;
+}
+
/*
* Class: sun_lwawt_macosx_CWrapper$NSView
* Method: addSubview
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m
new file mode 100644
index 000000000000..ee098fca5877
--- /dev/null
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2011, 2015, 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.
+ */
+
+/*
+ * Must include this before JavaNativeFoundation.h to get jni.h from build
+ */
+#include "jni.h"
+#include "jni_util.h"
+
+#import "sun_lwawt_macosx_concurrent_LibDispatchNative.h"
+
+#import
+#import
+#import "JNIUtilities.h"
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeIsDispatchSupported
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeIsDispatchSupported
+(JNIEnv *env, jclass clazz)
+{
+ return JNI_TRUE;
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeGetMainQueue
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeGetMainQueue
+(JNIEnv *env, jclass clazz)
+{
+ dispatch_queue_t queue = dispatch_get_main_queue();
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeCreateConcurrentQueue
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeCreateConcurrentQueue
+(JNIEnv *env, jclass clazz, jint priority)
+{
+ dispatch_queue_t queue = dispatch_get_global_queue((long)priority, 0);
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeCreateSerialQueue
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeCreateSerialQueue
+(JNIEnv *env, jclass clazz, jstring name)
+{
+ if (name == NULL) return 0L;
+
+ jboolean isCopy;
+ const char *queue_name = (*env)->GetStringUTFChars(env, name, &isCopy);
+ dispatch_queue_t queue = dispatch_queue_create(queue_name, NULL);
+ (*env)->ReleaseStringUTFChars(env, name, queue_name);
+
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeReleaseQueue
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeReleaseQueue
+(JNIEnv *env, jclass clazz, jlong nativeQueue)
+{
+ if (nativeQueue == 0L) return;
+ dispatch_release((dispatch_queue_t)jlong_to_ptr(nativeQueue));
+}
+
+
+
+static void perform_dispatch(JNIEnv *env, jlong nativeQueue, jobject runnable, void (*dispatch_fxn)(dispatch_queue_t, dispatch_block_t))
+{
+JNI_COCOA_ENTER(env);
+ DECLARE_CLASS(jc_Runnable, "java/lang/Runnable");
+ DECLARE_METHOD(jm_run, jc_Runnable, "run", "()V");
+
+ dispatch_queue_t queue = (dispatch_queue_t)jlong_to_ptr(nativeQueue);
+ if (queue == NULL) return; // shouldn't happen
+
+ // create a global-ref around the Runnable, so it can be safely passed to the dispatch thread
+ jobject runnableRef = (*env)->NewGlobalRef(env, runnable);
+ dispatch_fxn(queue, ^{
+ // attach the dispatch thread to the JVM if necessary, and get an env
+ JNIEnv* blockEnv = [ThreadUtilities getJNIEnvUncached];
+
+ JNI_COCOA_ENTER(blockEnv);
+ // call the user's runnable
+ (*blockEnv)->CallVoidMethod(blockEnv, runnableRef, jm_run);
+ CHECK_EXCEPTION_IN_ENV(blockEnv);
+ (*blockEnv)->DeleteGlobalRef(blockEnv, runnableRef);
+ JNI_COCOA_EXIT(blockEnv);
+ });
+
+JNI_COCOA_EXIT(env);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeExecuteAsync
+ * Signature: (JLjava/lang/Runnable;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeExecuteAsync
+(JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
+{
+ // enqueues and returns
+ perform_dispatch(env, nativeQueue, runnable, dispatch_async);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeExecuteSync
+ * Signature: (JLjava/lang/Runnable;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeExecuteSync
+(JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
+{
+ // blocks until the Runnable completes
+ perform_dispatch(env, nativeQueue, runnable, dispatch_sync);
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
index 13ad3d7db6e4..3bdca591edbf 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
@@ -97,7 +97,7 @@ NSSize JavaToNSSize(JNIEnv *env, jobject dimension) {
}
static NSScreen *primaryScreen(JNIEnv *env) {
- NSScreen *primaryScreen = [[NSScreen screens] objectAtIndex:0];
+ NSScreen *primaryScreen = [[NSScreen screens] firstObject];
if (primaryScreen != nil) return primaryScreen;
if ((env != NULL) && ([NSThread isMainThread] == NO)) {
JNU_ThrowByName(env, "java/lang/RuntimeException", "Failed to convert, no screen.");
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
index 289e63ba98c8..353fdedbd0e9 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
@@ -24,6 +24,8 @@
*/
#import
+#import
+#import
#import "InitIDs.h"
@@ -161,3 +163,30 @@
(JNIEnv *env, jclass cls)
{
}
+
+JNIEXPORT jarray JNICALL Java_sun_font_FontManagerNativeLibrary_loadedLibraries
+ (JNIEnv *env, jclass cls)
+{
+ const uint32_t count = _dyld_image_count();
+
+ jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
+ CHECK_NULL_RETURN(stringClazz, NULL);
+ jarray libsArray = (*env)->NewObjectArray(env, count, stringClazz, NULL);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+
+ if ((*env)->EnsureLocalCapacity(env, count + 2) != JNI_OK) {
+ return NULL; // OOME has been thrown already
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ const char * name = _dyld_get_image_name(i);
+ if (name) {
+ jstring jName = (*env)->NewStringUTF(env, name);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ (*env)->SetObjectArrayElement(env, libsArray, i, jName);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ }
+ }
+
+ return libsArray;
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
index b829335fc299..d0b17940bf60 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
@@ -28,7 +28,9 @@
extern NSString *const JavaAccessibilityIgnore;
extern NSMutableDictionary *sRoles;
+extern NSMutableDictionary *sAnnouncePriorities;
extern void initializeRoles();
+extern void initializeAnnouncePriorities();
#define GET_CACCESSIBILITY_CLASS() \
GET_CLASS(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility");
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
index bf66df162d13..8094f7faa633 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
@@ -24,6 +24,7 @@
*/
#import "JavaAccessibilityUtilities.h"
+#import "sun_swing_AccessibleAnnouncer.h"
#import "JNIUtilities.h"
#import