mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-25 10:49:41 +01:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3e2151a05 | ||
|
|
9141bec0d9 | ||
|
|
3c5524a0ce | ||
|
|
da6e21563c | ||
|
|
10c7694d60 | ||
|
|
59ea9d809d | ||
|
|
ab146e18bd | ||
|
|
0ca9b169b0 | ||
|
|
289f756f70 | ||
|
|
ba84093c7d | ||
|
|
46702cad3c | ||
|
|
ac8ffa0ba7 | ||
|
|
fae062d45f | ||
|
|
0e87047b7e | ||
|
|
0106ef8fef | ||
|
|
a69031c54b | ||
|
|
8b96b1e115 | ||
|
|
48186ebbfb |
@@ -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)
|
||||
|
||||
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,
|
||||
|
||||
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
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ 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);
|
||||
|
||||
@@ -35,6 +35,12 @@ static jmethodID sjm_getAccessibleName = NULL;
|
||||
GET_STATIC_METHOD_RETURN(sjm_getAccessibleName, sjc_CAccessibility, "getAccessibleName", \
|
||||
"(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Ljava/lang/String;", ret);
|
||||
|
||||
static jmethodID sjm_getAccessibleSelection = NULL;
|
||||
#define GET_ACCESSIBLESELECTION_METHOD_RETURN(ret) \
|
||||
GET_CACCESSIBILITY_CLASS_RETURN(ret); \
|
||||
GET_STATIC_METHOD_RETURN(sjm_getAccessibleSelection, sjc_CAccessibility, "getAccessibleSelection", \
|
||||
"(Ljavax/accessibility/AccessibleContext;Ljava/awt/Component;)Ljavax/accessibility/AccessibleSelection;", ret);
|
||||
|
||||
@implementation ComboBoxAccessibility
|
||||
|
||||
// NSAccessibilityElement protocol methods
|
||||
@@ -43,15 +49,21 @@ static jmethodID sjm_getAccessibleName = NULL;
|
||||
JNIEnv *env = [ThreadUtilities getJNIEnv];
|
||||
jobject axContext = [self axContextWithEnv:env];
|
||||
if (axContext == NULL) return nil;
|
||||
jclass axContextClass = (*env)->GetObjectClass(env, axContext);
|
||||
DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axContextClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil);
|
||||
jobject axSelectedChild = (*env)->CallObjectMethod(env, axContext, jm_getAccessibleSelection, 0);
|
||||
GET_ACCESSIBLESELECTION_METHOD_RETURN(nil);
|
||||
jobject axSelection = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleSelection, axContext, self->fComponent);
|
||||
CHECK_EXCEPTION();
|
||||
if (axSelection == NULL) {
|
||||
return nil;
|
||||
}
|
||||
jclass axSelectionClass = (*env)->GetObjectClass(env, axSelection);
|
||||
DECLARE_METHOD_RETURN(jm_getAccessibleSelection, axSelectionClass, "getAccessibleSelection", "(I)Ljavax/accessibility/Accessible;", nil);
|
||||
jobject axSelectedChild = (*env)->CallObjectMethod(env, axSelection, jm_getAccessibleSelection, 0);
|
||||
CHECK_EXCEPTION();
|
||||
(*env)->DeleteLocalRef(env, axSelection);
|
||||
(*env)->DeleteLocalRef(env, axContext);
|
||||
if (axSelectedChild == NULL) {
|
||||
return nil;
|
||||
}
|
||||
GET_CACCESSIBILITY_CLASS_RETURN(nil);
|
||||
GET_ACCESSIBLENAME_METHOD_RETURN(nil);
|
||||
jobject childName = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getAccessibleName, axSelectedChild, fComponent);
|
||||
CHECK_EXCEPTION();
|
||||
|
||||
@@ -1081,6 +1081,7 @@ static jobject sAccessibilityClass = NULL;
|
||||
{
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
||||
|
||||
GET_CACCESSIBILITY_CLASS_RETURN(nil);
|
||||
DECLARE_CLASS_RETURN(jc_Container, "java/awt/Container", nil);
|
||||
DECLARE_STATIC_METHOD_RETURN(jm_accessibilityHitTest, sjc_CAccessibility, "accessibilityHitTest",
|
||||
"(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;", nil);
|
||||
|
||||
@@ -164,19 +164,7 @@ JNI_COCOA_ENTER(env);
|
||||
// to indicate we should use CoreText to substitute the character
|
||||
CGGlyph glyph;
|
||||
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
|
||||
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
|
||||
if (IS_OSX_GT10_13 || IsEmojiFont(cgFallback)) {
|
||||
CGAffineTransform matrix = awtStrike->fAltTx;
|
||||
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
|
||||
CTFontRef font = CTFontCreateWithGraphicsFont(cgFallback, fontSize, NULL, NULL);
|
||||
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
CFRelease(font);
|
||||
advance.width /= fontSize;
|
||||
advance.height /= fontSize;
|
||||
} else {
|
||||
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
|
||||
}
|
||||
CFRelease(cgFallback);
|
||||
CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fStyle, &glyph, 1, NULL, &advance, IS_OSX_GT10_14);
|
||||
CFRelease(fallback);
|
||||
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
|
||||
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
|
||||
|
||||
@@ -86,7 +86,8 @@
|
||||
isAA:(jboolean)isAA;
|
||||
|
||||
- (id<MTLRenderCommandEncoder> _Nonnull)getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
|
||||
isSrcOpaque:(bool)isSrcOpaque;
|
||||
isSrcOpaque:(bool)isSrcOpaque
|
||||
gammaCorrection:(bool)gmc;
|
||||
|
||||
// Base method to obtain any MTLRenderCommandEncoder
|
||||
- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id<MTLTexture> _Nonnull)dest
|
||||
|
||||
@@ -40,19 +40,18 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
- (id)init;
|
||||
- (void)dealloc;
|
||||
|
||||
- (void)reset:(id<MTLTexture>)destination
|
||||
isDstOpaque:(jboolean)isDstOpaque
|
||||
isDstPremultiplied:(jboolean)isDstPremultiplied
|
||||
isAA:(jboolean)isAA
|
||||
isText:(jboolean)isText
|
||||
isLCD:(jboolean)isLCD;
|
||||
- (void) reset:(id<MTLTexture>)destination
|
||||
isDstOpaque:(jboolean)isDstOpaque
|
||||
isDstPremultiplied:(jboolean)isDstPremultiplied
|
||||
isAA:(jboolean)isAA
|
||||
isGMCText:(jboolean)isGMCText
|
||||
isLCD:(jboolean)isLCD;
|
||||
|
||||
- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
|
||||
context:(MTLContext *)mtlc
|
||||
renderOptions:(const RenderOptions *)renderOptions
|
||||
forceUpdate:(jboolean)forceUpdate;
|
||||
@property (assign) jboolean aa;
|
||||
@property (assign) jboolean text;
|
||||
@property (assign) jboolean lcd;
|
||||
@property (assign) jboolean aaShader;
|
||||
@property (retain) MTLPaint* paint;
|
||||
@@ -67,7 +66,7 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
SurfaceRasterFlags _dstFlags;
|
||||
|
||||
jboolean _isAA;
|
||||
jboolean _isText;
|
||||
jboolean _isGMCText;
|
||||
jboolean _isLCD;
|
||||
jboolean _isAAShader;
|
||||
|
||||
@@ -93,7 +92,6 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
MTLTransform * _transform;
|
||||
}
|
||||
@synthesize aa = _isAA;
|
||||
@synthesize text = _isText;
|
||||
@synthesize lcd = _isLCD;
|
||||
@synthesize aaShader = _isAAShader;
|
||||
@synthesize paint = _paint;
|
||||
@@ -126,13 +124,13 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
isDstOpaque:(jboolean)isDstOpaque
|
||||
isDstPremultiplied:(jboolean)isDstPremultiplied
|
||||
isAA:(jboolean)isAA
|
||||
isText:(jboolean)isText
|
||||
isGMCText:(jboolean)isGMCText
|
||||
isLCD:(jboolean)isLCD {
|
||||
_destination = destination;
|
||||
_dstFlags.isOpaque = isDstOpaque;
|
||||
_dstFlags.isPremultiplied = isDstPremultiplied;
|
||||
_isAA = isAA;
|
||||
_isText = isText;
|
||||
_isGMCText = isGMCText;
|
||||
_isLCD = isLCD;
|
||||
// NOTE: probably it's better to invalidate/reset all cached states now
|
||||
}
|
||||
@@ -182,7 +180,7 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode
|
||||
&& _isAA == renderOptions->isAA
|
||||
&& _isAAShader == renderOptions->isAAShader
|
||||
&& _isText == renderOptions->isText
|
||||
&& _isGMCText == renderOptions->isGMCText
|
||||
&& _isLCD == renderOptions->isLCD
|
||||
&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)
|
||||
return;
|
||||
@@ -193,7 +191,7 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
_interpolationMode = renderOptions->interpolation;
|
||||
_isAA = renderOptions->isAA;
|
||||
_isAAShader = renderOptions->isAAShader;
|
||||
_isText = renderOptions->isText;
|
||||
_isGMCText = renderOptions->isGMCText;
|
||||
_isLCD = renderOptions->isLCD;
|
||||
_srcFlags = renderOptions->srcFlags;
|
||||
|
||||
@@ -352,9 +350,11 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
}
|
||||
|
||||
- (id<MTLRenderCommandEncoder> _Nonnull) getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
|
||||
isSrcOpaque:(bool)isSrcOpaque
|
||||
isSrcOpaque:(bool)isSrcOpaque
|
||||
gammaCorrection:(bool)gmc
|
||||
{
|
||||
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE }, {dstOps->isOpaque, JNI_TRUE}, JNI_TRUE, JNI_FALSE, JNI_FALSE};
|
||||
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE },
|
||||
{dstOps->isOpaque, JNI_TRUE}, gmc, JNI_FALSE, JNI_FALSE};
|
||||
return [self getEncoder:dstOps->pTexture renderOptions:&roptions];
|
||||
}
|
||||
|
||||
@@ -437,11 +437,11 @@ const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
|
||||
_encoder = [[cbw getCommandBuffer] renderCommandEncoderWithDescriptor:rpd];
|
||||
|
||||
[_encoderStates reset:dest
|
||||
isDstOpaque:renderOptions->dstFlags.isOpaque
|
||||
isDstPremultiplied:YES
|
||||
isAA:renderOptions->isAA
|
||||
isText:renderOptions->isText
|
||||
isLCD:renderOptions->isLCD];
|
||||
isDstOpaque:renderOptions->dstFlags.isOpaque
|
||||
isDstPremultiplied:YES
|
||||
isAA:renderOptions->isAA
|
||||
isGMCText:renderOptions->isGMCText
|
||||
isLCD:renderOptions->isLCD];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -56,7 +56,8 @@ static MTLRenderPipelineDescriptor * templateLCDPipelineDesc = nil;
|
||||
static MTLRenderPipelineDescriptor * templateAAPipelineDesc = nil;
|
||||
static void
|
||||
setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat,
|
||||
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode);
|
||||
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode,
|
||||
bool gmcText);
|
||||
|
||||
static void initTemplatePipelineDescriptors() {
|
||||
if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil &&
|
||||
@@ -189,6 +190,7 @@ jint _color;
|
||||
|
||||
NSString *vertShader = @"vert_col";
|
||||
NSString *fragShader = @"frag_col";
|
||||
bool gmcText = NO;
|
||||
|
||||
if (renderOptions->isTexture) {
|
||||
vertShader = @"vert_txt";
|
||||
@@ -198,8 +200,9 @@ jint _color;
|
||||
fragShader = @"aa_frag_txt";
|
||||
rpDesc = [[templateAATexturePipelineDesc copy] autorelease];
|
||||
}
|
||||
if (renderOptions->isText) {
|
||||
fragShader = @"frag_text";
|
||||
if (renderOptions->isGMCText) {
|
||||
fragShader = @"frag_gmc_text";
|
||||
gmcText = YES;
|
||||
}
|
||||
if (renderOptions->isLCD) {
|
||||
vertShader = @"vert_txt_lcd";
|
||||
@@ -208,7 +211,7 @@ jint _color;
|
||||
}
|
||||
setTxtUniforms(mtlc, _color, encoder,
|
||||
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags,
|
||||
&renderOptions->dstFlags, 1);
|
||||
&renderOptions->dstFlags, 1, gmcText);
|
||||
} else if (renderOptions->isAAShader) {
|
||||
vertShader = @"vert_col_aa";
|
||||
fragShader = @"frag_col_aa";
|
||||
@@ -251,7 +254,7 @@ jint _color;
|
||||
|
||||
setTxtUniforms(mtlc, col, encoder,
|
||||
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
|
||||
&renderOptions->srcFlags, &renderOptions->dstFlags, 1);
|
||||
&renderOptions->srcFlags, &renderOptions->dstFlags, 1, NO);
|
||||
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex:0];
|
||||
|
||||
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
|
||||
@@ -792,7 +795,7 @@ jint _color;
|
||||
const SurfaceRasterFlags srcFlags = {_isOpaque, renderOptions->srcFlags.isPremultiplied};
|
||||
setTxtUniforms(mtlc, 0, encoder,
|
||||
renderOptions->interpolation, YES, [mtlc.composite getExtraAlpha],
|
||||
&srcFlags, &renderOptions->dstFlags, 0);
|
||||
&srcFlags, &renderOptions->dstFlags, 0, NO);
|
||||
|
||||
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
|
||||
vertexShaderId:vertShader
|
||||
@@ -874,9 +877,41 @@ jint _color;
|
||||
|
||||
static void
|
||||
setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder, int interpolation, bool repeat,
|
||||
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode) {
|
||||
struct TxtFrameUniforms uf = {RGBA_TO_V4(color), mode, srcFlags->isOpaque, dstFlags->isOpaque, extraAlpha};
|
||||
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
|
||||
jfloat extraAlpha, const SurfaceRasterFlags *srcFlags, const SurfaceRasterFlags *dstFlags, int mode,
|
||||
bool gmcText)
|
||||
{
|
||||
if (gmcText) {
|
||||
float ca = (((color) >> 24) & 0xFF)/255.0f;
|
||||
float cr = (((color) >> 16) & (0xFF))/255.0f;
|
||||
float cg = (((color) >> 8) & 0xFF)/255.0f;
|
||||
float cb = ((color) & 0xFF)/255.0f;
|
||||
float inv_light_gamma = 1.666f;
|
||||
float inv_dark_gamma = 0.333f;
|
||||
float inv_light_exp = 0.454f;
|
||||
float inv_dark_exp = 1.4f;
|
||||
|
||||
// calculate brightness of the fragment
|
||||
float b = (cr/3.0f + cg/3.0f + cb/3.0f)*ca;
|
||||
|
||||
// adjust fragment coverage
|
||||
float exp = inv_dark_exp*(1.0f - b) + inv_light_exp*b;
|
||||
|
||||
// adjust fragment color and alpha for alpha < 1.0
|
||||
if (ca < 1.0f) {
|
||||
float g = inv_dark_gamma*(1.0f - b) + inv_light_gamma*b;
|
||||
cr = pow(cr, g);
|
||||
cg = pow(cr, g);
|
||||
cb = pow(cr, g);
|
||||
ca = pow(ca, exp);
|
||||
}
|
||||
|
||||
struct GMCFrameUniforms uf = {{cr, cg, cb, ca}, exp};
|
||||
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
|
||||
} else {
|
||||
struct TxtFrameUniforms uf =
|
||||
{RGBA_TO_V4(color), mode, srcFlags->isOpaque, dstFlags->isOpaque, extraAlpha};
|
||||
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
|
||||
}
|
||||
[mtlc.samplerManager setSamplerWithEncoder:encoder interpolation:interpolation repeat:repeat];
|
||||
}
|
||||
|
||||
@@ -938,7 +973,7 @@ setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder
|
||||
setTxtUniforms(mtlc, 0, encoder,
|
||||
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
|
||||
&renderOptions->srcFlags,
|
||||
&renderOptions->dstFlags, 0);
|
||||
&renderOptions->dstFlags, 0, NO);
|
||||
|
||||
}
|
||||
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
|
||||
@@ -969,7 +1004,7 @@ setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder
|
||||
const int col = 0 ^ xorColor;
|
||||
setTxtUniforms(mtlc, col, encoder,
|
||||
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
|
||||
&renderOptions->srcFlags, &renderOptions->dstFlags, 0);
|
||||
&renderOptions->srcFlags, &renderOptions->dstFlags, 0, NO);
|
||||
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
|
||||
|
||||
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
|
||||
@@ -978,7 +1013,7 @@ setTxtUniforms(MTLContext *mtlc, int color, id <MTLRenderCommandEncoder> encoder
|
||||
setTxtUniforms(mtlc, 0, encoder,
|
||||
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
|
||||
&renderOptions->srcFlags,
|
||||
&renderOptions->dstFlags, 0);
|
||||
&renderOptions->dstFlags, 0, NO);
|
||||
|
||||
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
|
||||
vertexShaderId:vertShader
|
||||
|
||||
@@ -297,7 +297,8 @@ J2dTraceLn(J2D_TRACE_INFO, "MTLTR_EnableGlyphVertexCache");
|
||||
return;
|
||||
}
|
||||
}
|
||||
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
|
||||
|
||||
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps, YES);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -82,5 +82,5 @@ void
|
||||
MTLVertexCache_AddGlyphQuad(MTLContext *mtlc,
|
||||
jfloat tx1, jfloat ty1, jfloat tx2, jfloat ty2,
|
||||
jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2);
|
||||
void MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps);
|
||||
void MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps, bool gmc);
|
||||
#endif /* MTLVertexCache_h_Included */
|
||||
|
||||
@@ -44,6 +44,7 @@ static jint vertexCacheIndex = 0;
|
||||
|
||||
static MTLPooledTextureHandle * maskCacheTex = NULL;
|
||||
static jint maskCacheIndex = 0;
|
||||
static bool gammaCorrection = NO;
|
||||
static id<MTLRenderCommandEncoder> encoder = NULL;
|
||||
|
||||
#define MTLVC_ADD_VERTEX(TX, TY, DX, DY, DZ) \
|
||||
@@ -182,7 +183,7 @@ MTLVertexCache_EnableMaskCache(MTLContext *mtlc, BMTLSDOps *dstOps)
|
||||
return;
|
||||
}
|
||||
}
|
||||
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
|
||||
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps, gammaCorrection);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -194,15 +195,18 @@ MTLVertexCache_DisableMaskCache(MTLContext *mtlc)
|
||||
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_DisableMaskCache");
|
||||
MTLVertexCache_FlushVertexCache(mtlc);
|
||||
maskCacheIndex = 0;
|
||||
gammaCorrection = NO;
|
||||
free(vertexCache);
|
||||
vertexCache = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps) {
|
||||
MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps, bool gmc) {
|
||||
J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_CreateSamplingEncoder");
|
||||
gammaCorrection = gmc;
|
||||
encoder = [mtlc.encoderManager getTextEncoder:dstOps
|
||||
isSrcOpaque:NO];
|
||||
isSrcOpaque:NO
|
||||
gammaCorrection:gmc];
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -36,7 +36,7 @@ typedef struct {
|
||||
int interpolation;
|
||||
SurfaceRasterFlags srcFlags;
|
||||
SurfaceRasterFlags dstFlags;
|
||||
jboolean isText;
|
||||
jboolean isGMCText;
|
||||
jboolean isLCD;
|
||||
jboolean isAAShader;
|
||||
} RenderOptions;
|
||||
|
||||
@@ -157,6 +157,11 @@ struct LCDFrameUniforms {
|
||||
vector_float3 invgamma;
|
||||
};
|
||||
|
||||
struct GMCFrameUniforms {
|
||||
vector_float4 color;
|
||||
float exp;
|
||||
};
|
||||
|
||||
struct SwizzleUniforms {
|
||||
unsigned char swizzle[4];
|
||||
unsigned char hasAlpha;
|
||||
|
||||
@@ -332,6 +332,20 @@ fragment half4 frag_text(
|
||||
return half4(uniforms.color * pixelColor.a);
|
||||
}
|
||||
|
||||
fragment half4 frag_gmc_text(
|
||||
TxtShaderInOut vert [[stage_in]],
|
||||
texture2d<float, access::sample> renderTexture [[texture(0)]],
|
||||
constant GMCFrameUniforms& uniforms [[buffer(1)]],
|
||||
sampler textureSampler [[sampler(0)]]
|
||||
) {
|
||||
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
|
||||
float a = uniforms.color.a;
|
||||
float3 col = uniforms.color.rgb;
|
||||
// adjust fragment coverage
|
||||
float frag_cov = pow(pixelColor.a, uniforms.exp);
|
||||
return half4(col.r*frag_cov, col.g*frag_cov, col.b*frag_cov, a*frag_cov);
|
||||
}
|
||||
|
||||
fragment half4 frag_txt_tp(TxtShaderInOut vert [[stage_in]],
|
||||
texture2d<float, access::sample> renderTexture [[texture(0)]],
|
||||
texture2d<float, access::sample> paintTexture [[texture(1)]],
|
||||
|
||||
@@ -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.desktop;
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
/**
|
||||
* This class contains mapping between JBR API interfaces and implementation in {@code java.desktop} module.
|
||||
*/
|
||||
public class JBRApiModule {
|
||||
static {
|
||||
JBRApi.registerModule(MethodHandles.lookup(), JBRApiModule.class.getModule()::addExports)
|
||||
.service("com.jetbrains.ExtendedGlyphCache", null)
|
||||
.withStatic("getSubpixelResolution", "sun.font.FontUtilities");
|
||||
}
|
||||
}
|
||||
@@ -122,8 +122,6 @@ module java.desktop {
|
||||
exports sun.awt.dnd to jdk.unsupported.desktop;
|
||||
exports sun.swing to jdk.unsupported.desktop;
|
||||
|
||||
exports sun.font to jetbrains.api.impl;
|
||||
|
||||
opens javax.swing.plaf.basic to
|
||||
jdk.jconsole;
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ public final class FontUtilities {
|
||||
});
|
||||
}
|
||||
|
||||
public static Dimension getSubpixelResolution() {
|
||||
return subpixelResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Referenced by code in the JDK which wants to test for the
|
||||
* minimum char code for which layout may be required.
|
||||
|
||||
@@ -1198,7 +1198,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
@Override
|
||||
public Insets getScreenInsets(final GraphicsConfiguration gc) {
|
||||
if (useCachedInsets) {
|
||||
return cachedInsets.computeIfAbsent(gc, this::getScreenInsetsImpl);
|
||||
return (Insets)cachedInsets.computeIfAbsent(gc, this::getScreenInsetsImpl).clone();
|
||||
} else {
|
||||
return getScreenInsetsImpl(gc);
|
||||
}
|
||||
|
||||
@@ -1481,9 +1481,21 @@ final class XWM
|
||||
}
|
||||
XNETProtocol net_protocol = getWM().getNETProtocol();
|
||||
if (net_protocol != null && net_protocol.active()) {
|
||||
Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
|
||||
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
|
||||
Insets insets = null;
|
||||
final int MAX_RETRY_COUNT = 3;
|
||||
for (int i = 0; i < MAX_RETRY_COUNT; i++) {
|
||||
insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
|
||||
if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
|
||||
}
|
||||
if (insets == null) {
|
||||
final long timeForInsetExtentToBecomeReadyMs = (i + 1)*5;
|
||||
insLog.fine("_NET_FRAME_EXTENTS not available (yet?), retrying in {0} ms",
|
||||
timeForInsetExtentToBecomeReadyMs);
|
||||
try {
|
||||
Thread.sleep(timeForInsetExtentToBecomeReadyMs);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (insets != null) {
|
||||
|
||||
@@ -149,21 +149,26 @@ public final class X11GraphicsDevice extends GraphicsDevice
|
||||
}
|
||||
|
||||
private Rectangle boundsCached;
|
||||
private final Object boundsCacheLock = new Object();
|
||||
|
||||
private synchronized Rectangle getBoundsCached() {
|
||||
if (boundsCached == null) {
|
||||
boundsCached = getBoundsImpl();
|
||||
private Rectangle getBoundsCached() {
|
||||
synchronized (boundsCacheLock) {
|
||||
if (boundsCached == null) {
|
||||
boundsCached = getBoundsImpl();
|
||||
}
|
||||
return boundsCached;
|
||||
}
|
||||
return boundsCached;
|
||||
}
|
||||
|
||||
public synchronized void resetBoundsCache() {
|
||||
boundsCached = null;
|
||||
public void resetBoundsCache() {
|
||||
synchronized (boundsCacheLock) {
|
||||
boundsCached = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
if (X11GraphicsEnvironment.useBoundsCache()) {
|
||||
return getBoundsCached();
|
||||
return new Rectangle(getBoundsCached());
|
||||
}
|
||||
else {
|
||||
return getBoundsImpl();
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.jetbrains;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface ExtendedGlyphCache extends JBRService {
|
||||
|
||||
|
||||
interface V1 extends ExtendedGlyphCache {
|
||||
|
||||
Dimension getSubpixelResolution();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2020 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;
|
||||
|
||||
public class JBRApi {
|
||||
|
||||
private static final int MAJOR_VERSION = 0;
|
||||
private static final int MINOR_VERSION = 0;
|
||||
|
||||
private JBRApi() {}
|
||||
|
||||
/**
|
||||
* Returns major API version.
|
||||
* It has nothing to do with {@link #isAvailable() actual implementation availability}.
|
||||
*/
|
||||
public static int getMajorVersion() {
|
||||
return MAJOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minor API version.
|
||||
* It has nothing to do with {@link #isAvailable() actual implementation availability}.
|
||||
*/
|
||||
public static int getMinorVersion() {
|
||||
return MINOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for JBR API implementation availability at runtime.
|
||||
* {@code true} means we're running on JBR with {@code jetbrains.api.impl} module.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return JBRService.isApiAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2020 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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Marker interface, can be used with Java SPI to enumerate JBR services.
|
||||
* All JBR services must implement this interface
|
||||
*/
|
||||
public interface JBRService {
|
||||
|
||||
|
||||
/**
|
||||
* Tries to load JBR service for given interface.
|
||||
* Returns null when there's no implementation for such interface, or when given Supplier throws
|
||||
* NoClassDefFoundError, which usually means that given interface is absent in module path.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends JBRService> T load(Supplier<Class<T>> serviceInterface) {
|
||||
try {
|
||||
JBRServiceManager.ServiceHolder<T> serviceHolder =
|
||||
(JBRServiceManager.ServiceHolder<T>) JBRServiceManager.services.get(serviceInterface.get());
|
||||
return serviceHolder == null ? null : serviceHolder.get();
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isApiAvailable() {
|
||||
return JBRServiceManager.services.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class JBRServiceManager {
|
||||
|
||||
static class ServiceHolder<T extends JBRService> {
|
||||
private ServiceLoader.Provider<T> provider;
|
||||
private T service;
|
||||
private ServiceHolder(ServiceLoader.Provider<T> provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
synchronized T get() {
|
||||
if(service != null) return service;
|
||||
service = provider.get();
|
||||
provider = null;
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
static final Map<Class<? extends JBRService>, ServiceHolder<?>> services;
|
||||
static {
|
||||
Map<Class<? extends JBRService>, ServiceHolder<?>> map = new HashMap<>();
|
||||
ServiceLoader.load(JBRService.class).stream()
|
||||
.forEach(p -> populateServiceMap(map, new ServiceHolder<>(p), p.type()));
|
||||
services = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all chains of class/interface hierarchy that leads from given {@code clazz} to {@link JBRService}
|
||||
* and assigns given {@code serviceHolder} to them
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean populateServiceMap(Map<Class<? extends JBRService>, ServiceHolder<?>> map,
|
||||
ServiceHolder<?> serviceHolder, Class<?> clazz) {
|
||||
if(clazz == null) return false;
|
||||
if(clazz.equals(JBRService.class)) return true;
|
||||
boolean isSubtypeOfJBRService = false;
|
||||
for(Class<?> ifc : clazz.getInterfaces()) {
|
||||
isSubtypeOfJBRService |= populateServiceMap(map, serviceHolder, ifc);
|
||||
}
|
||||
isSubtypeOfJBRService |= populateServiceMap(map, serviceHolder, clazz.getSuperclass());
|
||||
if(isSubtypeOfJBRService) {
|
||||
map.put((Class<? extends JBRService>) clazz, serviceHolder);
|
||||
}
|
||||
return isSubtypeOfJBRService;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/jetbrains.api/src/com/jetbrains/ExtendedGlyphCache.java
Normal file
23
src/jetbrains.api/src/com/jetbrains/ExtendedGlyphCache.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface ExtendedGlyphCache {
|
||||
Dimension getSubpixelResolution();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -14,14 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
|
||||
module jetbrains.api {
|
||||
|
||||
requires transitive java.desktop;
|
||||
|
||||
exports com.jetbrains;
|
||||
|
||||
uses JBRService;
|
||||
|
||||
requires static transitive java.desktop;
|
||||
}
|
||||
121
src/jetbrains.api/templates/CheckVersion.java
Normal file
121
src/jetbrains.api/templates/CheckVersion.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CheckVersion {
|
||||
private static final Map<String, Function<String, String>> FILE_TRANSFORMERS = Map.of(
|
||||
"com/jetbrains/JBR.java", c -> {
|
||||
// Exclude API version from hash calculation
|
||||
int versionMethodIndex = c.indexOf("getApiVersion()");
|
||||
int versionStartIndex = c.indexOf("\"", versionMethodIndex) + 1;
|
||||
int versionEndIndex = c.indexOf("\"", versionStartIndex);
|
||||
return c.substring(0, versionStartIndex) + c.substring(versionEndIndex);
|
||||
}
|
||||
);
|
||||
|
||||
private static Path module, gensrc;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>$0 - absolute path to {@code JetBrainsRuntime/src/jetbrains.api} dir</li>
|
||||
* <li>$1 - absolute path to gensrc dir ({@code JetBrainsRuntime/build/<conf>/jbr-api/gensrc})</li>
|
||||
* <li>$2 - true if hash mismatch is an error</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
|
||||
module = Path.of(args[0]);
|
||||
gensrc = Path.of(args[1]);
|
||||
boolean error = args[2].equals("true");
|
||||
Path versionFile = module.resolve("version.properties");
|
||||
|
||||
Properties props = new Properties();
|
||||
props.load(Files.newInputStream(versionFile));
|
||||
String hash = SourceHash.calculate();
|
||||
|
||||
if (hash.equals(props.getProperty("HASH"))) return;
|
||||
System.err.println("================================================================================");
|
||||
if (error) {
|
||||
System.err.println("Error: jetbrains.api code was changed, hash and API version must be updated in " + versionFile);
|
||||
} else {
|
||||
System.err.println("Warning: jetbrains.api code was changed, " +
|
||||
"update hash and increment API version in " + versionFile + " before committing these changes");
|
||||
}
|
||||
System.err.println("HASH = " + hash);
|
||||
if (!error) System.err.println("DO NOT COMMIT YOUR CHANGES WITH THIS WARNING");
|
||||
System.err.println("================================================================================");
|
||||
if (error) System.exit(-1);
|
||||
}
|
||||
|
||||
private static class SourceHash {
|
||||
|
||||
private static String calculate() throws NoSuchAlgorithmException, IOException {
|
||||
MessageDigest hash = MessageDigest.getInstance("MD5");
|
||||
calculate(module.resolve("src"), hash);
|
||||
calculate(gensrc, hash);
|
||||
byte[] digest = hash.digest();
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte b : digest) {
|
||||
result.append(String.format("%X", b));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static void calculate(Path dir, MessageDigest hash) throws IOException {
|
||||
for (Entry f : findFiles(dir)) {
|
||||
hash.update(f.name.getBytes(StandardCharsets.UTF_8));
|
||||
hash.update(f.content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Entry> findFiles(Path dir) throws IOException {
|
||||
List<Entry> files = new ArrayList<>();
|
||||
FileVisitor<Path> fileFinder = new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
try {
|
||||
Path abs = file.toAbsolutePath();
|
||||
Path rel = dir.relativize(abs);
|
||||
StringBuilder name = new StringBuilder();
|
||||
for (int i = 0; i < rel.getNameCount(); i++) {
|
||||
if (!name.isEmpty()) name.append('/');
|
||||
name.append(rel.getName(i));
|
||||
}
|
||||
String content = Files.readString(abs);
|
||||
String fileName = name.toString();
|
||||
files.add(new Entry(FILE_TRANSFORMERS.getOrDefault(fileName, c -> c).apply(content), fileName));
|
||||
return FileVisitResult.CONTINUE;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
Files.walkFileTree(dir, fileFinder);
|
||||
files.sort(Comparator.comparing(Entry::name));
|
||||
return files;
|
||||
}
|
||||
|
||||
private record Entry(String content, String name) {}
|
||||
}
|
||||
}
|
||||
233
src/jetbrains.api/templates/Gensrc.java
Normal file
233
src/jetbrains.api/templates/Gensrc.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.*;
|
||||
import static java.util.regex.Pattern.compile;
|
||||
|
||||
/**
|
||||
* Codegen script for {@code jetbrains.api} module.
|
||||
* It produces "main" {@link com.jetbrains.JBR} class from template by
|
||||
* inspecting interfaces and implementation code and inserting
|
||||
* static utility methods for public services as well as some metadata
|
||||
* needed by JBR at runtime.
|
||||
*/
|
||||
public class Gensrc {
|
||||
|
||||
private static Path srcroot, src, templates, gensrc;
|
||||
private static String apiVersion;
|
||||
private static JBRModules modules;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>$0 - absolute path to {@code JetBrainsRuntime/src} dir</li>
|
||||
* <li>$1 - absolute path to jbr-api output dir ({@code JetBrainsRuntime/build/<conf>/jbr-api})</li>
|
||||
* <li>$2 - {@code JBR} part of API version</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
srcroot = Path.of(args[0]);
|
||||
Path module = srcroot.resolve("jetbrains.api");
|
||||
src = module.resolve("src");
|
||||
templates = module.resolve("templates");
|
||||
Path output = Path.of(args[1]);
|
||||
gensrc = output.resolve("gensrc");
|
||||
Files.createDirectories(gensrc);
|
||||
|
||||
Properties props = new Properties();
|
||||
props.load(Files.newInputStream(module.resolve("version.properties")));
|
||||
apiVersion = args[2] + "." + props.getProperty("VERSION");
|
||||
Files.writeString(output.resolve("jbr-api.version"), apiVersion,
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
modules = new JBRModules();
|
||||
JBR.generate();
|
||||
}
|
||||
|
||||
private static String findRegex(String src, Pattern regex) {
|
||||
Matcher matcher = regex.matcher(src);
|
||||
if (!matcher.find()) throw new IllegalArgumentException("Regex not found: " + regex.pattern());
|
||||
return matcher.group(1);
|
||||
}
|
||||
|
||||
private static String replaceTemplate(String src, String placeholder, Iterable<String> statements) {
|
||||
int placeholderIndex = src.indexOf(placeholder);
|
||||
int indent = 0;
|
||||
while (placeholderIndex - indent >= 1 && src.charAt(placeholderIndex - indent - 1) == ' ') indent++;
|
||||
int nextLineIndex = src.indexOf('\n', placeholderIndex + placeholder.length()) + 1;
|
||||
if (nextLineIndex == 0) nextLineIndex = placeholderIndex + placeholder.length();
|
||||
String before = src.substring(0, placeholderIndex - indent), after = src.substring(nextLineIndex);
|
||||
StringBuilder sb = new StringBuilder(before);
|
||||
boolean firstStatement = true;
|
||||
for (String s : statements) {
|
||||
if (!firstStatement) sb.append('\n');
|
||||
sb.append(s.indent(indent));
|
||||
firstStatement = false;
|
||||
}
|
||||
sb.append(after);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Code for generating {@link com.jetbrains.JBR} class.
|
||||
*/
|
||||
private static class JBR {
|
||||
|
||||
private static void generate() throws IOException {
|
||||
String jbrFileName = "com/jetbrains/JBR.java";
|
||||
Path output = gensrc.resolve(jbrFileName);
|
||||
Files.createDirectories(output.getParent());
|
||||
String content = generate(Files.readString(templates.resolve(jbrFileName)));
|
||||
Files.writeString(output, content, CREATE, WRITE, TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
private static String generate(String content) {
|
||||
Service[] interfaces = findPublicServiceInterfaces();
|
||||
List<String> statements = new ArrayList<>();
|
||||
for (Service i : interfaces) statements.add(generateMethodsForService(i));
|
||||
content = replaceTemplate(content, "/*GENERATED_METHODS*/", statements);
|
||||
content = content.replace("/*KNOWN_SERVICES*/",
|
||||
modules.services.stream().map(s -> "\"" + s + "\"").collect(Collectors.joining(", ")));
|
||||
content = content.replace("/*KNOWN_PROXIES*/",
|
||||
modules.proxies.stream().map(s -> "\"" + s + "\"").collect(Collectors.joining(", ")));
|
||||
content = content.replace("/*API_VERSION*/", apiVersion);
|
||||
return content;
|
||||
}
|
||||
|
||||
private static Service[] findPublicServiceInterfaces() {
|
||||
Pattern javadocPattern = Pattern.compile("/\\*\\*((?:.|\n)*?)(\s|\n)*\\*/");
|
||||
return modules.services.stream()
|
||||
.map(fullName -> {
|
||||
if (fullName.indexOf('$') != -1) return null; // Only top level services can be public
|
||||
Path path = src.resolve(fullName.replace('.', '/') + ".java");
|
||||
String name = fullName.substring(fullName.lastIndexOf('.') + 1);
|
||||
try {
|
||||
String content = Files.readString(path);
|
||||
int indexOfDeclaration = content.indexOf("public interface " + name);
|
||||
if (indexOfDeclaration == -1) return null;
|
||||
Matcher javadocMatcher = javadocPattern.matcher(content.substring(0, indexOfDeclaration));
|
||||
String javadoc;
|
||||
int javadocEnd;
|
||||
if (javadocMatcher.find()) {
|
||||
javadoc = javadocMatcher.group(1);
|
||||
javadocEnd = javadocMatcher.end();
|
||||
} else {
|
||||
javadoc = "";
|
||||
javadocEnd = 0;
|
||||
}
|
||||
return new Service(name, javadoc,
|
||||
content.substring(javadocEnd, indexOfDeclaration).contains("@Deprecated"));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull).toArray(Service[]::new);
|
||||
}
|
||||
|
||||
private static String generateMethodsForService(Service service) {
|
||||
return """
|
||||
private static class $__Holder {<DEPRECATED>
|
||||
private static final $ INSTANCE = api != null ? api.getService($.class) : null;
|
||||
}
|
||||
/**
|
||||
* @return true if current runtime has implementation for all methods in {@link $}
|
||||
* and its dependencies (can fully implement given service).
|
||||
* @see #get$()
|
||||
*/<DEPRECATED>
|
||||
public static boolean is$Supported() {
|
||||
return $__Holder.INSTANCE != null;
|
||||
}
|
||||
/**<JAVADOC>
|
||||
* @return full implementation of {@link $} service if any, or {@code null} otherwise
|
||||
*/<DEPRECATED>
|
||||
public static $ get$() {
|
||||
return $__Holder.INSTANCE;
|
||||
}
|
||||
"""
|
||||
.replaceAll("\\$", service.name)
|
||||
.replace("<JAVADOC>", service.javadoc)
|
||||
.replaceAll("<DEPRECATED>", service.deprecated ? "\n@Deprecated" : "");
|
||||
}
|
||||
|
||||
private record Service(String name, String javadoc, boolean deprecated) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and analyzes JBR API implementation modules and collects proxy definitions.
|
||||
*/
|
||||
private static class JBRModules {
|
||||
|
||||
private final Set<String> proxies = new HashSet<>(), services = new HashSet<>();
|
||||
|
||||
private JBRModules() throws IOException {
|
||||
String[] moduleNames = findJBRApiModules();
|
||||
Path[] potentialModules = findPotentialJBRApiContributorModules();
|
||||
for (String moduleName : moduleNames) {
|
||||
Path module = findJBRApiModuleFile(moduleName, potentialModules);
|
||||
findInModule(Files.readString(module));
|
||||
}
|
||||
}
|
||||
|
||||
private void findInModule(String content) {
|
||||
Pattern servicePattern = compile("(service|proxy|twoWayProxy)\s*\\(([^)]+)");
|
||||
Matcher matcher = servicePattern.matcher(content);
|
||||
while (matcher.find()) {
|
||||
String type = matcher.group(1);
|
||||
String parameters = matcher.group(2);
|
||||
String interfaceName = extractFromStringLiteral(parameters.substring(0, parameters.indexOf(',')));
|
||||
if (type.equals("service")) services.add(interfaceName);
|
||||
else proxies.add(interfaceName);
|
||||
}
|
||||
}
|
||||
|
||||
private static String extractFromStringLiteral(String value) {
|
||||
value = value.strip();
|
||||
return value.substring(1, value.length() - 1);
|
||||
}
|
||||
|
||||
private static Path findJBRApiModuleFile(String module, Path[] potentialPaths) throws FileNotFoundException {
|
||||
for (Path p : potentialPaths) {
|
||||
Path m = p.resolve("share/classes").resolve(module + ".java");
|
||||
if (Files.exists(m)) return m;
|
||||
}
|
||||
throw new FileNotFoundException("JBR API module file not found: " + module);
|
||||
}
|
||||
|
||||
private static String[] findJBRApiModules() throws IOException {
|
||||
String bootstrap = Files.readString(
|
||||
srcroot.resolve("java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java"));
|
||||
Pattern modulePattern = compile("\"([^\"]+)");
|
||||
return Stream.of(findRegex(bootstrap, compile("MODULES *=([^;]+)")).split(","))
|
||||
.map(m -> findRegex(m, modulePattern).replace('.', '/')).toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static Path[] findPotentialJBRApiContributorModules() throws IOException {
|
||||
return Files.list(srcroot)
|
||||
.filter(p -> Files.exists(p.resolve("share/classes/com/jetbrains"))).toArray(Path[]::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/jetbrains.api/templates/com/jetbrains/JBR.java
Normal file
106
src/jetbrains.api/templates/com/jetbrains/JBR.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* This class is an entry point into JBR API.
|
||||
* JBR API is a collection of services, classes, interfaces, etc.,
|
||||
* which require tight interaction with JRE and therefore are implemented inside JBR.
|
||||
* <div>JBR API consists of two parts:</div>
|
||||
* <ul>
|
||||
* <li>Client side - {@code jetbrains.api} module, mostly containing interfaces</li>
|
||||
* <li>JBR side - actual implementation code inside JBR</li>
|
||||
* </ul>
|
||||
* Client and JBR side are linked dynamically at runtime and do not have to be of the same version.
|
||||
* In some cases (e.g. running on different JRE or old JBR) system will not be able to find
|
||||
* implementation for some services, so you'll need a fallback behavior for that case.
|
||||
* <h2>Simple usage example:</h2>
|
||||
* <blockquote><pre>{@code
|
||||
* if (JBR.isSomeServiceSupported()) {
|
||||
* JBR.getSomeService().doSomething();
|
||||
* } else {
|
||||
* planB();
|
||||
* }
|
||||
* }</pre></blockquote>
|
||||
* @implNote JBR API is initialized on first access to this class (in static initializer).
|
||||
* Actual implementation is linked on demand, when corresponding service is requested by client.
|
||||
*/
|
||||
public class JBR {
|
||||
|
||||
private static final ServiceApi api;
|
||||
private static final Exception bootstrapException;
|
||||
static {
|
||||
ServiceApi a = null;
|
||||
Exception exception = null;
|
||||
try {
|
||||
a = (ServiceApi) Class.forName("com.jetbrains.bootstrap.JBRApiBootstrap")
|
||||
.getMethod("bootstrap", MethodHandles.Lookup.class)
|
||||
.invoke(null, MethodHandles.lookup());
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof Error error) throw error;
|
||||
else throw new Error(t);
|
||||
} catch (IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
|
||||
exception = e;
|
||||
}
|
||||
api = a;
|
||||
bootstrapException = exception;
|
||||
}
|
||||
|
||||
private JBR() {}
|
||||
|
||||
/**
|
||||
* @return true when running on JBR which implements JBR API
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return api != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JBR API version in form {@code JBR.MAJOR.MINOR.PATCH}
|
||||
* @implNote This is an API version, which comes with client application,
|
||||
* it has nothing to do with JRE it runs on.
|
||||
*/
|
||||
public static String getApiVersion() {
|
||||
return "/*API_VERSION*/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal API interface, contains most basic methods for communication between client and JBR.
|
||||
*/
|
||||
private interface ServiceApi {
|
||||
|
||||
<T> T getService(Class<T> interFace);
|
||||
}
|
||||
|
||||
// ========================== Generated metadata ==========================
|
||||
|
||||
/**
|
||||
* Generated client-side metadata, needed by JBR when linking the implementation.
|
||||
*/
|
||||
private static final class Metadata {
|
||||
private static final String[] KNOWN_SERVICES = {/*KNOWN_SERVICES*/};
|
||||
private static final String[] KNOWN_PROXIES = {/*KNOWN_PROXIES*/};
|
||||
}
|
||||
|
||||
// ======================= Generated static methods =======================
|
||||
|
||||
/*GENERATED_METHODS*/
|
||||
}
|
||||
14
src/jetbrains.api/version.properties
Normal file
14
src/jetbrains.api/version.properties
Normal file
@@ -0,0 +1,14 @@
|
||||
# When any changes are made to jetbrains.api module, hash and version value MUST be updated in this file.
|
||||
# Version has the following format: MAJOR.MINOR.PATCH
|
||||
#
|
||||
# How to increment JBR API version?
|
||||
# 1. For small changes if no public API was changed (e.g. only javadoc changes) - increment PATCH
|
||||
# 2. When only new API is added, or some existing API was @Deprecated - increment MINOR, reset PATCH to 0
|
||||
# 3. For major backwards incompatible API changes - increment MAJOR, reset MINOR and PATCH to 0
|
||||
|
||||
VERSION = 0.0.0
|
||||
|
||||
# Hash is used to track changes to jetbrains.api, so you would not forget to update version when needed.
|
||||
# When you make any changes, "make jbr-api" will fail and ask you to update hash and version number here.
|
||||
|
||||
HASH = 2F1FBCDFD362481FFD12AA12C082DE
|
||||
105
test/jdk/java/awt/Insets/DialogInsets.java
Normal file
105
test/jdk/java/awt/Insets/DialogInsets.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key headful
|
||||
* @summary Verifies that JDialog's insets are the same every time the dialog is created.
|
||||
* @requires (os.family == "linux")
|
||||
* @run main DialogInsets
|
||||
*/
|
||||
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.WindowConstants;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Robot;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class DialogInsets {
|
||||
private static Robot robot;
|
||||
private static Insets insets;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
|
||||
final JFrame frame = new JFrame("Test Dialog Demo");
|
||||
frame.setBounds(100, 100, 400, 300);
|
||||
frame.setVisible(true);
|
||||
|
||||
robot.delay(300);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
runSwing(() -> showDialog(frame));
|
||||
robot.delay(300);
|
||||
}
|
||||
|
||||
runSwing( () -> frame.dispose() );
|
||||
}
|
||||
|
||||
private static void showDialog(Frame parent) {
|
||||
final JLabel content = new JLabel("test label", SwingConstants.CENTER);
|
||||
final JDialog dialog = new JDialog(parent, "Test Dialog", true);
|
||||
|
||||
if (insets == null) {
|
||||
// Slow down the first time in order to increase the chance to get the right insets.
|
||||
robot.delay(300);
|
||||
}
|
||||
|
||||
dialog.setContentPane(content);
|
||||
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
dialog.setResizable(false);
|
||||
dialog.pack();
|
||||
|
||||
final Insets localInsets = dialog.getInsets();
|
||||
System.out.println(localInsets);
|
||||
|
||||
if (insets == null) {
|
||||
insets = localInsets;
|
||||
} else if (!insets.equals(localInsets)) {
|
||||
throw new RuntimeException("insets differ (" + insets + " before, " + localInsets + " now)");
|
||||
}
|
||||
|
||||
final Timer closeTimer = new Timer(300, (ActionEvent event) -> { dialog.setVisible(false); dialog.dispose(); } );
|
||||
closeTimer.setRepeats(false);
|
||||
closeTimer.start();
|
||||
|
||||
dialog.setLocationRelativeTo(parent);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static void runSwing(Runnable r) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(r);
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 JetBrains s.r.o.
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -21,18 +21,20 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.jetbrains.impl;
|
||||
/**
|
||||
* @test
|
||||
* @summary Verifies that UNIX socket doesn't throw when opened with
|
||||
* a non-default file system installed. NB: the opened socket isn't
|
||||
* supposed to be useable.
|
||||
* @library /test/lib
|
||||
* @build TestProvider UnixSocketInNonDefaultFS
|
||||
* @run main/othervm -Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider UnixSocketInNonDefaultFS
|
||||
*/
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.net.StandardProtocolFamily;
|
||||
|
||||
import com.jetbrains.ExtendedGlyphCache;
|
||||
|
||||
import java.awt.*;
|
||||
import sun.font.FontUtilities;
|
||||
|
||||
public class ExtendedGlyphCacheImpl implements ExtendedGlyphCache.V1 {
|
||||
|
||||
@Override
|
||||
public Dimension getSubpixelResolution() {
|
||||
return FontUtilities.subpixelResolution;
|
||||
public class UnixSocketInNonDefaultFS {
|
||||
public static void main(String args[]) throws java.io.IOException {
|
||||
ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2020 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.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
import com.jetbrains.SampleJBRApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary JBR API service loading & usage example
|
||||
*/
|
||||
|
||||
public class JBRServiceTest {
|
||||
|
||||
|
||||
/**
|
||||
* All API members are organized into some kind of 'versioned namespaces', here's the example:
|
||||
* <ul>
|
||||
* <li>SampleJBRApi - root of our feature API.</li>
|
||||
* <li>SampleJBRApi.V1 - first version of our API. It contains all relevant methods, classes, etc.</li>
|
||||
* <li>SampleJBRApi.V1.SomeDataClass - data class, added by first version of API.</li>
|
||||
* <li>SampleJBRApi.V2 - second version of our API. It extends V1, adding some more things to API.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Actual interface hierarchy for our API looks like this:
|
||||
* <ul>
|
||||
* <li>JBRService: marker interface, root for every feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi: marker interface, root for that specific feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi.V1: first version of feature API</li>
|
||||
* <ul>
|
||||
* <li>SampleJBRApi.V2: second version of feature API</li>
|
||||
* </ul>
|
||||
* </ul>
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* General rule when using JBR API is to avoid loading classes unless you make sure they're available at runtime.
|
||||
* To check for service availability you can try loading it with {@link JBRService#load}.
|
||||
* Also avoid using 'instanceof' to test JBR service implementation against specific interface, because using
|
||||
* it in 'instanceof' statement will cause loading this interface, which may in turn cause NoClassDefFoundError.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// // Declaring variables is safe
|
||||
// SampleJBRApi.V1 service;
|
||||
// // We pass lambda instead of plain class. This allows JBRService#load to deal with NoClassDefFoundErrors
|
||||
// service = JBRService.load(() -> SampleJBRApi.V1.class);
|
||||
// // Null-checking variables is safe too
|
||||
// Objects.requireNonNull(service);
|
||||
// // When we ensured that SampleJBRApi.V1 is available, we can use anything that's inside
|
||||
// service.someMethod1(null);
|
||||
// // We can also create SampleJBRApi.V1.SomeDataClass instances, as we know they're supported with V1
|
||||
// service.someMethod1(new SampleJBRApi.V1.SomeDataClass());
|
||||
//
|
||||
// // But don't try doing 'service instanceof SampleJBRApi.V2' as it may cause NoClassDefFoundErrors!
|
||||
// SampleJBRApi.V2 service2 = Objects.requireNonNull(JBRService.load(() -> SampleJBRApi.V2.class));
|
||||
// // Versioned service interfaces are inherited, so V2 gives you access to both V2 and V1 API
|
||||
// service2.someMethod1(service2.someMethod2());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
97
test/jdk/jb/java/api/backend/FindDependenciesTest.java
Normal file
97
test/jdk/jb/java/api/backend/FindDependenciesTest.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/com.jetbrains.internal:+open
|
||||
* @build com.jetbrains.* com.jetbrains.api.FindDependencies com.jetbrains.jbr.FindDependencies
|
||||
* @run main FindDependenciesTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.jetbrains.Util.*;
|
||||
import static com.jetbrains.api.FindDependencies.*;
|
||||
import static com.jetbrains.jbr.FindDependencies.*;
|
||||
|
||||
public class FindDependenciesTest {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
JBRApi.ModuleRegistry r = init(new String[] {}, names("""
|
||||
A AL AR ARL /*ARR is skipped*/ ARRL ARRR
|
||||
BV B1 B2 B3 B4 B5 B6 B7 B8 B9
|
||||
C1 !C3 C5 C6
|
||||
""").toArray(String[]::new));
|
||||
// Simple tree with non-proxy type ARR
|
||||
validateDependencies(AR.class, cs("AR ARL /*ARR is skipped*/ ARRL ARRR"));
|
||||
validateDependencies(A.class, cs("A AL AR ARL /*ARR is skipped*/ ARRL ARRR"));
|
||||
validateDependencies(ARRR.class, ARRR.class);
|
||||
// Complex graph with many cycles
|
||||
for (Class<?> c : cs("B4 B6 B2 B3 B5")) {
|
||||
validateDependencies(c, cs("BV B2 B3 B4 B5 B6 B7 B8 B9"));
|
||||
}
|
||||
validateDependencies(B1.class, cs("BV B1 B2 B3 B4 B5 B6 B7 B8 B9"));
|
||||
validateDependencies(B7.class, B7.class, BV.class);
|
||||
validateDependencies(B8.class, B8.class, BV.class);
|
||||
validateDependencies(B9.class, B9.class);
|
||||
validateDependencies(BV.class, BV.class);
|
||||
// Client proxy dependencies
|
||||
r.clientProxy(C3.class.getName(), C2.class.getName());
|
||||
r.proxy(C5.class.getName(), C4.class.getName());
|
||||
validateDependencies(C1.class, C1.class, C3.class, C5.class, C6.class);
|
||||
validateDependencies(C5.class, C5.class, C6.class);
|
||||
validateDependencies(C6.class, C6.class);
|
||||
validateDependencies(C3.class, C3.class, C5.class, C6.class);
|
||||
}
|
||||
|
||||
private static Class<?>[] cs(String interfaces) {
|
||||
return names(interfaces).map(c -> {
|
||||
try {
|
||||
return Class.forName(c);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}).toArray(Class[]::new);
|
||||
}
|
||||
private static Stream<String> names(String interfaces) {
|
||||
return Stream.of(interfaces.replaceAll("/\\*[^*]*\\*/", "").split("(\s|\n)+")).map(String::strip)
|
||||
.map(s -> {
|
||||
if (s.startsWith("!")) return "com.jetbrains.jbr.FindDependencies$" + s.substring(1);
|
||||
else return "com.jetbrains.api.FindDependencies$" + s;
|
||||
});
|
||||
}
|
||||
|
||||
private static void validateDependencies(Class<?> src, Class<?>... expected) throws Throwable {
|
||||
Set<Class<?>> actual = getProxyDependencies(src);
|
||||
if (actual.size() != expected.length || !actual.containsAll(List.of(expected))) {
|
||||
throw new Error("Invalid proxy dependencies for class " + src +
|
||||
". Expected: [" + Stream.of(expected).map(Class::getSimpleName).collect(Collectors.joining(" ")) +
|
||||
"]. Actual: [" + actual.stream().map(Class::getSimpleName).collect(Collectors.joining(" ")) + "].");
|
||||
}
|
||||
}
|
||||
|
||||
private static final ReflectedMethod getProxyDependencies =
|
||||
getMethod("com.jetbrains.internal.ProxyDependencyManager", "getProxyDependencies", Class.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
static Set<Class<?>> getProxyDependencies(Class<?> interFace) throws Throwable {
|
||||
return (Set<Class<?>>) getProxyDependencies.invoke(null, interFace);
|
||||
}
|
||||
}
|
||||
71
test/jdk/jb/java/api/backend/MethodMappingTest.java
Normal file
71
test/jdk/jb/java/api/backend/MethodMappingTest.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/com.jetbrains.internal:+open
|
||||
* @build com.jetbrains.* com.jetbrains.api.MethodMapping com.jetbrains.jbr.MethodMapping
|
||||
* @run main MethodMappingTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import static com.jetbrains.Util.*;
|
||||
import static com.jetbrains.api.MethodMapping.*;
|
||||
import static com.jetbrains.jbr.MethodMapping.*;
|
||||
|
||||
public class MethodMappingTest {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
JBRApi.ModuleRegistry r = init();
|
||||
// Simple empty interface
|
||||
r.proxy(SimpleEmpty.class.getName(), SimpleEmptyImpl.class.getName());
|
||||
requireImplemented(SimpleEmpty.class);
|
||||
// Plain method mapping
|
||||
r.proxy(PlainFail.class.getName(), PlainImpl.class.getName());
|
||||
r.service(Plain.class.getName(), PlainImpl.class.getName())
|
||||
.withStatic("c", MethodMappingTest.class.getName(), "main");
|
||||
requireNotImplemented(PlainFail.class);
|
||||
requireImplemented(Plain.class);
|
||||
// Callback (client proxy)
|
||||
r.clientProxy(Callback.class.getName(), ApiCallback.class.getName());
|
||||
requireImplemented(Callback.class);
|
||||
// 2-way
|
||||
r.twoWayProxy(ApiTwoWay.class.getName(), JBRTwoWay.class.getName());
|
||||
requireImplemented(ApiTwoWay.class);
|
||||
requireImplemented(JBRTwoWay.class);
|
||||
// Conversion
|
||||
r.twoWayProxy(Conversion.class.getName(), ConversionImpl.class.getName());
|
||||
r.proxy(ConversionSelf.class.getName(), ConversionSelfImpl.class.getName());
|
||||
r.proxy(ConversionFail.class.getName(), ConversionFailImpl.class.getName());
|
||||
requireImplemented(Conversion.class);
|
||||
requireImplemented(ConversionImpl.class);
|
||||
requireImplemented(ConversionSelf.class);
|
||||
requireNotImplemented(ConversionFail.class);
|
||||
}
|
||||
|
||||
private static final ReflectedMethod methodsImplemented = getMethod("com.jetbrains.internal.Proxy", "areAllMethodsImplemented");
|
||||
private static void requireImplemented(Class<?> interFace) throws Throwable {
|
||||
if (!(boolean) methodsImplemented.invoke(getProxy(interFace))) {
|
||||
throw new Error("All methods must be implemented");
|
||||
}
|
||||
}
|
||||
private static void requireNotImplemented(Class<?> interFace) throws Throwable {
|
||||
if ((boolean) methodsImplemented.invoke(getProxy(interFace))) {
|
||||
throw new Error("Not all methods must be implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
58
test/jdk/jb/java/api/backend/ProxyInfoResolvingTest.java
Normal file
58
test/jdk/jb/java/api/backend/ProxyInfoResolvingTest.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/com.jetbrains.internal:+open
|
||||
* @build com.jetbrains.* com.jetbrains.api.ProxyInfoResolving com.jetbrains.jbr.ProxyInfoResolving
|
||||
* @run main ProxyInfoResolvingTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.jetbrains.Util.*;
|
||||
import static com.jetbrains.api.ProxyInfoResolving.*;
|
||||
import static com.jetbrains.jbr.ProxyInfoResolving.*;
|
||||
|
||||
public class ProxyInfoResolvingTest {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
JBRApi.ModuleRegistry r = init();
|
||||
// No mapping defined -> null
|
||||
requireNull(getProxy(ProxyInfoResolvingTest.class));
|
||||
// Invalid JBR-side target class -> error
|
||||
r.proxy(InterfaceWithoutImplementation.class.getName(), "absentImpl");
|
||||
mustFail(() -> getProxy(InterfaceWithoutImplementation.class), RuntimeException.class, ClassNotFoundException.class);
|
||||
// Invalid JBR-side target static method mapping -> error
|
||||
r.service(ServiceWithoutImplementation.class.getName(), null)
|
||||
.withStatic("someMethod", "NoClass");
|
||||
mustFail(() -> getProxy(ServiceWithoutImplementation.class), RuntimeException.class, ClassNotFoundException.class);
|
||||
// Service without target class or static method mapping -> null
|
||||
r.service(EmptyService.class.getName(), null);
|
||||
requireNull(getProxy(EmptyService.class));
|
||||
// Class passed instead of interface for client proxy -> error
|
||||
r.clientProxy(ClientProxyClass.class.getName(), ClientProxyClassImpl.class.getName());
|
||||
mustFail(() -> getProxy(ClientProxyClass.class), RuntimeException.class);
|
||||
// Class passed instead of interface for proxy -> null
|
||||
r.proxy(ProxyClass.class.getName(), ProxyClassImpl.class.getName());
|
||||
requireNull(getProxy(ProxyClass.class));
|
||||
// Valid proxy
|
||||
r.proxy(ValidApi.class.getName(), ValidApiImpl.class.getName());
|
||||
Objects.requireNonNull(getProxy(ValidApi.class));
|
||||
}
|
||||
}
|
||||
42
test/jdk/jb/java/api/backend/ProxyRegistrationTest.java
Normal file
42
test/jdk/jb/java/api/backend/ProxyRegistrationTest.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/com.jetbrains.internal:+open
|
||||
* @build com.jetbrains.*
|
||||
* @run main ProxyRegistrationTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import static com.jetbrains.Util.*;
|
||||
|
||||
public class ProxyRegistrationTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
JBRApi.ModuleRegistry r = init();
|
||||
// Only service may not have target type
|
||||
r.service("s", null);
|
||||
mustFail(() -> r.proxy("i", null), NullPointerException.class);
|
||||
mustFail(() -> r.clientProxy("i", null), NullPointerException.class);
|
||||
mustFail(() -> r.twoWayProxy("i", null), NullPointerException.class);
|
||||
// Invalid 2-way mapping
|
||||
r.proxy("a", "b");
|
||||
mustFail(() -> r.clientProxy("b", "c"), IllegalArgumentException.class);
|
||||
mustFail(() -> r.clientProxy("c", "a"), IllegalArgumentException.class);
|
||||
}
|
||||
}
|
||||
79
test/jdk/jb/java/api/backend/RealTest.java
Normal file
79
test/jdk/jb/java/api/backend/RealTest.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/com.jetbrains.internal:+open
|
||||
* @build com.jetbrains.* com.jetbrains.api.Real com.jetbrains.jbr.Real
|
||||
* @run main RealTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.jetbrains.Util.*;
|
||||
import static com.jetbrains.api.Real.*;
|
||||
import static com.jetbrains.jbr.Real.*;
|
||||
|
||||
public class RealTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
init()
|
||||
.service(Service.class.getName(), ServiceImpl.class.getName())
|
||||
.twoWayProxy(Api2Way.class.getName(), JBR2Way.class.getName())
|
||||
.twoWayProxy(ApiLazyNumber.class.getName(), JBRLazyNumber.class.getName());
|
||||
|
||||
// Get service
|
||||
Service service = Objects.requireNonNull(JBRApi.getService(Service.class));
|
||||
|
||||
// Test JBR-side proxy wrapping & unwrapping
|
||||
Api2Way stw = Objects.requireNonNull(service.get2Way());
|
||||
Api2Way nstw = Objects.requireNonNull(service.passthrough(stw));
|
||||
// stw and nstw are different proxy objects, because *real* object is on JBR-side
|
||||
if (stw.getClass() != nstw.getClass()) {
|
||||
throw new Error("Different classes when passing through the same object");
|
||||
}
|
||||
|
||||
// Test client-side proxy wrapping & unwrapping
|
||||
TwoWayImpl tw = new TwoWayImpl();
|
||||
Api2Way ntw = service.passthrough(tw);
|
||||
if (tw != ntw) {
|
||||
throw new Error("Client pass through doesn't work, there are probably issues with extracting target object");
|
||||
}
|
||||
// Service must have set tw.value by calling accept()
|
||||
Objects.requireNonNull(tw.value);
|
||||
|
||||
// Passing through null object -> null
|
||||
requireNull(service.passthrough(null));
|
||||
|
||||
if (!service.isSelf(service)) {
|
||||
throw new Error("service.isSelf(service) == false");
|
||||
}
|
||||
|
||||
if (service.sum(() -> 200, () -> 65).get() != 265) {
|
||||
throw new Error("Lazy numbers conversion error");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TwoWayImpl implements Api2Way {
|
||||
private Object value;
|
||||
@Override
|
||||
public void accept(Object o) {
|
||||
value = o;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
test/jdk/jb/java/api/backend/ReflectiveBootstrapTest.java
Normal file
36
test/jdk/jb/java/api/backend/ReflectiveBootstrapTest.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @build com.jetbrains.JBR
|
||||
* @run main ReflectiveBootstrapTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBR;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ReflectiveBootstrapTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
JBR.ServiceApi api = (JBR.ServiceApi) Class.forName("com.jetbrains.bootstrap.JBRApiBootstrap")
|
||||
.getMethod("bootstrap", MethodHandles.Lookup.class)
|
||||
.invoke(null, MethodHandles.privateLookupIn(JBR.class, MethodHandles.lookup()));
|
||||
Objects.requireNonNull(api, "JBR API bootstrap failed");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2020 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -14,15 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBRService;
|
||||
import com.jetbrains.impl.*;
|
||||
package com.jetbrains;
|
||||
|
||||
module jetbrains.api.impl {
|
||||
/**
|
||||
* Special open JBR API frontend for tests.
|
||||
*/
|
||||
public class JBR {
|
||||
|
||||
requires jetbrains.api;
|
||||
public interface ServiceApi {
|
||||
|
||||
// We probably don't want to add `provides with` for each version of API, so we just provide `JBRService`
|
||||
// implementation, `JBRService#load` will take care of the rest
|
||||
provides JBRService with ExtendedGlyphCacheImpl;
|
||||
<T> T getService(Class<T> interFace);
|
||||
}
|
||||
|
||||
}
|
||||
static final class Metadata {
|
||||
static String[] KNOWN_SERVICES = {};
|
||||
static String[] KNOWN_PROXIES = {};
|
||||
}
|
||||
}
|
||||
98
test/jdk/jb/java/api/backend/com/jetbrains/Util.java
Normal file
98
test/jdk/jb/java/api/backend/com/jetbrains/Util.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.jetbrains.internal.JBRApi;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static ReflectedMethod getMethod(String className, String method, Class<?>... parameterTypes) {
|
||||
try {
|
||||
Method m = Class.forName(className, false, JBRApi.class.getClassLoader())
|
||||
.getDeclaredMethod(method, parameterTypes);
|
||||
m.setAccessible(true);
|
||||
return (o, a) -> {
|
||||
try {
|
||||
return m.invoke(o, a);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
};
|
||||
} catch (NoSuchMethodException | ClassNotFoundException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JBRApi.ModuleRegistry init() {
|
||||
return init(new String[0], new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known services & proxies at runtime and invoke internal
|
||||
* {@link JBRApi#init} bypassing {@link com.jetbrains.bootstrap.JBRApiBootstrap#bootstrap}
|
||||
* in order not to init normal JBR API modules.
|
||||
*/
|
||||
public static JBRApi.ModuleRegistry init(String[] knownServices, String[] knownProxies) {
|
||||
JBR.Metadata.KNOWN_SERVICES = knownServices;
|
||||
JBR.Metadata.KNOWN_PROXIES = knownProxies;
|
||||
JBRApi.init(MethodHandles.lookup());
|
||||
return JBRApi.registerModule(MethodHandles.lookup(), JBR.class.getModule()::addExports);
|
||||
}
|
||||
|
||||
private static final ReflectedMethod getProxy = getMethod(JBRApi.class.getName(), "getProxy", Class.class);
|
||||
public static Object getProxy(Class<?> interFace) throws Throwable {
|
||||
return getProxy.invoke(null, interFace);
|
||||
}
|
||||
|
||||
public static void requireNull(Object o) {
|
||||
if (o != null) throw new RuntimeException("Value must be null");
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static void mustFail(ThrowingRunnable action, Class<? extends Throwable>... exceptionTypeChain) {
|
||||
try {
|
||||
action.run();
|
||||
} catch(Throwable exception) {
|
||||
Throwable e = exception;
|
||||
error: {
|
||||
for (Class<? extends Throwable> c : exceptionTypeChain) {
|
||||
if (e == null || !c.isInstance(e)) break error;
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e == null) return;
|
||||
}
|
||||
throw new Error("Unexpected exception", exception);
|
||||
}
|
||||
throw new Error("Operation must fail, but succeeded");
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingRunnable {
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ReflectedMethod {
|
||||
Object invoke(Object obj, Object... args) throws Throwable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
public class FindDependencies {
|
||||
public interface A {
|
||||
void f(AL l, AR r);
|
||||
}
|
||||
public interface AL {}
|
||||
public interface AR {
|
||||
void f(ARL l, ARR r);
|
||||
}
|
||||
public interface ARL {}
|
||||
public interface ARR {
|
||||
void f(ARRL l, ARRR r);
|
||||
}
|
||||
public interface ARRL {}
|
||||
public interface ARRR {}
|
||||
|
||||
public interface BV {}
|
||||
public interface B1 {
|
||||
void f(BV bvs, B2 t, BV bve);
|
||||
}
|
||||
public interface B2 {
|
||||
void f(BV bvs, B3 t, BV bve);
|
||||
}
|
||||
public interface B3 {
|
||||
void f(B8 l, BV bvs, B4 t, B2 b, BV bve, B9 r);
|
||||
}
|
||||
public interface B4 {
|
||||
void f(BV bvs, B2 b, B5 t, BV bve);
|
||||
}
|
||||
public interface B5 {
|
||||
void f(BV bvs, B6 s, B3 b, B7 t, BV bve);
|
||||
}
|
||||
public interface B6 {
|
||||
void f(BV bvs, B5 b, BV bve);
|
||||
}
|
||||
public interface B7 {
|
||||
void f(BV v);
|
||||
}
|
||||
public interface B8 {
|
||||
void f(BV v);
|
||||
}
|
||||
public interface B9 {}
|
||||
|
||||
public interface C1 {
|
||||
void f(C2 c);
|
||||
}
|
||||
public interface C2 {}
|
||||
public interface C5 {
|
||||
void f(C6 c);
|
||||
}
|
||||
public interface C6 {}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
public class MethodMapping {
|
||||
public interface SimpleEmpty {}
|
||||
public interface Plain {
|
||||
void a();
|
||||
boolean b();
|
||||
void c(String... a);
|
||||
}
|
||||
public interface PlainFail extends Plain {}
|
||||
public interface ApiCallback {
|
||||
void hello();
|
||||
}
|
||||
public interface ApiTwoWay {
|
||||
void something();
|
||||
}
|
||||
public interface Conversion {
|
||||
SimpleEmpty convert(Plain a, ApiCallback b, ApiTwoWay c);
|
||||
}
|
||||
public interface ConversionSelf extends Conversion {
|
||||
ConversionSelf convert(Object a, Object b, Object c);
|
||||
}
|
||||
public interface ConversionFail extends Conversion {
|
||||
void missingMethod();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
public class ProxyInfoResolving {
|
||||
public interface InterfaceWithoutImplementation {}
|
||||
public interface ServiceWithoutImplementation {}
|
||||
public interface EmptyService {}
|
||||
public static class ClientProxyClassImpl {}
|
||||
public static class ProxyClass {}
|
||||
public interface ValidApi {}
|
||||
}
|
||||
43
test/jdk/jb/java/api/backend/com/jetbrains/api/Real.java
Normal file
43
test/jdk/jb/java/api/backend/com/jetbrains/api/Real.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Real {
|
||||
|
||||
public interface Service {
|
||||
Api2Way get2Way();
|
||||
Api2Way passthrough(Api2Way a);
|
||||
boolean isSelf(Service a);
|
||||
ApiLazyNumber sum(ApiLazyNumber a, ApiLazyNumber b);
|
||||
/**
|
||||
* When generating bridge class, convertible method parameters are changed to Objects,
|
||||
* which may cause name collisions
|
||||
*/
|
||||
void testMethodNameConflict(Api2Way a);
|
||||
void testMethodNameConflict(ApiLazyNumber a);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Api2Way extends Consumer<Object> {}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ApiLazyNumber {
|
||||
int get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.jbr;
|
||||
|
||||
public class FindDependencies {
|
||||
public interface C3 {
|
||||
void f(C4 c);
|
||||
}
|
||||
public interface C4 {}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.jbr;
|
||||
|
||||
public class MethodMapping {
|
||||
public static class SimpleEmptyImpl {}
|
||||
public static class PlainImpl {
|
||||
public void a() {}
|
||||
public boolean b() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public interface Callback {
|
||||
void hello();
|
||||
}
|
||||
public interface JBRTwoWay {
|
||||
void something();
|
||||
}
|
||||
public interface ConversionImpl {
|
||||
default SimpleEmptyImpl convert(PlainImpl a, Callback b, JBRTwoWay c) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static class ConversionSelfImpl implements ConversionImpl {
|
||||
public ConversionSelfImpl convert(Object a, Object b, Object c) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static class ConversionFailImpl implements ConversionImpl {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.jbr;
|
||||
|
||||
public class ProxyInfoResolving {
|
||||
public static class ClientProxyClass {}
|
||||
public static class ProxyClassImpl {}
|
||||
public static class ValidApiImpl {}
|
||||
}
|
||||
48
test/jdk/jb/java/api/backend/com/jetbrains/jbr/Real.java
Normal file
48
test/jdk/jb/java/api/backend/com/jetbrains/jbr/Real.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.jbr;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Real {
|
||||
|
||||
public static class ServiceImpl {
|
||||
JBR2Way get2Way() {
|
||||
return a -> {};
|
||||
}
|
||||
JBR2Way passthrough(JBR2Way a) {
|
||||
if (a != null) a.accept(a);
|
||||
return a;
|
||||
}
|
||||
boolean isSelf(ServiceImpl a) {
|
||||
return a == this;
|
||||
}
|
||||
JBRLazyNumber sum(JBRLazyNumber a, JBRLazyNumber b) {
|
||||
return () -> a.get() + b.get();
|
||||
}
|
||||
void testMethodNameConflict(JBR2Way a) {}
|
||||
void testMethodNameConflict(JBRLazyNumber a) {}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface JBR2Way extends Consumer<Object> {}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface JBRLazyNumber {
|
||||
int get();
|
||||
}
|
||||
}
|
||||
52
test/jdk/jb/java/api/frontend/JBRApiTest.java
Normal file
52
test/jdk/jb/java/api/frontend/JBRApiTest.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run shell build.sh
|
||||
* @run main JBRApiTest
|
||||
*/
|
||||
|
||||
import com.jetbrains.JBR;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class JBRApiTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (!JBR.getApiVersion().matches("\\w+\\.\\d+\\.\\d+\\.\\d+")) throw new Error("Invalid JBR API version");
|
||||
if (!JBR.isAvailable()) throw new Error("JBR API is not available");
|
||||
checkMetadata();
|
||||
testServices();
|
||||
}
|
||||
|
||||
private static void checkMetadata() throws Exception {
|
||||
Class<?> metadata = Class.forName(JBR.class.getName() + "$Metadata");
|
||||
Field field = metadata.getDeclaredField("KNOWN_SERVICES");
|
||||
field.setAccessible(true);
|
||||
List<String> knownServices = List.of((String[]) field.get(null));
|
||||
if (!knownServices.contains("com.jetbrains.JBR$ServiceApi")) {
|
||||
throw new Error("com.jetbrains.JBR$ServiceApi was not found in known services of com.jetbrains.JBR$Metadata");
|
||||
}
|
||||
}
|
||||
|
||||
private static void testServices() {
|
||||
Objects.requireNonNull((Dimension) JBR.getExtendedGlyphCache().getSubpixelResolution());
|
||||
}
|
||||
}
|
||||
29
test/jdk/jb/java/api/frontend/build.sh
Normal file
29
test/jdk/jb/java/api/frontend/build.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
SRC="$TESTSRC/../../../../../../src"
|
||||
PWD="`pwd`"
|
||||
# Generate sources
|
||||
"$COMPILEJAVA/bin/java" "$SRC/jetbrains.api/templates/Gensrc.java" "$SRC" "$PWD/jbr-api" "TEST" || exit $?
|
||||
# Validate version
|
||||
"$COMPILEJAVA/bin/java" "$SRC/jetbrains.api/templates/CheckVersion.java" "$SRC/jetbrains.api" "$PWD/jbr-api/gensrc" "true" || exit $?
|
||||
# Compile API
|
||||
find "$SRC/jetbrains.api/src" -name *.java > compile.list
|
||||
find jbr-api/gensrc -name *.java >> compile.list
|
||||
"$COMPILEJAVA/bin/javac" $TESTJAVACOPTS -d "$TESTCLASSES" @compile.list || exit $?
|
||||
rm "$TESTCLASSES/module-info.class"
|
||||
exit 0
|
||||
@@ -0,0 +1,96 @@
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.VolatileImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
/**
|
||||
* @test
|
||||
* @key headful
|
||||
* @summary Test runs two times to generate and compare images with accelerated OGL and Metal text rendering.
|
||||
* @run main/othervm -Dsun.java2d.metal=False OGLMetalTextRender
|
||||
* @run main/othervm -Dsun.java2d.metal=True OGLMetalTextRender
|
||||
*/
|
||||
|
||||
public class OGLMetalTextRender {
|
||||
final static int WIDTH = 210;
|
||||
final static int HEIGHT = 120;
|
||||
final static int TD = 3;
|
||||
final static File mtlImg = new File("t-metal.png");
|
||||
final static File oglImg = new File("t-ogl.png");
|
||||
final static File cmpImg = new File("t-mtlogl.png");
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
|
||||
GraphicsEnvironment ge =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsConfiguration gc =
|
||||
ge.getDefaultScreenDevice().getDefaultConfiguration();
|
||||
|
||||
VolatileImage vi = gc.createCompatibleVolatileImage(WIDTH, HEIGHT);
|
||||
BufferedImage bi = gc.createCompatibleImage(WIDTH, HEIGHT);
|
||||
String text = "The quick brown fox jumps over the lazy dog";
|
||||
int c = 10;
|
||||
do {
|
||||
Graphics2D g2d = vi.createGraphics();
|
||||
Font font = new Font("Serif", Font.PLAIN, 10);
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.fillRect(0, 0, WIDTH, HEIGHT);
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
g2d.setColor(new Color(0, 0, 0, i/10.0f));
|
||||
g2d.drawString(text, 10, 10*(i + 1));
|
||||
}
|
||||
g2d.dispose();
|
||||
} while (vi.contentsLost() && (--c > 0));
|
||||
|
||||
Graphics2D g2d = bi.createGraphics();
|
||||
g2d.drawImage(vi, 0, 0, null);
|
||||
g2d.dispose();
|
||||
int errors = 0;
|
||||
BufferedImage result = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
String opt = System.getProperty("sun.java2d.metal");
|
||||
if (opt != null && ("true".equals(opt) || "True".equals(opt))) {
|
||||
ImageIO.write(bi, "png", mtlImg);
|
||||
if (oglImg.exists()) {
|
||||
errors = compare(bi, ImageIO.read(oglImg), result);
|
||||
}
|
||||
} else {
|
||||
ImageIO.write(bi, "png", oglImg);
|
||||
if (mtlImg.exists()) {
|
||||
errors = compare(bi, ImageIO.read(mtlImg), result);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors > 0) {
|
||||
ImageIO.write(result, "png", cmpImg);
|
||||
throw new RuntimeException("Metal vs OGL rendering mismatch, errors found: " + errors);
|
||||
}
|
||||
}
|
||||
|
||||
private static int compare(BufferedImage bi1, BufferedImage bi2,
|
||||
BufferedImage result)
|
||||
{
|
||||
int errors = 0;
|
||||
for (int i = 0; i < WIDTH; i++) {
|
||||
for (int j = 0; j < HEIGHT; j++) {
|
||||
Color c1 = new Color(bi1.getRGB(i, j));
|
||||
Color c2 = new Color(bi2.getRGB(i, j));
|
||||
int dr = Math.abs(c1.getRed() - c2.getRed());
|
||||
int dg = Math.abs(c1.getGreen() - c2.getGreen());
|
||||
int db = Math.abs(c1.getBlue() - c2.getBlue());
|
||||
if (dr+dg+db > TD) {
|
||||
errors++;
|
||||
result.setRGB(i, j, Color.RED.getRGB());
|
||||
}
|
||||
Color r = new Color(dr, dg, db);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Regression test for JBR-3838 ensures that pressing each layout-free key + AltGr produces AltGr modifier
|
||||
* @requires (os.family == "windows")
|
||||
* @key headful
|
||||
* @run main AltGrMustGenerateAltGrModifierTest3838
|
||||
* @author Nikita Provotorov
|
||||
*/
|
||||
public class AltGrMustGenerateAltGrModifierTest3838 extends Frame {
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
final AltGrMustGenerateAltGrModifierTest3838 mainWindow = new AltGrMustGenerateAltGrModifierTest3838();
|
||||
|
||||
try {
|
||||
mainWindow.setVisible(true);
|
||||
mainWindow.toFront();
|
||||
|
||||
final Robot robot = new Robot();
|
||||
|
||||
forceFocusTo(mainWindow.textArea, robot);
|
||||
|
||||
Thread.sleep(PAUSE_MS);
|
||||
|
||||
mainWindow.pressAllKeysWithAltGr(robot);
|
||||
|
||||
Thread.sleep(PAUSE_MS);
|
||||
|
||||
if (mainWindow.allKeysWerePressedAndReleased &&
|
||||
mainWindow.allModifiersAreCorrect &&
|
||||
mainWindow.allModifiersExAreCorrect) {
|
||||
System.out.println("Test passed.");
|
||||
} else {
|
||||
throw new Exception("Test failed");
|
||||
}
|
||||
} finally {
|
||||
mainWindow.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private AltGrMustGenerateAltGrModifierTest3838()
|
||||
{
|
||||
super("AltGr must generate AltGr modifier");
|
||||
|
||||
setAlwaysOnTop(true);
|
||||
|
||||
textArea = new TextArea();
|
||||
textArea.setFocusable(true);
|
||||
|
||||
add(textArea);
|
||||
pack();
|
||||
setSize(250, 250);
|
||||
|
||||
textArea.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
keyHandlerImpl("keyPressed", e, pressedKeyToCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
keyHandlerImpl("keyReleased", e, pressedKeyToCheck);
|
||||
}
|
||||
|
||||
|
||||
private void keyHandlerImpl(final String eventName, final KeyEvent e, final Integer expectedPressedKey)
|
||||
{
|
||||
if (expectedPressedKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Old API.
|
||||
|
||||
// Not all keyboard layouts have AltGr (e.g. ENG-US).
|
||||
// On this keyboards right Alt produces only Alt without Ctrl.
|
||||
final int modifiersOnMask = /*KeyEvent.CTRL_MASK |*/ KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_MASK;
|
||||
final int modifiers = e.getModifiers();
|
||||
if ((modifiers & modifiersOnMask) != modifiersOnMask) {
|
||||
allModifiersAreCorrect = false;
|
||||
System.err.println(eventName + " {" + keyCodeToText(expectedPressedKey) + "}: wrong Modifiers;" +
|
||||
" expected: " + modifiersToText(modifiersOnMask) +
|
||||
", actual: " + modifiersToText(modifiers) + ".");
|
||||
}
|
||||
|
||||
// Modern API.
|
||||
|
||||
// Not all keyboard layouts have AltGr (e.g. ENG-US).
|
||||
// On this keyboards right Alt produces only Alt without Ctrl.
|
||||
final int modifiersExOnMask = /*KeyEvent.CTRL_DOWN_MASK |*/ KeyEvent.ALT_DOWN_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
|
||||
final int modifiersEx = e.getModifiersEx();
|
||||
if ((modifiersEx & modifiersExOnMask) != modifiersExOnMask) {
|
||||
allModifiersExAreCorrect = false;
|
||||
System.err.println(eventName + " {" + keyCodeToText(expectedPressedKey) + "}: wrong ModifiersEx;" +
|
||||
" expected: " + modifiersExToText(modifiersExOnMask) +
|
||||
", actual: " + modifiersExToText(modifiersEx) + ".");
|
||||
}
|
||||
} finally {
|
||||
keyPressedLatch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void pressAllKeysWithAltGr(final Robot robot) throws Exception
|
||||
{
|
||||
for (final int keyToPress : allLayoutFreeVirtualKeys) {
|
||||
keyPressedLatch = new CountDownLatch(2);
|
||||
|
||||
pressKeyWithAltGr(keyToPress, robot);
|
||||
|
||||
if (!keyPressedLatch.await(PAUSE_MS, TimeUnit.MILLISECONDS)) {
|
||||
allKeysWerePressedAndReleased = false;
|
||||
System.err.println("Pressing {" + keyCodeToText(KeyEvent.VK_ALT_GRAPH) + " + " + keyCodeToText(keyToPress) + "}" +
|
||||
": not all keys have been pressed or released.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pressKeyWithAltGr(final int keyToPress, final Robot robot)
|
||||
{
|
||||
pressedKeyToCheck = null;
|
||||
|
||||
robot.waitForIdle();
|
||||
|
||||
robot.keyPress(KeyEvent.VK_ALT_GRAPH);
|
||||
robot.waitForIdle();
|
||||
|
||||
try {
|
||||
robot.keyPress(pressedKeyToCheck = keyToPress);
|
||||
robot.keyRelease(keyToPress);
|
||||
|
||||
robot.waitForIdle();
|
||||
|
||||
pressedKeyToCheck = null;
|
||||
|
||||
// restore lock-keys state
|
||||
if ( (keyToPress == KeyEvent.VK_CAPS_LOCK) ||
|
||||
(keyToPress == KeyEvent.VK_NUM_LOCK) ||
|
||||
(keyToPress == KeyEvent.VK_KANA_LOCK) ||
|
||||
(keyToPress == KeyEvent.VK_SCROLL_LOCK) ) {
|
||||
robot.keyPress(keyToPress);
|
||||
robot.keyRelease(keyToPress);
|
||||
}
|
||||
} finally {
|
||||
robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
|
||||
robot.waitForIdle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sometimes top-level Frame does not get focus when requestFocus is called.
|
||||
// For example, when this test is launched after test/.../bug6361367.java:
|
||||
// jtreg test/jdk/javax/swing/text/JTextComponent/6361367/bug6361367.java test/jdk/jb/java/awt/keyboard/AltGrMustGenerateAltGrModifierTest3838.java
|
||||
//
|
||||
// So this method forces the focus acquiring via mouse clicking to the component.
|
||||
private static void forceFocusTo(final Component component, final Robot robot) {
|
||||
robot.waitForIdle();
|
||||
|
||||
final Point componentTopLeft = component.getLocationOnScreen();
|
||||
final int componentCenterX = componentTopLeft.x + component.getWidth() / 2;
|
||||
final int componentCenterY = componentTopLeft.y + component.getHeight() / 2;
|
||||
|
||||
robot.mouseMove(componentCenterX, componentCenterY);
|
||||
robot.waitForIdle();
|
||||
|
||||
robot.mousePress(InputEvent.BUTTON1_MASK);
|
||||
robot.mouseRelease(InputEvent.BUTTON1_MASK);
|
||||
|
||||
robot.waitForIdle();
|
||||
|
||||
component.requestFocus();
|
||||
|
||||
robot.waitForIdle();
|
||||
}
|
||||
|
||||
private static String keyCodeToText(int keyCode) {
|
||||
return "<" + KeyEvent.getKeyText(keyCode) + " (code=" + keyCode + ")>";
|
||||
}
|
||||
|
||||
private static String modifiersToText(int modifiers) {
|
||||
return "[" + KeyEvent.getKeyModifiersText(modifiers) + " (code=" + modifiers + ")]";
|
||||
}
|
||||
|
||||
private static String modifiersExToText(int modifiersEx) {
|
||||
return "[" + KeyEvent.getModifiersExText(modifiersEx) + " (code=" + modifiersEx + ")]";
|
||||
}
|
||||
|
||||
private static final int PAUSE_MS = 2000;
|
||||
|
||||
|
||||
private final TextArea textArea;
|
||||
|
||||
private volatile boolean allKeysWerePressedAndReleased = true;
|
||||
private volatile boolean allModifiersAreCorrect = true;
|
||||
private volatile boolean allModifiersExAreCorrect = true;
|
||||
|
||||
private volatile Integer pressedKeyToCheck = null;
|
||||
private volatile CountDownLatch keyPressedLatch = null;
|
||||
|
||||
private final int[] allLayoutFreeVirtualKeys = new int[] {
|
||||
// Modifier keys
|
||||
|
||||
KeyEvent.VK_CAPS_LOCK,
|
||||
KeyEvent.VK_SHIFT,
|
||||
/* VK_CONTROL releasing removes SHIFT_MASK, SHIFT_DOWN_MASK - notepad.exe, VSCode have the same behavior */
|
||||
//KeyEvent.VK_CONTROL,
|
||||
KeyEvent.VK_ALT,
|
||||
//KeyEvent.VK_ALT_GRAPH,
|
||||
KeyEvent.VK_NUM_LOCK,
|
||||
|
||||
// Miscellaneous Windows keys
|
||||
|
||||
KeyEvent.VK_WINDOWS,
|
||||
KeyEvent.VK_WINDOWS,
|
||||
KeyEvent.VK_CONTEXT_MENU,
|
||||
|
||||
// Alphabet
|
||||
|
||||
KeyEvent.VK_A,
|
||||
KeyEvent.VK_B,
|
||||
KeyEvent.VK_C,
|
||||
KeyEvent.VK_D,
|
||||
KeyEvent.VK_E,
|
||||
KeyEvent.VK_F,
|
||||
KeyEvent.VK_G,
|
||||
KeyEvent.VK_H,
|
||||
KeyEvent.VK_I,
|
||||
KeyEvent.VK_J,
|
||||
KeyEvent.VK_K,
|
||||
KeyEvent.VK_L,
|
||||
KeyEvent.VK_M,
|
||||
KeyEvent.VK_N,
|
||||
KeyEvent.VK_O,
|
||||
KeyEvent.VK_P,
|
||||
KeyEvent.VK_Q,
|
||||
KeyEvent.VK_R,
|
||||
KeyEvent.VK_S,
|
||||
KeyEvent.VK_T,
|
||||
KeyEvent.VK_U,
|
||||
KeyEvent.VK_V,
|
||||
KeyEvent.VK_W,
|
||||
KeyEvent.VK_X,
|
||||
KeyEvent.VK_Y,
|
||||
KeyEvent.VK_Z,
|
||||
|
||||
// Standard numeric row
|
||||
|
||||
KeyEvent.VK_0,
|
||||
KeyEvent.VK_1,
|
||||
KeyEvent.VK_2,
|
||||
KeyEvent.VK_3,
|
||||
KeyEvent.VK_4,
|
||||
KeyEvent.VK_5,
|
||||
KeyEvent.VK_6,
|
||||
KeyEvent.VK_7,
|
||||
KeyEvent.VK_8,
|
||||
KeyEvent.VK_9,
|
||||
|
||||
// Misc key from main block
|
||||
|
||||
KeyEvent.VK_ENTER,
|
||||
/* avoid SC_KEYMENU event on layouts which don't have AltGr */
|
||||
//KeyEvent.VK_SPACE,
|
||||
KeyEvent.VK_BACK_SPACE,
|
||||
/* avoid focus switching */
|
||||
//KeyEvent.VK_TAB,
|
||||
//KeyEvent.VK_ESCAPE,
|
||||
|
||||
// NumPad with NumLock off & extended block (rectangular)
|
||||
|
||||
KeyEvent.VK_INSERT,
|
||||
KeyEvent.VK_DELETE,
|
||||
KeyEvent.VK_HOME,
|
||||
KeyEvent.VK_END,
|
||||
KeyEvent.VK_PAGE_UP,
|
||||
KeyEvent.VK_PAGE_DOWN,
|
||||
KeyEvent.VK_CLEAR,
|
||||
|
||||
// NumPad with NumLock off & extended arrows block (triangular)
|
||||
|
||||
KeyEvent.VK_LEFT,
|
||||
KeyEvent.VK_RIGHT,
|
||||
KeyEvent.VK_UP,
|
||||
KeyEvent.VK_DOWN,
|
||||
|
||||
// NumPad with NumLock on: numbers
|
||||
|
||||
KeyEvent.VK_NUMPAD0,
|
||||
KeyEvent.VK_NUMPAD1,
|
||||
KeyEvent.VK_NUMPAD2,
|
||||
KeyEvent.VK_NUMPAD3,
|
||||
KeyEvent.VK_NUMPAD4,
|
||||
KeyEvent.VK_NUMPAD5,
|
||||
KeyEvent.VK_NUMPAD6,
|
||||
KeyEvent.VK_NUMPAD7,
|
||||
KeyEvent.VK_NUMPAD8,
|
||||
KeyEvent.VK_NUMPAD9,
|
||||
|
||||
// NumPad with NumLock on
|
||||
|
||||
KeyEvent.VK_MULTIPLY,
|
||||
KeyEvent.VK_ADD,
|
||||
KeyEvent.VK_SEPARATOR,
|
||||
KeyEvent.VK_SUBTRACT,
|
||||
KeyEvent.VK_DECIMAL,
|
||||
KeyEvent.VK_DIVIDE,
|
||||
|
||||
// Functional keys
|
||||
|
||||
KeyEvent.VK_F1,
|
||||
KeyEvent.VK_F2,
|
||||
KeyEvent.VK_F3,
|
||||
KeyEvent.VK_F4,
|
||||
KeyEvent.VK_F5,
|
||||
KeyEvent.VK_F6,
|
||||
KeyEvent.VK_F7,
|
||||
KeyEvent.VK_F8,
|
||||
KeyEvent.VK_F9,
|
||||
KeyEvent.VK_F10,
|
||||
KeyEvent.VK_F11,
|
||||
KeyEvent.VK_F12,
|
||||
KeyEvent.VK_F13,
|
||||
KeyEvent.VK_F14,
|
||||
KeyEvent.VK_F15,
|
||||
KeyEvent.VK_F16,
|
||||
KeyEvent.VK_F17,
|
||||
KeyEvent.VK_F18,
|
||||
KeyEvent.VK_F19,
|
||||
KeyEvent.VK_F20,
|
||||
KeyEvent.VK_F21,
|
||||
KeyEvent.VK_F22,
|
||||
KeyEvent.VK_F23,
|
||||
KeyEvent.VK_F24,
|
||||
|
||||
/* Windows does not produce WM_KEYDOWN for VK_SNAPSHOT; see JDK-4455060 */
|
||||
//KeyEvent.VK_PRINTSCREEN,
|
||||
KeyEvent.VK_SCROLL_LOCK,
|
||||
KeyEvent.VK_PAUSE,
|
||||
KeyEvent.VK_CANCEL,
|
||||
KeyEvent.VK_HELP,
|
||||
|
||||
// Japanese
|
||||
|
||||
/* -> [VK_ALT] + VK_CONVERT -> [VK_ALT] + VK_ALL_CANDIDATES */
|
||||
KeyEvent.VK_CONVERT,
|
||||
KeyEvent.VK_NONCONVERT,
|
||||
KeyEvent.VK_INPUT_METHOD_ON_OFF,
|
||||
/* -> [VK_ALT] + VK_ALPHANUMERIC -> [VK_ALT] + VK_CODE_INPUT */
|
||||
KeyEvent.VK_ALPHANUMERIC,
|
||||
KeyEvent.VK_KATAKANA,
|
||||
KeyEvent.VK_HIRAGANA,
|
||||
KeyEvent.VK_FULL_WIDTH,
|
||||
KeyEvent.VK_HALF_WIDTH,
|
||||
KeyEvent.VK_ROMAN_CHARACTERS,
|
||||
|
||||
KeyEvent.VK_ALL_CANDIDATES,
|
||||
/* [VK_ALT] + VK_PREVIOUS_CANDIDATE -> [VK_ALT + VK_SHIFT] + VK_CONVERT -> [VK_ALT + VK_SHIFT] + VK_ALL_CANDIDATES */
|
||||
KeyEvent.VK_PREVIOUS_CANDIDATE,
|
||||
KeyEvent.VK_CODE_INPUT,
|
||||
/* can only be found if is available */
|
||||
//KeyEvent.VK_KANA_LOCK,
|
||||
};
|
||||
}
|
||||
50
test/jdk/jbA11yProblemList.txt
Normal file
50
test/jdk/jbA11yProblemList.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
java/awt/Focus/ModalDialogInitialFocusTest/ModalDialogInitialFocusTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/WindowIsFocusableAccessByThreadsTest/WindowIsFocusableAccessByThreadsTest.java JBR-3730 macosx-all
|
||||
java/awt/Mixing/ValidBounds.java JBR-3730 macosx-all
|
||||
java/awt/Mixing/Validating.java JBR-3730 macosx-all
|
||||
sanity/client/SwingSet/src/TableDemoTest.java JBR-3389 macosx-all intermittent
|
||||
java/awt/Component/7097771/bug7097771.java JBR-3730 macosx-all
|
||||
java/awt/Component/NoUpdateUponShow/NoUpdateUponShow.java JBR-3730 macosx-all
|
||||
java/awt/Component/SetComponentsBounds/SetComponentsBounds.java JBR-3722 macosx-all
|
||||
java/awt/event/ComponentEvent/MovedResizedTwiceTest/MovedResizedTwiceTest.java JBR-3730 macosx-all
|
||||
java/awt/event/MouseEvent/EnterAsGrabbedEvent/EnterAsGrabbedEvent.java JBR-3730 macosx-all
|
||||
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/AppletInitialFocusTest/AppletInitialFocusTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/AppletInitialFocusTest/AppletInitialFocusTest1.java JBR-3730 macosx-all
|
||||
java/awt/Focus/ClearGlobalFocusOwnerTest/ClearGlobalFocusOwnerTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/ChildWindowFocusTest/ChildWindowFocusTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/DisposedWindow/DisposeDialogNotActivateOwnerTest/DisposeDialogNotActivateOwnerTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/NonFocusableWindowTest/NoEventsTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/RemoveAfterRequest/RemoveAfterRequest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/RequestFocusAndHideTest/RequestFocusAndHideTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/RequestFocusByCause/RequestFocusByCauseTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/RequestOnCompWithNullParent/RequestOnCompWithNullParent1.java JBR-3730 macosx-all
|
||||
java/awt/Focus/RestoreFocusOnDisabledComponentTest/RestoreFocusOnDisabledComponentTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/ShowFrameCheckForegroundTest/ShowFrameCheckForegroundTest.java JBR-3733 macosx-all
|
||||
java/awt/Focus/ToFrontFocusTest/ToFrontFocus.java JBR-3730 macosx-all
|
||||
java/awt/Focus/WindowInitialFocusTest/WindowInitialFocusTest.java JBR-3730 macosx-all
|
||||
java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java JBR-3730 macosx-all
|
||||
java/awt/Frame/MiscUndecorated/ActiveAWTWindowTest.java JBR-3730 macosx-all
|
||||
java/awt/Frame/MiscUndecorated/FrameCloseTest.java JBR-3733 macosx-all
|
||||
java/awt/Frame/ObscuredFrame/ObscuredFrameTest.java JBR-3730 macosx-all
|
||||
java/awt/GridLayout/ChangeGridSize/ChangeGridSize.java JBR-3730 macosx-all
|
||||
java/awt/GridLayout/ComponentPreferredSize/ComponentPreferredSize.java JBR-3730 macosx-all
|
||||
java/awt/Insets/CombinedTestApp1.java JBR-3730 macosx-all
|
||||
java/awt/KeyboardFocusmanager/TypeAhead/EnqueueWithDialogButtonTest/EnqueueWithDialogButtonTest.java JBR-3730 macosx-all
|
||||
java/awt/KeyboardFocusmanager/TypeAhead/EnqueueWithDialogTest/EnqueueWithDialogTest.java JBR-3730 macosx-all
|
||||
java/awt/KeyboardFocusmanager/TypeAhead/FreezeTest/FreezeTest.java JBR-3730 macosx-all
|
||||
java/awt/Mixing/MixingOnDialog.java JBR-3730 macosx-all
|
||||
java/awt/Mixing/MixingOnShrinkingHWButton.java JBR-3730 macosx-all
|
||||
java/awt/MouseAdapter/MouseAdapterUnitTest/MouseAdapterUnitTest.java JBR-3730 macosx-all
|
||||
java/awt/Paint/ButtonRepaint.java JBR-3730 macosx-all
|
||||
java/awt/print/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java JBR-3730 macosx-all
|
||||
java/awt/Toolkit/RealSync/Test.java JBR-3730 macosx-all
|
||||
java/awt/TrayIcon/ActionEventMask/ActionEventMask.java JBR-3730 macosx-all
|
||||
java/awt/Window/ShapedAndTranslucentWindows/TranslucentChoice.java JBR-3730 macosx-all
|
||||
java/awt/Window/ShapedAndTranslucentWindows/TranslucentWindowClick.java JBR-3730 macosx-all
|
||||
java/awt/Window/setLocRelativeTo/SetLocationRelativeToTest.java JBR-3730 macosx-all
|
||||
javax/swing/ProgressMonitor/ProgressMonitorEscapeKeyPress.java JBR-3730 macosx-all
|
||||
|
||||
sanity/client/SwingSet/src/TreeDemoTest.java JBR-3389 macosx-all
|
||||
@@ -953,4 +953,8 @@ javax/print/PrintServiceLookup/CountPrintServices.java
|
||||
javax/print/attribute/AttributeTest.java nobug generic-all
|
||||
javax/print/attribute/GetCopiesSupported.java nobug generic-all
|
||||
javax/print/attribute/SidesPageRangesTest.java nobug generic-all
|
||||
javax/print/attribute/SupportedPrintableAreas.java nobug generic-all
|
||||
javax/print/attribute/SupportedPrintableAreas.java nobug generic-all
|
||||
|
||||
# Fedora & ArchLinux (Wayland) & Ubuntu 21.04
|
||||
sun/java2d/ClassCastExceptionForInvalidSurface.java JBR-3167 linux-5.11.0-37-generic,linux-5.10.12-200.fc33.x86_64,linux-5.11.6-arch1-1
|
||||
java/awt/Frame/HugeFrame/HugeFrame.java JBR-3167 linux-5.11.0-37-generic,linux-5.10.12-200.fc33.x86_64,linux-5.11.6-arch1-1
|
||||
|
||||
Reference in New Issue
Block a user