mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-31 13:49:55 +01:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
799b46db7d | ||
|
|
172b3a6400 | ||
|
|
9fa58395c9 | ||
|
|
18e1552b9d | ||
|
|
7714eca9e2 | ||
|
|
5b559a8764 | ||
|
|
34b306300d | ||
|
|
0cf6d2da8d | ||
|
|
67ab7032c8 | ||
|
|
cd4ef878a0 | ||
|
|
5b43a3aef3 | ||
|
|
ce2a99a6ec | ||
|
|
882d1064e7 | ||
|
|
45ca486b96 | ||
|
|
a3bfa02ec0 | ||
|
|
a9ed023347 | ||
|
|
92c7e3a9c3 | ||
|
|
e1724ac304 | ||
|
|
51bb3ddb2f | ||
|
|
10c7694d60 | ||
|
|
59ea9d809d | ||
|
|
ab146e18bd | ||
|
|
0ca9b169b0 | ||
|
|
289f756f70 | ||
|
|
ba84093c7d | ||
|
|
46702cad3c | ||
|
|
ac8ffa0ba7 | ||
|
|
fae062d45f | ||
|
|
0e87047b7e | ||
|
|
0106ef8fef | ||
|
|
a69031c54b | ||
|
|
8b96b1e115 | ||
|
|
48186ebbfb | ||
|
|
6877bb6cd1 | ||
|
|
aab6c2301d | ||
|
|
8b63dd1c22 | ||
|
|
9baaee86b0 | ||
|
|
9e0ff20be5 | ||
|
|
f51e55a20f | ||
|
|
e68d154a63 | ||
|
|
b526f948ff | ||
|
|
fdbb6f02b8 | ||
|
|
6743f36c32 | ||
|
|
f706b93717 | ||
|
|
6f046e9f68 | ||
|
|
bac641fe77 | ||
|
|
8dfcb3fd5a | ||
|
|
ef651ca1bb | ||
|
|
3092ca0461 | ||
|
|
3caa06a639 | ||
|
|
6c50ed6690 | ||
|
|
805a5b4f75 | ||
|
|
efabfd0370 | ||
|
|
f757a39090 | ||
|
|
1039653b97 | ||
|
|
65fa801231 | ||
|
|
3d9ae4dbe8 | ||
|
|
ef8e01b0d4 | ||
|
|
eaa9c1618e | ||
|
|
ee3c7edd84 | ||
|
|
8a19c38728 | ||
|
|
4fcd80acf0 | ||
|
|
6f5dd836de | ||
|
|
2ff21b425e |
34
README.md
34
README.md
@@ -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/).
|
||||
|
||||
13
jb/project/idea-project-files/jetbrains.api.iml
Normal file
13
jb/project/idea-project-files/jetbrains.api.iml
Normal 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>
|
||||
|
||||
12
jb/project/idea-project-files/modules.xml
Normal file
12
jb/project/idea-project-files/modules.xml
Normal 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>
|
||||
|
||||
16
jb/project/tools/common/scripts/build-jbr-api.sh
Normal file
16
jb/project/tools/common/scripts/build-jbr-api.sh
Normal 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']"
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 +++++++++++++++++++++++++++++++++++
|
||||
|
||||
@@ -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 +--
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 +-
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 ++--
|
||||
|
||||
@@ -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 ------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
83
jb/project/tools/test/perfcmp.sh
Normal file
83
jb/project/tools/test/perfcmp.sh
Normal 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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,6 +49,4 @@ jdk.unsupported,
|
||||
jdk.xml.dom,
|
||||
jdk.zipfs,
|
||||
jdk.hotspot.agent,
|
||||
jetbrains.api,
|
||||
jetbrains.api.impl,
|
||||
jdk.jcmd
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
132
src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
Normal file
132
src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
280
src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
Normal file
280
src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
211
src/java.base/share/classes/com/jetbrains/internal/Proxy.java
Normal file
211
src/java.base/share/classes/com/jetbrains/internal/Proxy.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
67
src/java.base/windows/classes/sun/nio/fs/WindowsFileKey.java
Normal file
67
src/java.base/windows/classes/sun/nio/fs/WindowsFileKey.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
160
src/java.base/windows/native/libnio/fs/ntifs_min.h
Normal file
160
src/java.base/windows/native/libnio/fs/ntifs_min.h
Normal 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_
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <jni.h>
|
||||
|
||||
extern NSMutableDictionary *sActions;
|
||||
extern NSMutableDictionary *sActionSelectors;
|
||||
extern NSMutableArray *sAllActionSelectors;
|
||||
void initializeActions();
|
||||
|
||||
@protocol JavaAccessibilityAction
|
||||
|
||||
|
||||
@@ -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))];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user