Compare commits

..

64 Commits

Author SHA1 Message Date
Vitaly Provodin
799b46db7d port dcevm fixes for JBR-3867 and JBR-3887
JBR-3867 update keys of jvmti TAG map after redefinition

JBR-3887 fix M1 dcevm SIGBUS crash by using correct var arg processing
2021-10-23 08:32:07 +07:00
Anton Tarasov
172b3a6400 include a11y test into regular runs after fixing JBR-3730 & JBR-3733 2021-10-23 08:32:02 +07:00
Vitaly Provodin
9fa58395c9 JBR-3902 create jbr_all test group 2021-10-23 08:31:59 +07:00
Vitaly Provodin
18e1552b9d 8267887 exclude javax/management/remote/mandatory/connection/RMIConnector_NPETest.java 2021-10-22 19:32:10 +07:00
Thejasvi Voniadka
7714eca9e2 8270280: security/infra/java/security/cert/CertPathValidator/certification/LetsEncryptCA.java OCSP response error
Backport-of: f4b3ee5dca
(cherry picked from commit e9fd4b9fc4)
2021-10-22 19:32:10 +07:00
Alexander Zuev
5b559a8764 8269269: [macos11] SystemIconTest fails with ClassCastException
Reviewed-by: jdv
(cherry picked from commit eab959cbfa)
2021-10-22 19:32:09 +07:00
Thomas Schatzl
34b306300d 8263461: jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java uses wrong mechanism to cause evacuation failure
Reviewed-by: kbarrett, iwalulya, ayang

(cherry-picked from commit ee0247f056)
2021-10-22 19:32:09 +07:00
Alexander Lobas
0cf6d2da8d JBR-2893 Big Sur: Add support of opening project as tabs IDEA-257932 Big Sur: IDEA hangs after closing a project tab after exiting and entering full screen
Converted JNF to JNIUtilites

(cherry picked from commit f02e31a440)
(cherry-picked from commit a84736ebcc)
2021-10-21 14:10:37 +02:00
Artem Semenov
67ab7032c8 JBR-3881 8271071: accessibility of a table on macOS lacks cell navigation
Reviewed-by: ant, kizune, pbansal
2021-10-18 11:06:11 +07:00
Alexey Ushakov
cd4ef878a0 JBR-3843 IDE text is misaligned vertically when using Consolas font
Use usWinAscent/usWinDescent for metrics on Windows
2021-10-14 18:58:52 +02:00
Alexey Ushakov
5b43a3aef3 JBR-3872 [JBR17+Metal] Wrong color for scrollbars and inlay hints
Corrected typo in setTxtUniforms and fixed alpha blending in frag_gmc_text shader
2021-10-14 18:58:52 +02:00
Nikita Gubarkov
ce2a99a6ec JBR-3876 Changed visibility of FontUtilities#getSubpixelResolution and FontUtilities#subpixelResolution to package-private 2021-10-14 14:41:29 +03:00
Matthias Baesken
882d1064e7 JBR-3874 8274840: Update OS detection code to recognize Windows 11
Co-authored-by: Arno Zeller <arno.zeller@sap.com>
Reviewed-by: clanger, dholmes
(cherry picked from commit 51a590b2e8)
2021-10-14 15:47:51 +07:00
Nikita Gubarkov
45ca486b96 Fixed text spacing on macOS 2021-10-14 07:09:20 +03:00
Vitaly Provodin
a3bfa02ec0 move the script comparing performance results from jdk8u_test 2021-10-14 09:23:35 +07:00
Vitaly Provodin
a9ed023347 JBR-3167 exclude java/awt/Frame/HugeFrame/HugeFrame.java & sun/java2d/ClassCastExceptionForInvalidSurface.java crashing GNOME on Ubuntu-21.04 2021-10-13 10:00:52 +07:00
Maxim Kartashev
92c7e3a9c3 JBR-3871 Disable remote.x11.workaround by default
-Dremote.x11.workaround=true can still be used to enabled it, but
there's no automatic on switch anymore.

(cherry picked from commit fb12f6d7f1)
2021-10-12 00:59:17 -07:00
Vitaly Provodin
e1724ac304 exclude image/multiresolution/MultiresolutionIconTest.java and Window/GetScreenLocation/GetScreenLocationTest.java failing on linux 2021-10-10 05:50:35 +07:00
Nikita Gubarkov
51bb3ddb2f Fixed JBR API JTreg tests 2021-10-09 19:26:37 +03:00
Nikita Provotorov
10c7694d60 fixup! JBR-3838 AltGr on Polish keyboard triggers Ctrl+Alt shortcut.
Workaround for the regression test when it misses a focus for the awt.Component.
2021-10-06 18:09:50 +07:00
Maxim Kartashev
59ea9d809d JBR-3833 ServerSocketChannel.open(StandardProtocolFamily.UNIX) fails with IAE with non-standard filesystems on JBR17
Lazy-initialize the static member UNNAMED of UnixDomainSockets so that this
initialization doesn't throw unless actually used. This is only
necessary when using a non-default file system.
2021-10-05 16:00:27 +03:00
Alexey Ushakov
ab146e18bd JBR-3740 JBR17: Font metrics problem in text fields
Aligned code with openjdk (JDK-8263583)
2021-10-05 13:45:03 +02:00
Nikita Gubarkov
0ca9b169b0 Added JBR API 2021-10-05 02:21:20 +03:00
Maxim Kartashev
289f756f70 JBR-3835 Cropped messages in all Message Dialogs in Idea on Ubuntu 18.04.5 LTS with swing alerts enabled
The _NET_FRAME_EXTENTS property that is used to obtain the initial
insets of a dialog window does not immediately get its value and may be
returned as 0 if queried too soon after the window creation.

In order to avoid (incorrect) guessing of dialog's insets, make 3
attempts at getting the insets with a small but increasing pause
in between them.
2021-10-04 12:15:04 -07:00
Nikita Provotorov
ba84093c7d JBR-3838 AltGr on Polish keyboard triggers Ctrl+Alt shortcut.
Add regression test.

(cherry picked from commit 8df43eef4b)
2021-10-03 13:24:39 +07:00
Vitaly Provodin
46702cad3c fix name for -windows-test-x64 tarball 2021-10-03 05:44:17 +07:00
Artem Semenov
ac8ffa0ba7 8274381: missing CAccessibility definitions in JNI code
Reviewed-by: pbansal, ant, kizune
2021-10-02 08:55:36 +07:00
Alexey Ushakov
fae062d45f JBR-3820 Gamma correction for grayscale text in Metal rendering pipeline
Implemented gamma correction using the same approach that we did for OGL grayscale text rendering (OGLTextRenderer.c). Applied some optimisations to shader code.
2021-10-01 14:11:10 +02:00
Artem Semenov
0e87047b7e 8274383: JNI call of getAccessibleSelection on a wrong thread
Reviewed-by: kizune, ant
2021-09-30 16:17:18 +07:00
Vitaly Provodin
0106ef8fef add the link to the latest JBR release 2021-09-29 11:27:53 +07:00
Vitaly Provodin
a69031c54b a11y: exclude tests crashing JBR in the runs with enabled Voice Over 2021-09-29 05:49:39 +07:00
Nikita Gubarkov
8b96b1e115 Remove old JBR API 2021-09-29 00:04:48 +03:00
Maxim Kartashev
48186ebbfb JBR-3813 Regression after fix for JBR-3688
1. Cached bounds and insets must be cloned before return because they
aren't immutable objects.
2. Fixed the deadlock in resetBoundsCache() by synchronizing on a dedicated
lock.

(cherry picked from commit cd5314db8b)
2021-09-27 11:53:08 -07:00
Maxim Kartashev
6877bb6cd1 JBR-3680 Cherry-pick Google's NIO patches to get faster file listing
Fix regression introduced by Google's NIO patches:
- do not attempt to get the next entry after the directory stream has
been closed already,
- fix FaultyFileSystem that is used in StreamTest.java to throw
the right exception even when getFileAttributeView() is used instead of
readAttributes(),
- removed unnecessary type cast that caused a compilation warning.

Added a test for walking a directory with a non-latin name.

(cherry picked from commit 152a4e886d)
2021-09-24 16:50:53 +03:00
Lance Andersen
aab6c2301d 8273935: (zipfs) Files.getFileAttributeView() throws UOE instead of returning null when view not supported
Reviewed-by: alanb, bpb, sgehwolf

(Cherry-picked from 161fdb4afb)
(AKA JBR-3680 Cherry-pick Google's NIO patches to get faster file listing)
2021-09-24 16:50:53 +03:00
Renaud Paquay
8b63dd1c22 Add BasicWithKeyFileAttributeView interface
This new interface is similar to `BasicFileAttributeView` except it
gives implementations a hint that the fileKey() should be acquired
even at some performance cost.

`FileTreeWalker` uses this new interface to request a file key
in addition to regular file attributes so that file equality can
be efficiently performed when checking for loops during file
tree traversal.

This makes `FileTreeWalker` about 2x faster when traversing non
trivial file system trees with the FOLLOW_LINKS option.

Change-Id: I8de047c8fc241dbab9ad57c5e361118a3a94893d

(AKA JBR-3680 Cherry-pick Google's NIO patches to get faster file listing)

(cherry picked from commit 6d1c3f06c4)
2021-09-24 16:50:53 +03:00
Renaud Paquay
9baaee86b0 Improve performance of WindowsDirectoryStream
Use `NtQueryDirectoryInformation` instead of `FindFirst/FindNext` to
retrieve the list of entries of a directory.

`NtQueryDirectionInformation` has 2 main benefits over
`FindFist`/`FindNext`:

* Performance is about 40% faster

* Each retrieved entry retrieved contains a 64-bit `FileId` in addition
  to the usual attributes, ensuring that returned `java.nio.Path`
  instances hold onto a `BasicFileAttributes` instance that exposes a
  non-null `java.nio.file.attribute.BasicFileAttributes.fileKey()`.

This change also requires creating a new WindowsFileKey class, similar
to UnixFileKey class, so that
`java.nio.file.attribute.BasicFileAttributes.fileKey()` can return an
Object instance that can be used to compare files for equality.

With this change, the Windows implementation of Files.walkFileTree is
about 40% faster when the FOLLOW_LINKS option is not used, and about
2.5x faster when the FOLLOW_LINKS option is used.

When the FOLLOW_LINKS option is used, most calls to
`Files.isSameFile`, which is expensive as it requires 2 file I/O
operations, are avoided because the Path entries returned by the
new WindowsDirectoryStream implementation now contain a non-null
BasicFileAttributes.fileKey(). The remaining calls to
`Files.isSameFile` are performed when Files.walkFileTree need
to compare the initial directory with other entries.

Change-Id: Id79d89d477a6d5dcf151c63a9d6072c6f7ef43b2

(AKA JBR-3680 Cherry-pick Google's NIO patches to get faster file listing)

(cherry picked from commit 7c2d7541ba)
2021-09-24 16:50:53 +03:00
Artem Semenov
9e0ff20be5 8274056: JavaAccessibilityUtilities leaks JNI objects
Reviewed-by: aivanov, ant
2021-09-24 16:07:18 +07:00
Roger Riggs
f51e55a20f 8269850: Most JDK releases report macOS version 12 as 10.16 instead of 12.0
Reviewed-by: naoto, clanger
(cherry picked from commit 3b1b8fc646)
(AKA JBR-3804 Cherry-pick 8269850 from OpenJDK11)
2021-09-24 09:09:10 +03:00
Dmitry Batrak
e68d154a63 JBR-3799 Broken input of supplementary plane Unicode characters on macOS
apply upstream fix (JDK-8272602)

(cherry picked from commit 7d915be754)
2021-09-22 15:20:21 +03:00
Dmitry
b526f948ff JBR-3793 Accept unrecognized options and make them available in Java
This patch:

- Change default behavior to ignore unrecognized options (+IgnoreUnrecognizedVMOptions)

- Store all ignored values to: "java.vm.unrecognized.options" as "+BogusOption01 +BogusOption02" (space separated), see testcase

- Explicitly set -IgnoreUnrecognizedVMOptions for couple of tests, that rely on default behavior
2021-09-21 18:47:56 +03:00
Dmitry Batrak
fdbb6f02b8 improve the stability of TypeaheadRequestFocusTest
it failed sometimes under MATE desktop environment on Linux

(cherry picked from commit 37901295e1)
2021-09-21 13:04:10 +03:00
Ivan Migalev
6743f36c32 JBR-3785: don't touch the active keyboard layout on input method activation / deactivation.
origin PR: github.com/JetBrains/JetBrainsRuntime/pull/78.

(cherry picked from commit 2f772fd1a2)
2021-09-21 09:45:55 +01:00
Vitaly Provodin
f706b93717 exclude tests failing due to JDK-8253184 2021-09-21 12:04:20 +07:00
Dmitry Batrak
6f046e9f68 make NestedDialogHideTest more reliable
currently, if fails on KDE, if multiple workspaces are configured, because mouse cursor happens to be located over 'pin' button in window title bar,
and that hover causes a tooltip to be shown, which blocks mouse clicks

(cherry picked from commit ad1595b5c2)
2021-09-20 18:21:26 +03:00
Dmitry Batrak
bac641fe77 JBR-3786 javax/swing/plaf/aqua/CustomComboBoxFocusTest.java fails on MacOS by timeout
(cherry picked from commit f5c5388fb5)
2021-09-20 17:27:07 +03:00
Dmitry Batrak
8dfcb3fd5a JBR-3779 Unexpected Alt+Tab behaviour for Java frames on Cinnamon DE
(cherry picked from commit 0bf13985d5)
2021-09-20 12:27:42 +03:00
Maxim Kartashev
ef651ca1bb JBR-3688 PyCharm incredibly slow with fakexrandr
Cache screen bounds and insets and (conservatively) reset those caches
upon any possibility of a change.
This feature can be disabled with -Dx11.cache.screen.insets=false and
-Dx11.cache.screen.bounds=false.

Based on commit accef6f21e.
2021-09-20 10:55:38 +03:00
Maxim Kartashev
3092ca0461 JBR-3772 java/beans/PropertyEditor/TestFontClass.java: access denied ("java.util.PropertyPermission" "sun.awt.x11.trace" "read")
Instead of using System.getProperty() directly, wrap the call into
GetPropertyAction and use AccessController to execute it.
2021-09-16 09:19:54 +03:00
Dmitry Batrak
3caa06a639 JBR-3726 Modal windows 'disappear' on minimize in KDE
revert changes not needed anymore

(cherry picked from commit f0ed32fca4)
2021-09-14 18:41:53 +03:00
Dmitry Batrak
6c50ed6690 JBR-3726 Modal windows 'disappear' on minimize in KDE
re-implement the fix without using _NET_WM_STATE_MODAL - it causes other issues

(cherry picked from commit 5c4fd9ceaf)
2021-09-14 18:41:45 +03:00
Nikita Gubarkov
805a5b4f75 Add ExtendedGlyphCache and remove SampleJBRApi from JBR API 2021-09-13 16:47:25 +03:00
Artem Semenov
efabfd0370 8267385: Create NSAccessibilityElement implementation for JavaComponentAccessibility
8262031: Create implementation for NSAccessibilityNavigableStaticText protocol
8264287: Create implementation for NSAccessibilityComboBox protocol peer
8264303: Create implementation for NSAccessibilityTabGroup protocol peer
8264292: Create implementation for NSAccessibilityList protocol peer
8267387: Create implementation for NSAccessibilityOutline protocol
8267388: Create implementation for NSAccessibilityTable protocol
8264286: Create implementation for NSAccessibilityColumn protocol peer
8264298: Create implementation for NSAccessibilityRow protocol peer
8264291: Create implementation for NSAccessibilityCell protocol peer

Reviewed-by: kizune, pbansal, serb
2021-09-11 06:08:56 +07:00
Dmitry Batrak
f757a39090 JBR-3504 a11y focus is set on the wrong element when opening popups
(cherry-picked from commit a69e12e0d2)
2021-09-10 17:49:14 +03:00
Dmitry Batrak
1039653b97 JBR-3726 Modal windows 'disappear' on minimize in KDE
restrict change to KDE only, as it causes problems on GNOME (JBR-3750)

(cherry picked from commit 9c2841028f)
2021-09-10 17:49:09 +03:00
Maxim Kartashev
65fa801231 JBR-3665 Typing is slow in remote X session
Only call XGetKeyboardMapping() once for all valid codes and cache the
resulting table. Use the cache on the subsequent calls to
keycodeToKeysym().
2021-09-10 12:35:45 +03:00
Nikita Gubarkov
3d9ae4dbe8 JBR-3638 Adjust subpixel glyph positions for correct rounding in CStrike#getGlyphImageBounds 2021-09-08 04:23:24 +03:00
Maxim Kartashev
ef8e01b0d4 JBR-2273 JBR musl port
Detect if we're running on a musl-based system by checking for the presence
of the libgcompat.so glibc compatibility library in the process' map.
If so, java is re-started with LD_LIBRARY_PATH set to point to the right
directory with libjvm.so. This works around the problem with the musl
dynamic library loader.

(based on commit 13a904ddb5)
2021-09-07 15:02:17 +03:00
Alexey Ushakov
eaa9c1618e JBR-3727 JBR17-Metal: Flickering on tooltip appearance
Used setOpaque() method to set correct background of platform window
2021-09-03 15:14:58 +02:00
Maxim Kartashev
ee3c7edd84 JBR-3664 Logging for communications with X server
Introduced logging controlled with -Dsun.awt.x11.trace.
Currently, only looks at the AWT lock and reports methods holding it
sorted by average hold time.

(based on commit 792a58ea0e)
(based on commit 770b4dc9c1)
2021-09-01 23:02:37 -07:00
Dmitry Batrak
8a19c38728 JBR-3726 Modal windows 'disappear' on minimize in KDE
(cherry picked from commit d9baf2d9db)
2021-09-01 16:19:38 +03:00
Maxim Kartashev
4fcd80acf0 JBR-3712 Add project creation instructions to JBR README 2021-08-31 14:21:38 +03:00
Maxim Kartashev
6f5dd836de 8269223: -Xcheck:jni WARNINGs working with fonts on Linux
Reviewed-by: prr, serb

(AKA JBR-3542 Fix -Xcheck:jni warnings)
(Based on commit 9bc023220f, includes additional fixes for JBR-specific code)
2021-08-31 13:12:36 +03:00
Maxim Kartashev
2ff21b425e 8267307: Introduce new client property for XAWT: xawt.mwm_decor_title
Reviewed-by: azvegint, serb

(AKA JBR-3416 Introduce new client property for Linux: xawt.mwm_decor_title)
2021-08-30 06:50:11 -07:00
248 changed files with 16924 additions and 6244 deletions

View File

@@ -5,6 +5,14 @@
JetBrains Runtime is a fork of [OpenJDK](https://github.com/openjdk/jdk) available for Windows, Mac OS X, and Linux.
It includes a number enhancements in font rendering, HiDPI support, ligatures, performance improvements, and bugfixes.
## Releases
Download the latest releases of JetBrains Runtime to use with JetBrains IDEs. The full list
can be found on the [releases page](https://github.com/JetBrains/JetBrainsRuntime/releases).
| IDE Version | Latest JBR | Date Released |
| --- | --- | --- |
| 2021.3 | [17-b106.1](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr17b106.1) | 28-Sep-2021 |
## Contents
- [Welcome to JetBrains Runtime](#jetbrains-runtime)
- [Products Built on JetBrains Runtime](#products-built-on-jetbrains-runtime)
@@ -16,6 +24,7 @@ It includes a number enhancements in font rendering, HiDPI support, ligatures, p
- [Ubuntu Linux](#ubuntu-linux)
- [Windows](#build-windows)
- [macOS](#macos)
- [Developing](#developing)
- [Contributing](#contributing)
- [Resources](#resources)
@@ -130,6 +139,31 @@ $ make images
```
This will build the release configuration under `./build/macosx-x86_64-server-release/`.
## Developing
You can use [CLion](https://www.jetbrains.com/clion/) to develop native parts of the JetBrains Runtime and
[IntelliJ IDEA](https://www.jetbrains.com/idea/) for the parts written in Java.
Both require projects to be created.
### CLion
Run
```
$ make compile-commands
```
in the git root and open the resulting `build/.../compile_commands.json` file as a project.
Then use `Tools | Compilation Database | Change Project Root` to point to git root of this repository.
See also this detailed step-by-step tutorial for all platforms:
[How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
### IDEA
Run
```
$ sh ./bin/idea.sh
```
in the git root to generate project files (add `--help` for options). If you have multiple
configurations (for example, `release` and `fastdebug`), supply the `--conf <conf_name>` argument.
Then open the git root directory as a project in IDEA.
## Contributing
We are happy to receive your pull requests!
Before you submit one, please sign our [Contributor License Agreement (CLA)](https://www.jetbrains.com/agreements/cla/).

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/src/jetbrains.api">
<sourceFolder url="file://$MODULE_DIR$/src/jetbrains.api/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/jetbrains.api/templates" isTestSource="false" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="inheritedJdk" />
</component>
</module>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/jdk.iml" filepath="$PROJECT_DIR$/.idea/jdk.iml" />
###MODULE_IMLS###
<module fileurl="file://$PROJECT_DIR$/.idea/jetbrains.api.iml" filepath="$PROJECT_DIR$/.idea/jetbrains.api.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/test.iml" filepath="$PROJECT_DIR$/.idea/test.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,16 @@
#!/bin/sh
# $1 - Boot JDK
# $2 - JBR part of API version
cd "`dirname "$0"`/../../../../.."
PWD="`pwd`"
CONF="$PWD/build/jbr-api.conf"
./configure --with-debug-level=release --with-boot-jdk=$1 || exit $?
make jbr-api CONF=release MAKEOVERRIDES= "JBR_API_CONF_FILE=$CONF" JBR_API_JBR_VERSION=$2 || exit $?
. $CONF || exit $?
echo "##teamcity[buildNumber '$VERSION']"
cp "$JAR" ./jbr-api-${VERSION}.jar || exit $?
cp "$SOURCES_JAR" ./jbr-api-${VERSION}-sources.jar || exit $?
echo "##teamcity[publishArtifacts '$PWD/jbr-api-${VERSION}.jar']"
echo "##teamcity[publishArtifacts '$PWD/jbr-api-${VERSION}-sources.jar']"

View File

@@ -1,56 +0,0 @@
#!/bin/bash -x
# How to call this script:
# eval $(jb/project/tools/mkjbrapi.sh)
# It is used to build jetbrains.api module
# After properly calling this script, you can use following variables:
# JBR_API_JAR - absolute path to resulting JAR
# JBR_API_SOURCES_JAR - absolute path to JAR with sources
# JBR_API_VERSION - JBR API version in form <major>.<minor>
# JBR_API_VERSION_MAJOR, JBR_API_VERSION_MINOR - JBR API version components
# JBR_BUILD_DIR - absolute path to JBR build directory
# JBR_BOOT_JDK - absolute path to used boot JDK
ROOT=$(pwd)
sh configure --with-debug-level=release --disable-warnings-as-errors 1>&2 || exit $?
# Get boot JDK & build directory using make script
make -f $ROOT/make/JBRApi.gmk -I $ROOT jbr-api MAKEOVERRIDES= CONF=release OUT="$ROOT/build/jbr-api.cfg" 1>&2 || exit $?
source "$ROOT/build/jbr-api.cfg" || exit $?
# Build module
make jetbrains.api 1>&2 || exit $?
# Get JBR API version from compiled class
JSHELL_COMMAND='
System.out.println("\nVERSION_MAJOR=" + com.jetbrains.JBRApi.getMajorVersion());
System.out.println("\nVERSION_MINOR=" + com.jetbrains.JBRApi.getMinorVersion());
/exit'
VERSION_VARIABLES=$("$BOOT_JDK/bin/jshell" -s --module-path "$BUILD_DIR/jdk/modules/jetbrains.api" \
--add-modules jetbrains.api <<< "$JSHELL_COMMAND" | grep "^VERSION\|^|") || exit $?
eval "$VERSION_VARIABLES" || exit $?
# Create JAR
(
cd "$BUILD_DIR/jdk/modules/jetbrains.api"
"$BOOT_JDK/bin/jar" -cf "$BUILD_DIR/jbr-api.jar" * 1>&2
) || exit $?
# Create source JAR
(
cd "src/jetbrains.api/share/classes"
"$BOOT_JDK/bin/jar" -cf "$BUILD_DIR/jbr-api-sources.jar" * 1>&2
) || exit $?
# Print output values
echo "JBR_API_JAR=$BUILD_DIR/jbr-api.jar"
echo "JBR_API_SOURCES_JAR=$BUILD_DIR/jbr-api-sources.jar"
echo "JBR_API_VERSION=$VERSION_MAJOR.$VERSION_MINOR"
echo "JBR_API_VERSION_MAJOR=$VERSION_MAJOR"
echo "JBR_API_VERSION_MINOR=$VERSION_MINOR"
echo "JBR_BUILD_DIR=$BUILD_DIR"
echo "JBR_BOOT_JDK=$BOOT_JDK"
echo "Success!" 1>&2

View File

@@ -1,11 +1,11 @@
diff --git modules.list modules.list
index dcf610a6a56..f8797505c23 100644
index 054f21d1ee0..d9a121f0273 100644
--- modules.list
+++ modules.list
@@ -51,4 +51,7 @@ jdk.zipfs,
@@ -49,4 +49,7 @@ jdk.unsupported,
jdk.xml.dom,
jdk.zipfs,
jdk.hotspot.agent,
jetbrains.api,
jetbrains.api.impl,
-jdk.jcmd
+jdk.jcmd,
+jcef,

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
From d937dae078891013a644c6a53eb17f8f5deb8ec8 Mon Sep 17 00:00:00 2001
From 6e99aea309783dab138d3789b0039128bcc7344a Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Wed, 11 Mar 2020 14:19:34 +0100
Subject: [PATCH 03/39] Fix class cast exception on redefinition of class A,
Subject: [PATCH 03/42] Fix class cast exception on redefinition of class A,
that is superclass of B that has anonymous class C
---

View File

@@ -1,7 +1,7 @@
From 53d50b53f83aa5135ff5092d0d566424024b8b4b Mon Sep 17 00:00:00 2001
From 146b85fb6af764594999cb9922fd820798f3e9cc Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 4 Oct 2020 21:12:12 +0200
Subject: [PATCH 04/39] Support for Lambda class redefinition
Subject: [PATCH 04/42] Support for Lambda class redefinition
---
.../share/classfile/classLoaderData.cpp | 9 +++
@@ -80,114 +80,114 @@ index 2bfd9cb802f..cea614a574f 100644
// Hidden classes that are not strong must update ClassLoaderData holder
// so that they can be unloaded when the mirror is no longer referenced.
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 5551d3ca123..bf31819479d 100644
index 5308b079e67..c1b1b354541 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -492,6 +492,8 @@ void VM_EnhancedRedefineClasses::doit() {
ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
+ // SystemDictionary::methods_do(fix_invoke_method);
+
// JSR-292 support
if (_any_class_has_resolved_methods) {
bool trace_name_printed = false;
ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
+ // SystemDictionary::methods_do(fix_invoke_method);
+
// JSR-292 support
if (_any_class_has_resolved_methods) {
bool trace_name_printed = false;
@@ -754,12 +756,34 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
// load hook event.
state->set_class_being_redefined(the_class, _class_load_kind);
- InstanceKlass* k = SystemDictionary::resolve_from_stream(the_class_sym,
- the_class_loader,
- protection_domain,
- &st,
- the_class,
- THREAD);
+ InstanceKlass* k;
+
+ if (InstanceKlass::cast(the_class)->is_anonymous()) {
+ const InstanceKlass* host_class = the_class->host_klass();
+
+ // Make sure it's the real host class, not another anonymous class.
+ while (host_class != NULL && host_class->is_anonymous()) {
+ host_class = host_class->host_klass();
+ }
+
+ k = SystemDictionary::parse_stream(the_class_sym,
+ the_class_loader,
+ protection_domain,
+ &st,
+ host_class,
+ the_class,
+ NULL,
+ THREAD);
+ k->class_loader_data()->exchange_holders(the_class->class_loader_data());
+ the_class->class_loader_data()->inc_keep_alive();
+ } else {
+ k = SystemDictionary::resolve_from_stream(the_class_sym,
+ the_class_loader,
+ protection_domain,
+ &st,
+ the_class,
+ THREAD);
+ }
// Clear class_being_redefined just to be sure.
state->clear_class_being_redefined();
// load hook event.
state->set_class_being_redefined(the_class, _class_load_kind);
- InstanceKlass* k = SystemDictionary::resolve_from_stream(the_class_sym,
- the_class_loader,
- protection_domain,
- &st,
- the_class,
- THREAD);
+ InstanceKlass* k;
+
+ if (InstanceKlass::cast(the_class)->is_anonymous()) {
+ const InstanceKlass* host_class = the_class->host_klass();
+
+ // Make sure it's the real host class, not another anonymous class.
+ while (host_class != NULL && host_class->is_anonymous()) {
+ host_class = host_class->host_klass();
+ }
+
+ k = SystemDictionary::parse_stream(the_class_sym,
+ the_class_loader,
+ protection_domain,
+ &st,
+ host_class,
+ the_class,
+ NULL,
+ THREAD);
+ k->class_loader_data()->exchange_holders(the_class->class_loader_data());
+ the_class->class_loader_data()->inc_keep_alive();
+ } else {
+ k = SystemDictionary::resolve_from_stream(the_class_sym,
+ the_class_loader,
+ protection_domain,
+ &st,
+ the_class,
+ THREAD);
+ }
// Clear class_being_redefined just to be sure.
state->clear_class_being_redefined();
@@ -1440,6 +1464,30 @@ void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
}
}
+void VM_EnhancedRedefineClasses::fix_invoke_method(Method* method) {
+
+ constantPoolHandle other_cp = constantPoolHandle(method->constants());
+
+ for (int i = 0; i < other_cp->length(); i++) {
+ if (other_cp->tag_at(i).is_klass()) {
+ Klass* klass = other_cp->resolved_klass_at(i);
+ if (klass->new_version() != NULL) {
+ // Constant pool entry points to redefined class -- update to the new version
+ other_cp->klass_at_put(i, klass->newest_version());
+ }
+ assert(other_cp->resolved_klass_at(i)->new_version() == NULL, "Must be new klass!");
+ }
+ }
+
+ ConstantPoolCache* cp_cache = other_cp->cache();
+ if (cp_cache != NULL) {
+ cp_cache->clear_entries();
+ }
+
+}
+
+
+
void VM_EnhancedRedefineClasses::update_jmethod_ids() {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
}
}
+void VM_EnhancedRedefineClasses::fix_invoke_method(Method* method) {
+
+ constantPoolHandle other_cp = constantPoolHandle(method->constants());
+
+ for (int i = 0; i < other_cp->length(); i++) {
+ if (other_cp->tag_at(i).is_klass()) {
+ Klass* klass = other_cp->resolved_klass_at(i);
+ if (klass->new_version() != NULL) {
+ // Constant pool entry points to redefined class -- update to the new version
+ other_cp->klass_at_put(i, klass->newest_version());
+ }
+ assert(other_cp->resolved_klass_at(i)->new_version() == NULL, "Must be new klass!");
+ }
+ }
+
+ ConstantPoolCache* cp_cache = other_cp->cache();
+ if (cp_cache != NULL) {
+ cp_cache->clear_entries();
+ }
+
+}
+
+
+
void VM_EnhancedRedefineClasses::update_jmethod_ids() {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
@@ -1977,7 +2025,10 @@ jvmtiError VM_EnhancedRedefineClasses::find_sorted_affected_classes(TRAPS) {
// Find classes not directly redefined, but affected by a redefinition (because one of its supertypes is redefined)
AffectedKlassClosure closure(_affected_klasses);
// Updated in j10, from original SystemDictionary::classes_do
- ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
+ ClassLoaderDataGraph::classes_do(&closure);
+ //ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
// Sort the affected klasses such that a supertype is always on a smaller array index than its subtype.
// Find classes not directly redefined, but affected by a redefinition (because one of its supertypes is redefined)
AffectedKlassClosure closure(_affected_klasses);
// Updated in j10, from original SystemDictionary::classes_do
- ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
+ ClassLoaderDataGraph::classes_do(&closure);
+ //ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
// Sort the affected klasses such that a supertype is always on a smaller array index than its subtype.
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index 60b62c3170a..d8a11b51fe9 100644
index a48e07e3a6a..3551b06ecde 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -116,6 +116,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
void rollback();
static void mark_as_scavengable(nmethod* nm);
static void unpatch_bytecode(Method* method);
+ static void fix_invoke_method(Method* method);
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void rollback();
static void mark_as_scavengable(nmethod* nm);
static void unpatch_bytecode(Method* method);
+ static void fix_invoke_method(Method* method);
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
diff --git a/src/hotspot/share/prims/resolvedMethodTable.cpp b/src/hotspot/share/prims/resolvedMethodTable.cpp
index 6a8128e844f..8644937dbbb 100644
--- a/src/hotspot/share/prims/resolvedMethodTable.cpp

View File

@@ -1,7 +1,7 @@
From 7bb6ea77608cd43cb7ca8c1ea8d492ae07789a0f Mon Sep 17 00:00:00 2001
From 57d2451903b84dfc772cc0bf6957a52ee5e8c0f3 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 23 May 2020 10:02:15 +0200
Subject: [PATCH 05/39] Fix "no original bytecode found" error if method with
Subject: [PATCH 05/42] Fix "no original bytecode found" error if method with
bkp is missing
Sometimes IDE can deploy class with erroneous method, such method has
@@ -102,34 +102,34 @@ index 030ddd1f675..a8d6507ff6c 100644
void set_breakpoint(int bci);
void clear_breakpoint(int bci);
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index bf31819479d..07935bd0ada 100644
index c1b1b354541..ad05b4f34cf 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -1360,14 +1360,16 @@ void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
if (code == Bytecodes::_breakpoint) {
int bci = method->bci_from(bcp);
- code = method->orig_bytecode_at(bci);
- java_code = Bytecodes::java_code(code);
- if (code != java_code &&
- (java_code == Bytecodes::_getfield ||
- java_code == Bytecodes::_putfield ||
- java_code == Bytecodes::_aload_0)) {
- // Let breakpoint table handling unpatch bytecode
- method->set_orig_bytecode_at(bci, java_code);
+ code = method->orig_bytecode_at(bci, true);
+ if (code != Bytecodes::_shouldnotreachhere) {
+ java_code = Bytecodes::java_code(code);
+ if (code != java_code &&
+ (java_code == Bytecodes::_getfield ||
+ java_code == Bytecodes::_putfield ||
+ java_code == Bytecodes::_aload_0)) {
+ // Let breakpoint table handling unpatch bytecode
+ method->set_orig_bytecode_at(bci, java_code);
+ }
}
} else {
java_code = Bytecodes::java_code(code);
if (code == Bytecodes::_breakpoint) {
int bci = method->bci_from(bcp);
- code = method->orig_bytecode_at(bci);
- java_code = Bytecodes::java_code(code);
- if (code != java_code &&
- (java_code == Bytecodes::_getfield ||
- java_code == Bytecodes::_putfield ||
- java_code == Bytecodes::_aload_0)) {
- // Let breakpoint table handling unpatch bytecode
- method->set_orig_bytecode_at(bci, java_code);
+ code = method->orig_bytecode_at(bci, true);
+ if (code != Bytecodes::_shouldnotreachhere) {
+ java_code = Bytecodes::java_code(code);
+ if (code != java_code &&
+ (java_code == Bytecodes::_getfield ||
+ java_code == Bytecodes::_putfield ||
+ java_code == Bytecodes::_aload_0)) {
+ // Let breakpoint table handling unpatch bytecode
+ method->set_orig_bytecode_at(bci, java_code);
+ }
}
} else {
java_code = Bytecodes::java_code(code);
--
2.23.0

View File

@@ -1,7 +1,7 @@
From c40cd307310822e6e60c61931c14f97a8501f975 Mon Sep 17 00:00:00 2001
From d606e7de6dfe1204128a8ac034d8f57ad6130e0a Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 24 May 2020 12:07:42 +0200
Subject: [PATCH 06/39] Replace deleted method with
Subject: [PATCH 06/42] Replace deleted method with
Universe::throw_no_such_method_error
+ Change log level in advanced redefinition
@@ -13,18 +13,18 @@ Subject: [PATCH 06/39] Replace deleted method with
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 07935bd0ada..3c86e8c68ac 100644
index ad05b4f34cf..95be32f0070 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -881,7 +881,7 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
// Calculated the difference between new and old class (field change, method change, supertype change, ...).
int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_class) {
int result = Klass::NoRedefinition;
- log_info(redefine, class, load)("Comparing different class versions of class %s",new_class->name()->as_C_string());
+ log_debug(redefine, class, load)("Comparing different class versions of class %s",new_class->name()->as_C_string());
assert(new_class->old_version() != NULL, "must have old version");
InstanceKlass* the_class = InstanceKlass::cast(new_class->old_version());
// Calculated the difference between new and old class (field change, method change, supertype change, ...).
int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_class) {
int result = Klass::NoRedefinition;
- log_info(redefine, class, load)("Comparing different class versions of class %s",new_class->name()->as_C_string());
+ log_debug(redefine, class, load)("Comparing different class versions of class %s",new_class->name()->as_C_string());
assert(new_class->old_version() != NULL, "must have old version");
InstanceKlass* the_class = InstanceKlass::cast(new_class->old_version());
diff --git a/src/hotspot/share/prims/resolvedMethodTable.cpp b/src/hotspot/share/prims/resolvedMethodTable.cpp
index 8644937dbbb..b8d039adff6 100644
--- a/src/hotspot/share/prims/resolvedMethodTable.cpp

View File

@@ -1,7 +1,7 @@
From a3775e77a2b4c5ec15051d5cfbf150fc67719d90 Mon Sep 17 00:00:00 2001
From 2365d0dc56b6b28ee3355e89ecf981a42fdfe440 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Jun 2020 17:43:52 +0200
Subject: [PATCH 07/39] Support for G1 gc
Subject: [PATCH 07/42] Support for G1 gc
---
src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 23 +++
@@ -1032,258 +1032,258 @@ index 8091ab2808d..9fcbb6c41b3 100644
SystemDictionary::oops_do(oopClosure);
}
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 3c86e8c68ac..1da6661dd3e 100644
index 95be32f0070..2679d3a556a 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -36,7 +36,6 @@
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
-#include "gc/serial/markSweep.hpp"
#include "oops/fieldStreams.hpp"
#include "oops/klassVtable.hpp"
#include "oops/oop.inline.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
-#include "gc/serial/markSweep.hpp"
#include "oops/fieldStreams.hpp"
#include "oops/klassVtable.hpp"
#include "oops/oop.inline.hpp"
@@ -54,6 +53,8 @@
#include "prims/jvmtiThreadState.inline.hpp"
#include "utilities/events.hpp"
#include "oops/constantPool.inline.hpp"
+#include "gc/g1/g1CollectedHeap.hpp"
+#include "gc/shared/dcevmSharedGC.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
Array<Method*>* VM_EnhancedRedefineClasses::_new_methods = NULL;
#include "prims/jvmtiThreadState.inline.hpp"
#include "utilities/events.hpp"
#include "oops/constantPool.inline.hpp"
+#include "gc/g1/g1CollectedHeap.hpp"
+#include "gc/shared/dcevmSharedGC.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
Array<Method*>* VM_EnhancedRedefineClasses::_new_methods = NULL;
@@ -77,7 +78,7 @@ Klass* VM_EnhancedRedefineClasses::_the_class_oop = NULL;
// - class_defs class definition - either new class or redefined class
// note that this is not the final array of classes to be redefined
// we need to scan for all affected classes (e.g. subclasses) and
-// caculcate redefinition for them as well.
+// calculate redefinition for them as well.
// @param class_load_kind always jvmti_class_load_kind_redefine
VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind) :
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
// - class_defs class definition - either new class or redefined class
// note that this is not the final array of classes to be redefined
// we need to scan for all affected classes (e.g. subclasses) and
-// caculcate redefinition for them as well.
+// calculate redefinition for them as well.
// @param class_load_kind always jvmti_class_load_kind_redefine
VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind) :
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
@@ -215,6 +216,20 @@ void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
}
}
+void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
+ // It should work not only for G1 but also for another GCs, but this way is safer now
+ if (!nm->is_zombie() && !nm->is_unloaded()) {
+ Universe::heap()->unregister_nmethod(nm);
+ }
+}
+
+void VM_EnhancedRedefineClasses::register_nmethod_g1(nmethod* nm) {
+ // It should work not only for G1 but also for another GCs, but this way is safer now
+ if (!nm->is_zombie() && !nm->is_unloaded()) {
+ Universe::heap()->register_nmethod(nm);
+ }
+}
+
// TODO comment
struct StoreBarrier {
// TODO: j10 review change ::oop_store -> HeapAccess<>::oop_store
}
}
+void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
+ // It should work not only for G1 but also for another GCs, but this way is safer now
+ if (!nm->is_zombie() && !nm->is_unloaded()) {
+ Universe::heap()->unregister_nmethod(nm);
+ }
+}
+
+void VM_EnhancedRedefineClasses::register_nmethod_g1(nmethod* nm) {
+ // It should work not only for G1 but also for another GCs, but this way is safer now
+ if (!nm->is_zombie() && !nm->is_unloaded()) {
+ Universe::heap()->register_nmethod(nm);
+ }
+}
+
// TODO comment
struct StoreBarrier {
// TODO: j10 review change ::oop_store -> HeapAccess<>::oop_store
@@ -429,12 +444,7 @@ public:
src->set_klass(obj->klass()->new_version());
// FIXME: instance updates...
//guarantee(false, "instance updates!");
- MarkSweep::update_fields(obj, src, new_klass->update_information());
-
- if (size_diff > 0) {
- HeapWord* dead_space = ((HeapWord *)obj) + obj->size();
- CollectedHeap::fill_with_object(dead_space, size_diff);
- }
+ DcevmSharedGC::update_fields(obj, src, new_klass->update_information());
}
} else {
obj->set_klass(obj->klass()->new_version());
src->set_klass(obj->klass()->new_version());
// FIXME: instance updates...
//guarantee(false, "instance updates!");
- MarkSweep::update_fields(obj, src, new_klass->update_information());
-
- if (size_diff > 0) {
- HeapWord* dead_space = ((HeapWord *)obj) + obj->size();
- CollectedHeap::fill_with_object(dead_space, size_diff);
- }
+ DcevmSharedGC::update_fields(obj, src, new_klass->update_information());
}
} else {
obj->set_klass(obj->klass()->new_version());
@@ -456,6 +466,10 @@ public:
void VM_EnhancedRedefineClasses::doit() {
Thread *thread = Thread::current();
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_vm_op_doit.start();
+ }
+
#if INCLUDE_CDS
if (UseSharedSpaces) {
// Sharing is enabled so we remap the shared readonly space to
void VM_EnhancedRedefineClasses::doit() {
Thread *thread = Thread::current();
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_vm_op_doit.start();
+ }
+
#if INCLUDE_CDS
if (UseSharedSpaces) {
// Sharing is enabled so we remap the shared readonly space to
@@ -511,12 +525,37 @@ void VM_EnhancedRedefineClasses::doit() {
// mark such nmethod's as "scavengable".
// For now, mark all nmethod's as scavengable that are not scavengable already
if (ScavengeRootsInCode) {
- CodeCache::nmethods_do(mark_as_scavengable);
+ if (UseG1GC) {
+ // G1 holds references to nmethods in regions based on oops values. Since oops in nmethod can be changed in ChangePointers* closures
+ // we unregister nmethods from G1 heap, then closures are processed (oops are changed) and finally we register nmethod to G1 again
+ CodeCache::nmethods_do(unregister_nmethod_g1);
+ } else {
+ CodeCache::nmethods_do(mark_as_scavengable);
+ }
}
Universe::heap()->ensure_parsability(false);
- Universe::heap()->object_iterate(&objectClosure);
+ if (UseG1GC) {
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_iterate.start();
+ }
+ G1CollectedHeap::heap()->object_par_iterate(&objectClosure);
+ _timer_heap_iterate.stop();
+ } else {
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_iterate.start();
+ }
+ Universe::heap()->object_iterate(&objectClosure);
+ _timer_heap_iterate.stop();
+ }
+
Universe::root_oops_do(&oopClosureNoBarrier);
+
+ if (UseG1GC) {
+ // this should work also for other GCs
+ CodeCache::nmethods_do(register_nmethod_g1);
+ }
+
}
log_trace(redefine, class, obsolete, metadata)("After updating instances");
// mark such nmethod's as "scavengable".
// For now, mark all nmethod's as scavengable that are not scavengable already
if (ScavengeRootsInCode) {
- CodeCache::nmethods_do(mark_as_scavengable);
+ if (UseG1GC) {
+ // G1 holds references to nmethods in regions based on oops values. Since oops in nmethod can be changed in ChangePointers* closures
+ // we unregister nmethods from G1 heap, then closures are processed (oops are changed) and finally we register nmethod to G1 again
+ CodeCache::nmethods_do(unregister_nmethod_g1);
+ } else {
+ CodeCache::nmethods_do(mark_as_scavengable);
+ }
}
Universe::heap()->ensure_parsability(false);
- Universe::heap()->object_iterate(&objectClosure);
+ if (UseG1GC) {
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_iterate.start();
+ }
+ G1CollectedHeap::heap()->object_par_iterate(&objectClosure);
+ _timer_heap_iterate.stop();
+ } else {
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_iterate.start();
+ }
+ Universe::heap()->object_iterate(&objectClosure);
+ _timer_heap_iterate.stop();
+ }
+
Universe::root_oops_do(&oopClosureNoBarrier);
+
+ if (UseG1GC) {
+ // this should work also for other GCs
+ CodeCache::nmethods_do(register_nmethod_g1);
+ }
+
}
log_trace(redefine, class, obsolete, metadata)("After updating instances");
@@ -569,11 +608,19 @@ void VM_EnhancedRedefineClasses::doit() {
if (objectClosure.needs_instance_update()) {
// Do a full garbage collection to update the instance sizes accordingly
+
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_full_gc.start();
+ }
+
Universe::set_redefining_gc_run(true);
notify_gc_begin(true);
+ // TODO: check _metadata_GC_clear_soft_refs with ScavengeRootsInCode
Universe::heap()->collect_as_vm_thread(GCCause::_heap_inspection);
notify_gc_end();
Universe::set_redefining_gc_run(false);
+
+ _timer_heap_full_gc.stop();
}
// Unmark Klass*s as "redefining"
if (objectClosure.needs_instance_update()) {
// Do a full garbage collection to update the instance sizes accordingly
+
+ if (log_is_enabled(Info, redefine, class, timer)) {
+ _timer_heap_full_gc.start();
+ }
+
Universe::set_redefining_gc_run(true);
notify_gc_begin(true);
+ // TODO: check _metadata_GC_clear_soft_refs with ScavengeRootsInCode
Universe::heap()->collect_as_vm_thread(GCCause::_heap_inspection);
notify_gc_end();
Universe::set_redefining_gc_run(false);
+
+ _timer_heap_full_gc.stop();
}
// Unmark Klass*s as "redefining"
@@ -621,6 +668,7 @@ void VM_EnhancedRedefineClasses::doit() {
}
#endif
+ _timer_vm_op_doit.stop();
}
// Cleanup - runs in JVM thread
}
#endif
+ _timer_vm_op_doit.stop();
}
// Cleanup - runs in JVM thread
@@ -644,16 +692,14 @@ void VM_EnhancedRedefineClasses::doit_epilogue() {
if (log_is_enabled(Info, redefine, class, timer)) {
// Used to have separate timers for "doit" and "all", but the timer
// overhead skewed the measurements.
- jlong doit_time = _timer_rsc_phase1.milliseconds() +
- _timer_rsc_phase2.milliseconds();
- jlong all_time = _timer_vm_op_prologue.milliseconds() + doit_time;
+ jlong all_time = _timer_vm_op_prologue.milliseconds() + _timer_vm_op_doit.milliseconds();
log_info(redefine, class, timer)
("vm_op: all=" JLONG_FORMAT " prologue=" JLONG_FORMAT " doit=" JLONG_FORMAT,
- all_time, _timer_vm_op_prologue.milliseconds(), doit_time);
+ all_time, _timer_vm_op_prologue.milliseconds(), _timer_vm_op_doit.milliseconds());
log_info(redefine, class, timer)
- ("redefine_single_class: phase1=" JLONG_FORMAT " phase2=" JLONG_FORMAT,
- _timer_rsc_phase1.milliseconds(), _timer_rsc_phase2.milliseconds());
+ ("doit: heap iterate=" JLONG_FORMAT " fullgc=" JLONG_FORMAT,
+ _timer_heap_iterate.milliseconds(), _timer_heap_full_gc.milliseconds());
}
}
if (log_is_enabled(Info, redefine, class, timer)) {
// Used to have separate timers for "doit" and "all", but the timer
// overhead skewed the measurements.
- jlong doit_time = _timer_rsc_phase1.milliseconds() +
- _timer_rsc_phase2.milliseconds();
- jlong all_time = _timer_vm_op_prologue.milliseconds() + doit_time;
+ jlong all_time = _timer_vm_op_prologue.milliseconds() + _timer_vm_op_doit.milliseconds();
log_info(redefine, class, timer)
("vm_op: all=" JLONG_FORMAT " prologue=" JLONG_FORMAT " doit=" JLONG_FORMAT,
- all_time, _timer_vm_op_prologue.milliseconds(), doit_time);
+ all_time, _timer_vm_op_prologue.milliseconds(), _timer_vm_op_doit.milliseconds());
log_info(redefine, class, timer)
- ("redefine_single_class: phase1=" JLONG_FORMAT " phase2=" JLONG_FORMAT,
- _timer_rsc_phase1.milliseconds(), _timer_rsc_phase2.milliseconds());
+ ("doit: heap iterate=" JLONG_FORMAT " fullgc=" JLONG_FORMAT,
+ _timer_heap_iterate.milliseconds(), _timer_heap_full_gc.milliseconds());
}
}
@@ -1402,7 +1448,7 @@ void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp.
void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
if (!k->is_instance_klass()) {
- return;
+ return;
}
HandleMark hm(_thread);
// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp.
void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
if (!k->is_instance_klass()) {
- return;
+ return;
}
HandleMark hm(_thread);
@@ -1588,7 +1634,7 @@ class TransferNativeFunctionRegistration {
// Recursively search the binary tree of possibly prefixed method names.
// Iteration could be used if all agents were well behaved. Full tree walk is
- // more resilent to agents not cleaning up intermediate methods.
+ // more resilient to agents not cleaning up intermediate methods.
// Branch at each depth in the binary tree is:
// (1) without the prefix.
// (2) with the prefix.
// Recursively search the binary tree of possibly prefixed method names.
// Iteration could be used if all agents were well behaved. Full tree walk is
- // more resilent to agents not cleaning up intermediate methods.
+ // more resilient to agents not cleaning up intermediate methods.
// Branch at each depth in the binary tree is:
// (1) without the prefix.
// (2) with the prefix.
@@ -1693,7 +1739,7 @@ void VM_EnhancedRedefineClasses::transfer_old_native_function_registrations(Inst
transfer.transfer_registrations(_matching_old_methods, _matching_methods_length);
}
-// DCEVM - it always deoptimases everything! (because it is very difficult to find only correct dependencies)
+// DCEVM - it always deoptimizes everything! (because it is very difficult to find only correct dependencies)
// Deoptimize all compiled code that depends on this class.
//
// If the can_redefine_classes capability is obtained in the onload
transfer.transfer_registrations(_matching_old_methods, _matching_methods_length);
}
-// DCEVM - it always deoptimases everything! (because it is very difficult to find only correct dependencies)
+// DCEVM - it always deoptimizes everything! (because it is very difficult to find only correct dependencies)
// Deoptimize all compiled code that depends on this class.
//
// If the can_redefine_classes capability is obtained in the onload
@@ -1820,10 +1866,6 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
HandleMark hm(THREAD); // make sure handles from this call are freed
- if (log_is_enabled(Info, redefine, class, timer)) {
- _timer_rsc_phase1.start();
- }
-
InstanceKlass* new_class = new_class_oop;
InstanceKlass* the_class = InstanceKlass::cast(new_class_oop->old_version());
assert(the_class != NULL, "must have old version");
HandleMark hm(THREAD); // make sure handles from this call are freed
- if (log_is_enabled(Info, redefine, class, timer)) {
- _timer_rsc_phase1.start();
- }
-
InstanceKlass* new_class = new_class_oop;
InstanceKlass* the_class = InstanceKlass::cast(new_class_oop->old_version());
assert(the_class != NULL, "must have old version");
@@ -1878,7 +1920,6 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
new_class->external_name(),
java_lang_Class::classRedefinedCount(new_class->java_mirror()));
}
- _timer_rsc_phase2.stop();
} // end redefine_single_class()
new_class->external_name(),
java_lang_Class::classRedefinedCount(new_class->java_mirror()));
}
- _timer_rsc_phase2.stop();
} // end redefine_single_class()
@@ -2061,8 +2102,8 @@ static bool match_second(void* value, KlassPair elem) {
// For each class to be redefined parse the bytecode and figure out the superclass and all interfaces.
// First newly introduced classes (_class_defs) are scanned and then affected classed (_affected_klasses).
// Affected flag is cleared (clear_redefinition_flag(Klass::MarkedAsAffected))
-// For each dependency create a KlassPair instance. Finnaly, affected classes (_affected_klasses) are sorted according to pairs.
-// TODO - the class file is potentionally parsed multiple times - introduce a cache?
+// For each dependency create a KlassPair instance. Finally, affected classes (_affected_klasses) are sorted according to pairs.
+// TODO - the class file is potentially parsed multiple times - introduce a cache?
jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(TRAPS) {
ResourceMark mark(THREAD);
// For each class to be redefined parse the bytecode and figure out the superclass and all interfaces.
// First newly introduced classes (_class_defs) are scanned and then affected classed (_affected_klasses).
// Affected flag is cleared (clear_redefinition_flag(Klass::MarkedAsAffected))
-// For each dependency create a KlassPair instance. Finnaly, affected classes (_affected_klasses) are sorted according to pairs.
-// TODO - the class file is potentionally parsed multiple times - introduce a cache?
+// For each dependency create a KlassPair instance. Finally, affected classes (_affected_klasses) are sorted according to pairs.
+// TODO - the class file is potentially parsed multiple times - introduce a cache?
jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(TRAPS) {
ResourceMark mark(THREAD);
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index d8a11b51fe9..4c0412d343d 100644
index 3551b06ecde..d00109a0b92 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -86,9 +86,10 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
// Performance measurement support. These timers do not cover all
// the work done for JVM/TI RedefineClasses() but they do cover
// the heavy lifting.
- elapsedTimer _timer_rsc_phase1;
- elapsedTimer _timer_rsc_phase2;
+ elapsedTimer _timer_vm_op_doit;
elapsedTimer _timer_vm_op_prologue;
+ elapsedTimer _timer_heap_iterate;
+ elapsedTimer _timer_heap_full_gc;
// These routines are roughly in call order unless otherwise noted.
// Performance measurement support. These timers do not cover all
// the work done for JVM/TI RedefineClasses() but they do cover
// the heavy lifting.
- elapsedTimer _timer_rsc_phase1;
- elapsedTimer _timer_rsc_phase2;
+ elapsedTimer _timer_vm_op_doit;
elapsedTimer _timer_vm_op_prologue;
+ elapsedTimer _timer_heap_iterate;
+ elapsedTimer _timer_heap_full_gc;
// These routines are roughly in call order unless otherwise noted.
@@ -115,6 +116,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
void rollback();
static void mark_as_scavengable(nmethod* nm);
+ static void unregister_nmethod_g1(nmethod* nm);
+ static void register_nmethod_g1(nmethod* nm);
static void unpatch_bytecode(Method* method);
static void fix_invoke_method(Method* method);
void rollback();
static void mark_as_scavengable(nmethod* nm);
+ static void unregister_nmethod_g1(nmethod* nm);
+ static void register_nmethod_g1(nmethod* nm);
static void unpatch_bytecode(Method* method);
static void fix_invoke_method(Method* method);
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 1373408f18d..b7bb88e2a46 100644
index 11e7997b4a6..2d33448ec8d 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -1981,9 +1981,9 @@ bool Arguments::check_gc_consistency() {
@@ -1985,9 +1985,9 @@ bool Arguments::check_gc_consistency() {
if (AllowEnhancedClassRedefinition) {
// Must use serial GC. This limitation applies because the instance size changing GC modifications
// are only built into the mark and compact algorithm.

View File

@@ -1,7 +1,7 @@
From e062743b148a099a8593a3110d5f1d9156f4ca23 Mon Sep 17 00:00:00 2001
From e818e4a83fafc07142b7b7279c633823d5d8ab23 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Tue, 6 Oct 2020 22:15:31 +0200
Subject: [PATCH 08/39] AllowEnhancedClassRedefinition is false (disabled) by
Subject: [PATCH 08/42] AllowEnhancedClassRedefinition is false (disabled) by
default
---
@@ -9,7 +9,7 @@ Subject: [PATCH 08/39] AllowEnhancedClassRedefinition is false (disabled) by
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 2fcb02fcf49..7051b634a9b 100644
index 845a39b0225..fb6d1667156 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2088,7 +2088,7 @@ const intx ObjectAlignmentInBytes = 8;

View File

@@ -1,7 +1,7 @@
From 703cb7aa230b6a159c7f1f86b749a8e0119ef881 Mon Sep 17 00:00:00 2001
From 737c3616d674224661dd8751861f0365d9591d80 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Mon, 19 Oct 2020 20:00:04 +0200
Subject: [PATCH 09/39] Set HOTSPOT_VM_DISTRO=Dynamic Code Evolution
Subject: [PATCH 09/42] Set HOTSPOT_VM_DISTRO=Dynamic Code Evolution
---
make/autoconf/version-numbers | 55 +++++++++++++++++++++++++++++++++++

View File

@@ -1,7 +1,7 @@
From 3b4788c779cb9ffe2751e996bba3b445b474eba7 Mon Sep 17 00:00:00 2001
From b14f6ba2d743aacf15dedfe8ad677f176683d51c Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 23 Oct 2020 10:20:26 +0200
Subject: [PATCH 10/39] Clear dcevm code separation
Subject: [PATCH 10/42] Clear dcevm code separation
---
src/hotspot/share/classfile/systemDictionary.cpp | 3 +--

View File

@@ -1,7 +1,7 @@
From bb8f91da24b2649507f2e200f1ff2bae2d2658bf Mon Sep 17 00:00:00 2001
From 3957393df0bd440fe90f4a8eea007b3354a45b98 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Wed, 11 Nov 2020 18:45:15 +0100
Subject: [PATCH 11/39] Fix LoadedClassesClosure - fixes problems with remote
Subject: [PATCH 11/42] Fix LoadedClassesClosure - fixes problems with remote
debugging
---

View File

@@ -1,7 +1,7 @@
From 2dd09ce40a545a4256f990346e5cebf23dbf0b56 Mon Sep 17 00:00:00 2001
From f583e16509936d6873c4a2dac936d9089cc402a1 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 10:10:02 +0100
Subject: [PATCH 12/39] dcevm15 - fix java15 compilation issues
Subject: [PATCH 12/42] dcevm15 - fix java15 compilation issues
---
.../share/classfile/classFileParser.hpp | 8 +-
@@ -411,496 +411,496 @@ index a7fe090ba01..2966343c835 100644
// The hidden class loader data has been artificially been kept alive to
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 1da6661dd3e..efaf11e1666 100644
index 2679d3a556a..a27c2ff87ae 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -24,11 +24,14 @@
#include "precompiled.hpp"
#include "aot/aotLoader.hpp"
+#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/metadataOnStackMark.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/verifier.hpp"
#include "classfile/dictionary.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
+#include "interpreter/linkResolver.hpp"
#include "interpreter/oopMapCache.hpp"
#include "interpreter/rewriter.hpp"
#include "logging/logStream.hpp"
#include "precompiled.hpp"
#include "aot/aotLoader.hpp"
+#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/metadataOnStackMark.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/verifier.hpp"
#include "classfile/dictionary.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
+#include "interpreter/linkResolver.hpp"
#include "interpreter/oopMapCache.hpp"
#include "interpreter/rewriter.hpp"
#include "logging/logStream.hpp"
@@ -37,17 +40,22 @@
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/klassVtable.hpp"
#include "oops/oop.inline.hpp"
#include "oops/constantPool.inline.hpp"
+#include "oops/metadata.hpp"
+#include "oops/methodData.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiClassFileReconstituter.hpp"
#include "prims/jvmtiEnhancedRedefineClasses.hpp"
#include "prims/methodComparator.hpp"
#include "prims/resolvedMethodTable.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/relocator.hpp"
+#include "runtime/fieldDescriptor.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "utilities/bitMap.inline.hpp"
#include "prims/jvmtiThreadState.inline.hpp"
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
#include "oops/fieldStreams.hpp"
+#include "oops/fieldStreams.inline.hpp"
#include "oops/klassVtable.hpp"
#include "oops/oop.inline.hpp"
#include "oops/constantPool.inline.hpp"
+#include "oops/metadata.hpp"
+#include "oops/methodData.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiClassFileReconstituter.hpp"
#include "prims/jvmtiEnhancedRedefineClasses.hpp"
#include "prims/methodComparator.hpp"
#include "prims/resolvedMethodTable.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/relocator.hpp"
+#include "runtime/fieldDescriptor.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "utilities/bitMap.inline.hpp"
#include "prims/jvmtiThreadState.inline.hpp"
@@ -55,6 +63,8 @@
#include "oops/constantPool.inline.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/shared/dcevmSharedGC.hpp"
+#include "gc/shared/scavengableNMethods.hpp"
+#include "ci/ciObjectFactory.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
Array<Method*>* VM_EnhancedRedefineClasses::_new_methods = NULL;
#include "oops/constantPool.inline.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/shared/dcevmSharedGC.hpp"
+#include "gc/shared/scavengableNMethods.hpp"
+#include "ci/ciObjectFactory.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
Array<Method*>* VM_EnhancedRedefineClasses::_new_methods = NULL;
@@ -66,6 +76,7 @@ int VM_EnhancedRedefineClasses::_matching_methods_length = 0;
int VM_EnhancedRedefineClasses::_deleted_methods_length = 0;
int VM_EnhancedRedefineClasses::_added_methods_length = 0;
Klass* VM_EnhancedRedefineClasses::_the_class_oop = NULL;
+u8 VM_EnhancedRedefineClasses::_id_counter = 0;
//
// Create new instance of enhanced class redefiner.
int VM_EnhancedRedefineClasses::_deleted_methods_length = 0;
int VM_EnhancedRedefineClasses::_added_methods_length = 0;
Klass* VM_EnhancedRedefineClasses::_the_class_oop = NULL;
+u8 VM_EnhancedRedefineClasses::_id_counter = 0;
//
// Create new instance of enhanced class redefiner.
@@ -88,6 +99,7 @@ VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const j
_class_load_kind = class_load_kind;
_res = JVMTI_ERROR_NONE;
_any_class_has_resolved_methods = false;
+ _id = next_id();
}
static inline InstanceKlass* get_ik(jclass def) {
_class_load_kind = class_load_kind;
_res = JVMTI_ERROR_NONE;
_any_class_has_resolved_methods = false;
+ _id = next_id();
}
static inline InstanceKlass* get_ik(jclass def) {
@@ -211,9 +223,7 @@ class FieldCopier : public FieldClosure {
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
- if (!nm->on_scavenge_root_list()) {
- CodeCache::add_scavenge_root_nmethod(nm);
- }
+ ScavengableNMethods::register_nmethod(nm);
}
void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
- if (!nm->on_scavenge_root_list()) {
- CodeCache::add_scavenge_root_nmethod(nm);
- }
+ ScavengableNMethods::register_nmethod(nm);
}
void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
@@ -414,7 +424,7 @@ public:
_tmp_obj_size = size;
_tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
}
- Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)_tmp_obj, size);
+ Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
virtual void do_object(oop obj) {
_tmp_obj_size = size;
_tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
}
- Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)_tmp_obj, size);
+ Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
virtual void do_object(oop obj) {
@@ -498,16 +508,13 @@ void VM_EnhancedRedefineClasses::doit() {
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
- flush_dependent_code(NULL, thread);
+ flush_dependent_code(thread);
// }
// Adjust constantpool caches for all classes that reference methods of the evolved class.
ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
-
- // SystemDictionary::methods_do(fix_invoke_method);
-
// JSR-292 support
if (_any_class_has_resolved_methods) {
bool trace_name_printed = false;
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
- flush_dependent_code(NULL, thread);
+ flush_dependent_code(thread);
// }
// Adjust constantpool caches for all classes that reference methods of the evolved class.
ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
-
- // SystemDictionary::methods_do(fix_invoke_method);
-
// JSR-292 support
if (_any_class_has_resolved_methods) {
bool trace_name_printed = false;
@@ -564,8 +571,8 @@ void VM_EnhancedRedefineClasses::doit() {
InstanceKlass* old = InstanceKlass::cast(cur->old_version());
// Swap marks to have same hashcodes
- markOop cur_mark = cur->prototype_header();
- markOop old_mark = old->prototype_header();
+ markWord cur_mark = cur->prototype_header();
+ markWord old_mark = old->prototype_header();
cur->set_prototype_header(old_mark);
old->set_prototype_header(cur_mark);
InstanceKlass* old = InstanceKlass::cast(cur->old_version());
// Swap marks to have same hashcodes
- markOop cur_mark = cur->prototype_header();
- markOop old_mark = old->prototype_header();
+ markWord cur_mark = cur->prototype_header();
+ markWord old_mark = old->prototype_header();
cur->set_prototype_header(old_mark);
old->set_prototype_header(cur_mark);
@@ -579,14 +586,14 @@ void VM_EnhancedRedefineClasses::doit() {
// Revert pool holder for old version of klass (it was updated by one of ours closure!)
old->constants()->set_pool_holder(old);
- Klass* array_klasses = old->array_klasses();
+ ObjArrayKlass* array_klasses = old->array_klasses();
if (array_klasses != NULL) {
assert(cur->array_klasses() == NULL, "just checking");
// Transfer the array classes, otherwise we might get cast exceptions when casting array types.
// Also, set array klasses element klass.
cur->set_array_klasses(array_klasses);
- ObjArrayKlass::cast(array_klasses)->set_element_klass(cur);
+ array_klasses->set_element_klass(cur);
java_lang_Class::release_set_array_klass(cur->java_mirror(), array_klasses);
java_lang_Class::set_component_mirror(array_klasses->java_mirror(), cur->java_mirror());
}
// Revert pool holder for old version of klass (it was updated by one of ours closure!)
old->constants()->set_pool_holder(old);
- Klass* array_klasses = old->array_klasses();
+ ObjArrayKlass* array_klasses = old->array_klasses();
if (array_klasses != NULL) {
assert(cur->array_klasses() == NULL, "just checking");
// Transfer the array classes, otherwise we might get cast exceptions when casting array types.
// Also, set array klasses element klass.
cur->set_array_klasses(array_klasses);
- ObjArrayKlass::cast(array_klasses)->set_element_klass(cur);
+ array_klasses->set_element_klass(cur);
java_lang_Class::release_set_array_klass(cur->java_mirror(), array_klasses);
java_lang_Class::set_component_mirror(array_klasses->java_mirror(), cur->java_mirror());
}
@@ -640,12 +647,7 @@ void VM_EnhancedRedefineClasses::doit() {
//MethodDataCleaner clean_weak_method_links;
//ClassLoaderDataGraph::classes_do(&clean_weak_method_links);
- // Disable any dependent concurrent compilations
- SystemDictionary::notice_modification();
-
- // Set flag indicating that some invariants are no longer true.
- // See jvmtiExport.hpp for detailed explanation.
- JvmtiExport::set_has_redefined_a_class();
+ JvmtiExport::increment_redefinition_count();
#ifdef PRODUCT
if (log_is_enabled(Trace, redefine, class, obsolete, metadata)) {
//MethodDataCleaner clean_weak_method_links;
//ClassLoaderDataGraph::classes_do(&clean_weak_method_links);
- // Disable any dependent concurrent compilations
- SystemDictionary::notice_modification();
-
- // Set flag indicating that some invariants are no longer true.
- // See jvmtiExport.hpp for detailed explanation.
- JvmtiExport::set_has_redefined_a_class();
+ JvmtiExport::increment_redefinition_count();
#ifdef PRODUCT
if (log_is_enabled(Trace, redefine, class, obsolete, metadata)) {
@@ -718,7 +720,7 @@ bool VM_EnhancedRedefineClasses::is_modifiable_class(oop klass_mirror) {
}
// Cannot redefine or retransform an anonymous class.
- if (InstanceKlass::cast(k)->is_anonymous()) {
+ if (InstanceKlass::cast(k)->is_unsafe_anonymous()) {
return false;
}
return true;
}
// Cannot redefine or retransform an anonymous class.
- if (InstanceKlass::cast(k)->is_anonymous()) {
+ if (InstanceKlass::cast(k)->is_unsafe_anonymous()) {
return false;
}
return true;
@@ -804,22 +806,30 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
InstanceKlass* k;
- if (InstanceKlass::cast(the_class)->is_anonymous()) {
- const InstanceKlass* host_class = the_class->host_klass();
+ if (InstanceKlass::cast(the_class)->is_unsafe_anonymous()) {
+ const InstanceKlass* host_class = the_class->unsafe_anonymous_host();
// Make sure it's the real host class, not another anonymous class.
- while (host_class != NULL && host_class->is_anonymous()) {
- host_class = host_class->host_klass();
+ while (host_class != NULL && host_class->is_unsafe_anonymous()) {
+ host_class = host_class->unsafe_anonymous_host();
}
+ ClassLoadInfo cl_info(protection_domain,
+ host_class,
+ NULL, // dynamic_nest_host
+ NULL, // cp_patches
+ Handle(), // classData
+ false, // is_hidden
+ false, // is_strong_hidden
+ true); // FIXME: check if correct. can_access_vm_annotations
+
k = SystemDictionary::parse_stream(the_class_sym,
the_class_loader,
- protection_domain,
&st,
- host_class,
+ cl_info,
the_class,
- NULL,
THREAD);
+
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
the_class->class_loader_data()->inc_keep_alive();
} else {
InstanceKlass* k;
- if (InstanceKlass::cast(the_class)->is_anonymous()) {
- const InstanceKlass* host_class = the_class->host_klass();
+ if (InstanceKlass::cast(the_class)->is_unsafe_anonymous()) {
+ const InstanceKlass* host_class = the_class->unsafe_anonymous_host();
// Make sure it's the real host class, not another anonymous class.
- while (host_class != NULL && host_class->is_anonymous()) {
- host_class = host_class->host_klass();
+ while (host_class != NULL && host_class->is_unsafe_anonymous()) {
+ host_class = host_class->unsafe_anonymous_host();
}
+ ClassLoadInfo cl_info(protection_domain,
+ host_class,
+ NULL, // dynamic_nest_host
+ NULL, // cp_patches
+ Handle(), // classData
+ false, // is_hidden
+ false, // is_strong_hidden
+ true); // FIXME: check if correct. can_access_vm_annotations
+
k = SystemDictionary::parse_stream(the_class_sym,
the_class_loader,
- protection_domain,
&st,
- host_class,
+ cl_info,
the_class,
- NULL,
THREAD);
+
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
the_class->class_loader_data()->inc_keep_alive();
} else {
@@ -966,7 +976,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
// Check interfaces
// Interfaces removed?
- Array<Klass*>* old_interfaces = the_class->transitive_interfaces();
+ Array<InstanceKlass*>* old_interfaces = the_class->transitive_interfaces();
for (i = 0; i < old_interfaces->length(); i++) {
InstanceKlass* old_interface = InstanceKlass::cast(old_interfaces->at(i));
if (!new_class->implements_interface_any_version(old_interface)) {
// Check interfaces
// Interfaces removed?
- Array<Klass*>* old_interfaces = the_class->transitive_interfaces();
+ Array<InstanceKlass*>* old_interfaces = the_class->transitive_interfaces();
for (i = 0; i < old_interfaces->length(); i++) {
InstanceKlass* old_interface = InstanceKlass::cast(old_interfaces->at(i));
if (!new_class->implements_interface_any_version(old_interface)) {
@@ -976,7 +986,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
}
// Interfaces added?
- Array<Klass*>* new_interfaces = new_class->transitive_interfaces();
+ Array<InstanceKlass*>* new_interfaces = new_class->transitive_interfaces();
for (i = 0; i<new_interfaces->length(); i++) {
if (!the_class->implements_interface_any_version(new_interfaces->at(i))) {
result = result | Klass::ModifyClass;
}
// Interfaces added?
- Array<Klass*>* new_interfaces = new_class->transitive_interfaces();
+ Array<InstanceKlass*>* new_interfaces = new_class->transitive_interfaces();
for (i = 0; i<new_interfaces->length(); i++) {
if (!the_class->implements_interface_any_version(new_interfaces->at(i))) {
result = result | Klass::ModifyClass;
@@ -1389,8 +1399,8 @@ void VM_EnhancedRedefineClasses::rollback() {
// Rewrite faster byte-codes back to their slower equivalent. Undoes rewriting happening in templateTable_xxx.cpp
// The reason is that once we zero cpool caches, we need to re-resolve all entries again. Faster bytecodes do not
// do that, they assume that cache entry is resolved already.
-void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
- RawBytecodeStream bcs(method);
+void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method, TRAPS) {
+ RawBytecodeStream bcs(methodHandle(THREAD, method));
Bytecodes::Code code;
Bytecodes::Code java_code;
while (!bcs.is_last_bytecode()) {
// Rewrite faster byte-codes back to their slower equivalent. Undoes rewriting happening in templateTable_xxx.cpp
// The reason is that once we zero cpool caches, we need to re-resolve all entries again. Faster bytecodes do not
// do that, they assume that cache entry is resolved already.
-void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
- RawBytecodeStream bcs(method);
+void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method, TRAPS) {
+ RawBytecodeStream bcs(methodHandle(THREAD, method));
Bytecodes::Code code;
Bytecodes::Code java_code;
while (!bcs.is_last_bytecode()) {
@@ -1454,11 +1464,11 @@ void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
HandleMark hm(_thread);
InstanceKlass *ik = InstanceKlass::cast(k);
- constantPoolHandle other_cp = constantPoolHandle(ik->constants());
+ constantPoolHandle other_cp = constantPoolHandle(_thread, ik->constants());
// Update host klass of anonymous classes (for example, produced by lambdas) to newest version.
- if (ik->is_anonymous() && ik->host_klass()->new_version() != NULL) {
- ik->set_host_klass(InstanceKlass::cast(ik->host_klass()->newest_version()));
+ if (ik->is_unsafe_anonymous() && ik->unsafe_anonymous_host()->new_version() != NULL) {
+ ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
// Update implementor if there is only one, in this case implementor() can reference old class
HandleMark hm(_thread);
InstanceKlass *ik = InstanceKlass::cast(k);
- constantPoolHandle other_cp = constantPoolHandle(ik->constants());
+ constantPoolHandle other_cp = constantPoolHandle(_thread, ik->constants());
// Update host klass of anonymous classes (for example, produced by lambdas) to newest version.
- if (ik->is_anonymous() && ik->host_klass()->new_version() != NULL) {
- ik->set_host_klass(InstanceKlass::cast(ik->host_klass()->newest_version()));
+ if (ik->is_unsafe_anonymous() && ik->unsafe_anonymous_host()->new_version() != NULL) {
+ ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
// Update implementor if there is only one, in this case implementor() can reference old class
@@ -1492,7 +1502,18 @@ void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
// If bytecode rewriting is enabled, we also need to unpatch bytecode to force resolution of zeroed entries
if (RewriteBytecodes) {
- ik->methods_do(unpatch_bytecode);
+ ik->methods_do(unpatch_bytecode, _thread);
+ }
+}
+
+u8 VM_EnhancedRedefineClasses::next_id() {
+ while (true) {
+ u8 id = _id_counter;
+ u8 next_id = id + 1;
+ u8 result = Atomic::cmpxchg(&_id_counter, id, next_id);
+ if (result == id) {
+ return next_id;
+ }
}
}
// If bytecode rewriting is enabled, we also need to unpatch bytecode to force resolution of zeroed entries
if (RewriteBytecodes) {
- ik->methods_do(unpatch_bytecode);
+ ik->methods_do(unpatch_bytecode, _thread);
+ }
+}
+
+u8 VM_EnhancedRedefineClasses::next_id() {
+ while (true) {
+ u8 id = _id_counter;
+ u8 next_id = id + 1;
+ u8 result = Atomic::cmpxchg(&_id_counter, id, next_id);
+ if (result == id) {
+ return next_id;
+ }
}
}
@@ -1512,31 +1533,8 @@ void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
}
}
-void VM_EnhancedRedefineClasses::fix_invoke_method(Method* method) {
-
- constantPoolHandle other_cp = constantPoolHandle(method->constants());
-
- for (int i = 0; i < other_cp->length(); i++) {
- if (other_cp->tag_at(i).is_klass()) {
- Klass* klass = other_cp->resolved_klass_at(i);
- if (klass->new_version() != NULL) {
- // Constant pool entry points to redefined class -- update to the new version
- other_cp->klass_at_put(i, klass->newest_version());
- }
- assert(other_cp->resolved_klass_at(i)->new_version() == NULL, "Must be new klass!");
- }
- }
- ConstantPoolCache* cp_cache = other_cp->cache();
- if (cp_cache != NULL) {
- cp_cache->clear_entries();
- }
-
-}
-
-
-
-void VM_EnhancedRedefineClasses::update_jmethod_ids() {
+void VM_EnhancedRedefineClasses::update_jmethod_ids(TRAPS) {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
jmethodID jmid = old_method->find_jmethod_id_or_null();
}
}
-void VM_EnhancedRedefineClasses::fix_invoke_method(Method* method) {
-
- constantPoolHandle other_cp = constantPoolHandle(method->constants());
-
- for (int i = 0; i < other_cp->length(); i++) {
- if (other_cp->tag_at(i).is_klass()) {
- Klass* klass = other_cp->resolved_klass_at(i);
- if (klass->new_version() != NULL) {
- // Constant pool entry points to redefined class -- update to the new version
- other_cp->klass_at_put(i, klass->newest_version());
- }
- assert(other_cp->resolved_klass_at(i)->new_version() == NULL, "Must be new klass!");
- }
- }
- ConstantPoolCache* cp_cache = other_cp->cache();
- if (cp_cache != NULL) {
- cp_cache->clear_entries();
- }
-
-}
-
-
-
-void VM_EnhancedRedefineClasses::update_jmethod_ids() {
+void VM_EnhancedRedefineClasses::update_jmethod_ids(TRAPS) {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
jmethodID jmid = old_method->find_jmethod_id_or_null();
@@ -1547,10 +1545,10 @@ void VM_EnhancedRedefineClasses::update_jmethod_ids() {
if (jmid != NULL) {
// There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(_matching_new_methods[j]);
+ methodHandle new_method_h(THREAD, _matching_new_methods[j]);
if (old_method->new_version() == NULL) {
- methodHandle old_method_h(_matching_old_methods[j]);
+ methodHandle old_method_h(THREAD, _matching_old_methods[j]);
jmethodID new_jmethod_id = Method::make_jmethod_id(old_method_h->method_holder()->class_loader_data(), old_method_h());
bool result = InstanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id);
} else {
if (jmid != NULL) {
// There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(_matching_new_methods[j]);
+ methodHandle new_method_h(THREAD, _matching_new_methods[j]);
if (old_method->new_version() == NULL) {
- methodHandle old_method_h(_matching_old_methods[j]);
+ methodHandle old_method_h(THREAD, _matching_old_methods[j]);
jmethodID new_jmethod_id = Method::make_jmethod_id(old_method_h->method_holder()->class_loader_data(), old_method_h());
bool result = InstanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id);
} else {
@@ -1739,6 +1737,18 @@ void VM_EnhancedRedefineClasses::transfer_old_native_function_registrations(Inst
transfer.transfer_registrations(_matching_old_methods, _matching_methods_length);
}
+// First step is to walk the code cache for each class redefined and mark
+// dependent methods. Wait until all classes are processed to deoptimize everything.
+void VM_EnhancedRedefineClasses::mark_dependent_code(InstanceKlass* ik) {
+ assert_locked_or_safepoint(Compile_lock);
+
+ // All dependencies have been recorded from startup or this is a second or
+ // subsequent use of RedefineClasses
+ if (0 && JvmtiExport::all_dependencies_are_recorded()) {
+ CodeCache::mark_for_evol_deoptimization(ik);
+ }
+}
+
// DCEVM - it always deoptimizes everything! (because it is very difficult to find only correct dependencies)
// Deoptimize all compiled code that depends on this class.
//
transfer.transfer_registrations(_matching_old_methods, _matching_methods_length);
}
+// First step is to walk the code cache for each class redefined and mark
+// dependent methods. Wait until all classes are processed to deoptimize everything.
+void VM_EnhancedRedefineClasses::mark_dependent_code(InstanceKlass* ik) {
+ assert_locked_or_safepoint(Compile_lock);
+
+ // All dependencies have been recorded from startup or this is a second or
+ // subsequent use of RedefineClasses
+ if (0 && JvmtiExport::all_dependencies_are_recorded()) {
+ CodeCache::mark_for_evol_deoptimization(ik);
+ }
+}
+
// DCEVM - it always deoptimizes everything! (because it is very difficult to find only correct dependencies)
// Deoptimize all compiled code that depends on this class.
//
@@ -1755,33 +1765,21 @@ void VM_EnhancedRedefineClasses::transfer_old_native_function_registrations(Inst
// subsequent calls to RedefineClasses need only throw away code
// that depends on the class.
//
-void VM_EnhancedRedefineClasses::flush_dependent_code(InstanceKlass* k_h, TRAPS) {
+void VM_EnhancedRedefineClasses::flush_dependent_code(TRAPS) {
assert_locked_or_safepoint(Compile_lock);
// All dependencies have been recorded from startup or this is a second or
// subsequent use of RedefineClasses
// FIXME: for now, deoptimize all!
- if (0 && k_h != NULL && JvmtiExport::all_dependencies_are_recorded()) {
- CodeCache::flush_evol_dependents_on(k_h);
- Klass* superCl = k_h->super();
- // Deoptimize super classes since redefined class can has a new method override
- while (superCl != NULL && !superCl->is_redefining()) {
- CodeCache::flush_evol_dependents_on(InstanceKlass::cast(superCl));
- superCl = superCl->super();
+ if (0 && JvmtiExport::all_dependencies_are_recorded()) {
+ int deopt = CodeCache::mark_dependents_for_evol_deoptimization();
+ log_debug(redefine, class, nmethod)("Marked %d dependent nmethods for deopt", deopt);
+ if (deopt != 0) {
+ CodeCache::flush_evol_dependents();
}
} else {
- CodeCache::mark_all_nmethods_for_deoptimization();
-
- ResourceMark rm(THREAD);
- DeoptimizationMarker dm;
-
- // Deoptimize all activations depending on marked nmethods
- Deoptimization::deoptimize_dependents();
-
- // Make the dependent methods not entrant
- CodeCache::make_marked_nmethods_not_entrant();
-
- // From now on we know that the dependency information is complete
+ CodeCache::mark_all_nmethods_for_evol_deoptimization();
+ CodeCache::flush_evol_dependents();
JvmtiExport::set_all_dependencies_are_recorded(true);
}
}
// subsequent calls to RedefineClasses need only throw away code
// that depends on the class.
//
-void VM_EnhancedRedefineClasses::flush_dependent_code(InstanceKlass* k_h, TRAPS) {
+void VM_EnhancedRedefineClasses::flush_dependent_code(TRAPS) {
assert_locked_or_safepoint(Compile_lock);
// All dependencies have been recorded from startup or this is a second or
// subsequent use of RedefineClasses
// FIXME: for now, deoptimize all!
- if (0 && k_h != NULL && JvmtiExport::all_dependencies_are_recorded()) {
- CodeCache::flush_evol_dependents_on(k_h);
- Klass* superCl = k_h->super();
- // Deoptimize super classes since redefined class can has a new method override
- while (superCl != NULL && !superCl->is_redefining()) {
- CodeCache::flush_evol_dependents_on(InstanceKlass::cast(superCl));
- superCl = superCl->super();
+ if (0 && JvmtiExport::all_dependencies_are_recorded()) {
+ int deopt = CodeCache::mark_dependents_for_evol_deoptimization();
+ log_debug(redefine, class, nmethod)("Marked %d dependent nmethods for deopt", deopt);
+ if (deopt != 0) {
+ CodeCache::flush_evol_dependents();
}
} else {
- CodeCache::mark_all_nmethods_for_deoptimization();
-
- ResourceMark rm(THREAD);
- DeoptimizationMarker dm;
-
- // Deoptimize all activations depending on marked nmethods
- Deoptimization::deoptimize_dependents();
-
- // Make the dependent methods not entrant
- CodeCache::make_marked_nmethods_not_entrant();
-
- // From now on we know that the dependency information is complete
+ CodeCache::mark_all_nmethods_for_evol_deoptimization();
+ CodeCache::flush_evol_dependents();
JvmtiExport::set_all_dependencies_are_recorded(true);
}
}
@@ -1874,11 +1872,7 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints();
jvmti_breakpoints.clearall_in_class_at_safepoint(the_class);
- // DCEVM Deoptimization is always for whole java world, call only once after all classes are redefined
- // Deoptimize all compiled code that depends on this class
-// if (_max_redefinition_flags <= Klass::ModifyClass) {
-// flush_dependent_code(the_class, THREAD);
-// }
+ mark_dependent_code(the_class);
_old_methods = the_class->methods();
_new_methods = new_class->methods();
JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints();
jvmti_breakpoints.clearall_in_class_at_safepoint(the_class);
- // DCEVM Deoptimization is always for whole java world, call only once after all classes are redefined
- // Deoptimize all compiled code that depends on this class
-// if (_max_redefinition_flags <= Klass::ModifyClass) {
-// flush_dependent_code(the_class, THREAD);
-// }
+ mark_dependent_code(the_class);
_old_methods = the_class->methods();
_new_methods = new_class->methods();
@@ -1887,7 +1881,7 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
// track number of methods that are EMCP for add_previous_version() call below
check_methods_and_mark_as_obsolete();
- update_jmethod_ids();
+ update_jmethod_ids(THREAD);
_any_class_has_resolved_methods = the_class->has_resolved_methods() || _any_class_has_resolved_methods;
// track number of methods that are EMCP for add_previous_version() call below
check_methods_and_mark_as_obsolete();
- update_jmethod_ids();
+ update_jmethod_ids(THREAD);
_any_class_has_resolved_methods = the_class->has_resolved_methods() || _any_class_has_resolved_methods;
@@ -2119,12 +2113,12 @@ jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(TRAPS) {
Handle protection_domain(THREAD, klass->protection_domain());
+ ClassLoadInfo cl_info(protection_domain);
+
ClassFileParser parser(&st,
klass->name(),
klass->class_loader_data(),
- protection_domain,
- NULL, // host_klass
- NULL, // cp_patches
+ &cl_info,
ClassFileParser::INTERNAL, // publicity level
true,
THREAD);
Handle protection_domain(THREAD, klass->protection_domain());
+ ClassLoadInfo cl_info(protection_domain);
+
ClassFileParser parser(&st,
klass->name(),
klass->class_loader_data(),
- protection_domain,
- NULL, // host_klass
- NULL, // cp_patches
+ &cl_info,
ClassFileParser::INTERNAL, // publicity level
true,
THREAD);
@@ -2134,7 +2128,7 @@ jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(TRAPS) {
links.append(KlassPair(super_klass, klass));
}
- Array<Klass*>* local_interfaces = parser.local_interfaces();
+ Array<InstanceKlass*>* local_interfaces = parser.local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* iface = local_interfaces->at(j);
if (iface != NULL && _affected_klasses->contains(iface)) {
links.append(KlassPair(super_klass, klass));
}
- Array<Klass*>* local_interfaces = parser.local_interfaces();
+ Array<InstanceKlass*>* local_interfaces = parser.local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* iface = local_interfaces->at(j);
if (iface != NULL && _affected_klasses->contains(iface)) {
@@ -2157,7 +2151,7 @@ jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(TRAPS) {
links.append(KlassPair(super_klass, klass));
}
- Array<Klass*>* local_interfaces = klass->local_interfaces();
+ Array<InstanceKlass*>* local_interfaces = klass->local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* interfaceKlass = local_interfaces->at(j);
if (_affected_klasses->contains(interfaceKlass)) {
links.append(KlassPair(super_klass, klass));
}
- Array<Klass*>* local_interfaces = klass->local_interfaces();
+ Array<InstanceKlass*>* local_interfaces = klass->local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* interfaceKlass = local_interfaces->at(j);
if (_affected_klasses->contains(interfaceKlass)) {
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index 4c0412d343d..bd5e7d153be 100644
index d00109a0b92..61f62e7f831 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -32,7 +32,7 @@
#include "memory/resourceArea.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
-#include "gc/shared/vmGCOperations.hpp"
+#include "gc/shared/gcVMOperations.hpp"
#include "../../../java.base/unix/native/include/jni_md.h"
//
#include "memory/resourceArea.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
-#include "gc/shared/vmGCOperations.hpp"
+#include "gc/shared/gcVMOperations.hpp"
#include "../../../java.base/unix/native/include/jni_md.h"
//
@@ -59,6 +59,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
static int _deleted_methods_length;
static int _added_methods_length;
static Klass* _the_class_oop;
+ static u8 _id_counter;
// The instance fields are used to pass information from
// doit_prologue() to doit() and doit_epilogue().
static int _deleted_methods_length;
static int _added_methods_length;
static Klass* _the_class_oop;
+ static u8 _id_counter;
// The instance fields are used to pass information from
// doit_prologue() to doit() and doit_epilogue().
@@ -91,6 +92,9 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
elapsedTimer _timer_heap_iterate;
elapsedTimer _timer_heap_full_gc;
+ // Redefinition id used by JFR
+ u8 _id;
+
// These routines are roughly in call order unless otherwise noted.
// Load and link new classes (either redefined or affected by redefinition - subclass, ...)
elapsedTimer _timer_heap_iterate;
elapsedTimer _timer_heap_full_gc;
+ // Redefinition id used by JFR
+ u8 _id;
+
// These routines are roughly in call order unless otherwise noted.
// Load and link new classes (either redefined or affected by redefinition - subclass, ...)
@@ -118,15 +122,14 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
static void mark_as_scavengable(nmethod* nm);
static void unregister_nmethod_g1(nmethod* nm);
static void register_nmethod_g1(nmethod* nm);
- static void unpatch_bytecode(Method* method);
- static void fix_invoke_method(Method* method);
+ static void unpatch_bytecode(Method* method, TRAPS);
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void compute_added_deleted_matching_methods();
// Change jmethodIDs to point to the new methods
- void update_jmethod_ids();
+ void update_jmethod_ids(TRAPS);
// marking methods as old and/or obsolete
void check_methods_and_mark_as_obsolete();
static void mark_as_scavengable(nmethod* nm);
static void unregister_nmethod_g1(nmethod* nm);
static void register_nmethod_g1(nmethod* nm);
- static void unpatch_bytecode(Method* method);
- static void fix_invoke_method(Method* method);
+ static void unpatch_bytecode(Method* method, TRAPS);
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void compute_added_deleted_matching_methods();
// Change jmethodIDs to point to the new methods
- void update_jmethod_ids();
+ void update_jmethod_ids(TRAPS);
// marking methods as old and/or obsolete
void check_methods_and_mark_as_obsolete();
@@ -139,7 +142,11 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
// and in all direct and indirect subclasses.
void increment_class_counter(InstanceKlass *ik, TRAPS);
- void flush_dependent_code(InstanceKlass* k_h, TRAPS);
+ void mark_dependent_code(InstanceKlass* ik);
+
+ void flush_dependent_code(TRAPS);
+
+ u8 next_id();
static void check_class(InstanceKlass* k_oop, TRAPS);
// and in all direct and indirect subclasses.
void increment_class_counter(InstanceKlass *ik, TRAPS);
- void flush_dependent_code(InstanceKlass* k_h, TRAPS);
+ void mark_dependent_code(InstanceKlass* ik);
+
+ void flush_dependent_code(TRAPS);
+
+ u8 next_id();
static void check_class(InstanceKlass* k_oop, TRAPS);
@@ -181,6 +188,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
bool allow_nested_vm_operations() const { return true; }
jvmtiError check_error() { return _res; }
+ u8 id() { return _id; }
// Modifiable test must be shared between IsModifiableClass query
// and redefine implementation
bool allow_nested_vm_operations() const { return true; }
jvmtiError check_error() { return _res; }
+ u8 id() { return _id; }
// Modifiable test must be shared between IsModifiableClass query
// and redefine implementation
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
index 6d1692949cb..8970d7991b7 100644
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -1151,10 +1151,10 @@ index b8d039adff6..3c29eda4da0 100644
class VerifyResolvedMethod : StackObj {
public:
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index b7bb88e2a46..567803b70b7 100644
index 2d33448ec8d..9661f2b62b9 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -1975,13 +1975,15 @@ bool Arguments::check_gc_consistency() {
@@ -1979,13 +1979,15 @@ bool Arguments::check_gc_consistency() {
// of collectors.
uint i = 0;
if (UseSerialGC) i++;

View File

@@ -1,7 +1,7 @@
From 9815ee603b27484953651bdc6d5705994a4d38aa Mon Sep 17 00:00:00 2001
From 4c98b29fed42b51e399f2ae0dc06f0c7a4397e2c Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 22 Nov 2020 19:51:46 +0100
Subject: [PATCH 13/39] dcevm15 - add ClassLoaderDataGraph_lock on
Subject: [PATCH 13/42] dcevm15 - add ClassLoaderDataGraph_lock on
ClassLoaderDataGraph::classes_do
ClassLoaderDataGraph::classes_do and need safepoint or lock,
@@ -13,31 +13,31 @@ ClassLoaderDataGraph::rollback_redefinition need safepoint too
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index efaf11e1666..9f42d14ce98 100644
index a27c2ff87ae..001d2c782d8 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -1380,7 +1380,9 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
// Rollback all changes - clear new classes from the system dictionary, return old classes to directory, free memory.
void VM_EnhancedRedefineClasses::rollback() {
log_info(redefine, class, load)("Rolling back redefinition, result=%d", _res);
+ ClassLoaderDataGraph_lock->lock();
ClassLoaderDataGraph::rollback_redefinition();
+ ClassLoaderDataGraph_lock->unlock();
for (int i = 0; i < _new_classes->length(); i++) {
SystemDictionary::remove_from_hierarchy(_new_classes->at(i));
// Rollback all changes - clear new classes from the system dictionary, return old classes to directory, free memory.
void VM_EnhancedRedefineClasses::rollback() {
log_info(redefine, class, load)("Rolling back redefinition, result=%d", _res);
+ ClassLoaderDataGraph_lock->lock();
ClassLoaderDataGraph::rollback_redefinition();
+ ClassLoaderDataGraph_lock->unlock();
for (int i = 0; i < _new_classes->length(); i++) {
SystemDictionary::remove_from_hierarchy(_new_classes->at(i));
@@ -2063,7 +2065,10 @@ jvmtiError VM_EnhancedRedefineClasses::find_sorted_affected_classes(TRAPS) {
AffectedKlassClosure closure(_affected_klasses);
// Updated in j10, from original SystemDictionary::classes_do
- ClassLoaderDataGraph::classes_do(&closure);
+ {
+ MutexLocker mcld(ClassLoaderDataGraph_lock);
+ ClassLoaderDataGraph::classes_do(&closure);
+ }
//ClassLoaderDataGraph::dictionary_classes_do(&closure);
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
AffectedKlassClosure closure(_affected_klasses);
// Updated in j10, from original SystemDictionary::classes_do
- ClassLoaderDataGraph::classes_do(&closure);
+ {
+ MutexLocker mcld(ClassLoaderDataGraph_lock);
+ ClassLoaderDataGraph::classes_do(&closure);
+ }
//ClassLoaderDataGraph::dictionary_classes_do(&closure);
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
--
2.23.0

View File

@@ -1,7 +1,7 @@
From d3e5f8acd389021128bb8a899bb538294de353f6 Mon Sep 17 00:00:00 2001
From b312e64db8532c57658d1f39269d73969ecf98d4 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 22 Nov 2020 12:05:50 +0100
Subject: [PATCH 14/39] dcevm15 - fix Universe::root_oops_do
Subject: [PATCH 14/42] dcevm15 - fix Universe::root_oops_do
Removed ClassLoaderDataGraph::cld_do was cause of crashes due multiple
oop patching. ClassLoaderDataGraph::cld_do replaced in dcevm15

View File

@@ -1,7 +1,7 @@
From 574cddeb00c3d93bddbaf1845a7d9d3ffdced324 Mon Sep 17 00:00:00 2001
From b99bae1157fea7a62a2c9d2f10aaf722de1a5299 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 28 Nov 2020 19:29:42 +0100
Subject: [PATCH 15/39] dcevm15 - check if has_nestmate_access_to has newest
Subject: [PATCH 15/42] dcevm15 - check if has_nestmate_access_to has newest
host class
---

View File

@@ -1,54 +1,54 @@
From 983b4aaed2c56a74287b9400ddae4b7d7f3fd715 Mon Sep 17 00:00:00 2001
From 936d83f295468e7c0e51b759def1d8664cca2a6e Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 29 Nov 2020 17:18:16 +0100
Subject: [PATCH 16/39] dcevm15 - mark_as_scavengable only alive methods
Subject: [PATCH 16/42] dcevm15 - mark_as_scavengable only alive methods
---
.../share/prims/jvmtiEnhancedRedefineClasses.cpp | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 9f42d14ce98..db5fb1c472b 100644
index 001d2c782d8..d742679d199 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -223,19 +223,21 @@ class FieldCopier : public FieldClosure {
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
- ScavengableNMethods::register_nmethod(nm);
+ if (nm->is_alive()) {
+ ScavengableNMethods::register_nmethod(nm);
+ }
}
void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
// It should work not only for G1 but also for another GCs, but this way is safer now
- if (!nm->is_zombie() && !nm->is_unloaded()) {
+ if (nm->is_alive()) {
Universe::heap()->unregister_nmethod(nm);
}
}
void VM_EnhancedRedefineClasses::register_nmethod_g1(nmethod* nm) {
// It should work not only for G1 but also for another GCs, but this way is safer now
- if (!nm->is_zombie() && !nm->is_unloaded()) {
+ if (nm->is_alive()) {
Universe::heap()->register_nmethod(nm);
}
}
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
- ScavengableNMethods::register_nmethod(nm);
+ if (nm->is_alive()) {
+ ScavengableNMethods::register_nmethod(nm);
+ }
}
void VM_EnhancedRedefineClasses::unregister_nmethod_g1(nmethod* nm) {
// It should work not only for G1 but also for another GCs, but this way is safer now
- if (!nm->is_zombie() && !nm->is_unloaded()) {
+ if (nm->is_alive()) {
Universe::heap()->unregister_nmethod(nm);
}
}
void VM_EnhancedRedefineClasses::register_nmethod_g1(nmethod* nm) {
// It should work not only for G1 but also for another GCs, but this way is safer now
- if (!nm->is_zombie() && !nm->is_unloaded()) {
+ if (nm->is_alive()) {
Universe::heap()->register_nmethod(nm);
}
}
@@ -511,9 +513,9 @@ void VM_EnhancedRedefineClasses::doit() {
flush_dependent_code(thread);
// }
- // Adjust constantpool caches for all classes that reference methods of the evolved class.
- ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
- ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
+ // Adjust constantpool caches for all classes that reference methods of the evolved class.
+ ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
+ ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
// JSR-292 support
if (_any_class_has_resolved_methods) {
flush_dependent_code(thread);
// }
- // Adjust constantpool caches for all classes that reference methods of the evolved class.
- ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
- ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
+ // Adjust constantpool caches for all classes that reference methods of the evolved class.
+ ClearCpoolCacheAndUnpatch clear_cpool_cache(thread);
+ ClassLoaderDataGraph::classes_do(&clear_cpool_cache);
// JSR-292 support
if (_any_class_has_resolved_methods) {
--
2.23.0

View File

@@ -1,91 +1,91 @@
From ed546016ead6064d8b95a9c1e4cdc6bc192f8d67 Mon Sep 17 00:00:00 2001
From 50e3023f20fbea76fcc644a037bebc3ee5afc212 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 29 Nov 2020 20:05:03 +0100
Subject: [PATCH 17/39] dcevm15 - fix hidded classes
Subject: [PATCH 17/42] dcevm15 - fix hidded classes
---
.../prims/jvmtiEnhancedRedefineClasses.cpp | 41 ++++++++++++++-----
1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index db5fb1c472b..590f7fdfafe 100644
index d742679d199..1433e429e30 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -722,7 +722,8 @@ bool VM_EnhancedRedefineClasses::is_modifiable_class(oop klass_mirror) {
}
// Cannot redefine or retransform an anonymous class.
- if (InstanceKlass::cast(k)->is_unsafe_anonymous()) {
+ // TODO: check if is correct in j15
+ if (InstanceKlass::cast(k)->is_unsafe_anonymous() || InstanceKlass::cast(k)->is_hidden()) {
return false;
}
return true;
}
// Cannot redefine or retransform an anonymous class.
- if (InstanceKlass::cast(k)->is_unsafe_anonymous()) {
+ // TODO: check if is correct in j15
+ if (InstanceKlass::cast(k)->is_unsafe_anonymous() || InstanceKlass::cast(k)->is_hidden()) {
return false;
}
return true;
@@ -808,21 +809,27 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
InstanceKlass* k;
- if (InstanceKlass::cast(the_class)->is_unsafe_anonymous()) {
- const InstanceKlass* host_class = the_class->unsafe_anonymous_host();
+ if (the_class->is_unsafe_anonymous() || the_class->is_hidden()) {
+ InstanceKlass* dynamic_host_class = NULL;
+ InstanceKlass* unsafe_anonymous_host = NULL;
- // Make sure it's the real host class, not another anonymous class.
- while (host_class != NULL && host_class->is_unsafe_anonymous()) {
- host_class = host_class->unsafe_anonymous_host();
+ if (the_class->is_hidden()) {
+ log_debug(redefine, class, load)("loading hidden class %s", the_class->name()->as_C_string());
+ dynamic_host_class = the_class->nest_host(THREAD);
+ }
+
+ if (the_class->is_unsafe_anonymous()) {
+ log_debug(redefine, class, load)("loading usafe anonymous %s", the_class->name()->as_C_string());
+ unsafe_anonymous_host = the_class->unsafe_anonymous_host();
}
ClassLoadInfo cl_info(protection_domain,
- host_class,
- NULL, // dynamic_nest_host
+ unsafe_anonymous_host,
NULL, // cp_patches
+ dynamic_host_class, // dynamic_nest_host
Handle(), // classData
- false, // is_hidden
- false, // is_strong_hidden
+ the_class->is_hidden(), // is_hidden
+ !the_class->is_non_strong_hidden(), // is_strong_hidden
true); // FIXME: check if correct. can_access_vm_annotations
k = SystemDictionary::parse_stream(the_class_sym,
InstanceKlass* k;
- if (InstanceKlass::cast(the_class)->is_unsafe_anonymous()) {
- const InstanceKlass* host_class = the_class->unsafe_anonymous_host();
+ if (the_class->is_unsafe_anonymous() || the_class->is_hidden()) {
+ InstanceKlass* dynamic_host_class = NULL;
+ InstanceKlass* unsafe_anonymous_host = NULL;
- // Make sure it's the real host class, not another anonymous class.
- while (host_class != NULL && host_class->is_unsafe_anonymous()) {
- host_class = host_class->unsafe_anonymous_host();
+ if (the_class->is_hidden()) {
+ log_debug(redefine, class, load)("loading hidden class %s", the_class->name()->as_C_string());
+ dynamic_host_class = the_class->nest_host(THREAD);
+ }
+
+ if (the_class->is_unsafe_anonymous()) {
+ log_debug(redefine, class, load)("loading usafe anonymous %s", the_class->name()->as_C_string());
+ unsafe_anonymous_host = the_class->unsafe_anonymous_host();
}
ClassLoadInfo cl_info(protection_domain,
- host_class,
- NULL, // dynamic_nest_host
+ unsafe_anonymous_host,
NULL, // cp_patches
+ dynamic_host_class, // dynamic_nest_host
Handle(), // classData
- false, // is_hidden
- false, // is_strong_hidden
+ the_class->is_hidden(), // is_hidden
+ !the_class->is_non_strong_hidden(), // is_strong_hidden
true); // FIXME: check if correct. can_access_vm_annotations
k = SystemDictionary::parse_stream(the_class_sym,
@@ -833,7 +840,17 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
THREAD);
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
- the_class->class_loader_data()->inc_keep_alive();
+
+ if (the_class->is_hidden()) {
+ // from jvm_lookup_define_class() (jvm.cpp):
+ // The hidden class loader data has been artificially been kept alive to
+ // this point. The mirror and any instances of this class have to keep
+ // it alive afterwards.
+ the_class->class_loader_data()->dec_keep_alive();
+ } else {
+ the_class->class_loader_data()->inc_keep_alive();
+ }
+
} else {
k = SystemDictionary::resolve_from_stream(the_class_sym,
the_class_loader,
THREAD);
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
- the_class->class_loader_data()->inc_keep_alive();
+
+ if (the_class->is_hidden()) {
+ // from jvm_lookup_define_class() (jvm.cpp):
+ // The hidden class loader data has been artificially been kept alive to
+ // this point. The mirror and any instances of this class have to keep
+ // it alive afterwards.
+ the_class->class_loader_data()->dec_keep_alive();
+ } else {
+ the_class->class_loader_data()->inc_keep_alive();
+ }
+
} else {
k = SystemDictionary::resolve_from_stream(the_class_sym,
the_class_loader,
@@ -1475,6 +1492,8 @@ void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
+ // FIXME: check new nest_host for hidden
+
// Update implementor if there is only one, in this case implementor() can reference old class
if (ik->is_interface()) {
Klass* implKlass = ik->implementor();
ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
+ // FIXME: check new nest_host for hidden
+
// Update implementor if there is only one, in this case implementor() can reference old class
if (ik->is_interface()) {
Klass* implKlass = ik->implementor();
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 9983c44fe6903daba758ed0c43b8c86e738e0741 Mon Sep 17 00:00:00 2001
From 21a17a7caf43236590d2e7319e176cb19c42f9aa Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 29 Nov 2020 20:08:57 +0100
Subject: [PATCH 18/39] dcevm15 - DON'T clear F2 in CP cache after indy
Subject: [PATCH 18/42] dcevm15 - DON'T clear F2 in CP cache after indy
unevolving
It's not clear why it was cleared in dcevm7-11

View File

@@ -1,7 +1,7 @@
From 3b09df69c007285ea6c258388532a8e5f9fe3d45 Mon Sep 17 00:00:00 2001
From e286bbfb6ef8235be6aa9b41e30e9ed36cfd177d Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 22 Nov 2020 12:03:32 +0100
Subject: [PATCH 19/39] Cleanup and review comments
Subject: [PATCH 19/42] Cleanup and review comments
---
src/hotspot/share/classfile/classLoaderDataGraph.hpp | 2 +-
@@ -37,30 +37,30 @@ index 26a2b67b8e9..8e78696bef5 100644
} else if (os::is_server_class_machine()) {
#if INCLUDE_G1GC
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 590f7fdfafe..2a7dd35bdd1 100644
index 1433e429e30..af34d751bdb 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -842,6 +842,7 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
if (the_class->is_hidden()) {
+ // TODO: (DCEVM) review if is correct
// from jvm_lookup_define_class() (jvm.cpp):
// The hidden class loader data has been artificially been kept alive to
// this point. The mirror and any instances of this class have to keep
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
if (the_class->is_hidden()) {
+ // TODO: (DCEVM) review if is correct
// from jvm_lookup_define_class() (jvm.cpp):
// The hidden class loader data has been artificially been kept alive to
// this point. The mirror and any instances of this class have to keep
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index bd5e7d153be..5de375fb888 100644
index 61f62e7f831..43f1aff8e91 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -78,7 +78,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
// have any entries.
bool _any_class_has_resolved_methods;
- // Enhanced class redefinition, affected klasses contain all classes which should be redefined
+ // (DCEVM) Enhanced class redefinition, affected klasses contain all classes which should be redefined
// either because of redefine, class hierarchy or interface change
GrowableArray<Klass*>* _affected_klasses;
// have any entries.
bool _any_class_has_resolved_methods;
- // Enhanced class redefinition, affected klasses contain all classes which should be redefined
+ // (DCEVM) Enhanced class redefinition, affected klasses contain all classes which should be redefined
// either because of redefine, class hierarchy or interface change
GrowableArray<Klass*>* _affected_klasses;
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 5c41ecc9f48d22b81b3ac610e5655f1a74d25614 Mon Sep 17 00:00:00 2001
From a755a90e2845ef3a30f3f03185ecee1a71182606 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 10:11:10 +0100
Subject: [PATCH 20/39] Disable AllowEnhancedClassRedefinition in flight
Subject: [PATCH 20/42] Disable AllowEnhancedClassRedefinition in flight
recorder
---
@@ -9,10 +9,10 @@ Subject: [PATCH 20/39] Disable AllowEnhancedClassRedefinition in flight
1 file changed, 7 insertions(+)
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 567803b70b7..cbcd8f91c66 100644
index 9661f2b62b9..252ebbc4e57 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -3994,6 +3994,13 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
@@ -4027,6 +4027,13 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
// Set object alignment values.
set_object_alignment();

View File

@@ -1,7 +1,7 @@
From e560b33cdec3ef0e4ce91714663d3befa87ff4e5 Mon Sep 17 00:00:00 2001
From 8770c3da756a6193d69bb8c23aa992584edcb723 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 09:44:28 +0100
Subject: [PATCH 21/39] dcevm17 - fix compilation issues
Subject: [PATCH 21/42] dcevm17 - fix compilation issues
---
.../share/classfile/classLoaderData.cpp | 2 +-
@@ -533,120 +533,120 @@ index b4d09e92830..c2de2bec7d4 100644
// Verify local interfaces
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 2a7dd35bdd1..071dbc6d6ad 100644
index af34d751bdb..0b1db1581d1 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -26,8 +26,10 @@
#include "aot/aotLoader.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
+#include "classfile/classLoadInfo.hpp"
#include "classfile/metadataOnStackMark.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/symbolTable.hpp"
#include "classfile/verifier.hpp"
#include "classfile/dictionary.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "aot/aotLoader.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
+#include "classfile/classLoadInfo.hpp"
#include "classfile/metadataOnStackMark.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/symbolTable.hpp"
#include "classfile/verifier.hpp"
#include "classfile/dictionary.hpp"
#include "classfile/classLoaderDataGraph.hpp"
@@ -198,8 +200,8 @@ class FieldCopier : public FieldClosure {
if (found && result.is_static()) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
- memcpy(cur_oop->obj_field_addr_raw<HeapWord>(fd->offset()),
- old_oop->obj_field_addr_raw<HeapWord>(result.offset()),
+ memcpy(cur_oop->obj_field_addr<HeapWord>(fd->offset()),
+ old_oop->obj_field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
// Static fields may have references to java.lang.Class
if (found && result.is_static()) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
- memcpy(cur_oop->obj_field_addr_raw<HeapWord>(fd->offset()),
- old_oop->obj_field_addr_raw<HeapWord>(result.offset()),
+ memcpy(cur_oop->obj_field_addr<HeapWord>(fd->offset()),
+ old_oop->obj_field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
// Static fields may have references to java.lang.Class
@@ -242,6 +244,17 @@ void VM_EnhancedRedefineClasses::register_nmethod_g1(nmethod* nm) {
}
}
+void VM_EnhancedRedefineClasses::root_oops_do(OopClosure *oopClosure) {
+ Universe::vm_global()->oops_do(oopClosure);
+
+ Threads::oops_do(oopClosure, NULL);
+ AOT_ONLY(AOTLoader::oops_do(oopClosure);)
+ OopStorageSet::strong_oops_do(oopClosure);
+
+ CodeBlobToOopClosure blobClosure(oopClosure, CodeBlobToOopClosure::FixRelocations);
+ CodeCache::blobs_do(&blobClosure);
+}
+
// TODO comment
struct StoreBarrier {
// TODO: j10 review change ::oop_store -> HeapAccess<>::oop_store
}
}
+void VM_EnhancedRedefineClasses::root_oops_do(OopClosure *oopClosure) {
+ Universe::vm_global()->oops_do(oopClosure);
+
+ Threads::oops_do(oopClosure, NULL);
+ AOT_ONLY(AOTLoader::oops_do(oopClosure);)
+ OopStorageSet::strong_oops_do(oopClosure);
+
+ CodeBlobToOopClosure blobClosure(oopClosure, CodeBlobToOopClosure::FixRelocations);
+ CodeCache::blobs_do(&blobClosure);
+}
+
// TODO comment
struct StoreBarrier {
// TODO: j10 review change ::oop_store -> HeapAccess<>::oop_store
@@ -558,7 +571,7 @@ void VM_EnhancedRedefineClasses::doit() {
_timer_heap_iterate.stop();
}
- Universe::root_oops_do(&oopClosureNoBarrier);
+ root_oops_do(&oopClosureNoBarrier);
if (UseG1GC) {
// this should work also for other GCs
_timer_heap_iterate.stop();
}
- Universe::root_oops_do(&oopClosureNoBarrier);
+ root_oops_do(&oopClosureNoBarrier);
if (UseG1GC) {
// this should work also for other GCs
@@ -737,8 +750,8 @@ bool VM_EnhancedRedefineClasses::is_modifiable_class(oop klass_mirror) {
// - link new classes
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
- _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count, true);
- _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count, true);
+ _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count);
+ _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count);
ResourceMark rm(THREAD);
// - link new classes
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
- _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count, true);
- _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count, true);
+ _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count);
+ _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count);
ResourceMark rm(THREAD);
@@ -832,12 +845,12 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
!the_class->is_non_strong_hidden(), // is_strong_hidden
true); // FIXME: check if correct. can_access_vm_annotations
- k = SystemDictionary::parse_stream(the_class_sym,
- the_class_loader,
- &st,
- cl_info,
- the_class,
- THREAD);
+ k = SystemDictionary::resolve_from_stream(&st,
+ the_class_sym,
+ the_class_loader,
+ cl_info,
+ the_class,
+ THREAD);
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
!the_class->is_non_strong_hidden(), // is_strong_hidden
true); // FIXME: check if correct. can_access_vm_annotations
- k = SystemDictionary::parse_stream(the_class_sym,
- the_class_loader,
- &st,
- cl_info,
- the_class,
- THREAD);
+ k = SystemDictionary::resolve_from_stream(&st,
+ the_class_sym,
+ the_class_loader,
+ cl_info,
+ the_class,
+ THREAD);
k->class_loader_data()->exchange_holders(the_class->class_loader_data());
@@ -853,12 +866,12 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
}
} else {
- k = SystemDictionary::resolve_from_stream(the_class_sym,
- the_class_loader,
- protection_domain,
- &st,
- the_class,
- THREAD);
+ k = SystemDictionary::resolve_from_stream(&st,
+ the_class_sym,
+ the_class_loader,
+ protection_domain,
+ the_class,
+ THREAD);
}
// Clear class_being_redefined just to be sure.
state->clear_class_being_redefined();
}
} else {
- k = SystemDictionary::resolve_from_stream(the_class_sym,
- the_class_loader,
- protection_domain,
- &st,
- the_class,
- THREAD);
+ k = SystemDictionary::resolve_from_stream(&st,
+ the_class_sym,
+ the_class_loader,
+ protection_domain,
+ the_class,
+ THREAD);
}
// Clear class_being_redefined just to be sure.
state->clear_class_being_redefined();
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index 5de375fb888..9be70039e32 100644
index 43f1aff8e91..ae0a15281f3 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -124,6 +124,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
static void register_nmethod_g1(nmethod* nm);
static void unpatch_bytecode(Method* method, TRAPS);
+ void root_oops_do(OopClosure *oopClosure);
+
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void compute_added_deleted_matching_methods();
static void register_nmethod_g1(nmethod* nm);
static void unpatch_bytecode(Method* method, TRAPS);
+ void root_oops_do(OopClosure *oopClosure);
+
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void compute_added_deleted_matching_methods();
diff --git a/src/hotspot/share/prims/resolvedMethodTable.cpp b/src/hotspot/share/prims/resolvedMethodTable.cpp
index 3c29eda4da0..a1eadc2958a 100644
--- a/src/hotspot/share/prims/resolvedMethodTable.cpp
@@ -678,10 +678,10 @@ index 3c29eda4da0..a1eadc2958a 100644
if (_local_table->insert(thread, lookup, wh)) {
log_insert(method);
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 1b7f8fe9f47..6a8ceb7fa8a 100644
index 3adc03a6583..b8eb4d0ab24 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -482,6 +482,8 @@ class Arguments : AllStatic {
@@ -487,6 +487,8 @@ class Arguments : AllStatic {
// Adjusts the arguments after the OS have adjusted the arguments
static jint adjust_after_os();

View File

@@ -1,27 +1,27 @@
From 6e18ad67981ab5a1bbdac46e1e0c7cc80beb4a5b Mon Sep 17 00:00:00 2001
From d170fa0a21d5c62348fe77eeaa81b16c8c51531a Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 11:38:48 +0100
Subject: [PATCH 22/39] Fix crash on GrowableArray allocation in C_HEAP
Subject: [PATCH 22/42] Fix crash on GrowableArray allocation in C_HEAP
---
src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 071dbc6d6ad..a5264077bfe 100644
index 0b1db1581d1..b88c071be13 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -750,8 +750,8 @@ bool VM_EnhancedRedefineClasses::is_modifiable_class(oop klass_mirror) {
// - link new classes
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
- _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count);
- _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count);
+ _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count, mtInternal);
+ _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count, mtInternal);
ResourceMark rm(THREAD);
// - link new classes
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
- _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count);
- _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count);
+ _affected_klasses = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Klass*>(_class_count, mtInternal);
+ _new_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<InstanceKlass*>(_class_count, mtInternal);
ResourceMark rm(THREAD);
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 96680710816d9cd6e9cd8d5ce6dcbe054a7e53bd Mon Sep 17 00:00:00 2001
From 8f59a799ec9996f3d92843f1a9f8504390c20009 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 11:39:05 +0100
Subject: [PATCH 23/39] Rename confusing method name old_if_redefined to
Subject: [PATCH 23/42] Rename confusing method name old_if_redefined to
old_if_redefining
---

View File

@@ -1,7 +1,7 @@
From 5d10d789150dfa6f8366dceb7fce3251d725ab8a Mon Sep 17 00:00:00 2001
From cc05b370b2fdf4c367bb9b6e4a21b9f97ae53dcf Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Fri, 12 Feb 2021 12:33:47 +0100
Subject: [PATCH 24/39] Check InstanceKlass::has_nestmate_access_to with active
Subject: [PATCH 24/42] Check InstanceKlass::has_nestmate_access_to with active
classes
Dcevm can leave old host in nested class if nested class is not

View File

@@ -1,7 +1,7 @@
From dac9d7a7d87de680a50be7c2857646323c4c8ffa Mon Sep 17 00:00:00 2001
From fdc87a0203f5aa1a21b3bf2e7a5aa114739f66cf Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 13 Feb 2021 20:47:52 +0100
Subject: [PATCH 25/39] JBR-3111 Update class in all dictionaries where it was
Subject: [PATCH 25/42] JBR-3111 Update class in all dictionaries where it was
already defined
This patch keeps compatibility with std redefinition, that does not

View File

@@ -1,7 +1,7 @@
From f42115a3d488c93a2d163aebd030530f060dcef8 Mon Sep 17 00:00:00 2001
From a33424d92f6001c9f9cb627b620f490fdfbc1196 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 20 Feb 2021 15:47:47 +0100
Subject: [PATCH 26/39] Add ClassLoaderDataGraph_lock to define new class in
Subject: [PATCH 26/42] Add ClassLoaderDataGraph_lock to define new class in
enhanced redefiniton
ClassLoaderDataGraph locking for introduced in redefinition in

View File

@@ -1,7 +1,7 @@
From 30308e893e43425da166175f5f18f84abf2c6110 Mon Sep 17 00:00:00 2001
From e85a74a6bd33e5d81569ec209311e698de402990 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Thu, 18 Jun 2020 18:40:11 +0200
Subject: [PATCH 27/39] JBR-3140 - support for modularized HotswapAgent
Subject: [PATCH 27/42] JBR-3140 - support for modularized HotswapAgent
Add -XX:HotswapAgent=[disabled,fatjar.core]
---
@@ -13,10 +13,10 @@ Add -XX:HotswapAgent=[disabled,fatjar.core]
5 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index cbcd8f91c66..4f9cfde9323 100644
index 252ebbc4e57..9c6fab8ae4b 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -4001,6 +4001,8 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
@@ -4034,6 +4034,8 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
}
}
@@ -25,7 +25,7 @@ index cbcd8f91c66..4f9cfde9323 100644
#if !INCLUDE_CDS
if (DumpSharedSpaces || RequireSharedSpaces) {
jio_fprintf(defaultStream::error_stream(),
@@ -4357,3 +4359,78 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
@@ -4390,3 +4392,78 @@ 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
}
@@ -105,10 +105,10 @@ index cbcd8f91c66..4f9cfde9323 100644
+
+}
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 6a8ceb7fa8a..7669a283375 100644
index b8eb4d0ab24..a14e11b48ef 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -491,6 +491,10 @@ class Arguments : AllStatic {
@@ -496,6 +496,10 @@ class Arguments : AllStatic {
static size_t conservative_max_heap_alignment() { return _conservative_max_heap_alignment; }
@@ -154,7 +154,7 @@ index 8bc3a9a1548..c5f58fd16ee 100644
RUNTIME_CONSTRAINTS(DECLARE_CONSTRAINT)
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 7051b634a9b..d23e43407e1 100644
index fb6d1667156..20838a878ce 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2090,7 +2090,16 @@ const intx ObjectAlignmentInBytes = 8;

View File

@@ -1,7 +1,7 @@
From 51f0036ff15d39330b787512af9e38d15c748e4b Mon Sep 17 00:00:00 2001
From b2e59ba5f246798f4901fa16d8fd3aeceac8309e Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 7 Mar 2021 20:22:54 +0100
Subject: [PATCH 28/39] Support for redefinition of Well Known classses
Subject: [PATCH 28/42] Support for redefinition of Well Known classses
(java.*,jdk.*, sun.*)
---
@@ -222,126 +222,126 @@ index 4b2211c415e..4d4c444220e 100644
// Reference pending list manipulation. Access is protected by
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index a5264077bfe..e00f0e19e20 100644
index b88c071be13..fb9da134e97 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -55,6 +55,7 @@
#include "prims/resolvedMethodTable.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/deoptimization.hpp"
+#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/relocator.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/deoptimization.hpp"
+#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/relocator.hpp"
#include "runtime/fieldDescriptor.hpp"
@@ -66,6 +67,7 @@
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/shared/dcevmSharedGC.hpp"
#include "gc/shared/scavengableNMethods.hpp"
+#include "gc/shared/oopStorageSet.inline.hpp"
#include "ci/ciObjectFactory.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/shared/dcevmSharedGC.hpp"
#include "gc/shared/scavengableNMethods.hpp"
+#include "gc/shared/oopStorageSet.inline.hpp"
#include "ci/ciObjectFactory.hpp"
Array<Method*>* VM_EnhancedRedefineClasses::_old_methods = NULL;
@@ -521,6 +523,16 @@ void VM_EnhancedRedefineClasses::doit() {
redefine_single_class(_new_classes->at(i), thread);
}
+ // Update possible redefinition of vm classes (like ClassLoader)
+ for (int i = 0; i < _new_classes->length(); i++) {
+ InstanceKlass* cur = _new_classes->at(i);
+ if (cur->old_version() != NULL && vmClasses::update_vm_klass(InstanceKlass::cast(cur->old_version()), cur))
+ {
+ log_trace(redefine, class, obsolete, metadata)("Well known class updated %s", cur->external_name());
+ ciObjectFactory::set_reinitialize_vm_klasses();
+ }
+ }
+
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
flush_dependent_code(thread);
redefine_single_class(_new_classes->at(i), thread);
}
+ // Update possible redefinition of vm classes (like ClassLoader)
+ for (int i = 0; i < _new_classes->length(); i++) {
+ InstanceKlass* cur = _new_classes->at(i);
+ if (cur->old_version() != NULL && vmClasses::update_vm_klass(InstanceKlass::cast(cur->old_version()), cur))
+ {
+ log_trace(redefine, class, obsolete, metadata)("Well known class updated %s", cur->external_name());
+ ciObjectFactory::set_reinitialize_vm_klasses();
+ }
+ }
+
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
flush_dependent_code(thread);
@@ -688,12 +700,56 @@ void VM_EnhancedRedefineClasses::doit() {
_timer_vm_op_doit.stop();
}
+void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
+ if (!_new_classes->is_empty()) {
+ ResourceMark rm(Thread::current());
+
+ for (int i = 0; i < _new_classes->length(); i++) {
+ InstanceKlass* cur = _new_classes->at(i);
+
+ if (cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/")) {
+
+ if (cur == vmClasses::ClassLoader_klass()) {
+ // ClassLoader.addClass method is cached in Universe, we must redefine
+ Universe::reinitialize_loader_addClass_method(Thread::current());
+ log_trace(redefine, class, obsolete, metadata)("Reinitialize ClassLoade addClass method cache.");
+ }
+
+ // naive assumptions that only JDK classes has native static "registerNative" and "initIDs" methods
+ int end;
+ Symbol* signature = vmSymbols::registerNatives_method_name();
+ int midx = cur->find_method_by_name(signature, &end);
+ if (midx == -1) {
+ signature = vmSymbols::initIDs_method_name();
+ midx = cur->find_method_by_name(signature, &end);
+ }
+ Method* m = NULL;
+ if (midx != -1) {
+ m = cur->methods()->at(midx);
+ }
+ if (m != NULL && m->is_static() && m->is_native()) {
+ // call static registerNative if present
+ JavaValue result(T_VOID);
+ JavaCalls::call_static(&result,
+ cur,
+ signature,
+ vmSymbols::void_method_signature(),
+ Thread::current());
+ log_trace(redefine, class, obsolete, metadata)("Reregister natives of JDK class %s", cur->external_name());
+ }
+ }
+ }
+ }
+}
+
// Cleanup - runs in JVM thread
// - free used memory
// - end GC
void VM_EnhancedRedefineClasses::doit_epilogue() {
VM_GC_Operation::doit_epilogue();
+ reinitializeJDKClasses();
+
if (_new_classes != NULL) {
delete _new_classes;
}
_timer_vm_op_doit.stop();
}
+void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
+ if (!_new_classes->is_empty()) {
+ ResourceMark rm(Thread::current());
+
+ for (int i = 0; i < _new_classes->length(); i++) {
+ InstanceKlass* cur = _new_classes->at(i);
+
+ if (cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/")) {
+
+ if (cur == vmClasses::ClassLoader_klass()) {
+ // ClassLoader.addClass method is cached in Universe, we must redefine
+ Universe::reinitialize_loader_addClass_method(Thread::current());
+ log_trace(redefine, class, obsolete, metadata)("Reinitialize ClassLoade addClass method cache.");
+ }
+
+ // naive assumptions that only JDK classes has native static "registerNative" and "initIDs" methods
+ int end;
+ Symbol* signature = vmSymbols::registerNatives_method_name();
+ int midx = cur->find_method_by_name(signature, &end);
+ if (midx == -1) {
+ signature = vmSymbols::initIDs_method_name();
+ midx = cur->find_method_by_name(signature, &end);
+ }
+ Method* m = NULL;
+ if (midx != -1) {
+ m = cur->methods()->at(midx);
+ }
+ if (m != NULL && m->is_static() && m->is_native()) {
+ // call static registerNative if present
+ JavaValue result(T_VOID);
+ JavaCalls::call_static(&result,
+ cur,
+ signature,
+ vmSymbols::void_method_signature(),
+ Thread::current());
+ log_trace(redefine, class, obsolete, metadata)("Reregister natives of JDK class %s", cur->external_name());
+ }
+ }
+ }
+ }
+}
+
// Cleanup - runs in JVM thread
// - free used memory
// - end GC
void VM_EnhancedRedefineClasses::doit_epilogue() {
VM_GC_Operation::doit_epilogue();
+ reinitializeJDKClasses();
+
if (_new_classes != NULL) {
delete _new_classes;
}
@@ -1623,7 +1679,12 @@ void VM_EnhancedRedefineClasses::check_methods_and_mark_as_obsolete() {
// obsolete methods need a unique idnum so they become new entries in
// the jmethodID cache in InstanceKlass
- assert(old_method->method_idnum() == new_method->method_idnum(), "must match");
+ if (old_method->method_idnum() != new_method->method_idnum()) {
+ log_error(redefine, class, normalize)
+ ("Method not matched: %d != %d old: %s = new: %s", old_method->method_idnum(), new_method->method_idnum(),
+ old_method->name_and_sig_as_C_string(), new_method->name_and_sig_as_C_string());
+ // assert(old_method->method_idnum() == new_method->method_idnum(), "must match");
+ }
// u2 num = InstanceKlass::cast(_the_class_oop)->next_method_idnum();
// if (num != ConstMethod::UNSET_IDNUM) {
// old_method->set_method_idnum(num);
// obsolete methods need a unique idnum so they become new entries in
// the jmethodID cache in InstanceKlass
- assert(old_method->method_idnum() == new_method->method_idnum(), "must match");
+ if (old_method->method_idnum() != new_method->method_idnum()) {
+ log_error(redefine, class, normalize)
+ ("Method not matched: %d != %d old: %s = new: %s", old_method->method_idnum(), new_method->method_idnum(),
+ old_method->name_and_sig_as_C_string(), new_method->name_and_sig_as_C_string());
+ // assert(old_method->method_idnum() == new_method->method_idnum(), "must match");
+ }
// u2 num = InstanceKlass::cast(_the_class_oop)->next_method_idnum();
// if (num != ConstMethod::UNSET_IDNUM) {
// old_method->set_method_idnum(num);
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index 9be70039e32..673688dff84 100644
index ae0a15281f3..d0d2f78aa62 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -150,6 +150,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
u8 next_id();
+ void reinitializeJDKClasses();
+
static void check_class(InstanceKlass* k_oop, TRAPS);
static void dump_methods();
u8 next_id();
+ void reinitializeJDKClasses();
+
static void check_class(InstanceKlass* k_oop, TRAPS);
static void dump_methods();
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 6cde71e353f823f2df2d0875cd03b656fe060d5c Mon Sep 17 00:00:00 2001
From 76c2c517d54f0fdbae2cb10cceb6c1aab7ee3a7b Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 15 May 2021 10:31:28 +0200
Subject: [PATCH 29/39] Fix fastdebug compilation issues - cast_to_oop
Subject: [PATCH 29/42] Fix fastdebug compilation issues - cast_to_oop
---
.../share/gc/g1/g1FullGCCompactTask.cpp | 12 +++++-----
@@ -243,18 +243,18 @@ index cafa2503ef4..076ce6b0cce 100644
debug_only(prev_obj = cur_obj);
cur_obj += size;
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index e00f0e19e20..62477b5ce10 100644
index fb9da134e97..890382e2029 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -439,7 +439,7 @@ public:
int size = o->size();
if (_tmp_obj_size < size) {
_tmp_obj_size = size;
- _tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
+ _tmp_obj = cast_to_oop(resource_allocate_bytes(size * HeapWordSize));
}
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
int size = o->size();
if (_tmp_obj_size < size) {
_tmp_obj_size = size;
- _tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
+ _tmp_obj = cast_to_oop(resource_allocate_bytes(size * HeapWordSize));
}
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 585e2f11d293081db5dc919fa8c13fb5bc7cbc53 Mon Sep 17 00:00:00 2001
From e3dcad4df8369cfa4a5eb90058ce265c0233b272 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <lada.dvorak7@gmail.com>
Date: Fri, 19 Mar 2021 19:13:38 +0100
Subject: [PATCH 30/39] JBR-3458: Skip dynamic proxy classes based on
Subject: [PATCH 30/42] JBR-3458: Skip dynamic proxy classes based on
com.sun.proxy
---
@@ -9,19 +9,19 @@ Subject: [PATCH 30/39] JBR-3458: Skip dynamic proxy classes based on
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 62477b5ce10..7bdbc861c1f 100644
index 890382e2029..90dfc1ae545 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -707,7 +707,8 @@ void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
for (int i = 0; i < _new_classes->length(); i++) {
InstanceKlass* cur = _new_classes->at(i);
- if (cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/")) {
+ if ((cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/"))
+ && cur->name()->index_of_at(0, "$$") == -1) { // skip dynamic proxies
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
for (int i = 0; i < _new_classes->length(); i++) {
InstanceKlass* cur = _new_classes->at(i);
- if (cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/")) {
+ if ((cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/"))
+ && cur->name()->index_of_at(0, "$$") == -1) { // skip dynamic proxies
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
--
2.23.0

View File

@@ -1,7 +1,7 @@
From d4d56b7ad6cd8f73f7485cdc608655c014c55f3c Mon Sep 17 00:00:00 2001
From b57c414d7885df5142a4a6bbf78001e96938f8ac Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <lada.dvorak7@gmail.com>
Date: Sat, 20 Mar 2021 20:51:08 +0100
Subject: [PATCH 31/39] JBR-3459: Fix race condition in
Subject: [PATCH 31/42] JBR-3459: Fix race condition in
ClassLoaderDataGraph::classes_do
InstanceKlass in ClassLoaderData can be uninitialized when
@@ -51,31 +51,31 @@ index ebdb0bc2c8c..da712eaf55f 100644
static void methods_do(void f(Method*));
static void modules_do(void f(ModuleEntry*));
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 7bdbc861c1f..4e8e7141e61 100644
index 90dfc1ae545..10407755457 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -2164,9 +2164,19 @@ jvmtiError VM_EnhancedRedefineClasses::find_sorted_affected_classes(TRAPS) {
{
MutexLocker mcld(ClassLoaderDataGraph_lock);
- ClassLoaderDataGraph::classes_do(&closure);
+
+ // 0. we can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
+ // fully initialized class is in system dictionary
+ // ClassLoaderDataGraph::classes_do(&closure);
+
+ // 1. Scan over dictionaries
+ ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
+ // 2. Anonymous or hidden class is not in dictionary, we have to iterate anonymous cld directly, but there is race cond...
+ // TODO: review ... anonymous class is added to cld before InstanceKlass initialization,
+ // find out how to check if the InstanceKlass is initialized
+ ClassLoaderDataGraph::anonymous_or_hidden_classes_do(&closure);
}
- //ClassLoaderDataGraph::dictionary_classes_do(&closure);
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
{
MutexLocker mcld(ClassLoaderDataGraph_lock);
- ClassLoaderDataGraph::classes_do(&closure);
+
+ // 0. we can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
+ // fully initialized class is in system dictionary
+ // ClassLoaderDataGraph::classes_do(&closure);
+
+ // 1. Scan over dictionaries
+ ClassLoaderDataGraph::dictionary_classes_do(&closure);
+
+ // 2. Anonymous or hidden class is not in dictionary, we have to iterate anonymous cld directly, but there is race cond...
+ // TODO: review ... anonymous class is added to cld before InstanceKlass initialization,
+ // find out how to check if the InstanceKlass is initialized
+ ClassLoaderDataGraph::anonymous_or_hidden_classes_do(&closure);
}
- //ClassLoaderDataGraph::dictionary_classes_do(&closure);
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 5356c4dbbcc2564e401fc92b7680b870ee7070ec Mon Sep 17 00:00:00 2001
From 66d53205a84c8e4db40fb2fc91f563cde458afcc Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sat, 15 May 2021 13:01:25 +0200
Subject: [PATCH 32/39] Fix compilation problems
Subject: [PATCH 32/42] Fix compilation problems
---
src/hotspot/share/oops/instanceKlass.cpp | 2 +-
@@ -22,18 +22,18 @@ index 3cc96f98f41..c54b6b1bf46 100644
}
}
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index 4e8e7141e61..f6bef8262d3 100644
index 10407755457..e87d4452d31 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -708,7 +708,7 @@ void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
InstanceKlass* cur = _new_classes->at(i);
if ((cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/"))
- && cur->name()->index_of_at(0, "$$") == -1) { // skip dynamic proxies
+ && cur->name()->index_of_at(0, "$$", (int) strlen("$$")) == -1) { // skip dynamic proxies
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
InstanceKlass* cur = _new_classes->at(i);
if ((cur->name()->starts_with("java/") || cur->name()->starts_with("jdk/") || cur->name()->starts_with("sun/"))
- && cur->name()->index_of_at(0, "$$") == -1) { // skip dynamic proxies
+ && cur->name()->index_of_at(0, "$$", (int) strlen("$$")) == -1) { // skip dynamic proxies
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 3eb64e8ed65ae214091b55e497d24dff0eb1e353 Mon Sep 17 00:00:00 2001
From fd5b6cd29a5dfed532b0c6ac80c94271f388c5bc Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 20 Jun 2021 19:11:16 +0200
Subject: [PATCH 33/39] Fix dcevm issues related to refactorization of Thread
Subject: [PATCH 33/42] Fix dcevm issues related to refactorization of Thread
to JavaThread
---
@@ -23,328 +23,328 @@ index 5c058dc43ac..9de28f740ac 100644
{
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index f6bef8262d3..b662976afcb 100644
index e87d4452d31..0aeeea4e7a4 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -23,7 +23,6 @@
*/
#include "precompiled.hpp"
-#include "aot/aotLoader.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoadInfo.hpp"
*/
#include "precompiled.hpp"
-#include "aot/aotLoader.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoadInfo.hpp"
@@ -38,7 +37,7 @@
#include "interpreter/rewriter.hpp"
#include "logging/logStream.hpp"
#include "memory/metadataFactory.hpp"
-#include "memory/metaspaceShared.hpp"
+#include "cds/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
#include "oops/fieldStreams.hpp"
#include "interpreter/rewriter.hpp"
#include "logging/logStream.hpp"
#include "memory/metadataFactory.hpp"
-#include "memory/metaspaceShared.hpp"
+#include "cds/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/iterator.inline.hpp"
#include "oops/fieldStreams.hpp"
@@ -156,7 +155,7 @@ bool VM_EnhancedRedefineClasses::doit_prologue() {
// We first load new class versions in the prologue, because somewhere down the
// call chain it is required that the current thread is a Java thread.
- _res = load_new_class_versions(Thread::current());
+ _res = load_new_class_versions(JavaThread::current());
// prepare GC, lock heap
if (_res == JVMTI_ERROR_NONE && !VM_GC_Operation::doit_prologue()) {
// We first load new class versions in the prologue, because somewhere down the
// call chain it is required that the current thread is a Java thread.
- _res = load_new_class_versions(Thread::current());
+ _res = load_new_class_versions(JavaThread::current());
// prepare GC, lock heap
if (_res == JVMTI_ERROR_NONE && !VM_GC_Operation::doit_prologue()) {
@@ -250,7 +249,6 @@ void VM_EnhancedRedefineClasses::root_oops_do(OopClosure *oopClosure) {
Universe::vm_global()->oops_do(oopClosure);
Threads::oops_do(oopClosure, NULL);
- AOT_ONLY(AOTLoader::oops_do(oopClosure);)
OopStorageSet::strong_oops_do(oopClosure);
CodeBlobToOopClosure blobClosure(oopClosure, CodeBlobToOopClosure::FixRelocations);
Universe::vm_global()->oops_do(oopClosure);
Threads::oops_do(oopClosure, NULL);
- AOT_ONLY(AOTLoader::oops_do(oopClosure);)
OopStorageSet::strong_oops_do(oopClosure);
CodeBlobToOopClosure blobClosure(oopClosure, CodeBlobToOopClosure::FixRelocations);
@@ -520,7 +518,7 @@ void VM_EnhancedRedefineClasses::doit() {
// before the stack walk again.
for (int i = 0; i < _new_classes->length(); i++) {
- redefine_single_class(_new_classes->at(i), thread);
+ redefine_single_class(thread, _new_classes->at(i));
}
// Update possible redefinition of vm classes (like ClassLoader)
// before the stack walk again.
for (int i = 0; i < _new_classes->length(); i++) {
- redefine_single_class(_new_classes->at(i), thread);
+ redefine_single_class(thread, _new_classes->at(i));
}
// Update possible redefinition of vm classes (like ClassLoader)
@@ -535,7 +533,7 @@ void VM_EnhancedRedefineClasses::doit() {
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
- flush_dependent_code(thread);
+ flush_dependent_code();
// }
// Adjust constantpool caches for all classes that reference methods of the evolved class.
// Deoptimize all compiled code that depends on this class (do only once, because it clears whole cache)
// if (_max_redefinition_flags > Klass::ModifyClass) {
- flush_dependent_code(thread);
+ flush_dependent_code();
// }
// Adjust constantpool caches for all classes that reference methods of the evolved class.
@@ -692,7 +690,7 @@ void VM_EnhancedRedefineClasses::doit() {
assert(new_version->super() == NULL || new_version->super()->new_version() == NULL, "Super class must be newest version");
}
log_trace(redefine, class, obsolete, metadata)("calling check_class");
- ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(check_class, thread);
+ ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(check_class);
#ifdef PRODUCT
}
#endif
assert(new_version->super() == NULL || new_version->super()->new_version() == NULL, "Super class must be newest version");
}
log_trace(redefine, class, obsolete, metadata)("calling check_class");
- ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(check_class, thread);
+ ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(check_class);
#ifdef PRODUCT
}
#endif
@@ -702,7 +700,7 @@ void VM_EnhancedRedefineClasses::doit() {
void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
if (!_new_classes->is_empty()) {
- ResourceMark rm(Thread::current());
+ ResourceMark rm(JavaThread::current());
for (int i = 0; i < _new_classes->length(); i++) {
InstanceKlass* cur = _new_classes->at(i);
void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
if (!_new_classes->is_empty()) {
- ResourceMark rm(Thread::current());
+ ResourceMark rm(JavaThread::current());
for (int i = 0; i < _new_classes->length(); i++) {
InstanceKlass* cur = _new_classes->at(i);
@@ -712,7 +710,7 @@ void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
- Universe::reinitialize_loader_addClass_method(Thread::current());
+ Universe::reinitialize_loader_addClass_method(JavaThread::current());
log_trace(redefine, class, obsolete, metadata)("Reinitialize ClassLoade addClass method cache.");
}
if (cur == vmClasses::ClassLoader_klass()) {
// ClassLoader.addClass method is cached in Universe, we must redefine
- Universe::reinitialize_loader_addClass_method(Thread::current());
+ Universe::reinitialize_loader_addClass_method(JavaThread::current());
log_trace(redefine, class, obsolete, metadata)("Reinitialize ClassLoade addClass method cache.");
}
@@ -735,7 +733,7 @@ void VM_EnhancedRedefineClasses::reinitializeJDKClasses() {
cur,
signature,
vmSymbols::void_method_signature(),
- Thread::current());
+ JavaThread::current());
log_trace(redefine, class, obsolete, metadata)("Reregister natives of JDK class %s", cur->external_name());
}
}
cur,
signature,
vmSymbols::void_method_signature(),
- Thread::current());
+ JavaThread::current());
log_trace(redefine, class, obsolete, metadata)("Reregister natives of JDK class %s", cur->external_name());
}
}
@@ -793,7 +791,7 @@ bool VM_EnhancedRedefineClasses::is_modifiable_class(oop klass_mirror) {
// Cannot redefine or retransform an anonymous class.
// TODO: check if is correct in j15
- if (InstanceKlass::cast(k)->is_unsafe_anonymous() || InstanceKlass::cast(k)->is_hidden()) {
+ if (InstanceKlass::cast(k)->is_hidden()) {
return false;
}
return true;
// Cannot redefine or retransform an anonymous class.
// TODO: check if is correct in j15
- if (InstanceKlass::cast(k)->is_unsafe_anonymous() || InstanceKlass::cast(k)->is_hidden()) {
+ if (InstanceKlass::cast(k)->is_hidden()) {
return false;
}
return true;
@@ -879,23 +877,15 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
InstanceKlass* k;
- if (the_class->is_unsafe_anonymous() || the_class->is_hidden()) {
+ if (the_class->is_hidden()) {
InstanceKlass* dynamic_host_class = NULL;
- InstanceKlass* unsafe_anonymous_host = NULL;
if (the_class->is_hidden()) {
log_debug(redefine, class, load)("loading hidden class %s", the_class->name()->as_C_string());
dynamic_host_class = the_class->nest_host(THREAD);
}
- if (the_class->is_unsafe_anonymous()) {
- log_debug(redefine, class, load)("loading usafe anonymous %s", the_class->name()->as_C_string());
- unsafe_anonymous_host = the_class->unsafe_anonymous_host();
- }
-
ClassLoadInfo cl_info(protection_domain,
- unsafe_anonymous_host,
- NULL, // cp_patches
dynamic_host_class, // dynamic_nest_host
Handle(), // classData
the_class->is_hidden(), // is_hidden
InstanceKlass* k;
- if (the_class->is_unsafe_anonymous() || the_class->is_hidden()) {
+ if (the_class->is_hidden()) {
InstanceKlass* dynamic_host_class = NULL;
- InstanceKlass* unsafe_anonymous_host = NULL;
if (the_class->is_hidden()) {
log_debug(redefine, class, load)("loading hidden class %s", the_class->name()->as_C_string());
dynamic_host_class = the_class->nest_host(THREAD);
}
- if (the_class->is_unsafe_anonymous()) {
- log_debug(redefine, class, load)("loading usafe anonymous %s", the_class->name()->as_C_string());
- unsafe_anonymous_host = the_class->unsafe_anonymous_host();
- }
-
ClassLoadInfo cl_info(protection_domain,
- unsafe_anonymous_host,
- NULL, // cp_patches
dynamic_host_class, // dynamic_nest_host
Handle(), // classData
the_class->is_hidden(), // is_hidden
@@ -1143,7 +1133,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
Array<Method*>* k_new_methods(new_class->methods());
int n_old_methods = k_old_methods->length();
int n_new_methods = k_new_methods->length();
- Thread* thread = Thread::current();
+ JavaThread* thread = JavaThread::current();
int ni = 0;
int oi = 0;
Array<Method*>* k_new_methods(new_class->methods());
int n_old_methods = k_old_methods->length();
int n_new_methods = k_new_methods->length();
- Thread* thread = Thread::current();
+ JavaThread* thread = JavaThread::current();
int ni = 0;
int oi = 0;
@@ -1491,8 +1481,8 @@ void VM_EnhancedRedefineClasses::rollback() {
// Rewrite faster byte-codes back to their slower equivalent. Undoes rewriting happening in templateTable_xxx.cpp
// The reason is that once we zero cpool caches, we need to re-resolve all entries again. Faster bytecodes do not
// do that, they assume that cache entry is resolved already.
-void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method, TRAPS) {
- RawBytecodeStream bcs(methodHandle(THREAD, method));
+void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
+ RawBytecodeStream bcs(methodHandle(Thread::current(), method));
Bytecodes::Code code;
Bytecodes::Code java_code;
while (!bcs.is_last_bytecode()) {
// Rewrite faster byte-codes back to their slower equivalent. Undoes rewriting happening in templateTable_xxx.cpp
// The reason is that once we zero cpool caches, we need to re-resolve all entries again. Faster bytecodes do not
// do that, they assume that cache entry is resolved already.
-void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method, TRAPS) {
- RawBytecodeStream bcs(methodHandle(THREAD, method));
+void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method) {
+ RawBytecodeStream bcs(methodHandle(Thread::current(), method));
Bytecodes::Code code;
Bytecodes::Code java_code;
while (!bcs.is_last_bytecode()) {
@@ -1535,10 +1525,10 @@ void VM_EnhancedRedefineClasses::unpatch_bytecode(Method* method, TRAPS) {
assert(code2 == Bytecodes::_fast_igetfield ||
code2 == Bytecodes::_fast_agetfield ||
code2 == Bytecodes::_fast_fgetfield, "");
- *(bcp + 1) = Bytecodes::java_code(code2);
- }
+ *(bcp + 1) = Bytecodes::java_code(code2);
}
}
+}
// Unevolving classes may point to old methods directly
// from their constant pool caches, itables, and/or vtables. We
assert(code2 == Bytecodes::_fast_igetfield ||
code2 == Bytecodes::_fast_agetfield ||
code2 == Bytecodes::_fast_fgetfield, "");
- *(bcp + 1) = Bytecodes::java_code(code2);
- }
+ *(bcp + 1) = Bytecodes::java_code(code2);
}
}
+}
// Unevolving classes may point to old methods directly
// from their constant pool caches, itables, and/or vtables. We
@@ -1559,9 +1549,11 @@ void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
constantPoolHandle other_cp = constantPoolHandle(_thread, ik->constants());
// Update host klass of anonymous classes (for example, produced by lambdas) to newest version.
+ /*
if (ik->is_unsafe_anonymous() && ik->unsafe_anonymous_host()->new_version() != NULL) {
ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
+ */
// FIXME: check new nest_host for hidden
constantPoolHandle other_cp = constantPoolHandle(_thread, ik->constants());
// Update host klass of anonymous classes (for example, produced by lambdas) to newest version.
+ /*
if (ik->is_unsafe_anonymous() && ik->unsafe_anonymous_host()->new_version() != NULL) {
ik->set_unsafe_anonymous_host(InstanceKlass::cast(ik->unsafe_anonymous_host()->newest_version()));
}
+ */
// FIXME: check new nest_host for hidden
@@ -1596,7 +1588,7 @@ void VM_EnhancedRedefineClasses::ClearCpoolCacheAndUnpatch::do_klass(Klass* k) {
// If bytecode rewriting is enabled, we also need to unpatch bytecode to force resolution of zeroed entries
if (RewriteBytecodes) {
- ik->methods_do(unpatch_bytecode, _thread);
+ ik->methods_do(unpatch_bytecode);
}
}
// If bytecode rewriting is enabled, we also need to unpatch bytecode to force resolution of zeroed entries
if (RewriteBytecodes) {
- ik->methods_do(unpatch_bytecode, _thread);
+ ik->methods_do(unpatch_bytecode);
}
}
@@ -1628,7 +1620,7 @@ void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
}
-void VM_EnhancedRedefineClasses::update_jmethod_ids(TRAPS) {
+void VM_EnhancedRedefineClasses::update_jmethod_ids(Thread* current) {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
jmethodID jmid = old_method->find_jmethod_id_or_null();
}
-void VM_EnhancedRedefineClasses::update_jmethod_ids(TRAPS) {
+void VM_EnhancedRedefineClasses::update_jmethod_ids(Thread* current) {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
jmethodID jmid = old_method->find_jmethod_id_or_null();
@@ -1639,10 +1631,10 @@ void VM_EnhancedRedefineClasses::update_jmethod_ids(TRAPS) {
if (jmid != NULL) {
// There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(THREAD, _matching_new_methods[j]);
+ methodHandle new_method_h(current, _matching_new_methods[j]);
if (old_method->new_version() == NULL) {
- methodHandle old_method_h(THREAD, _matching_old_methods[j]);
+ methodHandle old_method_h(current, _matching_old_methods[j]);
jmethodID new_jmethod_id = Method::make_jmethod_id(old_method_h->method_holder()->class_loader_data(), old_method_h());
bool result = InstanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id);
} else {
if (jmid != NULL) {
// There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(THREAD, _matching_new_methods[j]);
+ methodHandle new_method_h(current, _matching_new_methods[j]);
if (old_method->new_version() == NULL) {
- methodHandle old_method_h(THREAD, _matching_old_methods[j]);
+ methodHandle old_method_h(current, _matching_old_methods[j]);
jmethodID new_jmethod_id = Method::make_jmethod_id(old_method_h->method_holder()->class_loader_data(), old_method_h());
bool result = InstanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id);
} else {
@@ -1864,7 +1856,7 @@ void VM_EnhancedRedefineClasses::mark_dependent_code(InstanceKlass* ik) {
// subsequent calls to RedefineClasses need only throw away code
// that depends on the class.
//
-void VM_EnhancedRedefineClasses::flush_dependent_code(TRAPS) {
+void VM_EnhancedRedefineClasses::flush_dependent_code() {
assert_locked_or_safepoint(Compile_lock);
// All dependencies have been recorded from startup or this is a second or
// subsequent calls to RedefineClasses need only throw away code
// that depends on the class.
//
-void VM_EnhancedRedefineClasses::flush_dependent_code(TRAPS) {
+void VM_EnhancedRedefineClasses::flush_dependent_code() {
assert_locked_or_safepoint(Compile_lock);
// All dependencies have been recorded from startup or this is a second or
@@ -1959,9 +1951,9 @@ void VM_EnhancedRedefineClasses::compute_added_deleted_matching_methods() {
// a helper method to be specified. The interesting parameters
// that we would like to pass to the helper method are saved in
// static global fields in the VM operation.
-void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_oop, TRAPS) {
+void VM_EnhancedRedefineClasses::redefine_single_class(Thread* current, InstanceKlass* new_class_oop) {
- HandleMark hm(THREAD); // make sure handles from this call are freed
+ HandleMark hm(current); // make sure handles from this call are freed
InstanceKlass* new_class = new_class_oop;
InstanceKlass* the_class = InstanceKlass::cast(new_class_oop->old_version());
// a helper method to be specified. The interesting parameters
// that we would like to pass to the helper method are saved in
// static global fields in the VM operation.
-void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_oop, TRAPS) {
+void VM_EnhancedRedefineClasses::redefine_single_class(Thread* current, InstanceKlass* new_class_oop) {
- HandleMark hm(THREAD); // make sure handles from this call are freed
+ HandleMark hm(current); // make sure handles from this call are freed
InstanceKlass* new_class = new_class_oop;
InstanceKlass* the_class = InstanceKlass::cast(new_class_oop->old_version());
@@ -1980,7 +1972,7 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
// track number of methods that are EMCP for add_previous_version() call below
check_methods_and_mark_as_obsolete();
- update_jmethod_ids(THREAD);
+ update_jmethod_ids(current);
_any_class_has_resolved_methods = the_class->has_resolved_methods() || _any_class_has_resolved_methods;
// track number of methods that are EMCP for add_previous_version() call below
check_methods_and_mark_as_obsolete();
- update_jmethod_ids(THREAD);
+ update_jmethod_ids(current);
_any_class_has_resolved_methods = the_class->has_resolved_methods() || _any_class_has_resolved_methods;
@@ -2002,14 +1994,14 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
*/
{
- ResourceMark rm(THREAD);
+ ResourceMark rm(current);
// increment the classRedefinedCount field in the_class and in any
// direct and indirect subclasses of the_class
- increment_class_counter(new_class, THREAD);
+ increment_class_counter(current, new_class);
log_info(redefine, class, load)
("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)",
new_class->external_name(), java_lang_Class::classRedefinedCount(new_class->java_mirror()), os::available_memory() >> 10);
- Events::log_redefinition(THREAD, "redefined class name=%s, count=%d",
+ Events::log_redefinition(current, "redefined class name=%s, count=%d",
new_class->external_name(),
java_lang_Class::classRedefinedCount(new_class->java_mirror()));
}
*/
{
- ResourceMark rm(THREAD);
+ ResourceMark rm(current);
// increment the classRedefinedCount field in the_class and in any
// direct and indirect subclasses of the_class
- increment_class_counter(new_class, THREAD);
+ increment_class_counter(current, new_class);
log_info(redefine, class, load)
("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)",
new_class->external_name(), java_lang_Class::classRedefinedCount(new_class->java_mirror()), os::available_memory() >> 10);
- Events::log_redefinition(THREAD, "redefined class name=%s, count=%d",
+ Events::log_redefinition(current, "redefined class name=%s, count=%d",
new_class->external_name(),
java_lang_Class::classRedefinedCount(new_class->java_mirror()));
}
@@ -2018,21 +2010,21 @@ void VM_EnhancedRedefineClasses::redefine_single_class(InstanceKlass* new_class_
// Increment the classRedefinedCount field in the specific InstanceKlass
// and in all direct and indirect subclasses.
-void VM_EnhancedRedefineClasses::increment_class_counter(InstanceKlass *ik, TRAPS) {
+void VM_EnhancedRedefineClasses::increment_class_counter(Thread* current, InstanceKlass *ik) {
oop class_mirror = ik->old_version()->java_mirror();
Klass* class_oop = java_lang_Class::as_Klass(class_mirror);
int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1;
java_lang_Class::set_classRedefinedCount(ik->java_mirror(), new_count);
}
-void VM_EnhancedRedefineClasses::check_class(InstanceKlass* ik, TRAPS) {
+void VM_EnhancedRedefineClasses::check_class(InstanceKlass* ik) {
if (ik->is_instance_klass() && ik->old_version() != NULL) {
- HandleMark hm(THREAD);
+ HandleMark hm(Thread::current());
assert(ik->new_version() == NULL, "must be latest version in system dictionary");
if (ik->vtable_length() > 0) {
- ResourceMark rm(THREAD);
+ ResourceMark rm(Thread::current());
assert(ik->vtable().check_no_old_or_obsolete_entries(), "old method found");
ik->vtable().verify(tty, true);
}
// Increment the classRedefinedCount field in the specific InstanceKlass
// and in all direct and indirect subclasses.
-void VM_EnhancedRedefineClasses::increment_class_counter(InstanceKlass *ik, TRAPS) {
+void VM_EnhancedRedefineClasses::increment_class_counter(Thread* current, InstanceKlass *ik) {
oop class_mirror = ik->old_version()->java_mirror();
Klass* class_oop = java_lang_Class::as_Klass(class_mirror);
int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1;
java_lang_Class::set_classRedefinedCount(ik->java_mirror(), new_count);
}
-void VM_EnhancedRedefineClasses::check_class(InstanceKlass* ik, TRAPS) {
+void VM_EnhancedRedefineClasses::check_class(InstanceKlass* ik) {
if (ik->is_instance_klass() && ik->old_version() != NULL) {
- HandleMark hm(THREAD);
+ HandleMark hm(Thread::current());
assert(ik->new_version() == NULL, "must be latest version in system dictionary");
if (ik->vtable_length() > 0) {
- ResourceMark rm(THREAD);
+ ResourceMark rm(Thread::current());
assert(ik->vtable().check_no_old_or_obsolete_entries(), "old method found");
ik->vtable().verify(tty, true);
}
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
index 673688dff84..378e6f78582 100644
index d0d2f78aa62..252287a3697 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.hpp
@@ -122,7 +122,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
static void mark_as_scavengable(nmethod* nm);
static void unregister_nmethod_g1(nmethod* nm);
static void register_nmethod_g1(nmethod* nm);
- static void unpatch_bytecode(Method* method, TRAPS);
+ static void unpatch_bytecode(Method* method);
void root_oops_do(OopClosure *oopClosure);
static void mark_as_scavengable(nmethod* nm);
static void unregister_nmethod_g1(nmethod* nm);
static void register_nmethod_g1(nmethod* nm);
- static void unpatch_bytecode(Method* method, TRAPS);
+ static void unpatch_bytecode(Method* method);
void root_oops_do(OopClosure *oopClosure);
@@ -131,28 +131,28 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
void compute_added_deleted_matching_methods();
// Change jmethodIDs to point to the new methods
- void update_jmethod_ids(TRAPS);
+ void update_jmethod_ids(Thread* current);
// marking methods as old and/or obsolete
void check_methods_and_mark_as_obsolete();
void transfer_old_native_function_registrations(InstanceKlass* the_class);
// Install the redefinition of a class
- void redefine_single_class(InstanceKlass* new_class_oop, TRAPS);
+ void redefine_single_class(Thread* current, InstanceKlass* new_class_oop);
// Increment the classRedefinedCount field in the specific InstanceKlass
// and in all direct and indirect subclasses.
- void increment_class_counter(InstanceKlass *ik, TRAPS);
+ void increment_class_counter(Thread* current, InstanceKlass *ik);
void mark_dependent_code(InstanceKlass* ik);
- void flush_dependent_code(TRAPS);
+ void flush_dependent_code();
u8 next_id();
void reinitializeJDKClasses();
- static void check_class(InstanceKlass* k_oop, TRAPS);
+ static void check_class(InstanceKlass* k_oop);
static void dump_methods();
void compute_added_deleted_matching_methods();
// Change jmethodIDs to point to the new methods
- void update_jmethod_ids(TRAPS);
+ void update_jmethod_ids(Thread* current);
// marking methods as old and/or obsolete
void check_methods_and_mark_as_obsolete();
void transfer_old_native_function_registrations(InstanceKlass* the_class);
// Install the redefinition of a class
- void redefine_single_class(InstanceKlass* new_class_oop, TRAPS);
+ void redefine_single_class(Thread* current, InstanceKlass* new_class_oop);
// Increment the classRedefinedCount field in the specific InstanceKlass
// and in all direct and indirect subclasses.
- void increment_class_counter(InstanceKlass *ik, TRAPS);
+ void increment_class_counter(Thread* current, InstanceKlass *ik);
void mark_dependent_code(InstanceKlass* ik);
- void flush_dependent_code(TRAPS);
+ void flush_dependent_code();
u8 next_id();
void reinitializeJDKClasses();
- static void check_class(InstanceKlass* k_oop, TRAPS);
+ static void check_class(InstanceKlass* k_oop);
static void dump_methods();
--
2.23.0

View File

@@ -1,7 +1,7 @@
From db2d7cd0cd956f297c58baa7500c1440092648bf Mon Sep 17 00:00:00 2001
From 52c661cebf92d4ad68058ada5d3463e54428aee7 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 20 Jun 2021 19:42:51 +0200
Subject: [PATCH 34/39] Fix init_method_MemberName after Thread to JavaThread
Subject: [PATCH 34/42] Fix init_method_MemberName after Thread to JavaThread
refactorization
---
@@ -116,27 +116,27 @@ index 317a006a4ec..36587031fec 100644
void array_klasses_do(void f(Klass* k, TRAPS), TRAPS);
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index b662976afcb..ff012383872 100644
index 0aeeea4e7a4..e7f26b7d08e 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -292,8 +292,15 @@ class ChangePointersOopClosure : public BasicOopIterateClosure {
// Note: we might set NULL at this point, which should force AbstractMethodError at runtime
Thread *thread = Thread::current();
CallInfo info(new_method, newest, thread);
- Handle objHandle(thread, obj);
- MethodHandles::init_method_MemberName(objHandle, info);
+ oop resolved_method = ResolvedMethodTable::find_method(info.resolved_method());
+ if (resolved_method != NULL) {
+ info.set_resolved_method_name_dcevm(resolved_method, thread);
+ Handle objHandle(thread, obj);
+ MethodHandles::init_method_MemberName(objHandle, info);
+ } else {
+ assert(0, "Must be resolved");
+ java_lang_invoke_MemberName::set_method(obj, NULL);
+ }
} else {
java_lang_invoke_MemberName::set_method(obj, NULL);
}
// Note: we might set NULL at this point, which should force AbstractMethodError at runtime
Thread *thread = Thread::current();
CallInfo info(new_method, newest, thread);
- Handle objHandle(thread, obj);
- MethodHandles::init_method_MemberName(objHandle, info);
+ oop resolved_method = ResolvedMethodTable::find_method(info.resolved_method());
+ if (resolved_method != NULL) {
+ info.set_resolved_method_name_dcevm(resolved_method, thread);
+ Handle objHandle(thread, obj);
+ MethodHandles::init_method_MemberName(objHandle, info);
+ } else {
+ assert(0, "Must be resolved");
+ java_lang_invoke_MemberName::set_method(obj, NULL);
+ }
} else {
java_lang_invoke_MemberName::set_method(obj, NULL);
}
--
2.23.0

View File

@@ -1,7 +1,7 @@
From bfbbae66340a223328542722b299f67c21166c52 Mon Sep 17 00:00:00 2001
From 523ce6dd1559af84ebd6e4f10e7de9142f88d9d0 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Mon, 21 Jun 2021 20:44:17 +0200
Subject: [PATCH 35/39] Fix "implicit conversion of NULL constant to 'bool'"
Subject: [PATCH 35/42] Fix "implicit conversion of NULL constant to 'bool'"
---
src/hotspot/share/prims/jvmtiRedefineClasses.cpp | 2 +-

View File

@@ -1,7 +1,7 @@
From 5bb6ef0fc266a748232c7c51191deab47d4f3857 Mon Sep 17 00:00:00 2001
From df4c29b24d41069168ee5647b2737348d9902090 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Wed, 30 Jun 2021 18:30:00 +0200
Subject: [PATCH 36/39] Fix, pass SystemDictionary::resolve_from_stream cl_info
Subject: [PATCH 36/42] Fix, pass SystemDictionary::resolve_from_stream cl_info
param
---
@@ -9,27 +9,27 @@ Subject: [PATCH 36/39] Fix, pass SystemDictionary::resolve_from_stream cl_info
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index ff012383872..cbcb484a290 100644
index e7f26b7d08e..4d145f30100 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -920,10 +920,16 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
}
} else {
+ ClassLoadInfo cl_info(protection_domain,
+ NULL, // dynamic_nest_host
+ Handle(), // classData
+ false, // is_hidden
+ !the_class->is_non_strong_hidden(), // is_strong_hidden
+ true); // FIXME: check if correct. can_access_vm_annotations
k = SystemDictionary::resolve_from_stream(&st,
the_class_sym,
the_class_loader,
- protection_domain,
+ cl_info,
the_class,
THREAD);
}
}
} else {
+ ClassLoadInfo cl_info(protection_domain,
+ NULL, // dynamic_nest_host
+ Handle(), // classData
+ false, // is_hidden
+ !the_class->is_non_strong_hidden(), // is_strong_hidden
+ true); // FIXME: check if correct. can_access_vm_annotations
k = SystemDictionary::resolve_from_stream(&st,
the_class_sym,
the_class_loader,
- protection_domain,
+ cl_info,
the_class,
THREAD);
}
--
2.23.0

View File

@@ -1,7 +1,7 @@
From 563e06d1c56cfc3f4e93e3975cd6c2dab8183c98 Mon Sep 17 00:00:00 2001
From 47ae71afaf12c7bab4f928579b154ec72aad3c02 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Wed, 30 Jun 2021 18:50:38 +0200
Subject: [PATCH 37/39] Search for affected classes in all initialized classes
Subject: [PATCH 37/42] Search for affected classes in all initialized classes
in cld
Fix also case when lambda interface is redefined. Lambda class is
@@ -83,31 +83,31 @@ index da712eaf55f..ff391ec74fc 100644
static void classes_do(void f(Klass* const));
static void methods_do(void f(Method*));
diff --git a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
index cbcb484a290..43899ea3d48 100644
index 4d145f30100..8615806ac98 100644
--- a/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiEnhancedRedefineClasses.cpp
@@ -2170,17 +2170,12 @@ jvmtiError VM_EnhancedRedefineClasses::find_sorted_affected_classes(TRAPS) {
{
MutexLocker mcld(ClassLoaderDataGraph_lock);
- // 0. we can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
- // fully initialized class is in system dictionary
+ // We can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
+ // fully initialized class is in system dictionary, but hidden classes are excluded. Therefore
+ // we use special method iterating over initialized classes only
// ClassLoaderDataGraph::classes_do(&closure);
- // 1. Scan over dictionaries
- ClassLoaderDataGraph::dictionary_classes_do(&closure);
-
- // 2. Anonymous or hidden class is not in dictionary, we have to iterate anonymous cld directly, but there is race cond...
- // TODO: review ... anonymous class is added to cld before InstanceKlass initialization,
- // find out how to check if the InstanceKlass is initialized
- ClassLoaderDataGraph::anonymous_or_hidden_classes_do(&closure);
+ ClassLoaderDataGraph::initialized_classes_do(&closure);
}
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
{
MutexLocker mcld(ClassLoaderDataGraph_lock);
- // 0. we can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
- // fully initialized class is in system dictionary
+ // We can't use ClassLoaderDataGraph::classes_do since classes can be uninitialized in cld,
+ // fully initialized class is in system dictionary, but hidden classes are excluded. Therefore
+ // we use special method iterating over initialized classes only
// ClassLoaderDataGraph::classes_do(&closure);
- // 1. Scan over dictionaries
- ClassLoaderDataGraph::dictionary_classes_do(&closure);
-
- // 2. Anonymous or hidden class is not in dictionary, we have to iterate anonymous cld directly, but there is race cond...
- // TODO: review ... anonymous class is added to cld before InstanceKlass initialization,
- // find out how to check if the InstanceKlass is initialized
- ClassLoaderDataGraph::anonymous_or_hidden_classes_do(&closure);
+ ClassLoaderDataGraph::initialized_classes_do(&closure);
}
log_trace(redefine, class, load)("%d classes affected", _affected_klasses->length());
--
2.23.0

View File

@@ -1,7 +1,7 @@
From acad06204bc4f7e7a89e9a44ded6b9d27082327b Mon Sep 17 00:00:00 2001
From e9c3042e13c5fe58cac7409b1f93a4dc287cc14a Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Wed, 30 Jun 2021 18:58:47 +0200
Subject: [PATCH 38/39] Fix compilation issue
Subject: [PATCH 38/42] Fix compilation issue
---
src/hotspot/share/interpreter/linkResolver.cpp | 4 ++--

View File

@@ -1,7 +1,7 @@
From 563465b112da14e8b81ef32eefc06c79015a809b Mon Sep 17 00:00:00 2001
From f303ef1fec703144efe6b9989a1a71fae0fb4263 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Tue, 27 Jul 2021 21:32:51 +0200
Subject: [PATCH 39/39] Remove duplicated lambdaFormInvokers.cpp
Subject: [PATCH 39/42] Remove duplicated lambdaFormInvokers.cpp
---
.../share/classfile/lambdaFormInvokers.cpp | 152 ------------------

View File

@@ -0,0 +1,222 @@
From 9cb4aaf17c8e35f39aed0dfa33e173b40968eee6 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Sun, 10 Oct 2021 20:25:30 +0200
Subject: [PATCH 40/42] JBR-3867 - update keys of jvmti TAG map after
redefinition
jdwp keeps relation class_ptr->class_ref in jvmti tag. class_ptr is used
as a tag key, tag value is refnode. There are new class_ptrs after
redefinition, therefore jdwp redefinition method update all affected
keys in the tag map.
---
src/hotspot/share/prims/jvmtiExtensions.cpp | 24 ++++++++++++++
.../share/native/libjdwp/VirtualMachineImpl.c | 20 +++++++++++
.../share/native/libjdwp/commonRef.c | 29 ++++++++++++++++
.../share/native/libjdwp/commonRef.h | 2 ++
.../share/native/libjdwp/util.c | 33 +++++++++++++++++++
.../share/native/libjdwp/util.h | 3 ++
6 files changed, 111 insertions(+)
diff --git a/src/hotspot/share/prims/jvmtiExtensions.cpp b/src/hotspot/share/prims/jvmtiExtensions.cpp
index a3fc430e1d1..ab2e663b95d 100644
--- a/src/hotspot/share/prims/jvmtiExtensions.cpp
+++ b/src/hotspot/share/prims/jvmtiExtensions.cpp
@@ -49,6 +49,15 @@ static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, ...) {
return JVMTI_ERROR_NONE;
}
+// extension function
+static jvmtiError JNICALL IsEnhancedClassRedefinitionEnabled(const jvmtiEnv* env, jboolean* enabled, ...) {
+ if (enabled == NULL) {
+ return JVMTI_ERROR_NULL_POINTER;
+ }
+ *enabled = (jboolean)AllowEnhancedClassRedefinition;
+ return JVMTI_ERROR_NONE;
+}
+
// register extension functions and events. In this implementation we
// have a single extension function (to prove the API) that tests if class
// unloading is enabled or disabled. We also have a single extension event
@@ -88,6 +97,21 @@ void JvmtiExtensions::register_extensions() {
event_params
};
_ext_events->append(&ext_event);
+
+ static jvmtiParamInfo func_params_enh_redef[] = {
+ { (char*)"IsEnhancedClassRedefinitionEnabled", JVMTI_KIND_OUT, JVMTI_TYPE_JBOOLEAN, JNI_FALSE }
+
+ };
+ static jvmtiExtensionFunctionInfo ext_func_enh_redef = {
+ (jvmtiExtensionFunction)IsEnhancedClassRedefinitionEnabled,
+ (char*)"com.sun.hotspot.functions.IsEnhancedClassRedefinitionEnabled",
+ (char*)"Tell if enhanced class redefinition is enabled (-noclassgc)",
+ sizeof(func_params_enh_redef)/sizeof(func_params_enh_redef[0]),
+ func_params_enh_redef,
+ 0, // no non-universal errors
+ NULL
+ };
+ _ext_functions->append(&ext_func_enh_redef);
}
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
index c07bdac2922..7e3070d7d1d 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
@@ -477,6 +477,14 @@ redefineClasses(PacketInputStream *in, PacketOutputStream *out)
if (ok == JNI_TRUE) {
jvmtiError error;
+ jlong classIds[classCount];
+
+ if (gdata->isEnhancedClassRedefinitionEnabled) {
+ for (i = 0; i < classCount; ++i) {
+ classIds[i] = commonRef_refToID(env, classDefs[i].klass);
+ }
+ }
+
error = JVMTI_FUNC_PTR(gdata->jvmti,RedefineClasses)
(gdata->jvmti, classCount, classDefs);
if (error != JVMTI_ERROR_NONE) {
@@ -486,6 +494,18 @@ redefineClasses(PacketInputStream *in, PacketOutputStream *out)
for ( i = 0 ; i < classCount; i++ ) {
eventHandler_freeClassBreakpoints(classDefs[i].klass);
}
+
+ if (gdata->isEnhancedClassRedefinitionEnabled) {
+ /* Update tags in jvmti to use new classes */
+ for ( i = 0 ; i < classCount; i++ ) {
+ /* pointer in classIds[i] is updated by advanced redefinition to a new class */
+ error = commonRef_updateTags(env, classIds[i]);
+ if (error != JVMTI_ERROR_NONE) {
+ break;
+ }
+ }
+ }
+
}
}
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.c b/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.c
index bc7ddb660bf..f8cb8e36f17 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.c
@@ -712,3 +712,32 @@ commonRef_unlock(void)
{
debugMonitorExit(gdata->refLock);
}
+
+/*
+ * Update JVMTI tags, used from enhanced redefinition
+ */
+jvmtiError
+commonRef_updateTags(JNIEnv *env, jlong id)
+{
+ jvmtiError error;
+
+ error = JVMTI_ERROR_NONE;
+
+ if (id == NULL_OBJECT_ID) {
+ return error;
+ }
+
+ debugMonitorEnter(gdata->refLock); {
+ RefNode *node;
+
+ node = findNodeByID(env, id);
+ if (node != NULL) {
+ error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
+ (gdata->jvmti, node->ref, ptr_to_jlong(node));
+ } else {
+ printf("Node not found\n");
+ }
+ } debugMonitorExit(gdata->refLock);
+
+ return error;
+}
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.h b/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.h
index c3700f8c743..35c77e4adbf 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.h
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/commonRef.h
@@ -43,4 +43,6 @@ void commonRef_compact(void);
void commonRef_lock(void);
void commonRef_unlock(void);
+jvmtiError commonRef_updateTags(JNIEnv *env, jlong id);
+
#endif
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
index 2403c5273ba..b4a5ad183e0 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
@@ -43,6 +43,7 @@ BackendGlobalData *gdata = NULL;
static jboolean isInterface(jclass clazz);
static jboolean isArrayClass(jclass clazz);
static char * getPropertyUTF8(JNIEnv *env, char *propertyName);
+static jboolean isEnhancedClassRedefinitionEnabled(JNIEnv *env);
/* Save an object reference for use later (create a NewGlobalRef) */
void
@@ -285,6 +286,8 @@ util_initialize(JNIEnv *env)
}
}
+ gdata->isEnhancedClassRedefinitionEnabled = isEnhancedClassRedefinitionEnabled(env);
+
} END_WITH_LOCAL_REFS(env);
}
@@ -1701,6 +1704,36 @@ getPropertyUTF8(JNIEnv *env, char *propertyName)
return value;
}
+static jboolean
+isEnhancedClassRedefinitionEnabled(JNIEnv *env)
+{
+ jvmtiError error;
+ jint count, i;
+ jvmtiExtensionFunctionInfo* ext_funcs;
+
+ error = JVMTI_FUNC_PTR(gdata->jvmti,GetExtensionFunctions)
+ (gdata->jvmti, &count, &ext_funcs);
+ if (error != JVMTI_ERROR_NONE) {
+ return JNI_FALSE;
+ }
+
+ for (i=0; i<count; i++) {
+ if (strcmp(ext_funcs[i].id, (char*)"com.sun.hotspot.functions.IsEnhancedClassRedefinitionEnabled") == 0) {
+ jboolean enabled;
+ error = (*ext_funcs[i].func)(gdata->jvmti, &enabled);
+
+ if (error != JVMTI_ERROR_NONE) {
+ return JNI_FALSE;
+ } else {
+ return enabled;
+ }
+ }
+ }
+
+ return JNI_FALSE;
+}
+
+
jboolean
isMethodObsolete(jmethodID method)
{
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.h b/src/jdk.jdwp.agent/share/native/libjdwp/util.h
index f62b5c409f0..6de8f17bb23 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/util.h
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.h
@@ -133,6 +133,9 @@ typedef struct {
int objectsByIDsize;
int objectsByIDcount;
+ /* true if enhanced class redefinition is enabled */
+ jboolean isEnhancedClassRedefinitionEnabled;
+
/* Indication that the agent has been loaded */
jboolean isLoaded;
--
2.23.0

View File

@@ -0,0 +1,54 @@
From db54a42adf258e2ba3459fe017221cf2cf49c33f Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Mon, 11 Oct 2021 20:22:26 +0200
Subject: [PATCH 41/42] JBR-3867 - fix msvc compilation issue with non const
array on stack
---
.../share/native/libjdwp/VirtualMachineImpl.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
index 7e3070d7d1d..669fac7cb5b 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
@@ -477,11 +477,16 @@ redefineClasses(PacketInputStream *in, PacketOutputStream *out)
if (ok == JNI_TRUE) {
jvmtiError error;
- jlong classIds[classCount];
+ jlong* classIds = NULL;
if (gdata->isEnhancedClassRedefinitionEnabled) {
- for (i = 0; i < classCount; ++i) {
- classIds[i] = commonRef_refToID(env, classDefs[i].klass);
+ classIds = jvmtiAllocate(classCount*(int)sizeof(jlong));
+ if (classIds == NULL) {
+ outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
+ return JNI_TRUE;
+ }
+ for (i = 0; i < classCount; i++) {
+ classIds[i] = commonRef_refToID(env, classDefs[i].klass);
}
}
@@ -495,7 +500,7 @@ redefineClasses(PacketInputStream *in, PacketOutputStream *out)
eventHandler_freeClassBreakpoints(classDefs[i].klass);
}
- if (gdata->isEnhancedClassRedefinitionEnabled) {
+ if (gdata->isEnhancedClassRedefinitionEnabled && classIds != NULL) {
/* Update tags in jvmti to use new classes */
for ( i = 0 ; i < classCount; i++ ) {
/* pointer in classIds[i] is updated by advanced redefinition to a new class */
@@ -504,6 +509,7 @@ redefineClasses(PacketInputStream *in, PacketOutputStream *out)
break;
}
}
+ jvmtiDeallocate((void*) classIds);
}
}
--
2.23.0

View File

@@ -0,0 +1,31 @@
From 976b5f8b1326fee64febef8771d7cebee46bada9 Mon Sep 17 00:00:00 2001
From: Vladimir Dvorak <vladimir.dvorak@jetbrains.com>
Date: Tue, 19 Oct 2021 17:02:35 +0200
Subject: [PATCH 42/42] Attempt to fix JBR-3887
---
src/hotspot/share/prims/jvmtiExtensions.cpp | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/hotspot/share/prims/jvmtiExtensions.cpp b/src/hotspot/share/prims/jvmtiExtensions.cpp
index ab2e663b95d..698d32a7228 100644
--- a/src/hotspot/share/prims/jvmtiExtensions.cpp
+++ b/src/hotspot/share/prims/jvmtiExtensions.cpp
@@ -50,7 +50,13 @@ static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, ...) {
}
// extension function
-static jvmtiError JNICALL IsEnhancedClassRedefinitionEnabled(const jvmtiEnv* env, jboolean* enabled, ...) {
+static jvmtiError JNICALL IsEnhancedClassRedefinitionEnabled(const jvmtiEnv* env, ...) {
+ jboolean* enabled = NULL;
+ va_list ap;
+
+ va_start(ap, env);
+ enabled = va_arg(ap, jboolean *);
+ va_end(ap);
if (enabled == NULL) {
return JVMTI_ERROR_NULL_POINTER;
}
--
2.23.0

View File

@@ -0,0 +1,83 @@
#!/bin/bash -x
usage ()
{
echo "Usage: perfcmp.sh [options] <test_results_cur> <test_results_ref> <results> <test_prefix> <noHeaders>"
echo "Options:"
echo -e " -h, --help\tdisplay this help"
echo -e " -tc\tprint teacmity statistic"
echo -e "test_results_cur - the file with metrics values for the current measuring"
echo -e "test_results_ref - the file with metrics values for the reference measuring"
echo -e "results - results of comaprison"
echo -e "test_prefix - specifys measuring type, makes sense for enabled -tc, by default no prefixes"
echo -e "noHeaders - by default 1-st line contains headers"
echo -e ""
echo -e "test_results_* files content should be in csv format with header and tab separator:"
echo -e "The 1-st column is the test name"
echo -e "The 2-st column is the test value"
echo -e ""
echo -e "Example:"
echo -e "Test Value"
echo -e "Testname 51.54"
}
while [ -n "$1" ]
do
case "$1" in
-h | --help) usage
exit 1 ;;
-tc) tc=1
shift
break ;;
*) break;;
esac
done
if [[ "$#" < "3" ]]; then
echo "Error: Invalid arguments"
usage
exit 1
fi
curFile=$1
refFile=$2
resFile=$3
testNamePrefix=$4
noHeaders=$5
echo $curFile
echo $refFile
echo $resFile
curValues=`cat "$curFile" | cut -f 2 | tr -d '\t'`
if [ -z noHeaders ]; then
curValuesHeader=`echo "$curValues" | head -n +1`_cur
header=`cat "$refFile" | head -n +1 | awk -F'\t' -v x=$curValuesHeader '{print " "$1"\t"$2"_ref\t"x"\tratio"}'`
testContent=`paste -d '\t' $refFile <(echo "$curValues") | tail -n +2`
else
testContent=`paste -d '\t' $refFile <(echo "$curValues") | tail -n +1`
fi
testContent=`echo "$testContent" | awk -F'\t' '{ if ($3>$2+$2*0.1) {print "* "$1"\t"$2"\t"$3"\t"(($2==0)?"-":$3/$2)} else {print " "$1"\t"$2"\t"$3"\t"(($2==0)?"-":$3/$2)} }'`
if [ -z noHeaders ]; then
echo "$header" > $resFile
fi
echo "$testContent" >> $resFile
cat "$resFile" | tr '\t' ';' | column -t -s ';' | tee $resFile
if [ -z $tc ]; then
exit 0
fi
echo "$testContent" 2>&1 | (
while read -r s; do
testname=`echo "$s" | cut -f 1 | tr -d "[:space:]" | tr -d "*"`
duration=`echo "$s" | cut -f 3`
failed=`echo "$s" | cut -c1 | grep -c "*"`
echo \#\#teamcity[testStarted name=\'$testNamePrefix$testname\']
echo "===>$s"
echo \#\#teamcity[buildStatisticValue key=\'$testNamePrefix$testname\' value=\'$duration\']
[ $failed -eq 1 ] && echo \#\#teamcity[testFailed name=\'$testNamePrefix$testname\' message=\'$s\']
echo \#\#teamcity[testFinished name=\'$testNamePrefix$testname\' duration=\'$duration\']
failed=0
done
)

View File

@@ -42,7 +42,6 @@ function pack_jbr {
JBRSDK_BUNDLE=jbrsdk
RELEASE_NAME=windows-x86_64-server-release
IMAGES_DIR=build/$RELEASE_NAME/images
JBSDK=$JBRSDK_BASE_NAME-windows-x64-b$build_number
BASE_DIR=.
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "dcevm" ] || [ "$bundle_type" == "fd" ]; then
@@ -53,7 +52,7 @@ pack_jbr jbr${jbr_name_postfix} jbr
pack_jbr jbrsdk${jbr_name_postfix} jbrsdk
if [ -z "$bundle_type" ]; then
JBRSDK_TEST=$JBRSDK_BASE_NAME-windows-test-x64-b$build_number
JBRSDK_TEST=$JBRSDK_BUNDLE-$JBSDK_VERSION-windows-test-x64-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
/usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
fi

View File

@@ -1,20 +1,80 @@
include Makefile
include make/MainSupport.gmk
#
# Copyright 2000-2021 JetBrains s.r.o.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
.PHONY: jbr-api
include $(SPEC)
include MakeBase.gmk
include JavaCompilation.gmk
ifeq ($(SPEC),)
ifneq ($(words $(SPECS)),1)
@echo "Error: Multiple build specification files found. Please select one explicitly."
@exit 2
endif
jbr-api:
@cd $(topdir)
@$(MAKE) $(MFLAGS) $(MAKE_LOG_FLAGS) -r -R -j 1 -f $(topdir)/make/JBRApi.gmk SPEC=$(SPECS) HAS_SPEC=true ACTUAL_TOPDIR=$(topdir) MODULES="$(MODULES)" jbr-api
else #with SPEC
JBR_API_ROOT_DIR := $(TOPDIR)/src/jetbrains.api
JBR_API_GENSRC_TEMPLATES := $(JBR_API_ROOT_DIR)/templates
JBR_API_SRC_DIR := $(JBR_API_ROOT_DIR)/src
JBR_API_OUTPUT_DIR := $(OUTPUTDIR)/jbr-api
JBR_API_GENSRC_DIR := $(JBR_API_OUTPUT_DIR)/gensrc
JBR_API_BIN_DIR := $(JBR_API_OUTPUT_DIR)/bin
JBR_API_VERSION_PROPERTIES := $(JBR_API_ROOT_DIR)/version.properties
jbr-api:
$(ECHO) "BUILD_DIR=$(OUTPUTDIR)" > $(OUT)
$(ECHO) "BOOT_JDK=\"$(BOOT_JDK)\"" >> $(OUT)
JBR_API_GENSRC_SOURCES := $(call FindFiles, $(JBR_API_GENSRC_TEMPLATES))
JBR_API_GENSRC_FILES := $(foreach f, $(call FindFiles, $(JBR_API_GENSRC_TEMPLATES)/com), \
$(JBR_API_GENSRC_DIR)/$(call RelativePath, $f, $(JBR_API_GENSRC_TEMPLATES)))
JBR_API_SRC_FILES := $(call FindFiles, $(JBR_API_SRC_DIR))
ifeq ($(JBR_API_JBR_VERSION),)
JBR_API_JBR_VERSION := <DEVELOPMENT>
JBR_API_FAIL_ON_HASH_MISMATCH := false
else
.PHONY: $(JBR_API_VERSION_PROPERTIES)
JBR_API_FAIL_ON_HASH_MISMATCH := true
endif
ARCHIVE_BUILD_JBR_API_BIN := $(JBR_API_BIN_DIR)
$(eval $(call SetupJavaCompilation, BUILD_JBR_API, \
SMALL_JAVA := true, \
COMPILER := bootjdk, \
SRC := $(JBR_API_GENSRC_DIR) $(JBR_API_SRC_DIR), \
EXTRA_FILES := $(JBR_API_GENSRC_FILES), \
BIN := $(JBR_API_BIN_DIR), \
JAR := $(JBR_API_OUTPUT_DIR)/jbr-api.jar, \
))
$(eval $(call SetupJarArchive, BUILD_JBR_API_SOURCES_JAR, \
DEPENDENCIES := $(JBR_API_GENSRC_FILES) $(JBR_API_SRC_FILES), \
SRCS := $(JBR_API_GENSRC_DIR) $(JBR_API_SRC_DIR), \
JAR := $(JBR_API_OUTPUT_DIR)/jbr-api-sources.jar, \
SUFFIXES := .java, \
BIN := $(JBR_API_BIN_DIR), \
))
$(JBR_API_GENSRC_FILES): $(JBR_API_GENSRC_SOURCES) $(JBR_API_SRC_FILES) $(JBR_API_VERSION_PROPERTIES)
$(ECHO) Generating sources for JBR API
$(JAVA_CMD) $(JAVA_FLAGS_SMALL) "$(JBR_API_GENSRC_TEMPLATES)/Gensrc.java" \
"$(TOPDIR)/src" "$(JBR_API_OUTPUT_DIR)" "$(JBR_API_JBR_VERSION)"
jbr-api-check-version: $(JBR_API_GENSRC_FILES) $(JBR_API_SRC_FILES) $(JBR_API_VERSION_PROPERTIES)
$(JAVA_CMD) $(JAVA_FLAGS_SMALL) "$(JBR_API_GENSRC_TEMPLATES)/CheckVersion.java" \
"$(JBR_API_ROOT_DIR)" "$(JBR_API_GENSRC_DIR)" "$(JBR_API_FAIL_ON_HASH_MISMATCH)"
jbr-api: $(BUILD_JBR_API) $(BUILD_JBR_API_SOURCES_JAR) jbr-api-check-version
.PHONY: jbr-api jbr-api-check-version
ifneq ($(JBR_API_CONF_FILE),)
$(JBR_API_CONF_FILE): $(JBR_API_GENSRC_FILES)
$(ECHO) "VERSION=`$(CAT) $(JBR_API_OUTPUT_DIR)/jbr-api.version`" > $(JBR_API_CONF_FILE)
$(ECHO) "JAR=$(JBR_API_OUTPUT_DIR)/jbr-api.jar" >> $(JBR_API_CONF_FILE)
$(ECHO) "SOURCES_JAR=$(JBR_API_OUTPUT_DIR)/jbr-api-sources.jar" >> $(JBR_API_CONF_FILE)
jbr-api: $(JBR_API_CONF_FILE)
.PHONY: $(JBR_API_CONF_FILE)
endif

View File

@@ -1325,6 +1325,14 @@ create-main-targets-include:
@$(ECHO) ALL_MAIN_TARGETS := $(sort $(ALL_TARGETS)) > \
$(MAKESUPPORT_OUTPUTDIR)/main-targets.gmk
################################################################################
# JBR API
$(eval $(call SetupTarget, jbr-api, \
MAKEFILE := JBRApi, \
TARGET := jbr-api \
))
################################################################################
# Hook to include the corresponding custom file, if present.
$(eval $(call IncludeCustomExtension, Main-post.gmk))

View File

@@ -55,8 +55,6 @@ BOOT_MODULES= \
jdk.sctp \
jdk.unsupported \
jdk.naming.rmi \
jetbrains.api \
jetbrains.api.impl \
#
# Modules that directly or indirectly requiring upgradeable modules

View File

@@ -49,6 +49,4 @@ jdk.unsupported,
jdk.xml.dom,
jdk.zipfs,
jdk.hotspot.agent,
jetbrains.api,
jetbrains.api.impl,
jdk.jcmd

View File

@@ -2020,7 +2020,11 @@ void os::win32::print_windows_version(outputStream* st) {
case 10000:
if (is_workstation) {
st->print("10");
if (build_number >= 22000) {
st->print("11");
} else {
st->print("10");
}
} else {
// distinguish Windows Server 2016 and 2019 by build number
// Windows server 2019 GA 10/2018 build number is 17763

View File

@@ -117,6 +117,8 @@ bool Arguments::_has_jimage = false;
char* Arguments::_ext_dirs = NULL;
GrowableArray<const char *> *Arguments::_unrecognized_vm_options = NULL;
bool PathString::set_value(const char *value) {
if (_value != NULL) {
FreeHeap(_value);
@@ -132,7 +134,7 @@ bool PathString::set_value(const char *value) {
return true;
}
void PathString::append_value(const char *value) {
void PathString::append_value(const char *value, const char *separator) {
char *sp;
size_t len = 0;
if (value != NULL) {
@@ -145,7 +147,7 @@ void PathString::append_value(const char *value) {
if (sp != NULL) {
if (_value != NULL) {
strcpy(sp, _value);
strcat(sp, os::path_separator());
strcat(sp, separator);
strcat(sp, value);
FreeHeap(_value);
} else {
@@ -1217,8 +1219,10 @@ bool Arguments::process_argument(const char* arg,
}
} else {
if (ignore_unrecognized) {
store_unrecognized_vm_option(arg);
return true;
}
jio_fprintf(defaultStream::error_stream(),
"Unrecognized VM option '%s'\n", argname);
JVMFlag* fuzzy_matched = JVMFlag::fuzzy_match((const char*)argname, arg_len, true);
@@ -1229,7 +1233,7 @@ bool Arguments::process_argument(const char* arg,
fuzzy_matched->name(),
(fuzzy_matched->is_bool()) ? "" : "=<value>");
}
}
}
// allow for commandline "commenting out" options like -XX:#+Verbose
return arg[0] == '#';
@@ -2017,9 +2021,38 @@ bool Arguments::check_vm_args_consistency() {
return status;
}
void Arguments::set_unrecognized_vm_options_property() {
if (_unrecognized_vm_options != NULL) {
int num_of_entries = _unrecognized_vm_options->length();
const char* option_string = _unrecognized_vm_options->at(0);
SystemProperty* prop = new SystemProperty("java.vm.unrecognized.options", "", true, false);
prop->set_value(option_string);
for (int i = 1; i < num_of_entries; i++) {
option_string = _unrecognized_vm_options->at(i);
prop->append_value(option_string, "\n");
}
PropertyList_add(&_system_properties, prop);
}
}
void Arguments::store_unrecognized_vm_option(const char* option) {
if (_unrecognized_vm_options == NULL) {
// Create GrowableArray lazily, only if unrecognized vm options found
_unrecognized_vm_options = new (ResourceObj::C_HEAP, mtArguments) GrowableArray<const char *>(10, mtArguments);
}
_unrecognized_vm_options->push(option);
}
bool Arguments::is_bad_option(const JavaVMOption* option, jboolean ignore,
const char* option_type) {
if (ignore) return false;
if (ignore) {
store_unrecognized_vm_option(option->optionString);
return false;
}
const char* spacer = " ";
if (option_type == NULL) {

View File

@@ -62,7 +62,8 @@ class PathString : public CHeapObj<mtArguments> {
char* value() const { return _value; }
bool set_value(const char *value);
void append_value(const char *value);
void append_value(const char *value, const char *delemiter);
void append_value(const char *value) { append_value(value, os::path_separator()); }
PathString(const char* value);
~PathString();
@@ -346,6 +347,10 @@ class Arguments : AllStatic {
static void set_xdebug_mode(bool arg) { _xdebug_mode = arg; }
static bool xdebug_mode() { return _xdebug_mode; }
// List of unrecognized VM options
static GrowableArray<const char *> *_unrecognized_vm_options;
static void store_unrecognized_vm_option(const char* option);
// preview features
static bool _enable_preview;
@@ -559,6 +564,9 @@ class Arguments : AllStatic {
// Update/Initialize System properties after JDK version number is known
static void init_version_specific_system_properties();
// Store unrecognized vm options to system property
static void set_unrecognized_vm_options_property();
// Update VM info property - called after argument parsing
static void update_vm_info_property(const char* vm_info) {
_vm_info->set_value(vm_info);

View File

@@ -1051,7 +1051,7 @@ const intx ObjectAlignmentInBytes = 8;
product(bool, PrintVMOptions, false, \
"Print flags that appeared on the command line") \
\
product(bool, IgnoreUnrecognizedVMOptions, false, \
product(bool, IgnoreUnrecognizedVMOptions, true, \
"Ignore unrecognized VM options") \
\
product(bool, PrintCommandLineFlags, false, \

View File

@@ -2748,6 +2748,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
jint parse_result = Arguments::parse(args);
if (parse_result != JNI_OK) return parse_result;
// Store all unrecognized vm options to system property
// to make it accessible from Java
Arguments::set_unrecognized_vm_options_property();
os::init_before_ergo();
jint ergo_result = Arguments::apply_ergo();

View File

@@ -234,6 +234,7 @@ void setOSNameAndVersion(java_props_t *sprops) {
// Hardcode os_name, and fill in os_version
sprops->os_name = strdup("Mac OS X");
NSString *nsVerStr = NULL;
char* osVersionCStr = NULL;
// Mac OS 10.9 includes the [NSProcessInfo operatingSystemVersion] function,
// but it's not in the 10.9 SDK. So, call it via NSInvocation.
@@ -246,7 +247,6 @@ void setOSNameAndVersion(java_props_t *sprops) {
[invoke invokeWithTarget:[NSProcessInfo processInfo]];
[invoke getReturnValue:&osVer];
NSString *nsVerStr;
// Copy out the char* if running on version other than 10.16 Mac OS (10.16 == 11.x)
// or explicitly requesting version compatibility
if (!((long)osVer.majorVersion == 10 && (long)osVer.minorVersion >= 16) ||
@@ -258,36 +258,30 @@ void setOSNameAndVersion(java_props_t *sprops) {
nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
(long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
}
// Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]);
} else {
// Version 10.16, without explicit env setting of SYSTEM_VERSION_COMPAT
// AKA 11.x; compute the version number from the letter in the ProductBuildVersion
// AKA 11+ Read the *real* ProductVersion from the hidden link to avoid SYSTEM_VERSION_COMPAT
// If not found, fallback below to the SystemVersion.plist
NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile :
@"/System/Library/CoreServices/SystemVersion.plist"];
@"/System/Library/CoreServices/.SystemVersionPlatform.plist"];
if (version != NULL) {
NSString *nsBuildVerStr = [version objectForKey : @"ProductBuildVersion"];
if (nsBuildVerStr != NULL && nsBuildVerStr.length >= 3) {
int letter = [nsBuildVerStr characterAtIndex:2];
if (letter >= 'B' && letter <= 'Z') {
int vers = letter - 'A' - 1;
asprintf(&osVersionCStr, "11.%d", vers);
}
}
nsVerStr = [version objectForKey : @"ProductVersion"];
}
}
}
// Fallback if running on pre-10.9 Mac OS
if (osVersionCStr == NULL) {
if (nsVerStr == NULL) {
NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile :
@"/System/Library/CoreServices/SystemVersion.plist"];
if (version != NULL) {
NSString *nsVerStr = [version objectForKey : @"ProductVersion"];
if (nsVerStr != NULL) {
osVersionCStr = strdup([nsVerStr UTF8String]);
}
nsVerStr = [version objectForKey : @"ProductVersion"];
}
}
if (nsVerStr != NULL) {
// Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]);
}
if (osVersionCStr == NULL) {
osVersionCStr = strdup("Unknown");
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.base;
import com.jetbrains.internal.JBRApi;
import java.lang.invoke.MethodHandles;
/**
* This class contains mapping between JBR API interfaces and implementation in {@code java.base} module.
*/
public class JBRApiModule {
static {
JBRApi.registerModule(MethodHandles.lookup(), JBRApiModule.class.getModule()::addExports)
.service("com.jetbrains.JBR$ServiceApi", null)
.withStatic("getService", "com.jetbrains.internal.JBRApi");
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.bootstrap;
import com.jetbrains.internal.JBRApi;
import jdk.internal.loader.ClassLoaders;
import java.lang.invoke.MethodHandles;
/**
* Bootstrap class, used to initialize {@linkplain JBRApi JBR API}.
*/
public class JBRApiBootstrap {
private JBRApiBootstrap() {}
/**
* Classes that register JBR API implementation for their modules.
*/
private static final String[] MODULES = {
"com.jetbrains.base.JBRApiModule",
"com.jetbrains.desktop.JBRApiModule"
};
/**
* Called from static initializer of {@link com.jetbrains.JBR}.
* @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 (!System.getProperty("jetbrains.api.enabled", "true").equalsIgnoreCase("true")) return null;
try {
Class<?> apiInterface = outerLookup.findClass("com.jetbrains.JBR$ServiceApi");
if (!outerLookup.hasFullPrivilegeAccess() ||
outerLookup.lookupClass().getPackage() != apiInterface.getPackage()) {
throw new IllegalArgumentException("Lookup must be full-privileged from the com.jetbrains package: " +
outerLookup.lookupClass().getName());
}
JBRApi.init(outerLookup);
ClassLoader classLoader = ClassLoaders.platformClassLoader();
for (String m : MODULES) Class.forName(m, true, classLoader);
return JBRApi.getService(apiInterface);
} catch(ClassNotFoundException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.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);
}
}
/**
* Replaced with {@code VM.classFileVersion()} in more recent JDK
* @see java.lang.invoke.InnerClassLambdaMetafactory#CLASSFILE_VERSION
*/
public static final int CLASSFILE_VERSION = 59;
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.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
p.visitInsn(DUP);
p.visitLdcInsn("No implementation found for this method");
p.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V", false);
p.visitInsn(ATHROW);
p.visitMaxs(-1, -1);
}
public static void logDeprecated(MethodVisitor writer, String message) {
writer.visitTypeInsn(NEW, "java/lang/Exception");
writer.visitInsn(DUP);
writer.visitLdcInsn(message);
writer.visitMethodInsn(INVOKESPECIAL, "java/lang/Exception", "<init>", "(Ljava/lang/String;)V", false);
writer.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
}
protected 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);
}
public 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;
}
}
}

View File

@@ -0,0 +1,280 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
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.api} module. Actual implementation is linked by
* JBR at runtime by generating {@linkplain Proxy proxy objects}.
* Mapping between interfaces and implementation code is defined in
* {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}.
* <p>
* This class has most basic methods for working with JBR API and cache of generated proxies.
* <p>
* <h2>How to add a new service</h2>
* <ol>
* <li>Create your service interface in module {@link com.jetbrains.JBR jetbrains.api}:
* <blockquote><pre>{@code
* package com.jetbrains;
*
* interface StringOptimizer {
* void optimize(String string);
* }
* }</pre></blockquote>
* </li>
* <li>Create an implementation inside JBR:
* <blockquote><pre>{@link java.lang.String java.lang.String}:{@code
* private static void optimizeInternal(String string) {
* string.hash = 0;
* string.hashIsZero = true;
* }
* }</pre></blockquote>
* </li>
* <li>Register your service in corresponding
* {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES module registry class}:
* <blockquote><pre>{@link com.jetbrains.base.JBRApiModule}:{@code
* .service("com.jetbrains.StringOptimizer", null)
* .withStatic("optimize", "java.lang.String", "optimizeInternal")
* }</pre></blockquote>
* </li>
* <li>You can also bind the interface to implementation class
* (without actually implementing the interface):
* <blockquote><pre>{@link java.lang.String java.lang.String}:{@code
* private static class StringOptimizerImpl {
*
* private void optimize(String string) {
* string.hash = 0;
* string.hashIsZero = true;
* }
* }
* }</pre></blockquote>
* <blockquote><pre>{@link com.jetbrains.base.JBRApiModule}:{@code
* .service("com.jetbrains.StringOptimizer", "java.lang.String$StringOptimizerImpl")
* }</pre></blockquote>
* </li>
* </ol>
* <h2>How to add a new proxy</h2>
* Registering a proxy is similar to registering a service:
* <blockquote><pre>{@code
* .proxy("com.jetbrains.SomeProxy", "a.b.c.SomeProxyImpl")
* }</pre></blockquote>
* Note that unlike service, proxy <b>must</b> have a target type.
* Also, proxy expects target object as a single constructor argument
* and can only be instantiated by JBR, there's no API that would allow
* user to directly create proxy object.
*/
public class JBRApi {
private static final Map<String, RegisteredProxyInfo> registeredProxyInfoByInterfaceName = new HashMap<>();
private static final Map<String, RegisteredProxyInfo> registeredProxyInfoByTargetName = new HashMap<>();
private static final ConcurrentMap<Class<?>, Proxy<?>> proxyByInterface = new ConcurrentHashMap<>();
/**
* lookup context inside {@code jetbrains.api} module
*/
static Lookup outerLookup;
/**
* Known service and proxy interfaces extracted from {@link com.jetbrains.JBR.Metadata}
*/
static Set<String> knownServices, knownProxies;
public static void init(Lookup outerLookup) {
JBRApi.outerLookup = outerLookup;
try {
Class<?> metadataClass = outerLookup.findClass("com.jetbrains.JBR$Metadata");
knownServices = Set.of((String[]) outerLookup.findStaticVarHandle(metadataClass,
"KNOWN_SERVICES", String[].class).get());
knownProxies = Set.of((String[]) outerLookup.findStaticVarHandle(metadataClass,
"KNOWN_PROXIES", String[].class).get());
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
knownServices = Set.of();
knownProxies = Set.of();
}
}
/**
* @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.
*/
public static <T> T getService(Class<T> interFace) {
Proxy<T> p = getProxy(interFace);
return p != null && p.isSupported() ? p.getInstance() : null;
}
/**
* @return proxy for the given interface, or {@code null}
*/
@SuppressWarnings("unchecked")
static <T> Proxy<T> getProxy(Class<T> interFace) {
return (Proxy<T>) proxyByInterface.computeIfAbsent(interFace, i -> {
RegisteredProxyInfo info = registeredProxyInfoByInterfaceName.get(i.getName());
if (info == null) return null;
ProxyInfo resolved = ProxyInfo.resolve(info);
return resolved != null ? new Proxy<>(resolved) : null;
});
}
/**
* @return true if given class represents a proxy interface. Even if {@code jetbrains.api}
* introduces new interfaces JBR is not aware of, these interfaces would still be detected
* by this method.
*/
static boolean isKnownProxyInterface(Class<?> clazz) {
String name = clazz.getName();
return registeredProxyInfoByInterfaceName.containsKey(name) ||
knownServices.contains(name) || knownProxies.contains(name);
}
/**
* Reverse lookup by proxy target type name.
* @return user-side interface for given implementation target type name.
*/
static Class<?> getProxyInterfaceByTargetName(String targetName) {
RegisteredProxyInfo info = registeredProxyInfoByTargetName.get(targetName);
if (info == null) return null;
try {
return (info.type() == ProxyInfo.Type.CLIENT_PROXY ? info.apiModule() : outerLookup)
.findClass(info.interfaceName());
} catch (ClassNotFoundException | IllegalAccessException e) {
return null;
}
}
/**
* Called by {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}
* to register a new mapping for corresponding modules.
*/
public static ModuleRegistry registerModule(Lookup lookup, BiFunction<String, Module, Module> addExports) {
addExports.apply(lookup.lookupClass().getPackageName(), outerLookup.lookupClass().getModule());
return new ModuleRegistry(lookup);
}
public static class ModuleRegistry {
private final Lookup lookup;
private RegisteredProxyInfo lastProxy;
private ModuleRegistry(Lookup lookup) {
this.lookup = lookup;
}
private ModuleRegistry addProxy(String interfaceName, String target, ProxyInfo.Type type) {
lastProxy = new RegisteredProxyInfo(lookup, interfaceName, target, type, new ArrayList<>());
registeredProxyInfoByInterfaceName.put(interfaceName, lastProxy);
if (target != null) {
registeredProxyInfoByTargetName.put(target, lastProxy);
validate2WayMapping(lastProxy, registeredProxyInfoByInterfaceName.get(target));
validate2WayMapping(lastProxy, registeredProxyInfoByTargetName.get(interfaceName));
}
return this;
}
private static void validate2WayMapping(RegisteredProxyInfo p, RegisteredProxyInfo reverse) {
if (reverse != null &&
(!p.interfaceName().equals(reverse.target()) || !p.target().equals(reverse.interfaceName()))) {
throw new IllegalArgumentException("Invalid 2-way proxy mapping: " +
p.interfaceName() + " -> " + p.target() + " & " +
reverse.interfaceName() + " -> " + reverse.target());
}
}
/**
* Register new {@linkplain ProxyInfo.Type#PROXY proxy} mapping.
* <p>
* When {@code target} object is passed from JBR to client through service or any other proxy,
* it's converted to corresponding {@code interFace} type by creating a proxy object
* that implements {@code interFace} and delegates method calls to {@code target}.
* @param interFace interface name in {@link com.jetbrains.JBR jetbrains.api} module.
* @param target corresponding class/interface name in current JBR module.
* @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
*/
public ModuleRegistry proxy(String interFace, String target) {
Objects.requireNonNull(target);
return addProxy(interFace, target, ProxyInfo.Type.PROXY);
}
/**
* Register new {@linkplain ProxyInfo.Type#SERVICE service} mapping.
* <p>
* Service is a singleton, which may be accessed by client using {@link com.jetbrains.JBR} class.
* @param interFace interface name in {@link com.jetbrains.JBR jetbrains.api} module.
* @param target corresponding implementation class name in current JBR module, or null.
* @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
*/
public ModuleRegistry service(String interFace, String target) {
return addProxy(interFace, target, ProxyInfo.Type.SERVICE);
}
/**
* Register new {@linkplain ProxyInfo.Type#CLIENT_PROXY client proxy} mapping.
* This mapping type allows implementation of callbacks.
* <p>
* When {@code target} object is passed from client to JBR through service or any other proxy,
* it's converted to corresponding {@code interFace} type by creating a proxy object
* that implements {@code interFace} and delegates method calls to {@code target}.
* @param interFace interface name in current JBR module.
* @param target corresponding class/interface name in {@link com.jetbrains.JBR jetbrains.api} module.
* @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
*/
public ModuleRegistry clientProxy(String interFace, String target) {
Objects.requireNonNull(target);
return addProxy(interFace, target, ProxyInfo.Type.CLIENT_PROXY);
}
/**
* Register new 2-way mapping.
* It creates both {@linkplain ProxyInfo.Type#PROXY proxy} and
* {@linkplain ProxyInfo.Type#CLIENT_PROXY client proxy} between given interfaces.
* <p>
* It links together two given interfaces and allows passing such objects back and forth
* between JBR and {@link com.jetbrains.JBR jetbrains.api} module through services and other proxy methods.
* @param apiInterface interface name in {@link com.jetbrains.JBR jetbrains.api} module.
* @param jbrInterface interface name in current JBR module.
* @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
*/
public ModuleRegistry twoWayProxy(String apiInterface, String jbrInterface) {
clientProxy(jbrInterface, apiInterface);
proxy(apiInterface, jbrInterface);
return this;
}
/**
* Delegate interface "{@code methodName}" calls to static "{@code methodName}" in "{@code clazz}".
* @see #withStatic(String, String, String)
*/
public ModuleRegistry withStatic(String methodName, String clazz) {
return withStatic(methodName, clazz, methodName);
}
/**
* Delegate "{@code interfaceMethodName}" method calls to static "{@code methodName}" in "{@code clazz}".
*/
public ModuleRegistry withStatic(String interfaceMethodName, String clazz, String methodName) {
lastProxy.staticMethods().add(
new RegisteredProxyInfo.StaticMethodMapping(interfaceMethodName, clazz, methodName));
return this;
}
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import java.lang.invoke.MethodHandle;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Proxy is needed to dynamically link JBR API interfaces and implementation at runtime.
* It implements user-side interfaces and delegates method calls to actual implementation
* code through {@linkplain java.lang.invoke.MethodHandle method handles}.
* <p>
* There are 3 type of proxy objects:
* <ol>
* <li>{@linkplain ProxyInfo.Type#PROXY Proxy} - implements client-side interface from
* {@code jetbrains.api} and delegates calls to JBR-side target object and optionally static methods.</li>
* <li>{@linkplain ProxyInfo.Type#SERVICE Service} - singleton {@linkplain ProxyInfo.Type#PROXY proxy},
* may delegate calls only to static methods, without target object.</li>
* <li>{@linkplain ProxyInfo.Type#CLIENT_PROXY Client proxy} - reverse proxy, implements JBR-side interface
* and delegates calls to client-side target object by interface defined in {@code jetbrains.api}.
* May be used to implement callbacks which are created by client and called by JBR.</li>
* </ol>
* <p>
* Method signatures of proxy interfaces 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, more about it at {@link ProxyDependencyManager}.
* <p>
* Mapping between interfaces and implementation code is defined in
* {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}.
* @param <INTERFACE> interface type for this proxy.
*/
class Proxy<INTERFACE> {
private final ProxyInfo info;
private volatile ProxyGenerator generator;
private volatile Boolean allMethodsImplemented;
private volatile Boolean supported;
private volatile Class<?> proxyClass;
private volatile MethodHandle constructor;
private volatile MethodHandle targetExtractor;
private volatile INTERFACE instance;
Proxy(ProxyInfo info) {
this.info = info;
}
/**
* @return {@link ProxyInfo} structure of this proxy
*/
ProxyInfo getInfo() {
return info;
}
private synchronized void initGenerator() {
if (generator != null) return;
generator = new ProxyGenerator(info);
allMethodsImplemented = generator.areAllMethodsImplemented();
}
/**
* Checks if implementation is found for all abstract interface methods of this proxy.
*/
boolean areAllMethodsImplemented() {
if (allMethodsImplemented != null) return allMethodsImplemented;
synchronized (this) {
if (allMethodsImplemented == null) initGenerator();
return allMethodsImplemented;
}
}
/**
* Checks if all methods are {@linkplain #areAllMethodsImplemented() implemented}
* for this proxy and all proxies it {@linkplain ProxyDependencyManager uses}.
*/
boolean isSupported() {
if (supported != null) return supported;
synchronized (this) {
if (supported == null) {
Set<Class<?>> dependencies = ProxyDependencyManager.getProxyDependencies(info.interFace);
for (Class<?> d : dependencies) {
Proxy<?> p = JBRApi.getProxy(d);
if (p == null || !p.areAllMethodsImplemented()) {
supported = false;
return false;
}
}
supported = true;
}
return supported;
}
}
private synchronized void defineClasses() {
if (constructor != null) return;
initGenerator();
generator.defineClasses();
proxyClass = generator.getProxyClass();
constructor = generator.findConstructor();
targetExtractor = generator.findTargetExtractor();
}
/**
* @return generated proxy class
*/
Class<?> getProxyClass() {
if (proxyClass != null) return proxyClass;
synchronized (this) {
if (proxyClass == null) defineClasses();
return proxyClass;
}
}
/**
* @return method handle for the constructor of this proxy.
* <ul>
* <li>For {@linkplain ProxyInfo.Type#SERVICE services}, constructor is no-arg.</li>
* <li>For non-{@linkplain ProxyInfo.Type#SERVICE services}, constructor is single-arg,
* expecting target object to which it would delegate method calls.</li>
* </ul>
*/
MethodHandle getConstructor() {
if (constructor != null) return constructor;
synchronized (this) {
if (constructor == null) defineClasses();
return constructor;
}
}
/**
* @return method handle for that extracts target object of the proxy, or null.
*/
MethodHandle getTargetExtractor() {
// targetExtractor may be null, so check constructor instead
if (constructor != null) return targetExtractor;
synchronized (this) {
if (constructor == null) defineClasses();
return targetExtractor;
}
}
private synchronized void initClass(Set<Proxy<?>> actualUsages) {
defineClasses();
if (generator != null) {
actualUsages.addAll(generator.getDirectProxyDependencies());
generator.init();
generator = null;
}
}
private synchronized void initDependencyGraph() {
defineClasses();
if (generator == null) return;
Set<Class<?>> dependencyClasses = ProxyDependencyManager.getProxyDependencies(info.interFace);
Set<Proxy<?>> dependencies = new HashSet<>();
Set<Proxy<?>> actualUsages = new HashSet<>();
for (Class<?> d : dependencyClasses) {
Proxy<?> p = JBRApi.getProxy(d);
if (p != null) {
dependencies.add(p);
p.initClass(actualUsages);
}
}
actualUsages.removeAll(dependencies);
if (!actualUsages.isEmpty()) {
// Should never happen, this is a sign of broken dependency search
throw new RuntimeException("Some proxies are not in dependencies of " + info.interFace.getName() +
", but are actually used by it: " +
actualUsages.stream().map(p -> p.info.interFace.getName()).collect(Collectors.joining(", ")));
}
}
/**
* @return instance for this {@linkplain ProxyInfo.Type#SERVICE service},
* returns {@code null} for other proxy types.
*/
@SuppressWarnings("unchecked")
INTERFACE getInstance() {
if (instance != null) return instance;
if (info.type != ProxyInfo.Type.SERVICE) return null;
synchronized (this) {
if (instance == null) {
initDependencyGraph();
try {
instance = (INTERFACE) getConstructor().invoke();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
return instance;
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import java.lang.reflect.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
/**
* This class collects {@linkplain Proxy proxy} dependencies.
* <p>
* Dependencies of a class {@code C} are other classes that are
* used by {@code C} (i.e. supertypes, classes that appear in method
* parameters, return types) and all their dependencies. Any class
* is also considered a dependency of itself.
* <p>
* Dependencies allow JBR to validate whole set of interfaces for
* a particular feature instead of treating them as separate entities.
* <h2>Example</h2>
* Suppose we implemented some feature and added some API for it:
* <blockquote><pre>{@code
* interface SomeFeature {
* SomeOtherObject createSomeObject(int magicNumber);
* }
* interface SomeOtherObject {
* int getMagicNumber();
* }
* }</pre></blockquote>
* And then used it:
* <blockquote><pre>{@code
* if (JBR.isSomeFeatureSupported()) {
* SomeOtherObject object = JBR.getSomeFeature().createSomeObject(123);
* int magic = object.getMagicNumber();
* }
* }</pre></blockquote>
* Suppose JBR was able to find implementation for {@code SomeFeature.createSomeObject()},
* but not for {@code SomeOtherObject.getMagicNumber()}. So {@code JBR.getSomeFeature()}
* would succeed and return service instance, but {@code createSomeObject()} would fail,
* because JBR wasn't able to find implementation for {@code SomeOtherObject.getMagicNumber()}
* and therefore couldn't create proxy for {@code SomeOtherObject} class.
* <p>
* To avoid such issues, not only proxy interface itself, but all proxies that are accessible
* from current proxy interface must have proper implementation.
*/
class ProxyDependencyManager {
private static final ConcurrentMap<Class<?>, Set<Class<?>>> cache = new ConcurrentHashMap<>();
/**
* @return all proxy interfaces that are used (directly or indirectly) by given interface, including itself.
*/
static Set<Class<?>> getProxyDependencies(Class<?> interFace) {
Set<Class<?>> dependencies = cache.get(interFace);
if (dependencies != null) return dependencies;
step(null, interFace);
return cache.get(interFace);
}
/**
* Collect dependencies for given class and store them into cache.
*/
private static void step(Node parent, Class<?> clazz) {
if (!clazz.getPackageName().startsWith("com.jetbrains")) return;
if (parent != null && parent.findAndMergeCycle(clazz) != null) {
return;
}
Set<Class<?>> cachedDependencies = cache.get(clazz);
if (cachedDependencies != null) {
if (parent != null) parent.cycle.dependencies.addAll(cachedDependencies);
return;
}
Node node = new Node(parent, clazz);
ClassUsagesFinder.visitUsages(clazz, c -> step(node, c));
Class<?> correspondingProxyInterface = JBRApi.getProxyInterfaceByTargetName(clazz.getName());
if (correspondingProxyInterface != null) {
step(node, correspondingProxyInterface);
}
if (parent != null && parent.cycle != node.cycle) {
parent.cycle.dependencies.addAll(node.cycle.dependencies);
}
if (node.cycle.origin.equals(clazz)) {
// Put collected dependencies into cache only when we exit from the cycle
// Otherwise cache will contain incomplete data
for (Class<?> c : node.cycle.members) {
cache.put(c, node.cycle.dependencies);
}
}
}
/**
* Graph node, one per visited class
*/
private static class Node {
private final Node parent;
private final Class<?> clazz;
private Cycle cycle;
private Node(Node parent, Class<?> clazz) {
this.parent = parent;
this.clazz = clazz;
cycle = new Cycle(clazz);
}
/**
* When classes form dependency cycle, they share all their dependencies.
* If cycle was found, merge all found dependencies for nodes that form the cycle.
*/
private Cycle findAndMergeCycle(Class<?> clazz) {
if (this.clazz.equals(clazz)) return cycle;
if (parent == null) return null;
Cycle c = parent.findAndMergeCycle(clazz);
if (c != null && c != cycle) {
c.members.addAll(cycle.members);
c.dependencies.addAll(cycle.dependencies);
cycle = c;
}
return c;
}
}
/**
* Cycle info. For the sake of elegant code, single node
* also forms a cycle with itself as a single member and dependency.
*/
private static class Cycle {
/**
* Origin is the first visited class from that cycle.
*/
private final Class<?> origin;
private final Set<Class<?>> members = new HashSet<>();
private final Set<Class<?>> dependencies = new HashSet<>();
private Cycle(Class<?> origin) {
this.origin = origin;
members.add(origin);
if (JBRApi.isKnownProxyInterface(origin)) {
dependencies.add(origin);
}
}
}
/**
* Utility class that collects direct class usages using reflection
*/
private static class ClassUsagesFinder {
private static void visitUsages(Class<?> c, Consumer<Class<?>> action) {
collect(c.getGenericSuperclass(), action);
for (java.lang.reflect.Type t : c.getGenericInterfaces()) collect(t, action);
for (Field f : c.getDeclaredFields()) collect(f.getGenericType(), action);
for (Method m : c.getDeclaredMethods()) {
collect(m.getGenericParameterTypes(), action);
collect(m.getGenericReturnType(), action);
collect(m.getGenericExceptionTypes(), action);
}
}
private static void collect(java.lang.reflect.Type type, Consumer<Class<?>> action) {
if (type instanceof Class<?> c) {
while (c.isArray()) c = Objects.requireNonNull(c.getComponentType());
if (!c.isPrimitive()) action.accept(c);
} else if (type instanceof TypeVariable<?> v) {
collect(v.getBounds(), action);
} else if (type instanceof WildcardType w) {
collect(w.getUpperBounds(), action);
collect(w.getLowerBounds(), action);
} else if (type instanceof ParameterizedType p) {
collect(p.getActualTypeArguments(), action);
collect(p.getRawType(), action);
collect(p.getOwnerType(), action);
} else if (type instanceof GenericArrayType a) {
collect(a.getGenericComponentType(), action);
}
}
private static void collect(java.lang.reflect.Type[] types, Consumer<Class<?>> action) {
for (java.lang.reflect.Type t : types) collect(t, action);
}
}
}

View File

@@ -0,0 +1,460 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import jdk.internal.org.objectweb.asm.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static com.jetbrains.internal.ASMUtils.*;
import static java.lang.invoke.MethodHandles.Lookup;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* This class generates {@linkplain Proxy proxy} classes.
* Each proxy is just a generated class implementing some interface and
* delegating method calls to method handles.
* <p>
* There are 2 proxy dispatch modes:
* <ul>
* <li>interface -> proxy -> {@linkplain #generateBridge bridge} -> method handle -> implementation code</li>
* <li>interface -> proxy -> method handle -> implementation code</li>
* </ul>
* Generated proxy is always located in the same package with its interface and optional bridge is located in the
* same module with target implementation code. Bridge allows proxy to safely call hidden non-static implementation
* methods and is only needed for {@code jetbrains.api} -> JBR calls. For JBR -> {@code jetbrains.api} calls, proxy can
* invoke method handle directly.
*/
class ProxyGenerator {
private static final String OBJECT_DESCRIPTOR = "Ljava/lang/Object;";
private static final String MH_NAME = "java/lang/invoke/MethodHandle";
private static final String MH_DESCRIPTOR = "Ljava/lang/invoke/MethodHandle;";
private static final String CONVERSION_DESCRIPTOR = "(Ljava/lang/Object;)Ljava/lang/Object;";
/**
* Print warnings about usage of deprecated interfaces and methods to {@link System#err}.
*/
private static final boolean LOG_DEPRECATED = System.getProperty("jetbrains.api.logDeprecated", "true").equalsIgnoreCase("true");
private static final AtomicInteger nameCounter = new AtomicInteger();
private final ProxyInfo info;
private final boolean generateBridge;
private final String proxyName, bridgeName;
private final ClassVisitor proxyWriter, bridgeWriter;
private final List<Supplier<MethodHandle>> handles = new ArrayList<>();
private final List<Supplier<Class<?>>> classReferences = new ArrayList<>();
private final Set<Proxy<?>> directProxyDependencies = new HashSet<>();
private final List<Exception> exceptions = new ArrayList<>();
private int bridgeMethodCounter;
private boolean allMethodsImplemented = true;
private Lookup generatedHandlesHolder, generatedProxy;
/**
* Creates new proxy generator from given {@link ProxyInfo},
* looks for abstract interface methods, corresponding implementation methods
* and generates proxy bytecode. However, it doesn't actually load generated
* classes until {@link #defineClasses()} is called.
*/
ProxyGenerator(ProxyInfo info) {
this.info = info;
generateBridge = info.type != ProxyInfo.Type.CLIENT_PROXY;
int nameId = nameCounter.getAndIncrement();
proxyName = Type.getInternalName(info.interFace) + "$$JBRApiProxy$" + nameId;
bridgeName = generateBridge ? info.apiModule.lookupClass().getPackageName().replace('.', '/') + "/" +
info.interFace.getSimpleName() + "$$JBRApiBridge$" + nameId : null;
class ClassWriter extends jdk.internal.org.objectweb.asm.ClassWriter {
ClassWriter() { super(ClassWriter.COMPUTE_FRAMES); }
ClassVisitor createEmptyVisitor() {
return new ClassVisitor(api) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
return new MethodVisitor(api) {};
}
};
}
}
ClassWriter proxyClassWriter = new ClassWriter();
proxyWriter = proxyClassWriter;
bridgeWriter = generateBridge ? new ClassWriter() : proxyClassWriter.createEmptyVisitor();
proxyWriter.visit(CLASSFILE_VERSION, ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC, proxyName, null,
"java/lang/Object", new String[] {Type.getInternalName(info.interFace)});
bridgeWriter.visit(CLASSFILE_VERSION, ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC | ACC_PUBLIC, bridgeName, null,
"java/lang/Object", null);
generateConstructor();
generateMethods();
}
boolean areAllMethodsImplemented() {
return allMethodsImplemented;
}
Set<Proxy<?>> getDirectProxyDependencies() {
return directProxyDependencies;
}
/**
* Insert all method handles and class references into static fields, so that proxy can call implementation methods.
*/
void init() {
try {
for (int i = 0; i < handles.size(); i++) {
generatedHandlesHolder
.findStaticVarHandle(generatedHandlesHolder.lookupClass(), "h" + i, MethodHandle.class)
.set(handles.get(i).get());
}
for (int i = 0; i < classReferences.size(); i++) {
generatedHandlesHolder
.findStaticVarHandle(generatedHandlesHolder.lookupClass(), "c" + i, Class.class)
.set(classReferences.get(i).get());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Class<?> getProxyClass() {
return generatedProxy.lookupClass();
}
/**
* @return method handle to constructor of generated proxy class.
* <ul>
* <li>For {@linkplain ProxyInfo.Type#SERVICE services}, constructor is no-arg.</li>
* <li>For non-{@linkplain ProxyInfo.Type#SERVICE services}, constructor is single-arg,
* expecting target object to which it would delegate method calls.</li>
* </ul>
*/
MethodHandle findConstructor() {
try {
if (info.target == null) {
return generatedProxy.findConstructor(generatedProxy.lookupClass(), MethodType.methodType(void.class));
} else {
MethodHandle c = generatedProxy.findConstructor(generatedProxy.lookupClass(),
MethodType.methodType(void.class, Object.class));
if (info.type == ProxyInfo.Type.SERVICE) {
try {
return MethodHandles.foldArguments(c, info.target.findConstructor(info.target.lookupClass(),
MethodType.methodType(void.class)).asType(MethodType.methodType(Object.class)));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException("Service implementation must have no-args constructor: " +
info.target.lookupClass(), e);
}
}
return c;
}
} catch (IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/**
* @return method handle that receives proxy and returns its target, or null
*/
MethodHandle findTargetExtractor() {
if (info.target == null) return null;
try {
return generatedProxy.findGetter(generatedProxy.lookupClass(), "target", Object.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* Loads generated classes.
*/
void defineClasses() {
try {
Lookup bridge = !generateBridge ? null : MethodHandles.privateLookupIn(
info.apiModule.defineClass(((ClassWriter) bridgeWriter).toByteArray()), info.apiModule);
generatedProxy = info.interFaceLookup.defineHiddenClass(
((ClassWriter) proxyWriter).toByteArray(), true, Lookup.ClassOption.STRONG, Lookup.ClassOption.NESTMATE);
generatedHandlesHolder = generateBridge ? bridge : generatedProxy;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void generateConstructor() {
if (info.target != null) {
proxyWriter.visitField(ACC_PRIVATE | ACC_FINAL, "target", OBJECT_DESCRIPTOR, null, null);
}
MethodVisitor p = proxyWriter.visitMethod(ACC_PRIVATE, "<init>", "(" +
(info.target == null ? "" : OBJECT_DESCRIPTOR) + ")V", null, null);
if (LOG_DEPRECATED && info.interFace.isAnnotationPresent(Deprecated.class)) {
logDeprecated(p, "Warning: using deprecated JBR API interface " + info.interFace.getName());
}
p.visitVarInsn(ALOAD, 0);
if (info.target != null) {
p.visitInsn(DUP);
p.visitVarInsn(ALOAD, 1);
p.visitFieldInsn(PUTFIELD, proxyName, "target", OBJECT_DESCRIPTOR);
}
p.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
p.visitInsn(RETURN);
p.visitMaxs(-1, -1);
}
private void generateMethods() {
for (Method method : info.interFace.getMethods()) {
int mod = method.getModifiers();
if (!Modifier.isAbstract(mod)) continue;
MethodMapping methodMapping = getTargetMethodMapping(method);
Exception e1 = null;
if (info.target != null) {
try {
MethodHandle handle = info.target.findVirtual(
info.target.lookupClass(), method.getName(), methodMapping.type());
generateMethod(method, handle, methodMapping, true);
continue;
} catch (NoSuchMethodException | IllegalAccessException e) {
e1 = e;
}
}
Exception e2 = null;
ProxyInfo.StaticMethodMapping mapping = info.staticMethods.get(method.getName());
if (mapping != null) {
try {
MethodHandle staticHandle =
mapping.lookup().findStatic(mapping.lookup().lookupClass(), mapping.methodName(), methodMapping.type());
generateMethod(method, staticHandle, methodMapping, false);
continue;
} catch (NoSuchMethodException | IllegalAccessException e) {
e2 = e;
}
}
if (e1 != null) exceptions.add(e1);
if (e2 != null) exceptions.add(e2);
generateUnsupportedMethod(proxyWriter, method);
allMethodsImplemented = false;
}
}
private void generateMethod(Method interfaceMethod, MethodHandle handle, MethodMapping mapping, boolean passInstance) {
InternalMethodInfo methodInfo = getInternalMethodInfo(interfaceMethod);
String bridgeMethodDescriptor = mapping.getBridgeDescriptor(passInstance);
ClassVisitor handleWriter = generateBridge ? bridgeWriter : proxyWriter;
String bridgeOrProxyName = generateBridge ? bridgeName : proxyName;
String handleName = addHandle(handleWriter, () -> handle);
for (TypeMapping m : mapping) {
if (m.conversion() == TypeConversion.EXTRACT_TARGET ||
m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
Proxy<?> from = m.fromProxy();
m.metadata.extractTargetHandle = addHandle(handleWriter, from::getTargetExtractor);
directProxyDependencies.add(from);
}
if (m.conversion() == TypeConversion.WRAP_INTO_PROXY ||
m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
Proxy<?> to = m.toProxy();
m.metadata.proxyConstructorHandle = addHandle(handleWriter, to::getConstructor);
directProxyDependencies.add(to);
}
if (m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
String classField = "c" + classReferences.size();
m.metadata.extractableClassField = classField;
classReferences.add(m.fromProxy()::getProxyClass);
handleWriter.visitField(ACC_PRIVATE | ACC_STATIC, classField, "Ljava/lang/Class;", null, null);
}
}
String bridgeMethodName = methodInfo.name() + "$bridge$" + bridgeMethodCounter;
bridgeMethodCounter++;
MethodVisitor p = proxyWriter.visitMethod(ACC_PUBLIC | ACC_FINAL, methodInfo.name(),
methodInfo.descriptor(), methodInfo.genericSignature(), methodInfo.exceptionNames());
MethodVisitor b = bridgeWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, bridgeMethodName,
bridgeMethodDescriptor, null, null);
if (LOG_DEPRECATED && interfaceMethod.isAnnotationPresent(Deprecated.class)) {
logDeprecated(p, "Warning: using deprecated JBR API method " +
interfaceMethod.getDeclaringClass().getName() + "#" + interfaceMethod.getName());
}
MethodVisitor bp = generateBridge ? b : p;
bp.visitFieldInsn(GETSTATIC, bridgeOrProxyName, handleName, MH_DESCRIPTOR);
if (passInstance) {
p.visitVarInsn(ALOAD, 0);
p.visitFieldInsn(GETFIELD, proxyName, "target", OBJECT_DESCRIPTOR);
b.visitVarInsn(ALOAD, 0);
}
int lvIndex = 1;
for (TypeMapping param : mapping.parameterMapping) {
int opcode = getLoadOpcode(param.from());
p.visitVarInsn(opcode, lvIndex);
b.visitVarInsn(opcode, lvIndex - (passInstance ? 0 : 1));
lvIndex += getParameterSize(param.from());
convertValue(bp, bridgeOrProxyName, param);
}
if (generateBridge) {
p.visitMethodInsn(INVOKESTATIC, bridgeName, bridgeMethodName, bridgeMethodDescriptor, false);
}
bp.visitMethodInsn(INVOKEVIRTUAL, MH_NAME, "invoke", bridgeMethodDescriptor, false);
convertValue(bp, bridgeOrProxyName, mapping.returnMapping());
int returnOpcode = getReturnOpcode(mapping.returnMapping().to());
p.visitInsn(returnOpcode);
b.visitInsn(returnOpcode);
p.visitMaxs(-1, -1);
b.visitMaxs(-1, -1);
}
private String addHandle(ClassVisitor classWriter, Supplier<MethodHandle> handleSupplier) {
String handleName = "h" + handles.size();
handles.add(handleSupplier);
classWriter.visitField(ACC_PRIVATE | ACC_STATIC, handleName, MH_DESCRIPTOR, null, null);
return handleName;
}
private static void convertValue(MethodVisitor m, String handlesHolderName, TypeMapping mapping) {
if (mapping.conversion() == TypeConversion.IDENTITY) return;
Label skipConvert = new Label();
m.visitInsn(DUP);
m.visitJumpInsn(IFNULL, skipConvert);
switch (mapping.conversion()) {
case EXTRACT_TARGET ->
m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractTargetHandle, MH_DESCRIPTOR);
case WRAP_INTO_PROXY ->
m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.proxyConstructorHandle, MH_DESCRIPTOR);
case DYNAMIC_2_WAY -> {
m.visitInsn(DUP);
m.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractableClassField, "Ljava/lang/Class;");
Label elseBranch = new Label(), afterBranch = new Label();
m.visitJumpInsn(IF_ACMPNE, elseBranch);
m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractTargetHandle, MH_DESCRIPTOR);
m.visitJumpInsn(GOTO, afterBranch);
m.visitLabel(elseBranch);
m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.proxyConstructorHandle, MH_DESCRIPTOR);
m.visitLabel(afterBranch);
}
}
m.visitInsn(SWAP);
m.visitMethodInsn(INVOKEVIRTUAL, MH_NAME, "invoke", CONVERSION_DESCRIPTOR, false);
m.visitLabel(skipConvert);
}
private static MethodMapping getTargetMethodMapping(Method interfaceMethod) {
Class<?>[] params = interfaceMethod.getParameterTypes();
TypeMapping[] paramMappings = new TypeMapping[params.length];
for (int i = 0; i < params.length; i++) {
paramMappings[i] = getTargetTypeMapping(params[i]);
params[i] = paramMappings[i].to();
}
TypeMapping returnMapping = getTargetTypeMapping(interfaceMethod.getReturnType()).inverse();
return new MethodMapping(MethodType.methodType(returnMapping.from(), params), returnMapping, paramMappings);
}
private static <T> TypeMapping getTargetTypeMapping(Class<T> userType) {
TypeMappingMetadata m = new TypeMappingMetadata();
Proxy<T> p = JBRApi.getProxy(userType);
if (p != null && p.getInfo().target != null) {
Proxy<?> r = JBRApi.getProxy(p.getInfo().target.lookupClass());
if (r != null && r.getInfo().target != null) {
return new TypeMapping(userType, p.getInfo().target.lookupClass(), TypeConversion.DYNAMIC_2_WAY, p, r, m);
}
return new TypeMapping(userType, p.getInfo().target.lookupClass(), TypeConversion.EXTRACT_TARGET, p, null, m);
}
Class<?> interFace = JBRApi.getProxyInterfaceByTargetName(userType.getName());
if (interFace != null) {
Proxy<?> r = JBRApi.getProxy(interFace);
if (r != null) {
return new TypeMapping(userType, interFace, TypeConversion.WRAP_INTO_PROXY, null, r, m);
}
}
return new TypeMapping(userType, userType, TypeConversion.IDENTITY, null, null, m);
}
private record MethodMapping(MethodType type, TypeMapping returnMapping, TypeMapping[] parameterMapping)
implements Iterable<TypeMapping> {
@Override
public Iterator<TypeMapping> iterator() {
return new Iterator<>() {
private int index = -1;
@Override
public boolean hasNext() {
return index < parameterMapping.length;
}
@Override
public TypeMapping next() {
TypeMapping m = index == -1 ? returnMapping : parameterMapping[index];
index++;
return m;
}
};
}
/**
* Every convertable parameter type is replaced with {@link Object} for bridge descriptor.
* Optional {@link Object} is added as first parameter for instance methods.
*/
String getBridgeDescriptor(boolean passInstance) {
StringBuilder bd = new StringBuilder("(");
if (passInstance) bd.append(OBJECT_DESCRIPTOR);
for (TypeMapping m : parameterMapping) {
bd.append(m.getBridgeDescriptor());
}
bd.append(')');
bd.append(returnMapping.getBridgeDescriptor());
return bd.toString();
}
}
private record TypeMapping(Class<?> from, Class<?> to, TypeConversion conversion,
Proxy<?> fromProxy, Proxy<?> toProxy, TypeMappingMetadata metadata) {
TypeMapping inverse() {
return new TypeMapping(to, from, switch (conversion) {
case EXTRACT_TARGET -> TypeConversion.WRAP_INTO_PROXY;
case WRAP_INTO_PROXY -> TypeConversion.EXTRACT_TARGET;
default -> conversion;
}, toProxy, fromProxy, metadata);
}
String getBridgeDescriptor() {
if (conversion == TypeConversion.IDENTITY) return Type.getDescriptor(from);
else return "Ljava/lang/Object;";
}
}
private static class TypeMappingMetadata {
private String extractTargetHandle, proxyConstructorHandle, extractableClassField;
}
private enum TypeConversion {
/**
* No conversion.
*/
IDENTITY,
/**
* Take a proxy object and extract its target implementation object.
*/
EXTRACT_TARGET,
/**
* Create new proxy targeting given implementation object.
*/
WRAP_INTO_PROXY,
/**
* Decide between {@link #EXTRACT_TARGET} and {@link #WRAP_INTO_PROXY} at runtime, depending on actual object.
*/
DYNAMIC_2_WAY
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
import static java.lang.invoke.MethodHandles.Lookup;
/**
* Proxy info, like {@link RegisteredProxyInfo}, but with all classes and lookup contexts resolved.
* Contains all necessary information to create a {@linkplain Proxy proxy}.
*/
class ProxyInfo {
final Lookup apiModule;
final Type type;
final Lookup interFaceLookup;
final Class<?> interFace;
final Lookup target;
final Map<String, StaticMethodMapping> staticMethods = new HashMap<>();
private ProxyInfo(RegisteredProxyInfo i) {
this.apiModule = i.apiModule();
type = i.type();
interFaceLookup = lookup(getInterfaceLookup(), i.interfaceName());
interFace = interFaceLookup == null ? null : interFaceLookup.lookupClass();
target = i.target() == null ? null : lookup(getTargetLookup(), i.target());
for (RegisteredProxyInfo.StaticMethodMapping m : i.staticMethods()) {
Lookup l = lookup(getTargetLookup(), m.clazz());
if (l != null) {
staticMethods.put(m.interfaceMethodName(), new StaticMethodMapping(l, m.methodName()));
}
}
}
/**
* Resolve all classes and lookups for given {@link RegisteredProxyInfo}.
*/
static ProxyInfo resolve(RegisteredProxyInfo i) {
ProxyInfo info = new ProxyInfo(i);
if (info.interFace == null || (info.target == null && info.staticMethods.isEmpty())) return null;
if (!info.interFace.isInterface()) {
if (info.type == Type.CLIENT_PROXY) {
throw new RuntimeException("Tried to create client proxy for non-interface: " + info.interFace);
} else {
return null;
}
}
return info;
}
Lookup getInterfaceLookup() {
return type == Type.CLIENT_PROXY ? apiModule : JBRApi.outerLookup;
}
Lookup getTargetLookup() {
return type == Type.CLIENT_PROXY ? JBRApi.outerLookup : apiModule;
}
private Lookup lookup(Lookup lookup, String clazz) {
try {
return MethodHandles.privateLookupIn(lookup.findClass(clazz), lookup);
} catch (ClassNotFoundException | IllegalAccessException e) {
if (lookup == JBRApi.outerLookup) return null;
else throw new RuntimeException(e);
}
}
record StaticMethodMapping(Lookup lookup, String methodName) {}
/**
* Proxy type, see {@link Proxy}
*/
enum Type {
PROXY,
SERVICE,
CLIENT_PROXY
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.internal;
import java.lang.invoke.MethodHandles;
import java.util.List;
/**
* Raw proxy info, as it was registered through {@link JBRApi.ModuleRegistry}.
* Contains all necessary information to create a {@linkplain Proxy proxy}.
*/
record RegisteredProxyInfo(MethodHandles.Lookup apiModule,
String interfaceName,
String target,
ProxyInfo.Type type,
List<StaticMethodMapping> staticMethods) {
record StaticMethodMapping(String interfaceMethodName, String clazz, String methodName) {}
}

View File

@@ -25,6 +25,8 @@
package java.nio.file;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicWithKeyFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.Closeable;
import java.io.IOException;
@@ -217,7 +219,11 @@ class FileTreeWalker implements Closeable {
// links then a link target might not exist so get attributes of link
BasicFileAttributes attrs;
try {
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
BasicFileAttributeView view = Files.getFileAttributeView(file, BasicWithKeyFileAttributeView.class, linkOptions);
if (view == null) {
view = Files.getFileAttributeView(file, BasicFileAttributeView.class, linkOptions);
}
attrs = view.readAttributes();
} catch (IOException ioe) {
if (!followLinks)
throw ioe;

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
/*
* Copyright (c) 2007, 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
@@ -22,11 +21,18 @@
* 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.
-->
<configuration version="2.0" label="TestSettings" description="Configuration for testing promotion failed event" provider="Oracle">
*/
<event name="jdk.EvacuationFailed">
<setting name="enabled">true</setting>
<setting name="threshold">0 ms</setting>
</event>
</configuration>
package java.nio.file.attribute;
import java.io.IOException;
/**
* Similar to {@link BasicFileAttributeView} with a hint to implementors
* to retrieve a valid {@link BasicFileAttributes#fileKey()} if possible, even
* at a performance cost.
*/
public interface BasicWithKeyFileAttributeView
extends BasicFileAttributeView {
}

View File

@@ -129,11 +129,14 @@ module java.base {
exports javax.security.auth.spi;
exports javax.security.auth.x500;
exports javax.security.cert;
exports com.jetbrains.bootstrap;
// additional qualified exports may be inserted at build time
// see make/gensrc/GenModuleInfo.gmk
exports com.jetbrains.internal to
java.desktop;
exports com.sun.crypto.provider to
jdk.crypto.cryptoki;
exports sun.invoke.util to

View File

@@ -700,11 +700,11 @@ class SocketChannelImpl
private SocketAddress unixBind(SocketAddress local) throws IOException {
UnixDomainSockets.checkPermission();
if (local == null) {
return UnixDomainSockets.UNNAMED;
return UnixDomainSockets.getUNNAMED();
} else {
Path path = UnixDomainSockets.checkAddress(local).getPath();
if (path.toString().isEmpty()) {
return UnixDomainSockets.UNNAMED;
return UnixDomainSockets.getUNNAMED();
} else {
// bind to non-empty path
UnixDomainSockets.bind(fd, path);

View File

@@ -44,7 +44,9 @@ import sun.nio.fs.AbstractFileSystemProvider;
class UnixDomainSockets {
private UnixDomainSockets() { }
static final UnixDomainSocketAddress UNNAMED = UnixDomainSocketAddress.of("");
private static class UNNAMEDHolder {
static final UnixDomainSocketAddress UNNAMED = UnixDomainSocketAddress.of("");
}
private static final boolean supported;
@@ -71,7 +73,7 @@ class UnixDomainSockets {
// Security check passed
} catch (SecurityException e) {
// Return unnamed address only if security check fails
addr = UNNAMED;
addr = getUNNAMED();
}
return addr;
}
@@ -178,4 +180,8 @@ class UnixDomainSockets {
IOUtil.load();
supported = init();
}
static UnixDomainSocketAddress getUNNAMED() {
return UNNAMEDHolder.UNNAMED;
}
}

View File

@@ -27,6 +27,7 @@
#include "jvm_md.h"
#include <dirent.h>
#include <dlfcn.h>
#include <link.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
@@ -220,6 +221,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 environment variable needs to be set, see flowchart.
*/
@@ -243,6 +277,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 && dmllp == NULL) {

View File

@@ -41,16 +41,16 @@ import static sun.nio.fs.WindowsConstants.*;
class WindowsDirectoryStream
implements DirectoryStream<Path>
{
private static final int NATIVE_BUFFER_SIZE = 8192;
private final WindowsPath dir;
private final DirectoryStream.Filter<? super Path> filter;
// handle to directory
private final long handle;
// first entry in the directory
private final String firstName;
// Query directory information data structure
private final QueryDirectoryInformation queryDirectoryInformation;
// buffer for WIN32_FIND_DATA structure that receives information about file
private final NativeBuffer findDataBuffer;
// Buffer used to receive file entries from NtQueryDirectoryInformation calls
private final NativeBuffer queryDirectoryInformationBuffer;
private final Object closeLock = new Object();
@@ -65,21 +65,15 @@ class WindowsDirectoryStream
this.dir = dir;
this.filter = filter;
this.queryDirectoryInformationBuffer = NativeBuffers.getNativeBuffer(NATIVE_BUFFER_SIZE);
try {
// Need to append * or \* to match entries in directory.
// Open the directory for reading and read the first set of entries in the native buffer
String search = dir.getPathForWin32Calls();
char last = search.charAt(search.length() -1);
if (last == ':' || last == '\\') {
search += "*";
} else {
search += "\\*";
}
FirstFile first = FindFirstFile(search);
this.handle = first.handle();
this.firstName = first.name();
this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
this.queryDirectoryInformation = OpenNtQueryDirectoryInformation(search, this.queryDirectoryInformationBuffer);
} catch (WindowsException x) {
// Release the buffer, as this instance is not fully constructed
this.queryDirectoryInformationBuffer.release();
if (x.lastError() == ERROR_DIRECTORY) {
throw new NotDirectoryException(dir.getPathForExceptionMessage());
}
@@ -99,9 +93,9 @@ class WindowsDirectoryStream
return;
isOpen = false;
}
findDataBuffer.release();
queryDirectoryInformationBuffer.release();
try {
FindClose(handle);
CloseNtQueryDirectoryInformation(queryDirectoryInformation);
} catch (WindowsException x) {
x.rethrowAsIOException(dir);
}
@@ -115,20 +109,20 @@ class WindowsDirectoryStream
synchronized (this) {
if (iterator != null)
throw new IllegalStateException("Iterator already obtained");
iterator = new WindowsDirectoryIterator(firstName);
iterator = new WindowsDirectoryIterator();
return iterator;
}
}
private class WindowsDirectoryIterator implements Iterator<Path> {
private boolean atEof;
private String first;
private Path nextEntry;
private String prefix;
private int nextOffset;
WindowsDirectoryIterator(String first) {
WindowsDirectoryIterator() {
atEof = false;
this.first = first;
nextOffset = 0;
if (dir.needsSlashWhenResolving()) {
prefix = dir.toString() + "\\";
} else {
@@ -156,44 +150,40 @@ class WindowsDirectoryStream
// reads next directory entry
private Path readNextEntry() {
// handle first element returned by search
if (first != null) {
nextEntry = isSelfOrParent(first) ? null : acceptEntry(first, null);
first = null;
if (nextEntry != null)
return nextEntry;
}
for (;;) {
String name = null;
String name;
WindowsFileAttributes attrs;
// synchronize on closeLock to prevent close while reading
synchronized (closeLock) {
try {
if (isOpen) {
name = FindNextFile(handle, findDataBuffer.address());
}
} catch (WindowsException x) {
IOException ioe = x.asIOException(dir);
throw new DirectoryIteratorException(ioe);
}
// NO_MORE_FILES or stream closed
if (name == null) {
// Fetch next set of entries if we don't have anything available in buffer
if (!isOpen) {
atEof = true;
return null;
}
// ignore link to self and parent directories
if (isSelfOrParent(name))
continue;
if (nextOffset < 0) {
try {
atEof = !NextNtQueryDirectoryInformation(queryDirectoryInformation, queryDirectoryInformationBuffer);
} catch (WindowsException x) {
IOException ioe = x.asIOException(dir);
throw new DirectoryIteratorException(ioe);
}
if (atEof) {
return null;
}
nextOffset = 0;
}
// grab the attributes from the WIN32_FIND_DATA structure
// (needs to be done while holding closeLock because close
// will release the buffer)
attrs = WindowsFileAttributes
.fromFindData(findDataBuffer.address());
long fullDirInformationAddress = queryDirectoryInformationBuffer.address() + nextOffset;
int nextEntryOffset = WindowsFileAttributes.getNextOffsetFromFileIdFullDirInformation(fullDirInformationAddress);
nextOffset = nextEntryOffset == 0 ? -1 : nextOffset + nextEntryOffset;
name = WindowsFileAttributes.getFileNameFromFileIdFullDirInformation(fullDirInformationAddress);
if (isSelfOrParent(name)) {
// Skip "." and ".."
continue;
}
attrs = WindowsFileAttributes.fromFileIdFullDirInformation(fullDirInformationAddress, queryDirectoryInformation.volSerialNumber());
}
// return entry if accepted by filter

View File

@@ -150,6 +150,23 @@ class WindowsFileAttributeViews {
}
}
private static class BasicWithKey extends Basic {
BasicWithKey(WindowsPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public WindowsFileAttributes readAttributes() throws IOException {
file.checkRead();
try {
return WindowsFileAttributes.getWithFileKey(file, followLinks);
} catch (WindowsException x) {
x.rethrowAsIOException(file);
return null; // keep compiler happy
}
}
}
static class Dos extends Basic implements DosFileAttributeView {
private static final String READONLY_NAME = "readonly";
private static final String ARCHIVE_NAME = "archive";
@@ -289,6 +306,10 @@ class WindowsFileAttributeViews {
return new Basic(file, followLinks);
}
static Basic createBasicWithKeyView(WindowsPath file, boolean followLinks) {
return new BasicWithKey(file, followLinks);
}
static Dos createDosView(WindowsPath file, boolean followLinks) {
return new Dos(file, followLinks);
}

View File

@@ -108,10 +108,42 @@ class WindowsFileAttributes
private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
// used to adjust values between Windows and java epochs
private static final long WINDOWS_EPOCH_IN_MICROS = -11644473600000000L;
private static final long WINDOWS_EPOCH_IN_100NS = -116444736000000000L;
/**
* typedef struct _FILE_ID_FULL_DIR_INFORMATION {
* ULONG NextEntryOffset; // offset = 0
* ULONG FileIndex; // offset = 4
* LARGE_INTEGER CreationTime; // offset = 8
* LARGE_INTEGER LastAccessTime; // offset = 16
* LARGE_INTEGER LastWriteTime; // offset = 24
* LARGE_INTEGER ChangeTime; // offset = 32
* LARGE_INTEGER EndOfFile; // offset = 40
* LARGE_INTEGER AllocationSize; // offset = 48
* ULONG FileAttributes; // offset = 56
* ULONG FileNameLength; // offset = 60
* ULONG EaSize; // offset = 64
* LARGE_INTEGER FileId; // offset = 72
* WCHAR FileName[1]; // offset = 80
* } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
*/
private static final int OFFSETOF_FULL_DIR_INFO_NEXT_ENTRY_OFFSET = 0;
private static final int OFFSETOF_FULL_DIR_INFO_CREATION_TIME = 8;
private static final int OFFSETOF_FULL_DIR_INFO_LAST_ACCESS_TIME = 16;
private static final int OFFSETOF_FULL_DIR_INFO_LAST_WRITE_TIME = 24;
private static final int OFFSETOF_FULL_DIR_INFO_END_OF_FILE = 40;
private static final int OFFSETOF_FULL_DIR_INFO_FILE_ATTRIBUTES = 56;
private static final int OFFSETOF_FULL_DIR_INFO_FILENAME_LENGTH = 60;
private static final int OFFSETOF_FULL_DIR_INFO_EA_SIZE = 64;
private static final int OFFSETOF_FULL_DIR_INFO_FILE_ID = 72;
private static final int OFFSETOF_FULL_DIR_INFO_FILENAME = 80;
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
// indicates if accurate metadata is required (interesting on NTFS only)
private static final boolean ensureAccurateMetadata;
static {
@@ -133,6 +165,9 @@ class WindowsFileAttributes
private final int fileIndexHigh;
private final int fileIndexLow;
// created lazily
private volatile WindowsFileKey key;
/**
* Convert 64-bit value representing the number of 100-nanosecond intervals
* since January 1, 1601 to a FileTime.
@@ -257,6 +292,47 @@ class WindowsFileAttributes
0); // fileIndexLow
}
/**
* Create a WindowsFileAttributes from a FILE_ID_FULL_DIR_INFORMATION structure
*/
static WindowsFileAttributes fromFileIdFullDirInformation(long address, int volSerialNumber) {
int fileAttrs = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ATTRIBUTES);
long creationTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_CREATION_TIME);
long lastAccessTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_LAST_ACCESS_TIME);
long lastWriteTime = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_LAST_WRITE_TIME);
long size = unsafe.getLong(address + OFFSETOF_FULL_DIR_INFO_END_OF_FILE);
int reparseTag = isReparsePoint(fileAttrs) ?
unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_EA_SIZE) : 0;
int fileIndexLow = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ID);
int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILE_ID + 4);
return new WindowsFileAttributes(fileAttrs,
creationTime,
lastAccessTime,
lastWriteTime,
size,
reparseTag,
volSerialNumber,
fileIndexHigh, // fileIndexHigh
fileIndexLow); // fileIndexLow
}
static int getNextOffsetFromFileIdFullDirInformation(long address) {
return unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_NEXT_ENTRY_OFFSET);
}
static String getFileNameFromFileIdFullDirInformation(long address) {
// copy the name
int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FULL_DIR_INFO_FILENAME_LENGTH);
if ((nameLengthInBytes % 2) != 0) {
throw new AssertionError("FileNameLength is not a multiple of 2");
}
char[] nameAsArray = new char[nameLengthInBytes/2];
unsafe.copyMemory(null, address + OFFSETOF_FULL_DIR_INFO_FILENAME, nameAsArray,
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
return new String(nameAsArray);
}
/**
* Reads the attributes of an open file
*/
@@ -347,6 +423,15 @@ class WindowsFileAttributes
}
// file is reparse point so need to open file to get attributes
return getWithFileKey(path, followLinks);
}
/**
* Returns attributes of given file.
*/
static WindowsFileAttributes getWithFileKey(WindowsPath path, boolean followLinks)
throws WindowsException
{
long handle = path.openForReadAttributeAccess(followLinks);
try {
return readAttributes(handle);
@@ -414,7 +499,17 @@ class WindowsFileAttributes
@Override
public Object fileKey() {
return null;
if (volSerialNumber == 0) {
return null;
}
if (key == null) {
synchronized (this) {
if (key == null) {
key = new WindowsFileKey(volSerialNumber, ((long)fileIndexHigh << 32) + fileIndexLow);
}
}
}
return key;
}
// package private

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2008, 2009, 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.nio.fs;
/**
* Container for volume/file id to uniquely identify file.
*/
class WindowsFileKey {
private final int volSerialNumber;
private final long fileId;
WindowsFileKey(int volSerialNumber, long fileId) {
this.volSerialNumber = volSerialNumber;
this.fileId = fileId;
}
@Override
public int hashCode() {
return (volSerialNumber ^ (volSerialNumber >>> 16)) +
(int)(fileId ^ (fileId >>> 32));
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof WindowsFileKey))
return false;
WindowsFileKey other = (WindowsFileKey)obj;
return (this.volSerialNumber == other.volSerialNumber) && (this.fileId == other.fileId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(volId=")
.append(Integer.toHexString(volSerialNumber))
.append(",fileId=")
.append(Long.toHexString(fileId))
.append(')');
return sb.toString();
}
}

View File

@@ -168,6 +168,8 @@ class WindowsFileSystemProvider
boolean followLinks = Util.followLinks(options);
if (view == BasicFileAttributeView.class)
return (V) WindowsFileAttributeViews.createBasicView(file, followLinks);
if (view == BasicWithKeyFileAttributeView.class)
return (V) WindowsFileAttributeViews.createBasicWithKeyView(file, followLinks);
if (view == DosFileAttributeView.class)
return (V) WindowsFileAttributeViews.createDosView(file, followLinks);
if (view == AclFileAttributeView.class)
@@ -205,6 +207,8 @@ class WindowsFileSystemProvider
boolean followLinks = Util.followLinks(options);
if (name.equals("basic"))
return WindowsFileAttributeViews.createBasicView(file, followLinks);
if (name.equals("basicwithkey"))
return WindowsFileAttributeViews.createBasicWithKeyView(file, followLinks);
if (name.equals("dos"))
return WindowsFileAttributeViews.createDosView(file, followLinks);
if (name.equals("acl"))

View File

@@ -285,6 +285,38 @@ class WindowsNativeDispatcher {
*/
static native void FindClose(long handle) throws WindowsException;
static QueryDirectoryInformation OpenNtQueryDirectoryInformation(String path, NativeBuffer buffer) throws WindowsException {
NativeBuffer pathBuffer = asNativeBuffer(path);
try {
QueryDirectoryInformation data = new QueryDirectoryInformation();
OpenNtQueryDirectoryInformation0(pathBuffer.address(), buffer.address(), buffer.size(), data);
return data;
} finally {
pathBuffer.release();
}
}
static class QueryDirectoryInformation {
private long handle;
private int volSerialNumber;
private QueryDirectoryInformation() { }
public long handle() { return handle; }
public int volSerialNumber() { return volSerialNumber; }
}
private static native void OpenNtQueryDirectoryInformation0(long lpFileName, long buffer, int bufferSize, QueryDirectoryInformation obj)
throws WindowsException;
static boolean NextNtQueryDirectoryInformation(QueryDirectoryInformation data, NativeBuffer buffer) throws WindowsException {
return NextNtQueryDirectoryInformation0(data.handle(), buffer.address(), buffer.size());
}
private static native boolean NextNtQueryDirectoryInformation0(long handle, long buffer, int bufferSize)
throws WindowsException;
static void CloseNtQueryDirectoryInformation(QueryDirectoryInformation data) throws WindowsException {
CloseHandle(data.handle);
}
/**
* GetFileInformationByHandle(
* HANDLE hFile,

View File

@@ -471,6 +471,8 @@ GetJavaProperties(JNIEnv* env)
* Windows Server 2012 6 2 (!VER_NT_WORKSTATION)
* Windows Server 2012 R2 6 3 (!VER_NT_WORKSTATION)
* Windows 10 10 0 (VER_NT_WORKSTATION)
* Windows 11 10 0 (VER_NT_WORKSTATION)
* where (buildNumber >= 22000)
* Windows Server 2016 10 0 (!VER_NT_WORKSTATION)
* Windows Server 2019 10 0 (!VER_NT_WORKSTATION)
* where (buildNumber > 17762)
@@ -542,7 +544,14 @@ GetJavaProperties(JNIEnv* env)
} else if (majorVersion == 10) {
if (is_workstation) {
switch (minorVersion) {
case 0: sprops.os_name = "Windows 10"; break;
case 0:
/* Windows 11 21H2 (original release) build number is 22000 */
if (buildNumber >= 22000) {
sprops.os_name = "Windows 11";
} else {
sprops.os_name = "Windows 10";
}
break;
default: sprops.os_name = "Windows NT (unknown)";
}
} else {

View File

@@ -38,6 +38,8 @@
#include "jni_util.h"
#include "jlong.h"
#include "ntifs_min.h"
#include "sun_nio_fs_WindowsNativeDispatcher.h"
/**
@@ -50,6 +52,9 @@ static jfieldID findFirst_attributes;
static jfieldID findStream_handle;
static jfieldID findStream_name;
static jfieldID queryDirectoryInformation_handle;
static jfieldID queryDirectoryInformation_volSerialNumber;
static jfieldID volumeInfo_fsName;
static jfieldID volumeInfo_volName;
static jfieldID volumeInfo_volSN;
@@ -71,6 +76,13 @@ static jfieldID completionStatus_error;
static jfieldID completionStatus_bytesTransferred;
static jfieldID completionStatus_completionKey;
typedef NTSYSCALLAPI NTSTATUS(NTAPI* NtQueryDirectoryFile_Proc) (HANDLE, HANDLE, PIO_APC_ROUTINE,
PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS, BOOLEAN, PUNICODE_STRING, BOOLEAN);
typedef ULONG(NTAPI* RtlNtStatusToDosError_Proc) (NTSTATUS);
static NtQueryDirectoryFile_Proc NtQueryDirectoryFile_func;
static RtlNtStatusToDosError_Proc RtlNtStatusToDosError_func;
static void throwWindowsException(JNIEnv* env, DWORD lastError) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/WindowsException",
"(I)V", lastError);
@@ -87,6 +99,7 @@ JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
{
jclass clazz;
HMODULE h;
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile");
CHECK_NULL(clazz);
@@ -104,6 +117,13 @@ Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
CHECK_NULL(findStream_name);
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$QueryDirectoryInformation");
CHECK_NULL(clazz);
queryDirectoryInformation_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
CHECK_NULL(queryDirectoryInformation_handle);
queryDirectoryInformation_volSerialNumber = (*env)->GetFieldID(env, clazz, "volSerialNumber", "I");;
CHECK_NULL(queryDirectoryInformation_volSerialNumber);
clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation");
CHECK_NULL(clazz);
volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;");
@@ -148,6 +168,16 @@ Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
CHECK_NULL(completionStatus_bytesTransferred);
completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "J");
CHECK_NULL(completionStatus_completionKey);
// get handle to ntdll
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
L"ntdll.dll", &h) != 0)
{
NtQueryDirectoryFile_func =
(NtQueryDirectoryFile_Proc)GetProcAddress(h, "NtQueryDirectoryFile");
RtlNtStatusToDosError_func =
(RtlNtStatusToDosError_Proc)GetProcAddress(h, "RtlNtStatusToDosError");
}
}
JNIEXPORT jlong JNICALL
@@ -428,6 +458,118 @@ Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this,
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_OpenNtQueryDirectoryInformation0(JNIEnv* env, jclass this,
jlong address, jlong bufferAddress, jint bufferSize, jobject obj)
{
LPCWSTR lpFileName = jlong_to_ptr(address);
BOOL ok;
BY_HANDLE_FILE_INFORMATION info;
HANDLE handle;
NTSTATUS status;
ULONG win32ErrorCode;
IO_STATUS_BLOCK ioStatusBlock;
if ((NtQueryDirectoryFile_func == NULL) || (RtlNtStatusToDosError_func == NULL)) {
JNU_ThrowInternalError(env, "Should not get here");
return;
}
handle = CreateFileW(lpFileName, FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE) {
throwWindowsException(env, GetLastError());
return;
}
status = NtQueryDirectoryFile_func(
handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
jlong_to_ptr(bufferAddress), // FileInformation
bufferSize, // Length
FileIdFullDirectoryInformation, // FileInformationClass
FALSE, // ReturnSingleEntry
NULL, // FileName
FALSE); // RestartScan
if (!NT_SUCCESS(status)) {
/*
* NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
* asked to enumerate an invalid directory (ie it is a file
* instead of a directory). Verify that is the actual cause
* of the error.
*/
if (status == STATUS_INVALID_PARAMETER) {
DWORD attributes = GetFileAttributesW(lpFileName);
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
status = STATUS_NOT_A_DIRECTORY;
}
}
win32ErrorCode = RtlNtStatusToDosError_func(status);
throwWindowsException(env, win32ErrorCode);
CloseHandle(handle);
return;
}
// This call allows retrieving the volume ID of this directory (and all its entries)
ok = GetFileInformationByHandle(handle, &info);
if (!ok) {
throwWindowsException(env, GetLastError());
CloseHandle(handle);
return;
}
(*env)->SetLongField(env, obj, queryDirectoryInformation_handle, ptr_to_jlong(handle));
(*env)->SetIntField(env, obj, queryDirectoryInformation_volSerialNumber, info.dwVolumeSerialNumber);
}
JNIEXPORT jboolean JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_NextNtQueryDirectoryInformation0(JNIEnv* env, jclass this,
jlong handle, jlong address, jint size)
{
HANDLE h = (HANDLE)jlong_to_ptr(handle);
ULONG win32ErrorCode;
IO_STATUS_BLOCK ioStatusBlock;
NTSTATUS status;
if ((NtQueryDirectoryFile_func == NULL) || (RtlNtStatusToDosError_func == NULL)) {
JNU_ThrowInternalError(env, "Should not get here");
return JNI_FALSE;
}
status = NtQueryDirectoryFile_func(
h, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
jlong_to_ptr(address), // FileInformation
size, // Length
FileIdFullDirectoryInformation, // FileInformationClass
FALSE, // ReturnSingleEntry
NULL, // FileName
FALSE); // RestartScan
if (NT_SUCCESS(status)) {
return JNI_TRUE;
}
// Normal completion: no more files in directory
if (status == STATUS_NO_MORE_FILES) {
return JNI_FALSE;
}
win32ErrorCode = RtlNtStatusToDosError_func(status);
throwWindowsException(env, win32ErrorCode);
return JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByHandle(JNIEnv* env, jclass this,
jlong handle, jlong address)

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _NTIFS_MIN_
#define _NTIFS_MIN_
/*
* Copy necessary structures and definitions out of the Windows DDK
* to enable calling NtQueryDirectoryFile()
*/
typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation,
FileBothDirectoryInformation,
FileBasicInformation,
FileStandardInformation,
FileInternalInformation,
FileEaInformation,
FileAccessInformation,
FileNameInformation,
FileRenameInformation,
FileLinkInformation,
FileNamesInformation,
FileDispositionInformation,
FilePositionInformation,
FileFullEaInformation,
FileModeInformation,
FileAlignmentInformation,
FileAllInformation,
FileAllocationInformation,
FileEndOfFileInformation,
FileAlternateNameInformation,
FileStreamInformation,
FilePipeInformation,
FilePipeLocalInformation,
FilePipeRemoteInformation,
FileMailslotQueryInformation,
FileMailslotSetInformation,
FileCompressionInformation,
FileObjectIdInformation,
FileCompletionInformation,
FileMoveClusterInformation,
FileQuotaInformation,
FileReparsePointInformation,
FileNetworkOpenInformation,
FileAttributeTagInformation,
FileTrackingInformation,
FileIdBothDirectoryInformation,
FileIdFullDirectoryInformation,
FileValidDataLengthInformation,
FileShortNameInformation,
FileIoCompletionNotificationInformation,
FileIoStatusBlockRangeInformation,
FileIoPriorityHintInformation,
FileSfioReserveInformation,
FileSfioVolumeInformation,
FileHardLinkInformation,
FileProcessIdsUsingFileInformation,
FileNormalizedNameInformation,
FileNetworkPhysicalNameInformation,
FileIdGlobalTxDirectoryInformation,
FileIsRemoteDeviceInformation,
FileAttributeCacheInformation,
FileNumaNodeInformation,
FileStandardLinkInformation,
FileRemoteProtocolInformation,
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _FILE_ID_FULL_DIR_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
LARGE_INTEGER FileId;
WCHAR FileName[1];
} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
} u;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef VOID
(NTAPI *PIO_APC_ROUTINE)(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved);
NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_ PVOID FileInformation,
_In_ ULONG Length,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_In_ BOOLEAN ReturnSingleEntry,
_In_opt_ PUNICODE_STRING FileName,
_In_ BOOLEAN RestartScan
);
ULONG
NTAPI
RtlNtStatusToDosError(
NTSTATUS Status
);
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_NOT_A_DIRECTORY ((NTSTATUS)0xC0000103L)
#endif // _NTIFS_MIN_

View File

@@ -32,6 +32,7 @@ import java.awt.geom.*;
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 {
@@ -205,7 +206,15 @@ public final class CStrike extends PhysicalStrike {
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) {

View File

@@ -291,6 +291,10 @@ public class LWWindowPeer
updateFocusableWindowState();
super.setVisibleImpl(visible);
// TODO: update graphicsConfig, see 4868278
if (visible) {
// Set correct background for a window before making it visible
platformWindow.setOpaque(!isTranslucent());
}
platformWindow.setVisible(visible);
}

View File

@@ -35,12 +35,14 @@ import java.awt.EventQueue;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.annotation.Native;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Arrays;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleAction;
@@ -59,6 +61,8 @@ import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JTextArea;
import javax.swing.JList;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import sun.awt.AWTAccessor;
@@ -128,7 +132,7 @@ class CAccessibility implements PropertyChangeListener {
static <T> T invokeAndWait(final Callable<T> callable, final Component c) {
if (c != null) {
try {
return EventQueue.isDispatchThread() ? callable.call() : LWCToolkit.invokeAndWait(callable, c);
return EventQueue.isDispatchThread() ? callable.call() : invokeAndWait(callable, c, (T)null);
} catch (final Exception e) { e.printStackTrace(); }
}
return null;
@@ -561,6 +565,10 @@ class CAccessibility implements PropertyChangeListener {
if (pac == null) return;
AccessibleSelection as = pac.getAccessibleSelection();
if (as == null) return;
if (parent instanceof JList) {
((JList) parent).setSelectedIndex(i);
return;
}
as.addAccessibleSelection(i);
}
}, c);
@@ -661,77 +669,148 @@ class CAccessibility implements PropertyChangeListener {
// Duplicated from JavaComponentAccessibility
// Note that values >=0 are indexes into the child array
static final int JAVA_AX_ALL_CHILDREN = -1;
static final int JAVA_AX_SELECTED_CHILDREN = -2;
static final int JAVA_AX_VISIBLE_CHILDREN = -3;
@Native static final int JAVA_AX_ALL_CHILDREN = -1;
@Native static final int JAVA_AX_SELECTED_CHILDREN = -2;
@Native static final int JAVA_AX_VISIBLE_CHILDREN = -3;
// Each child takes up two entries in the array: one for itself and one for its role
public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
if (a == null) return null;
return invokeAndWait(new Callable<Object[]>() {
public Object[] call() throws Exception {
ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
_addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
/* In the case of fetching a selection, need to check to see if
* the active descendant is at the beginning of the list. If it
* is not it needs to be moved to the beginning of the list so
* VoiceOver will annouce it correctly. The list returned
* from Java is always in order from top to bottom, but when shift
* selecting downward (extending the list) or multi-selecting using
* the VO keys control+option+command+return the active descendant
* is not at the top of the list in the shift select down case and
* may not be in the multi select case.
*/
if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
if (!childrenAndRoles.isEmpty()) {
AccessibleContext activeDescendantAC =
CAccessible.getActiveDescendant(a);
if (activeDescendantAC != null) {
String activeDescendantName =
activeDescendantAC.getAccessibleName();
AccessibleRole activeDescendantRole =
activeDescendantAC.getAccessibleRole();
// Move active descendant to front of list.
// List contains pairs of each selected item's
// Accessible and AccessibleRole.
ArrayList<Object> newArray = new ArrayList<Object>();
int count = childrenAndRoles.size();
Accessible currentAccessible = null;
AccessibleContext currentAC = null;
String currentName = null;
AccessibleRole currentRole = null;
for (int i = 0; i < count; i+=2) {
// Is this the active descendant?
currentAccessible = (Accessible)childrenAndRoles.get(i);
currentAC = currentAccessible.getAccessibleContext();
currentName = currentAC.getAccessibleName();
currentRole = (AccessibleRole)childrenAndRoles.get(i+1);
if (currentName != null && currentName.equals(activeDescendantName) &&
currentRole.equals(activeDescendantRole) ) {
newArray.add(0, currentAccessible);
newArray.add(1, currentRole);
} else {
newArray.add(currentAccessible);
newArray.add(currentRole);
}
}
childrenAndRoles = newArray;
}
}
}
if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
return childrenAndRoles.toArray();
}
return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) };
return getChildrenAndRolesImpl(a, c, whichChildren, allowIgnored);
}
}, c);
}
private static final int JAVA_AX_ROWS = 1;
private static final int JAVA_AX_COLS = 2;
private static Object[] getChildrenAndRolesImpl(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
if (a == null) return null;
ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
_addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
/* In case of fetching a selection, we need to check if
* the active descendant is at the beginning of the list, or
* otherwise move it, so that VoiceOver announces it correctly.
* The java list is always in order from top to bottom, but when
* (1) shift-selecting downward (extending the list) or (2) multi-selecting with
* the VO keys (CTRL+ALT+CMD+RETURN) the active descendant
* is not at the top of the list in the 1st case and may not be in the 2nd.
*/
if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
if (!childrenAndRoles.isEmpty()) {
AccessibleContext activeDescendantAC =
CAccessible.getActiveDescendant(a);
if (activeDescendantAC != null) {
String activeDescendantName =
activeDescendantAC.getAccessibleName();
AccessibleRole activeDescendantRole =
activeDescendantAC.getAccessibleRole();
// Move active descendant to front of list.
// List contains pairs of each selected item's
// Accessible and AccessibleRole.
ArrayList<Object> newArray = new ArrayList<Object>();
int count = childrenAndRoles.size();
Accessible currentAccessible = null;
AccessibleContext currentAC = null;
String currentName = null;
AccessibleRole currentRole = null;
for (int i = 0; i < count; i += 2) {
// Is this the active descendant?
currentAccessible = (Accessible) childrenAndRoles.get(i);
currentAC = currentAccessible.getAccessibleContext();
currentName = currentAC.getAccessibleName();
currentRole = (AccessibleRole) childrenAndRoles.get(i + 1);
if (currentName != null && currentName.equals(activeDescendantName) &&
currentRole.equals(activeDescendantRole)) {
newArray.add(0, currentAccessible);
newArray.add(1, currentRole);
} else {
newArray.add(currentAccessible);
newArray.add(currentRole);
}
}
childrenAndRoles = newArray;
}
}
}
if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
return childrenAndRoles.toArray();
}
return new Object[]{childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1)};
}
// 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) {
if (a == null) return null;
return invokeAndWait(new Callable<Object[]>() {
public Object[] call() throws Exception {
ArrayList<Object> currentLevelChildren = new ArrayList<Object>();
ArrayList<Object> allChildren = new ArrayList<Object>();
ArrayList<Accessible> parentStack = new ArrayList<Accessible>();
parentStack.add(a);
ArrayList<Integer> indexses = new ArrayList<Integer>();
Integer index = 0;
int currentLevel = level;
while (!parentStack.isEmpty()) {
Accessible p = parentStack.get(parentStack.size() - 1);
currentLevelChildren.addAll(Arrays.asList(getChildrenAndRolesImpl(p, c, JAVA_AX_ALL_CHILDREN, allowIgnored)));
if ((currentLevelChildren.size() == 0) || (index >= currentLevelChildren.size())) {
if (!parentStack.isEmpty()) parentStack.remove(parentStack.size() - 1);
if (!indexses.isEmpty()) index = indexses.remove(indexses.size() - 1);
currentLevel -= 1;
currentLevelChildren.clear();
continue;
}
Accessible ca = null;
Object obj = currentLevelChildren.get(index);
if (!(obj instanceof Accessible)) {
index += 2;
currentLevelChildren.clear();
continue;
}
ca = (Accessible) obj;
Object role = currentLevelChildren.get(index + 1);
currentLevelChildren.clear();
AccessibleContext cac = ca.getAccessibleContext();
if (cac == null) {
index += 2;
continue;
}
if ((cac.getAccessibleStateSet().contains(AccessibleState.SELECTED) && (whichChildren == JAVA_AX_SELECTED_CHILDREN)) ||
(cac.getAccessibleStateSet().contains(AccessibleState.VISIBLE) && (whichChildren == JAVA_AX_VISIBLE_CHILDREN)) ||
(whichChildren == JAVA_AX_ALL_CHILDREN)) {
allChildren.add(ca);
allChildren.add(role);
allChildren.add(String.valueOf(currentLevel));
}
index += 2;
if (cac.getAccessibleStateSet().contains(AccessibleState.EXPANDED)) {
parentStack.add(ca);
indexses.add(index);
index = 0;
currentLevel += 1;
continue;
}
}
return allChildren.toArray();
}
}, c);
}
@Native private static final int JAVA_AX_ROWS = 1;
@Native private static final int JAVA_AX_COLS = 2;
public static int getTableInfo(final Accessible a, final Component c,
final int info) {
@@ -750,6 +829,23 @@ class CAccessibility implements PropertyChangeListener {
}, c);
}
private static int[] getTableSelectedInfo(final Accessible a, final Component c,
final int info) {
if (a == null) return null;
return invokeAndWait(() -> {
AccessibleContext ac = a.getAccessibleContext();
AccessibleTable table = ac.getAccessibleTable();
if (table != null) {
if (info == JAVA_AX_COLS) {
return table.getSelectedAccessibleColumns();
} else if (info == JAVA_AX_ROWS) {
return table.getSelectedAccessibleRows();
}
}
return null;
}, c);
}
private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) {
String text = l.getText();
if (text != null && text.length() > 0) {
@@ -868,4 +964,18 @@ class CAccessibility implements PropertyChangeListener {
}
}, (Component)ax);
}
private static boolean isTreeRootVisible(Accessible a, Component c) {
if (a == null) return false;
return invokeAndWait(new Callable<Boolean>() {
public Boolean call() throws Exception {
Accessible sa = CAccessible.getSwingAccessible(a);
if (sa instanceof JTree) {
return ((JTree) sa).isRootVisible();
}
return false;
}
}, c);
}
}

View File

@@ -31,11 +31,7 @@ import java.beans.PropertyChangeListener;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
@@ -75,6 +71,9 @@ class CAccessible extends CFRetainedResource implements Accessible {
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);
private static native void selectedCellsChanged(long ptr);
private Accessible accessible;
@@ -128,6 +127,19 @@ class CAccessible extends CFRetainedResource implements Accessible {
} else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) {
if (newValue == null || newValue instanceof AccessibleContext) {
activeDescendant = (AccessibleContext)newValue;
if (newValue instanceof Accessible) {
Accessible a = (Accessible)newValue;
AccessibleContext ac = a.getAccessibleContext();
if (ac != null) {
Accessible p = ac.getAccessibleParent();
if (p != null) {
AccessibleContext pac = p.getAccessibleContext();
if ((pac != null) && (pac.getAccessibleRole() == AccessibleRole.TABLE)) {
selectedCellsChanged(ptr);
}
}
}
}
}
} else if (name.compareTo(ACCESSIBLE_STATE_PROPERTY) == 0) {
AccessibleContext thisAC = accessible.getAccessibleContext();
@@ -137,6 +149,13 @@ class CAccessible extends CFRetainedResource implements Accessible {
if (parentAccessible != null) {
parentRole = parentAccessible.getAccessibleContext().getAccessibleRole();
}
if (newValue == AccessibleState.EXPANDED) {
treeNodeExpanded(ptr);
} else if (newValue == AccessibleState.COLLAPSED) {
treeNodeCollapsed(ptr);
}
// At least for now don't handle combo box menu state changes.
// This may change when later fixing issues which currently
// exist for combo boxes, but for now the following is only

View File

@@ -422,15 +422,15 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
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;
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.

View File

@@ -27,8 +27,7 @@
#import "CGLGraphicsConfig.h"
#import "AWTView.h"
#import "AWTWindow.h"
#import "JavaComponentAccessibility.h"
#import "JavaTextAccessibility.h"
#import "a11y/CommonComponentAccessibility.h"
#import "JavaAccessibilityUtilities.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
@@ -698,42 +697,29 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
- (id)getAxData:(JNIEnv*)env
{
jobject jcomponent = [self awtComponent:env];
id ax = [[[JavaComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
(*env)->DeleteLocalRef(env, jcomponent);
return ax;
}
- (NSArray *)accessibilityAttributeNames
{
return [[super accessibilityAttributeNames] arrayByAddingObject:NSAccessibilityChildrenAttribute];
}
// NSAccessibility messages
// attribute methods
- (id)accessibilityAttributeValue:(NSString *)attribute
- (id)accessibilityChildren
{
AWT_ASSERT_APPKIT_THREAD;
JNIEnv *env = [ThreadUtilities getJNIEnv];
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
{
JNIEnv *env = [ThreadUtilities getJNIEnv];
(*env)->PushLocalFrame(env, 4);
(*env)->PushLocalFrame(env, 4);
id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]);
id result = NSAccessibilityUnignoredChildrenForOnlyChild([self getAxData:env]);
(*env)->PopLocalFrame(env, NULL);
(*env)->PopLocalFrame(env, NULL);
return result;
}
else
{
return [super accessibilityAttributeValue:attribute];
}
return result;
}
- (BOOL)accessibilityIsIgnored
- (BOOL)isAccessibilityElement
{
return YES;
return NO;
}
- (id)accessibilityHitTest:(NSPoint)point
@@ -743,7 +729,7 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
(*env)->PushLocalFrame(env, 4);
id result = [[self getAxData:env] accessibilityHitTest:point withEnv:env];
id result = [[self getAxData:env] accessibilityHitTest:point];
(*env)->PopLocalFrame(env, NULL);
@@ -768,17 +754,24 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
// --- Services menu support for lightweights ---
// finds the focused accessible element, and if it is a text element, obtains the text from it
- (NSString *)accessibleSelectedText
- (NSString *)accessibilitySelectedText
{
id focused = [self accessibilityFocusedUIElement];
if (![focused isKindOfClass:[JavaTextAccessibility class]]) return nil;
return [(JavaTextAccessibility *)focused accessibilitySelectedTextAttribute];
if (![focused respondsToSelector:@selector(accessibilitySelectedText)]) return nil;
return [focused accessibilitySelectedText];
}
- (void)setAccessibilitySelectedText:(NSString *)accessibilitySelectedText {
id focused = [self accessibilityFocusedUIElement];
if ([focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) {
[focused setAccessibilitySelectedText:accessibilitySelectedText];
}
}
// same as above, but converts to RTFD
- (NSData *)accessibleSelectedTextAsRTFD
{
NSString *selectedText = [self accessibleSelectedText];
NSString *selectedText = [self accessibilitySelectedText];
NSAttributedString *styledText = [[NSAttributedString alloc] initWithString:selectedText];
NSData *rtfdData = [styledText RTFDFromRange:NSMakeRange(0, [styledText length])
documentAttributes:
@@ -791,8 +784,8 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
- (BOOL)replaceAccessibleTextSelection:(NSString *)text
{
id focused = [self accessibilityFocusedUIElement];
if (![focused isKindOfClass:[JavaTextAccessibility class]]) return NO;
[(JavaTextAccessibility *)focused accessibilitySetSelectedTextAttribute:text];
if (![focused respondsToSelector:@selector(setAccessibilitySelectedText)]) return NO;
[focused setAccessibilitySelectedText:text];
return YES;
}
@@ -802,7 +795,7 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
if ([[self window] firstResponder] != self) return nil; // let AWT components handle themselves
if ([sendType isEqual:NSStringPboardType] || [returnType isEqual:NSStringPboardType]) {
NSString *selectedText = [self accessibleSelectedText];
NSString *selectedText = [self accessibilitySelectedText];
if (selectedText) return self;
}
@@ -815,7 +808,7 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, N
if ([types containsObject:NSStringPboardType])
{
[pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
return [pboard setString:[self accessibleSelectedText] forType:NSStringPboardType];
return [pboard setString:[self accessibilitySelectedText] forType:NSStringPboardType];
}
if ([types containsObject:NSRTFDPboardType])

View File

@@ -47,6 +47,8 @@
NSRect standardFrame;
BOOL isMinimizing;
BOOL isJustCreated;
NSWindowTabbingMode javaWindowTabbingMode;
BOOL isEnterFullScreen;
}
// An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -63,6 +65,8 @@
@property (nonatomic) NSRect standardFrame;
@property (nonatomic) BOOL isMinimizing;
@property (nonatomic) BOOL isJustCreated;
@property (nonatomic) NSWindowTabbingMode javaWindowTabbingMode;
@property (nonatomic) BOOL isEnterFullScreen;
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow
ownerWindow:owner
@@ -72,6 +76,8 @@
- (BOOL) isTopmostWindowUnderMouse;
- (NSWindowTabbingMode) getJavaWindowTabbingMode;
// NSWindow overrides delegate methods
- (BOOL) canBecomeKeyWindow;
- (void) becomeKeyWindow;

View File

@@ -67,6 +67,8 @@ static AWTWindow* lastKeyWindow = nil;
// It would be NSZeroPoint if 'Location by Platform' is not used.
static NSPoint lastTopLeftPoint;
static BOOL ignoreResizeWindowDuringAnotherWindowEnd = NO;
// --------------------------------------------------------------
// NSWindow/NSPanel descendants implementation
#define AWT_NS_WINDOW_IMPLEMENTATION \
@@ -119,7 +121,7 @@ static NSPoint lastTopLeftPoint;
} \
\
- (NSWindowTabbingMode)tabbingMode { \
return NSWindowTabbingModeDisallowed; \
return ((AWTWindow*)[self delegate]).javaWindowTabbingMode; \
}
@implementation AWTWindow_Normal
@@ -229,6 +231,69 @@ AWT_NS_WINDOW_IMPLEMENTATION
b:[event deltaY]];
}
- (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];
}
@end
@implementation AWTWindow_Panel
AWT_NS_WINDOW_IMPLEMENTATION
@@ -251,6 +316,8 @@ AWT_NS_WINDOW_IMPLEMENTATION
@synthesize standardFrame;
@synthesize isMinimizing;
@synthesize isJustCreated;
@synthesize javaWindowTabbingMode;
@synthesize isEnterFullScreen;
- (void) updateMinMaxSize:(BOOL)resizable {
if (resizable) {
@@ -401,6 +468,9 @@ AWT_ASSERT_APPKIT_THREAD;
self.isJustCreated = YES;
self.javaWindowTabbingMode = [self getJavaWindowTabbingMode];
self.isEnterFullScreen = NO;
return self;
}
@@ -418,6 +488,35 @@ AWT_ASSERT_APPKIT_THREAD;
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 {
NSEnumerator *windowEnumerator = [[NSApp windows] objectEnumerator];
NSWindow *window;
@@ -635,6 +734,7 @@ AWT_ASSERT_APPKIT_THREAD;
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 {
@@ -696,6 +796,12 @@ AWT_ASSERT_APPKIT_THREAD;
- (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];
}
@@ -974,6 +1080,8 @@ AWT_ASSERT_APPKIT_THREAD;
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
[self allowMovingChildrenBetweenSpaces:YES];
self.isEnterFullScreen = YES;
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CPLATFORM_WINDOW_CLASS();
DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
@@ -987,6 +1095,8 @@ AWT_ASSERT_APPKIT_THREAD;
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
self.isEnterFullScreen = YES;
[self allowMovingChildrenBetweenSpaces:NO];
JNIEnv *env = [ThreadUtilities getJNIEnv];
@@ -1003,6 +1113,8 @@ AWT_ASSERT_APPKIT_THREAD;
}
- (void)windowWillExitFullScreen:(NSNotification *)notification {
self.isEnterFullScreen = NO;
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CPLATFORM_WINDOW_CLASS();
DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
@@ -1021,6 +1133,8 @@ AWT_ASSERT_APPKIT_THREAD;
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
self.isEnterFullScreen = NO;
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
if (platformWindow != NULL) {
@@ -1796,6 +1910,8 @@ JNI_COCOA_ENTER(env);
[nsWindow setDelegate: nil];
[window release];
ignoreResizeWindowDuringAnotherWindowEnd = NO;
}];
JNI_COCOA_EXIT(env);

View File

@@ -129,6 +129,7 @@ JNI_COCOA_ENTER(env);
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);
});

View File

@@ -26,6 +26,10 @@
#import <AppKit/AppKit.h>
#import <jni.h>
extern NSMutableDictionary *sActions;
extern NSMutableDictionary *sActionSelectors;
extern NSMutableArray *sAllActionSelectors;
void initializeActions();
@protocol JavaAccessibilityAction

View File

@@ -29,6 +29,10 @@
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
NSMutableDictionary *sActions = nil;
NSMutableDictionary *sActionSelectors = nil;
NSMutableArray *sAllActionSelectors = nil;
void initializeActions();
@implementation JavaAxAction
@@ -148,3 +152,31 @@
}
@end
void initializeActions() {
int actionsCount = 5;
sActions = [[NSMutableDictionary alloc] initWithCapacity:actionsCount];
[sActions setObject:NSAccessibilityPressAction forKey:@"click"];
[sActions setObject:NSAccessibilityIncrementAction forKey:@"increment"];
[sActions setObject:NSAccessibilityDecrementAction forKey:@"decrement"];
[sActions setObject:NSAccessibilityShowMenuAction forKey:@"togglePopup"];
[sActions setObject:NSAccessibilityPressAction forKey:@"toggleExpand"];
sActionSelectors = [[NSMutableDictionary alloc] initWithCapacity:actionsCount];
[sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPress)) forKey:NSAccessibilityPressAction];
[sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu)) forKey:NSAccessibilityShowMenuAction];
[sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformDecrement)) forKey:NSAccessibilityDecrementAction];
[sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformIncrement)) forKey:NSAccessibilityIncrementAction];
[sActionSelectors setObject:NSStringFromSelector(@selector(accessibilityPerformPick)) forKey:NSAccessibilityPickAction];
sAllActionSelectors = [[NSMutableArray alloc] initWithCapacity:actionsCount];
[sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPick))];
[sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformIncrement))];
[sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformDecrement))];
[sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformShowMenu))];
[sAllActionSelectors addObject:NSStringFromSelector(@selector(accessibilityPerformPress))];
}

View File

@@ -48,6 +48,7 @@ BOOL isVertical(JNIEnv *env, jobject axContext, jobject component);
BOOL isHorizontal(JNIEnv *env, jobject axContext, jobject component);
BOOL isShowing(JNIEnv *env, jobject axContext, jobject component);
BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component);
BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component);
NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component);
jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component);
@@ -60,3 +61,6 @@ void JavaAccessibilitySetAttributeValue(id element, NSString *attribute, id valu
void JavaAccessibilityRaiseSetAttributeToIllegalTypeException(const char *functionName, id element, NSString *attribute, id value);
void JavaAccessibilityRaiseUnimplementedAttributeException(const char *functionName, id element, NSString *attribute);
void JavaAccessibilityRaiseIllegalParameterTypeException(const char *functionName, id element, NSString *attribute, id parameter);
BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber);
NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array);

View File

@@ -27,6 +27,7 @@
#import "JNIUtilities.h"
#import <AppKit/AppKit.h>
#import "ThreadUtilities.h"
static BOOL JavaAccessibilityIsSupportedAttribute(id element, NSString *attribute);
static void JavaAccessibilityLogError(NSString *message);
@@ -50,17 +51,21 @@ static jclass sjc_CAccessibility = NULL;
NSSize getAxComponentSize(JNIEnv *env, jobject axComponent, jobject component)
{
GET_CACCESSIBILITY_CLASS_RETURN(NSZeroSize);
DECLARE_CLASS_RETURN(jc_Dimension, "java/awt/Dimension", NSZeroSize);
DECLARE_FIELD_RETURN(jf_width, jc_Dimension, "width", "I", NSZeroSize);
DECLARE_FIELD_RETURN(jf_height, jc_Dimension, "height", "I", NSZeroSize);
DECLARE_STATIC_METHOD_RETURN(jm_getSize, sjc_CAccessibility, "getSize",
"(Ljavax/accessibility/AccessibleComponent;Ljava/awt/Component;)Ljava/awt/Dimension;", NSZeroSize);
jobject dimension = (*env)->CallStaticObjectMethod(env, jc_Dimension, jm_getSize, axComponent, component);
jobject dimension = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getSize, axComponent, component);
CHECK_EXCEPTION();
if (dimension == NULL) return NSZeroSize;
return NSMakeSize((*env)->GetIntField(env, dimension, jf_width), (*env)->GetIntField(env, dimension, jf_height));
NSSize size = NSMakeSize((*env)->GetIntField(env, dimension, jf_width), (*env)->GetIntField(env, dimension, jf_height));
(*env)->DeleteLocalRef(env, dimension);
return size;
}
NSString *getJavaRole(JNIEnv *env, jobject axComponent, jobject component)
@@ -199,6 +204,20 @@ BOOL isSelectable(JNIEnv *env, jobject axContext, jobject component)
return selectable;
}
BOOL isExpanded(JNIEnv *env, jobject axContext, jobject component)
{
GET_ACCESSIBLESTATE_CLASS_RETURN(NO);
DECLARE_STATIC_FIELD_RETURN(jm_EXPANDED,
sjc_AccessibleState,
"EXPANDED",
"Ljavax/accessibility/AccessibleState;", NO );
jobject axExpandedState = (*env)->GetStaticObjectField(env, sjc_AccessibleState, jm_EXPANDED);
CHECK_EXCEPTION_NULL_RETURN(axExpandedState, NO);
BOOL expanded = containsAxState(env, axContext, axExpandedState, component);
(*env)->DeleteLocalRef(env, axExpandedState);
return expanded;
}
NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject component)
{
GET_CACCESSIBILITY_CLASS_RETURN(NSZeroPoint);
@@ -211,7 +230,9 @@ NSPoint getAxComponentLocationOnScreen(JNIEnv *env, jobject axComponent, jobject
axComponent, component);
CHECK_EXCEPTION();
if (jpoint == NULL) return NSZeroPoint;
return NSMakePoint((*env)->GetIntField(env, jpoint, sjf_X), (*env)->GetIntField(env, jpoint, sjf_Y));
NSPoint p = NSMakePoint((*env)->GetIntField(env, jpoint, sjf_X), (*env)->GetIntField(env, jpoint, sjf_Y));
(*env)->DeleteLocalRef(env, jpoint);
return p;
}
jint getAxTextCharCount(JNIEnv *env, jobject axText, jobject component)
@@ -348,6 +369,75 @@ static void JavaAccessibilityLogError(NSString *message)
NSLog(@"!!! %@", message);
}
/*
* Returns Object.equals for the two items
* This may use LWCToolkit.invokeAndWait(); don't call while holding fLock
* and try to pass a component so the event happens on the correct thread.
*/
BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component)
{
DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO);
DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO);
if ((a == NULL) && (b == NULL)) return YES;
if ((a == NULL) || (b == NULL)) return NO;
if (pthread_main_np() != 0) {
// If we are on the AppKit thread
DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO);
DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals",
"(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO);
return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component);
CHECK_EXCEPTION();
}
jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b);
CHECK_EXCEPTION();
return jb;
}
/*
* The java/lang/Number concrete class could be for any of the Java primitive
* numerical types or some other subclass.
* All existing A11Y code uses Integer so that is what we look for first
* But all must be able to return a double and NSNumber accepts a double,
* so that's the fall back.
*/
NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) {
if (jnumber == NULL) {
return nil;
}
DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil);
DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil);
DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil);
DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil);
if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) {
jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue);
CHECK_EXCEPTION();
return [NSNumber numberWithInteger:i];
} else {
jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue);
CHECK_EXCEPTION();
return [NSNumber numberWithDouble:d];
}
}
/*
* Converts an int array to an NSRange wrapped inside an NSValue
* takes [start, end] values and returns [start, end - start]
*/
NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) {
jint *values = (*env)->GetIntArrayElements(env, array, 0);
if (values == NULL) {
// Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck.
NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__);
return nil;
};
NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])];
(*env)->ReleaseIntArrayElements(env, array, values, 0);
return value;
}
// end appKit copies
/*
@@ -417,13 +507,13 @@ void initializeRoles()
[sRoles setObject:NSAccessibilitySplitGroupRole forKey:@"splitpane"];
[sRoles setObject:NSAccessibilityValueIndicatorRole forKey:@"statusbar"];
[sRoles setObject:NSAccessibilityGroupRole forKey:@"swingcomponent"];
[sRoles setObject:NSAccessibilityGridRole forKey:@"table"];
[sRoles setObject:NSAccessibilityTableRole forKey:@"table"];
[sRoles setObject:NSAccessibilityTextFieldRole forKey:@"text"];
[sRoles setObject:NSAccessibilityTextAreaRole forKey:@"textarea"]; // supports top/bottom of document notifications: CAccessability.getAccessibleRole()
[sRoles setObject:NSAccessibilityCheckBoxRole forKey:@"togglebutton"];
[sRoles setObject:NSAccessibilityToolbarRole forKey:@"toolbar"];
[sRoles setObject:JavaAccessibilityIgnore forKey:@"tooltip"];
[sRoles setObject:NSAccessibilityBrowserRole forKey:@"tree"];
[sRoles setObject:NSAccessibilityOutlineRole forKey:@"tree"];
[sRoles setObject:NSAccessibilityUnknownRole forKey:@"unknown"];
[sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"];
[sRoles setObject:JavaAccessibilityIgnore forKey:@"window"];

View File

@@ -29,7 +29,6 @@
// <https://www.ibm.com/able/guidelines/java/snsjavagjfc.html>
#import "JavaComponentAccessibility.h"
#import "a11y/CommonComponentAccessibility.h"
#import "sun_lwawt_macosx_CAccessibility.h"
#import <AppKit/AppKit.h>
@@ -45,6 +44,12 @@
#import "JNIUtilities.h"
#import "AWTView.h"
// these constants are duplicated in CAccessibility.java
#define JAVA_AX_ALL_CHILDREN (-1)
#define JAVA_AX_SELECTED_CHILDREN (-2)
#define JAVA_AX_VISIBLE_CHILDREN (-3)
// If the value is >=0, it's an index
// GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared.
static jclass sjc_CAccessibility = NULL;
@@ -82,7 +87,7 @@ static jclass sjc_CAccessible = NULL;
static NSMutableDictionary *sAttributeNamesForRoleCache = nil;
static NSObject *sAttributeNamesLOCK = nil;
@interface TabGroupAccessibility : JavaComponentAccessibility {
@interface TabGroupLegacyAccessibility : JavaComponentAccessibility {
NSInteger _numTabs;
}
@@ -112,7 +117,7 @@ static NSObject *sAttributeNamesLOCK = nil;
- (id)accessibilityValueAttribute;
@end
@interface TableAccessibility : JavaComponentAccessibility {
@interface TableLegacyAccessibility : JavaComponentAccessibility {
}
- (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env;
@@ -362,21 +367,18 @@ static NSObject *sAttributeNamesLOCK = nil;
// otherwise, create a new instance
JavaComponentAccessibility *newChild = nil;
newChild = [CommonComponentAccessibility getComponentAccessibility:javaRole];
if (newChild == nil) {
if ([javaRole isEqualToString:@"pagetablist"]) {
newChild = [TabGroupAccessibility alloc];
} else if ([javaRole isEqualToString:@"table"]) {
newChild = [TableAccessibility alloc];
if ([javaRole isEqualToString:@"pagetablist"]) {
newChild = [TabGroupLegacyAccessibility alloc];
} else if ([javaRole isEqualToString:@"table"]) {
newChild = [TableLegacyAccessibility alloc];
} else {
NSString *nsRole = [sRoles objectForKey:javaRole];
if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] ||
[nsRole isEqualToString:NSAccessibilityTextAreaRole] ||
[nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
newChild = [JavaTextAccessibility alloc];
} else {
NSString *nsRole = [sRoles objectForKey:javaRole];
if ([nsRole isEqualToString:NSAccessibilityStaticTextRole] ||
[nsRole isEqualToString:NSAccessibilityTextAreaRole] ||
[nsRole isEqualToString:NSAccessibilityTextFieldRole]) {
newChild = [JavaTextAccessibility alloc];
} else {
newChild = [JavaComponentAccessibility alloc];
}
newChild = [JavaComponentAccessibility alloc];
}
}
@@ -387,7 +389,7 @@ static NSObject *sAttributeNamesLOCK = nil;
// This is the only way to know if the menu is opening; visible state change
// can't be caught because the listeners are not set up in time.
if ( [javaRole isEqualToString:@"popupmenu"] &&
![[parent javaRole] isEqualToString:@"combobox"] ) {
![[parent javaRole] isEqualToString:@"combobox"] ) {
[newChild postMenuOpened];
}
@@ -904,32 +906,6 @@ static NSObject *sAttributeNamesLOCK = nil;
return index;
}
/*
* The java/lang/Number concrete class could be for any of the Java primitive
* numerical types or some other subclass.
* All existing A11Y code uses Integer so that is what we look for first
* But all must be able to return a double and NSNumber accepts a double,
* so that's the fall back.
*/
static NSNumber* JavaNumberToNSNumber(JNIEnv *env, jobject jnumber) {
if (jnumber == NULL) {
return nil;
}
DECLARE_CLASS_RETURN(jnumber_Class, "java/lang/Number", nil);
DECLARE_CLASS_RETURN(jinteger_Class, "java/lang/Integer", nil);
DECLARE_METHOD_RETURN(jm_intValue, jnumber_Class, "intValue", "()I", nil);
DECLARE_METHOD_RETURN(jm_doubleValue, jnumber_Class, "doubleValue", "()D", nil);
if ((*env)->IsInstanceOf(env, jnumber, jinteger_Class)) {
jint i = (*env)->CallIntMethod(env, jnumber, jm_intValue);
CHECK_EXCEPTION();
return [NSNumber numberWithInteger:i];
} else {
jdouble d = (*env)->CallDoubleMethod(env, jnumber, jm_doubleValue);
CHECK_EXCEPTION();
return [NSNumber numberWithDouble:d];
}
}
// Element's maximum value (id)
- (id)accessibilityMaxValueAttribute
{
@@ -1600,7 +1576,7 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_EXIT(env);
}
@implementation TabGroupAccessibility
@implementation TabGroupLegacyAccessibility
- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withView:(NSView *)view withJavaRole:(NSString *)javaRole
{
@@ -1802,9 +1778,6 @@ JNI_COCOA_EXIT(env);
@end
static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
@implementation TabGroupControlAccessibility
- (id)initWithParent:(NSObject *)parent withEnv:(JNIEnv *)env withAccessible:(jobject)accessible withIndex:(jint)index withTabGroup:(jobject)tabGroup withView:(NSView *)view withJavaRole:(NSString *)javaRole
@@ -1873,7 +1846,7 @@ static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
#define JAVA_AX_ROWS (1)
#define JAVA_AX_COLS (2)
@implementation TableAccessibility
@implementation TableLegacyAccessibility
- (NSArray *)initializeAttributeNamesWithEnv:(JNIEnv *)env
{
@@ -1907,30 +1880,3 @@ static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component);
return [self getTableInfo:JAVA_AX_COLS];
}
@end
/*
* Returns Object.equals for the two items
* This may use LWCToolkit.invokeAndWait(); don't call while holding fLock
* and try to pass a component so the event happens on the correct thread.
*/
static BOOL ObjectEquals(JNIEnv *env, jobject a, jobject b, jobject component)
{
DECLARE_CLASS_RETURN(sjc_Object, "java/lang/Object", NO);
DECLARE_METHOD_RETURN(jm_equals, sjc_Object, "equals", "(Ljava/lang/Object;)Z", NO);
if ((a == NULL) && (b == NULL)) return YES;
if ((a == NULL) || (b == NULL)) return NO;
if (pthread_main_np() != 0) {
// If we are on the AppKit thread
DECLARE_CLASS_RETURN(sjc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit", NO);
DECLARE_STATIC_METHOD_RETURN(jm_doEquals, sjc_LWCToolkit, "doEquals",
"(Ljava/lang/Object;Ljava/lang/Object;Ljava/awt/Component;)Z", NO);
return (*env)->CallStaticBooleanMethod(env, sjc_LWCToolkit, jm_doEquals, a, b, component);
CHECK_EXCEPTION();
}
jboolean jb = (*env)->CallBooleanMethod(env, a, jm_equals, b);
CHECK_EXCEPTION();
return jb;
}

View File

@@ -53,23 +53,6 @@ static jmethodID sjm_getAccessibleEditableText = NULL;
GET_STATIC_METHOD_RETURN(sjm_getAccessibleEditableText, sjc_CAccessibleText, "getAccessibleEditableText", \
"(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljavax/accessibility/AccessibleEditableText;", ret);
/*
* Converts an int array to an NSRange wrapped inside an NSValue
* takes [start, end] values and returns [start, end - start]
*/
NSValue *javaIntArrayToNSRangeValue(JNIEnv* env, jintArray array) {
jint *values = (*env)->GetIntArrayElements(env, array, 0);
if (values == NULL) {
// Note: Java will not be on the stack here so a java exception can't happen and no need to call ExceptionCheck.
NSLog(@"%s failed calling GetIntArrayElements", __FUNCTION__);
return nil;
};
NSValue *value = [NSValue valueWithRange:NSMakeRange(values[0], values[1] - values[0])];
(*env)->ReleaseIntArrayElements(env, array, values, 0);
return value;
}
@implementation JavaTextAccessibility
// based strongly upon NSTextViewAccessibility:accessibilityAttributeNames

View File

@@ -36,7 +36,7 @@
- (NSString * _Nullable)accessibilityLabel
{
return [self accessibilityTitleAttribute];
return [super accessibilityLabel];
}
- (BOOL)accessibilityPerformPress
@@ -44,4 +44,14 @@
return [self performAccessibleAction:0];
}
- (NSRect)accessibilityFrame
{
return [super accessibilityFrame];
}
- (id)accessibilityParent
{
return [super accessibilityParent];
}
@end

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