diff --git a/.hgignore b/.hgignore
index 9aab81ce35d..023de87e688 100644
--- a/.hgignore
+++ b/.hgignore
@@ -3,8 +3,7 @@
^.idea/
nbproject/private/
^webrev
-^.hgtip
-^.bridge2
+^.src-rev$
^.jib/
.DS_Store
.metadata/
diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index 58e9fe724f4..a1da5758a42 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -389,3 +389,4 @@ f64afae7f1a5608e438585bbf0bc23785e69cba0 jdk-9+141
8d337fd6333e28c48aa87880144b840aad82baaf jdk-9+144
ff98aa9ec9fae991e426ce5926fc9036d25f5562 jdk-9+145
a22e2671d88f6b22a4aa82e3966986542ed2a381 jdk-9+146
+5f6920274c48eb00d31afee6c034826a754c13d9 jdk-9+147
diff --git a/common/autoconf/boot-jdk.m4 b/common/autoconf/boot-jdk.m4
index ab2bd9a87d7..f75f3adfa2a 100644
--- a/common/autoconf/boot-jdk.m4
+++ b/common/autoconf/boot-jdk.m4
@@ -98,7 +98,7 @@ AC_DEFUN([BOOTJDK_DO_CHECK],
fi
])
-# Test: Is bootjdk explicitely set by command line arguments?
+# Test: Is bootjdk explicitly set by command line arguments?
AC_DEFUN([BOOTJDK_CHECK_ARGUMENTS],
[
if test "x$with_boot_jdk" != x; then
@@ -238,7 +238,7 @@ AC_DEFUN([BOOTJDK_CHECK_TOOL_IN_BOOTJDK],
$1=$BOOT_JDK/bin/$2
if test ! -x [$]$1; then
AC_MSG_RESULT(not found)
- AC_MSG_NOTICE([Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk])
+ AC_MSG_NOTICE([Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk])
AC_MSG_ERROR([Could not find $2 in the Boot JDK])
fi
AC_MSG_RESULT(ok)
@@ -262,7 +262,7 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK],
# we detected something (if so, the path to the jdk is in BOOT_JDK). But we
# must check if this is indeed valid; otherwise we'll continue looking.
- # Test: Is bootjdk explicitely set by command line arguments?
+ # Test: Is bootjdk explicitly set by command line arguments?
BOOTJDK_DO_CHECK([BOOTJDK_CHECK_ARGUMENTS])
if test "x$with_boot_jdk" != x && test "x$BOOT_JDK_FOUND" = xno; then
# Having specified an argument which is incorrect will produce an instant failure;
@@ -286,7 +286,7 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK],
if test "x$BOOT_JDK_FOUND" = xno; then
HELP_MSG_MISSING_DEPENDENCY([openjdk])
AC_MSG_NOTICE([Could not find a valid Boot JDK. $HELP_MSG])
- AC_MSG_NOTICE([This might be fixed by explicitely setting --with-boot-jdk])
+ AC_MSG_NOTICE([This might be fixed by explicitly setting --with-boot-jdk])
AC_MSG_ERROR([Cannot continue])
fi
diff --git a/common/autoconf/build-performance.m4 b/common/autoconf/build-performance.m4
index 863875065a6..fd77fe8c2b0 100644
--- a/common/autoconf/build-performance.m4
+++ b/common/autoconf/build-performance.m4
@@ -217,6 +217,13 @@ AC_DEFUN([BPERF_SETUP_CCACHE],
AC_DEFUN([BPERF_SETUP_CCACHE_USAGE],
[
if test "x$CCACHE" != x; then
+ if test "x$OPENJDK_BUILD_OS" = "xmacosx"; then
+ HAS_BAD_CCACHE=[`$ECHO $CCACHE_VERSION | \
+ $GREP -e '^1\.' -e '^2\.' -e '^3\.0\.' -e '^3\.1\.'`]
+ if test "x$HAS_BAD_CCACHE" != "x"; then
+ AC_MSG_ERROR([On macosx, ccache 3.2 or later is required, found $CCACHE_VERSION])
+ fi
+ fi
if test "x$USE_PRECOMPILED_HEADER" = "x1"; then
HAS_BAD_CCACHE=[`$ECHO $CCACHE_VERSION | \
$GREP -e '^1.*' -e '^2.*' -e '^3\.0.*' -e '^3\.1\.[0123]$'`]
diff --git a/common/autoconf/buildjdk-spec.gmk.in b/common/autoconf/buildjdk-spec.gmk.in
index dba07605a22..ddd388c0f1c 100644
--- a/common/autoconf/buildjdk-spec.gmk.in
+++ b/common/autoconf/buildjdk-spec.gmk.in
@@ -86,73 +86,13 @@ DISABLE_WARNING_PREFIX := @BUILD_CC_DISABLE_WARNING_PREFIX@
# Save speed and disk space by not enabling debug symbols for the buildjdk
ENABLE_DEBUG_SYMBOLS := false
-####################################################
-#
-# Legacy Hotspot support
+# Control wether Hotspot builds gtest tests
+BUILD_GTEST := false
-# Legacy setting: OPT or DBG
-VARIANT := OPT
-# Legacy setting: true or false
-FASTDEBUG := false
-# Legacy setting: debugging the class files?
-DEBUG_CLASSFILES := false
+JVM_VARIANTS := server
# Some users still set EXTRA_*FLAGS on the make command line. Must
# make sure to override that when building buildjdk.
override EXTRA_CFLAGS :=
override EXTRA_CXXFLAGS :=
override EXTRA_LDFLAGS :=
-
-# The HOSTCC/HOSTCXX is Hotspot terminology for the BUILD_CC/BUILD_CXX, i.e. the
-# compiler that produces code that can be run on the build platform.
-HOSTCC := $(BUILD_CC)
-HOSTCXX := $(BUILD_CXX)
-
-# Old name for OPENJDK_TARGET_OS (aix,bsd,hpux,linux,macosx,solaris,windows etc)
-PLATFORM := $(OPENJDK_BUILD_OS)
-# 32 or 64 bit
-ARCH_DATA_MODEL := $(OPENJDK_BUILD_CPU_BITS)
-
-ALT_BOOTDIR := $(BOOT_JDK)
-# Yet another name for arch used for an extra subdir below the jvm lib.
-# Uses i386 and amd64, instead of x86 and x86_64.
-LIBARCH := @OPENJDK_BUILD_CPU_LEGACY_LIB@
-# Set the cpu architecture. Some users still set ARCH on the make command line. Must
-# make sure to override that when building buildjdk.
-override ARCH := $(OPENJDK_BUILD_CPU_ARCH)
-# Legacy setting for building for a 64 bit machine.
-# If yes then this expands to _LP64 := 1
-ifeq ($(OPENJDK_BUILD_CPU_BITS), 64)
- _LP64 := 1
-endif
-
-ALT_OUTPUTDIR := $(HOTSPOT_OUTPUTDIR)
-ALT_EXPORT_PATH := $(HOTSPOT_DIST)
-
-JVM_INTERPRETER := @JVM_INTERPRETER@
-ifeq ($(JVM_INTERPRETER), cpp)
- CC_INTERP=true
-endif
-
-HOTSPOT_MAKE_ARGS := product docs export_product
-
-# Control wether Hotspot builds gtest tests
-BUILD_GTEST := false
-
-USE_PRECOMPILED_HEADER := @USE_PRECOMPILED_HEADER@
-
-# Hotspot expects the variable FULL_DEBUG_SYMBOLS=1/0 to control debug symbols
-# creation.
-FULL_DEBUG_SYMBOLS := 0
-ZIP_DEBUGINFO_FILES := 0
-# Disable stripping
-STRIP_POLICY := none
-
-JVM_VARIANTS := server
-JVM_VARIANT_SERVER := true
-JVM_VARIANT_CLIENT := false
-JVM_VARIANT_MINIMAL1 := false
-JVM_VARIANT_KERNEL := false
-JVM_VARIANT_ZERO := false
-JVM_VARIANT_ZEROSHARK := false
-JVM_VARIANT_CORE := false
diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac
index bb5491ba0b4..4879d05622b 100644
--- a/common/autoconf/configure.ac
+++ b/common/autoconf/configure.ac
@@ -182,7 +182,6 @@ TOOLCHAIN_POST_DETECTION
# Finally do some processing after the detection phase
TOOLCHAIN_SETUP_BUILD_COMPILERS
-TOOLCHAIN_SETUP_LEGACY
TOOLCHAIN_MISC_CHECKS
# Setup the JTReg Regression Test Harness.
diff --git a/common/autoconf/flags.m4 b/common/autoconf/flags.m4
index ba1e8dbc4b1..767f6bed510 100644
--- a/common/autoconf/flags.m4
+++ b/common/autoconf/flags.m4
@@ -1378,7 +1378,7 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_MISC],
AC_MSG_CHECKING([if native warnings are errors])
if test "x$enable_warnings_as_errors" = "xyes"; then
- AC_MSG_RESULT([yes (explicitely set)])
+ AC_MSG_RESULT([yes (explicitly set)])
WARNINGS_AS_ERRORS=true
elif test "x$enable_warnings_as_errors" = "xno"; then
AC_MSG_RESULT([no])
diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh
index 9179032971e..8a665cb09f4 100644
--- a/common/autoconf/generated-configure.sh
+++ b/common/autoconf/generated-configure.sh
@@ -700,8 +700,6 @@ JVM_FEATURES_client
JVM_FEATURES_server
INCLUDE_DTRACE
GCOV_ENABLED
-STRIP_POLICY
-DEBUG_BINARIES
ZIP_EXTERNAL_DEBUG_SYMBOLS
COPY_DEBUG_SYMBOLS
COMPILE_WITH_DEBUG_SYMBOLS
@@ -791,11 +789,6 @@ JTREGEXE
HOTSPOT_TOOLCHAIN_TYPE
USING_BROKEN_SUSE_LD
PACKAGE_PATH
-USE_CLANG
-HOTSPOT_LD
-HOTSPOT_CXX
-HOTSPOT_RC
-HOTSPOT_MT
BUILD_AS
BUILD_LDCXX
BUILD_LD
@@ -1974,8 +1967,8 @@ Optional Features:
--enable-debug set the debug level to fastdebug (shorthand for
--with-debug-level=fastdebug) [disabled]
--enable-headless-only only build headless (no GUI) support [disabled]
- --enable-unlimited-crypto
- Enable unlimited crypto policy [disabled]
+ --disable-unlimited-crypto
+ Disable unlimited crypto policy [enabled]
--disable-keep-packaged-modules
Do not keep packaged modules in jdk image [enable]
--enable-static-build enable static library build [disabled]
@@ -3851,7 +3844,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
# $1 A command line (typically autoconf macro) to execute
-# Test: Is bootjdk explicitely set by command line arguments?
+# Test: Is bootjdk explicitly set by command line arguments?
# Test: Is $JAVA_HOME set?
@@ -4911,7 +4904,7 @@ TOOLCHAIN_DESCRIPTION_xlc="IBM XL C/C++"
# Minimum supported versions, empty means unspecified
TOOLCHAIN_MINIMUM_VERSION_clang="3.2"
TOOLCHAIN_MINIMUM_VERSION_gcc="4.3"
-TOOLCHAIN_MINIMUM_VERSION_microsoft=""
+TOOLCHAIN_MINIMUM_VERSION_microsoft="16.00.30319.01" # VS2010
TOOLCHAIN_MINIMUM_VERSION_solstudio="5.13"
TOOLCHAIN_MINIMUM_VERSION_xlc=""
@@ -4982,10 +4975,6 @@ TOOLCHAIN_MINIMUM_VERSION_xlc=""
# for this, we can only do this after these have been setup.
-# Setup legacy variables that are still needed as alternative ways to refer to
-# parts of the toolchain.
-
-
# Do some additional checks on the detected tools.
@@ -5093,7 +5082,7 @@ VS_SDK_PLATFORM_NAME_2013=
#CUSTOM_AUTOCONF_INCLUDE
# Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1479997904
+DATE_WHEN_GENERATED=1480714260
###############################################################################
#
@@ -15523,7 +15512,7 @@ test -n "$target_alias" &&
;;
esac
- # ..and setup our own variables. (Do this explicitely to facilitate searching)
+ # ..and setup our own variables. (Do this explicitly to facilitate searching)
OPENJDK_BUILD_OS="$VAR_OS"
if test "x$VAR_OS_TYPE" != x; then
OPENJDK_BUILD_OS_TYPE="$VAR_OS_TYPE"
@@ -15662,7 +15651,7 @@ $as_echo "$OPENJDK_BUILD_OS-$OPENJDK_BUILD_CPU" >&6; }
;;
esac
- # ... and setup our own variables. (Do this explicitely to facilitate searching)
+ # ... and setup our own variables. (Do this explicitly to facilitate searching)
OPENJDK_TARGET_OS="$VAR_OS"
if test "x$VAR_OS_TYPE" != x; then
OPENJDK_TARGET_OS_TYPE="$VAR_OS_TYPE"
@@ -24240,7 +24229,7 @@ fi
if test "${enable_unlimited_crypto+set}" = set; then :
enableval=$enable_unlimited_crypto;
else
- enable_unlimited_crypto=no
+ enable_unlimited_crypto=yes
fi
if test "x$enable_unlimited_crypto" = "xyes"; then
@@ -24400,7 +24389,7 @@ fi
as_fn_error $? "Version string contains + but both 'BUILD' and 'OPT' are missing" "$LINENO" 5
fi
# Stop the version part process from setting default values.
- # We still allow them to explicitely override though.
+ # We still allow them to explicitly override though.
NO_DEFAULT_VERSION_PARTS=true
else
as_fn_error $? "--with-version-string fails to parse as a valid version string: $with_version_string" "$LINENO" 5
@@ -24769,7 +24758,7 @@ fi
# we detected something (if so, the path to the jdk is in BOOT_JDK). But we
# must check if this is indeed valid; otherwise we'll continue looking.
- # Test: Is bootjdk explicitely set by command line arguments?
+ # Test: Is bootjdk explicitly set by command line arguments?
if test "x$BOOT_JDK_FOUND" = xno; then
# Now execute the test
@@ -29887,8 +29876,8 @@ $as_echo "$BOOT_JDK_VERSION" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: Could not find a valid Boot JDK. $HELP_MSG" >&5
$as_echo "$as_me: Could not find a valid Boot JDK. $HELP_MSG" >&6;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Cannot continue" "$LINENO" 5
fi
@@ -29910,8 +29899,8 @@ $as_echo_n "checking for java in Boot JDK... " >&6; }
if test ! -x $JAVA; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find java in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -29938,8 +29927,8 @@ $as_echo_n "checking for java in Boot JDK... " >&6; }
if test ! -x $JAVA; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find java in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30048,8 +30037,8 @@ $as_echo_n "checking for javac in Boot JDK... " >&6; }
if test ! -x $JAVAC; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find javac in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30076,8 +30065,8 @@ $as_echo_n "checking for javac in Boot JDK... " >&6; }
if test ! -x $JAVAC; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find javac in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30186,8 +30175,8 @@ $as_echo_n "checking for javah in Boot JDK... " >&6; }
if test ! -x $JAVAH; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find javah in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30214,8 +30203,8 @@ $as_echo_n "checking for javah in Boot JDK... " >&6; }
if test ! -x $JAVAH; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find javah in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30324,8 +30313,8 @@ $as_echo_n "checking for jar in Boot JDK... " >&6; }
if test ! -x $JAR; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find jar in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30352,8 +30341,8 @@ $as_echo_n "checking for jar in Boot JDK... " >&6; }
if test ! -x $JAR; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find jar in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30462,8 +30451,8 @@ $as_echo_n "checking for jarsigner in Boot JDK... " >&6; }
if test ! -x $JARSIGNER; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find jarsigner in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -30490,8 +30479,8 @@ $as_echo_n "checking for jarsigner in Boot JDK... " >&6; }
if test ! -x $JARSIGNER; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
$as_echo "not found" >&6; }
- { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&5
-$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitely setting --with-boot-jdk" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&5
+$as_echo "$as_me: Your Boot JDK seems broken. This might be fixed by explicitly setting --with-boot-jdk" >&6;}
as_fn_error $? "Could not find jarsigner in the Boot JDK" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
@@ -31198,6 +31187,12 @@ $as_echo "$as_me: The path of IMPORT_MODULES_TOPDIR, which resolves as \"$path\"
if test -d "$IMPORT_MODULES_TOPDIR/modules_src"; then
IMPORT_MODULES_SRC="$IMPORT_MODULES_TOPDIR/modules_src"
fi
+ # Workaround for using different imported module-info.java in Jake due to a
+ # change in format. Remove once new format is standard in JDK 9 and javafx
+ # delivers just that.
+ if test -d "$IMPORT_MODULES_TOPDIR/modules_src_jake"; then
+ IMPORT_MODULES_SRC="$IMPORT_MODULES_TOPDIR/modules_src_jake $IMPORT_MODULES_SRC"
+ fi
if test -d "$IMPORT_MODULES_TOPDIR/make"; then
IMPORT_MODULES_MAKE="$IMPORT_MODULES_TOPDIR/make"
fi
@@ -33578,9 +33573,11 @@ $as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped comp
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
# cc -V output typically looks like
# cc: Sun C 5.12 Linux_i386 2011/11/16
+ # or
+ # cc: Studio 12.5 Sun C 5.14 SunOS_sparc 2016/05/31
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
# Check that this is likely to be the Solaris Studio cc.
- $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
+ $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.* Sun $COMPILER_NAME" > /dev/null
if test $? -ne 0; then
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
@@ -34875,9 +34872,11 @@ $as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped comp
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
# cc -V output typically looks like
# cc: Sun C 5.12 Linux_i386 2011/11/16
+ # or
+ # cc: Studio 12.5 Sun C 5.14 SunOS_sparc 2016/05/31
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
# Check that this is likely to be the Solaris Studio cc.
- $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
+ $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.* Sun $COMPILER_NAME" > /dev/null
if test $? -ne 0; then
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
@@ -35261,9 +35260,9 @@ $as_echo "$as_me: WARNING: This typically indicates a broken setup, and is not s
fi
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
- if [[ "[$]CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
-$as_echo "$as_me: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
+ if [[ "[$]CC_VERSION_NUMBER" =~ (.*\.){4} ]] ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
+$as_echo "$as_me: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
if [[ "[$]CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
@@ -35271,7 +35270,7 @@ $as_echo "$as_me: WARNING: C compiler version number has more than three parts (
$as_echo "$as_me: WARNING: C compiler version number has a part larger than 99999: $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
- COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$CC_VERSION_NUMBER"`
+ COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$CC_VERSION_NUMBER"`
if test "x$TOOLCHAIN_MINIMUM_VERSION" != x; then
@@ -35329,8 +35328,8 @@ $as_echo "$as_me: WARNING: C compiler version number has a part larger than 9999
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=$TOOLCHAIN_MINIMUM_VERSION
- if [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ; then
- as_fn_error $? "Internal error: Cannot compare to $TOOLCHAIN_MINIMUM_VERSION, only three parts (X.Y.Z) is supported" "$LINENO" 5
+ if [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ; then
+ as_fn_error $? "Internal error: Cannot compare to $TOOLCHAIN_MINIMUM_VERSION, only four parts (W.X.Y.Z) is supported" "$LINENO" 5
fi
if [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ; then
@@ -35338,7 +35337,7 @@ $as_echo "$as_me: WARNING: C compiler version number has a part larger than 9999
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$REFERENCE_VERSION"`
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -46956,9 +46955,11 @@ $as_echo "$as_me: Rewriting BUILD_STRIP to \"$new_complete\"" >&6;}
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
# cc -V output typically looks like
# cc: Sun C 5.12 Linux_i386 2011/11/16
+ # or
+ # cc: Studio 12.5 Sun C 5.14 SunOS_sparc 2016/05/31
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
# Check that this is likely to be the Solaris Studio cc.
- $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
+ $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.* Sun $COMPILER_NAME" > /dev/null
if test $? -ne 0; then
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
@@ -47076,9 +47077,11 @@ $as_echo "$as_me: Using $TOOLCHAIN_TYPE $COMPILER_NAME compiler version $COMPILE
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
# cc -V output typically looks like
# cc: Sun C 5.12 Linux_i386 2011/11/16
+ # or
+ # cc: Studio 12.5 Sun C 5.14 SunOS_sparc 2016/05/31
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
# Check that this is likely to be the Solaris Studio cc.
- $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
+ $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.* Sun $COMPILER_NAME" > /dev/null
if test $? -ne 0; then
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
@@ -47198,9 +47201,9 @@ $as_echo "$as_me: WARNING: This typically indicates a broken setup, and is not s
fi
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
- if [[ "[$]BUILD_CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than three parts (X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&5
-$as_echo "$as_me: WARNING: C compiler version number has more than three parts (X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
+ if [[ "[$]BUILD_CC_VERSION_NUMBER" =~ (.*\.){4} ]] ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&5
+$as_echo "$as_me: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
if [[ "[$]BUILD_CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
@@ -47208,7 +47211,7 @@ $as_echo "$as_me: WARNING: C compiler version number has more than three parts (
$as_echo "$as_me: WARNING: C compiler version number has a part larger than 99999: $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
- OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$BUILD_CC_VERSION_NUMBER"`
+ OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$BUILD_CC_VERSION_NUMBER"`
else
# If we are not cross compiling, use the normal target compilers for
@@ -47234,9 +47237,9 @@ $as_echo "$as_me: WARNING: This typically indicates a broken setup, and is not s
fi
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
- if [[ "[$]CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
-$as_echo "$as_me: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
+ if [[ "[$]CC_VERSION_NUMBER" =~ (.*\.){4} ]] ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
+$as_echo "$as_me: WARNING: C compiler version number has more than four parts (W.X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
if [[ "[$]CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
@@ -47244,7 +47247,7 @@ $as_echo "$as_me: WARNING: C compiler version number has more than three parts (
$as_echo "$as_me: WARNING: C compiler version number has a part larger than 99999: $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
fi
- OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$CC_VERSION_NUMBER"`
+ OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$CC_VERSION_NUMBER"`
fi
@@ -47259,68 +47262,6 @@ $as_echo "$as_me: WARNING: C compiler version number has a part larger than 9999
- if test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
- # For hotspot, we need these in Windows mixed path,
- # so rewrite them all. Need added .exe suffix.
- HOTSPOT_CXX="$CXX.exe"
- HOTSPOT_LD="$LD.exe"
- HOTSPOT_MT="$MT.exe"
- HOTSPOT_RC="$RC.exe"
-
- unix_path="$HOTSPOT_CXX"
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
- windows_path=`$CYGPATH -m "$unix_path"`
- HOTSPOT_CXX="$windows_path"
- elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
- windows_path=`cmd //c echo $unix_path`
- HOTSPOT_CXX="$windows_path"
- fi
-
-
- unix_path="$HOTSPOT_LD"
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
- windows_path=`$CYGPATH -m "$unix_path"`
- HOTSPOT_LD="$windows_path"
- elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
- windows_path=`cmd //c echo $unix_path`
- HOTSPOT_LD="$windows_path"
- fi
-
-
- unix_path="$HOTSPOT_MT"
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
- windows_path=`$CYGPATH -m "$unix_path"`
- HOTSPOT_MT="$windows_path"
- elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
- windows_path=`cmd //c echo $unix_path`
- HOTSPOT_MT="$windows_path"
- fi
-
-
- unix_path="$HOTSPOT_RC"
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
- windows_path=`$CYGPATH -m "$unix_path"`
- HOTSPOT_RC="$windows_path"
- elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
- windows_path=`cmd //c echo $unix_path`
- HOTSPOT_RC="$windows_path"
- fi
-
-
-
- else
- HOTSPOT_CXX="$CXX"
- HOTSPOT_LD="$LD"
- fi
-
-
-
- if test "x$TOOLCHAIN_TYPE" = xclang; then
- USE_CLANG=true
- fi
-
-
-
# The package path is used only on macosx?
@@ -49914,8 +49855,8 @@ $as_echo "$supports" >&6; }
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=6
- if [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ; then
- as_fn_error $? "Internal error: Cannot compare to 6, only three parts (X.Y.Z) is supported" "$LINENO" 5
+ if [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ; then
+ as_fn_error $? "Internal error: Cannot compare to 6, only four parts (W.X.Y.Z) is supported" "$LINENO" 5
fi
if [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ; then
@@ -49923,7 +49864,7 @@ $as_echo "$supports" >&6; }
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$REFERENCE_VERSION"`
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -50214,8 +50155,8 @@ $as_echo "$as_me: GCC >= 6 detected; adding ${NO_DELETE_NULL_POINTER_CHECKS_CFLA
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=4.8
- if [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ; then
- as_fn_error $? "Internal error: Cannot compare to 4.8, only three parts (X.Y.Z) is supported" "$LINENO" 5
+ if [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ; then
+ as_fn_error $? "Internal error: Cannot compare to 4.8, only four parts (W.X.Y.Z) is supported" "$LINENO" 5
fi
if [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ; then
@@ -50223,7 +50164,7 @@ $as_echo "$as_me: GCC >= 6 detected; adding ${NO_DELETE_NULL_POINTER_CHECKS_CFLA
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$REFERENCE_VERSION"`
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -50737,8 +50678,8 @@ $as_echo "$supports" >&6; }
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=6
- if [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ; then
- as_fn_error $? "Internal error: Cannot compare to 6, only three parts (X.Y.Z) is supported" "$LINENO" 5
+ if [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ; then
+ as_fn_error $? "Internal error: Cannot compare to 6, only four parts (W.X.Y.Z) is supported" "$LINENO" 5
fi
if [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ; then
@@ -50746,7 +50687,7 @@ $as_echo "$supports" >&6; }
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$REFERENCE_VERSION"`
if test $OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -51037,8 +50978,8 @@ $as_echo "$as_me: GCC >= 6 detected; adding ${NO_DELETE_NULL_POINTER_CHECKS_CFLA
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=4.8
- if [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ; then
- as_fn_error $? "Internal error: Cannot compare to 4.8, only three parts (X.Y.Z) is supported" "$LINENO" 5
+ if [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ; then
+ as_fn_error $? "Internal error: Cannot compare to 4.8, only four parts (W.X.Y.Z) is supported" "$LINENO" 5
fi
if [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ; then
@@ -51046,7 +50987,7 @@ $as_echo "$as_me: GCC >= 6 detected; adding ${NO_DELETE_NULL_POINTER_CHECKS_CFLA
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", $1, $2, $3, $4) }' <<< "$REFERENCE_VERSION"`
if test $OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -51917,8 +51858,8 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if native warnings are errors" >&5
$as_echo_n "checking if native warnings are errors... " >&6; }
if test "x$enable_warnings_as_errors" = "xyes"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (explicitely set)" >&5
-$as_echo "yes (explicitely set)" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (explicitly set)" >&5
+$as_echo "yes (explicitly set)" >&6; }
WARNINGS_AS_ERRORS=true
elif test "x$enable_warnings_as_errors" = "xno"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
@@ -52583,28 +52524,14 @@ $as_echo "$NATIVE_DEBUG_SYMBOLS" >&6; }
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=true
ZIP_EXTERNAL_DEBUG_SYMBOLS=true
-
- # Hotspot legacy support, not relevant with COPY_DEBUG_SYMBOLS=true
- DEBUG_BINARIES=false
- STRIP_POLICY=min_strip
-
elif test "x$NATIVE_DEBUG_SYMBOLS" = xnone; then
COMPILE_WITH_DEBUG_SYMBOLS=false
COPY_DEBUG_SYMBOLS=false
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- DEBUG_BINARIES=false
- STRIP_POLICY=no_strip
elif test "x$NATIVE_DEBUG_SYMBOLS" = xinternal; then
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=false
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- # Hotspot legacy support, will turn on -g when COPY_DEBUG_SYMBOLS=false
- DEBUG_BINARIES=true
- STRIP_POLICY=no_strip
- STRIP=""
-
elif test "x$NATIVE_DEBUG_SYMBOLS" = xexternal; then
if test "x$OPENJDK_TARGET_OS" = xsolaris || test "x$OPENJDK_TARGET_OS" = xlinux; then
@@ -52618,10 +52545,6 @@ $as_echo "$NATIVE_DEBUG_SYMBOLS" >&6; }
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=true
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- # Hotspot legacy support, not relevant with COPY_DEBUG_SYMBOLS=true
- DEBUG_BINARIES=false
- STRIP_POLICY=min_strip
else
as_fn_error $? "Allowed native debug symbols are: none, internal, external, zipped" "$LINENO" 5
fi
@@ -52670,10 +52593,6 @@ $as_echo "$as_me: WARNING: Please use --with-native-debug-symbols=zipped ." >&2;
- # Legacy values
-
-
-
# Check whether --enable-native-coverage was given.
if test "${enable_native_coverage+set}" = set; then :
@@ -53195,7 +53114,7 @@ fi
if test "x$with_msvcr_dll" != x; then
- # If given explicitely by user, do not probe. If not present, fail directly.
+ # If given explicitly by user, do not probe. If not present, fail directly.
DLL_NAME="$MSVCR_NAME"
POSSIBLE_MSVC_DLL="$with_msvcr_dll"
@@ -54544,7 +54463,7 @@ fi
if test "x$MSVCP_NAME" != "x"; then
if test "x$with_msvcp_dll" != x; then
- # If given explicitely by user, do not probe. If not present, fail directly.
+ # If given explicitly by user, do not probe. If not present, fail directly.
DLL_NAME="$MSVCP_NAME"
POSSIBLE_MSVC_DLL="$with_msvcp_dll"
@@ -55903,7 +55822,7 @@ $as_echo "$as_me: WARNING: X11 is not used, so --with-x is ignored" >&2;}
if test "x${with_x}" != x && test "x${with_x}" != xyes; then
# The user has specified a X11 base directory. Use it for includes and
- # libraries, unless explicitely overridden.
+ # libraries, unless explicitly overridden.
if test "x$x_includes" = xNONE; then
x_includes="${with_x}/include"
fi
@@ -65525,6 +65444,13 @@ $as_echo "$as_me: WARNING: --with-ccache-dir has no meaning when ccache is not e
if test "x$CCACHE" != x; then
if test "x$CCACHE" != x; then
+ if test "x$OPENJDK_BUILD_OS" = "xmacosx"; then
+ HAS_BAD_CCACHE=`$ECHO $CCACHE_VERSION | \
+ $GREP -e '^1\.' -e '^2\.' -e '^3\.0\.' -e '^3\.1\.'`
+ if test "x$HAS_BAD_CCACHE" != "x"; then
+ as_fn_error $? "On macosx, ccache 3.2 or later is required, found $CCACHE_VERSION" "$LINENO" 5
+ fi
+ fi
if test "x$USE_PRECOMPILED_HEADER" = "x1"; then
HAS_BAD_CCACHE=`$ECHO $CCACHE_VERSION | \
$GREP -e '^1.*' -e '^2.*' -e '^3\.0.*' -e '^3\.1\.[0123]$'`
diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4
index 8becdf3669c..acc0dccf8c4 100644
--- a/common/autoconf/jdk-options.m4
+++ b/common/autoconf/jdk-options.m4
@@ -163,9 +163,9 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS],
AC_SUBST(CACERTS_FILE)
# Enable or disable unlimited crypto
- AC_ARG_ENABLE(unlimited-crypto, [AS_HELP_STRING([--enable-unlimited-crypto],
- [Enable unlimited crypto policy @<:@disabled@:>@])],,
- [enable_unlimited_crypto=no])
+ AC_ARG_ENABLE(unlimited-crypto, [AS_HELP_STRING([--disable-unlimited-crypto],
+ [Disable unlimited crypto policy @<:@enabled@:>@])],,
+ [enable_unlimited_crypto=yes])
if test "x$enable_unlimited_crypto" = "xyes"; then
UNLIMITED_CRYPTO=true
else
@@ -265,28 +265,14 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS],
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=true
ZIP_EXTERNAL_DEBUG_SYMBOLS=true
-
- # Hotspot legacy support, not relevant with COPY_DEBUG_SYMBOLS=true
- DEBUG_BINARIES=false
- STRIP_POLICY=min_strip
-
elif test "x$NATIVE_DEBUG_SYMBOLS" = xnone; then
COMPILE_WITH_DEBUG_SYMBOLS=false
COPY_DEBUG_SYMBOLS=false
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- DEBUG_BINARIES=false
- STRIP_POLICY=no_strip
elif test "x$NATIVE_DEBUG_SYMBOLS" = xinternal; then
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=false
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- # Hotspot legacy support, will turn on -g when COPY_DEBUG_SYMBOLS=false
- DEBUG_BINARIES=true
- STRIP_POLICY=no_strip
- STRIP=""
-
elif test "x$NATIVE_DEBUG_SYMBOLS" = xexternal; then
if test "x$OPENJDK_TARGET_OS" = xsolaris || test "x$OPENJDK_TARGET_OS" = xlinux; then
@@ -300,10 +286,6 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS],
COMPILE_WITH_DEBUG_SYMBOLS=true
COPY_DEBUG_SYMBOLS=true
ZIP_EXTERNAL_DEBUG_SYMBOLS=false
-
- # Hotspot legacy support, not relevant with COPY_DEBUG_SYMBOLS=true
- DEBUG_BINARIES=false
- STRIP_POLICY=min_strip
else
AC_MSG_ERROR([Allowed native debug symbols are: none, internal, external, zipped])
fi
@@ -321,10 +303,6 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS],
AC_SUBST(COMPILE_WITH_DEBUG_SYMBOLS)
AC_SUBST(COPY_DEBUG_SYMBOLS)
AC_SUBST(ZIP_EXTERNAL_DEBUG_SYMBOLS)
-
- # Legacy values
- AC_SUBST(DEBUG_BINARIES)
- AC_SUBST(STRIP_POLICY)
])
################################################################################
diff --git a/common/autoconf/jdk-version.m4 b/common/autoconf/jdk-version.m4
index 4846c12647c..65df55e5690 100644
--- a/common/autoconf/jdk-version.m4
+++ b/common/autoconf/jdk-version.m4
@@ -110,7 +110,7 @@ AC_DEFUN_ONCE([JDKVER_SETUP_JDK_VERSION_NUMBERS],
AC_MSG_ERROR([Version string contains + but both 'BUILD' and 'OPT' are missing])
fi
# Stop the version part process from setting default values.
- # We still allow them to explicitely override though.
+ # We still allow them to explicitly override though.
NO_DEFAULT_VERSION_PARTS=true
else
AC_MSG_ERROR([--with-version-string fails to parse as a valid version string: $with_version_string])
diff --git a/common/autoconf/lib-x11.m4 b/common/autoconf/lib-x11.m4
index 0614299e849..d4b878d8c54 100644
--- a/common/autoconf/lib-x11.m4
+++ b/common/autoconf/lib-x11.m4
@@ -42,7 +42,7 @@ AC_DEFUN_ONCE([LIB_SETUP_X11],
if test "x${with_x}" != x && test "x${with_x}" != xyes; then
# The user has specified a X11 base directory. Use it for includes and
- # libraries, unless explicitely overridden.
+ # libraries, unless explicitly overridden.
if test "x$x_includes" = xNONE; then
x_includes="${with_x}/include"
fi
diff --git a/common/autoconf/platform.m4 b/common/autoconf/platform.m4
index e7ffe4a2fa5..392a23ad4cd 100644
--- a/common/autoconf/platform.m4
+++ b/common/autoconf/platform.m4
@@ -162,7 +162,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD],
# Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU variables.
PLATFORM_EXTRACT_VARS_FROM_OS($build_os)
PLATFORM_EXTRACT_VARS_FROM_CPU($build_cpu)
- # ..and setup our own variables. (Do this explicitely to facilitate searching)
+ # ..and setup our own variables. (Do this explicitly to facilitate searching)
OPENJDK_BUILD_OS="$VAR_OS"
if test "x$VAR_OS_TYPE" != x; then
OPENJDK_BUILD_OS_TYPE="$VAR_OS_TYPE"
@@ -192,7 +192,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD],
# Convert the autoconf OS/CPU value to our own data, into the VAR_OS/CPU variables.
PLATFORM_EXTRACT_VARS_FROM_OS($host_os)
PLATFORM_EXTRACT_VARS_FROM_CPU($host_cpu)
- # ... and setup our own variables. (Do this explicitely to facilitate searching)
+ # ... and setup our own variables. (Do this explicitly to facilitate searching)
OPENJDK_TARGET_OS="$VAR_OS"
if test "x$VAR_OS_TYPE" != x; then
OPENJDK_TARGET_OS_TYPE="$VAR_OS_TYPE"
diff --git a/common/autoconf/source-dirs.m4 b/common/autoconf/source-dirs.m4
index 940707e81a0..81137cf95ff 100644
--- a/common/autoconf/source-dirs.m4
+++ b/common/autoconf/source-dirs.m4
@@ -126,6 +126,12 @@ AC_DEFUN_ONCE([SRCDIRS_SETUP_IMPORT_MODULES],
if test -d "$IMPORT_MODULES_TOPDIR/modules_src"; then
IMPORT_MODULES_SRC="$IMPORT_MODULES_TOPDIR/modules_src"
fi
+ # Workaround for using different imported module-info.java in Jake due to a
+ # change in format. Remove once new format is standard in JDK 9 and javafx
+ # delivers just that.
+ if test -d "$IMPORT_MODULES_TOPDIR/modules_src_jake"; then
+ IMPORT_MODULES_SRC="$IMPORT_MODULES_TOPDIR/modules_src_jake $IMPORT_MODULES_SRC"
+ fi
if test -d "$IMPORT_MODULES_TOPDIR/make"; then
IMPORT_MODULES_MAKE="$IMPORT_MODULES_TOPDIR/make"
fi
diff --git a/common/autoconf/toolchain.m4 b/common/autoconf/toolchain.m4
index db3d39e2655..16b0df04b4f 100644
--- a/common/autoconf/toolchain.m4
+++ b/common/autoconf/toolchain.m4
@@ -53,7 +53,7 @@ TOOLCHAIN_DESCRIPTION_xlc="IBM XL C/C++"
# Minimum supported versions, empty means unspecified
TOOLCHAIN_MINIMUM_VERSION_clang="3.2"
TOOLCHAIN_MINIMUM_VERSION_gcc="4.3"
-TOOLCHAIN_MINIMUM_VERSION_microsoft=""
+TOOLCHAIN_MINIMUM_VERSION_microsoft="16.00.30319.01" # VS2010
TOOLCHAIN_MINIMUM_VERSION_solstudio="5.13"
TOOLCHAIN_MINIMUM_VERSION_xlc=""
@@ -69,15 +69,15 @@ AC_DEFUN([TOOLCHAIN_PREPARE_FOR_VERSION_COMPARISONS],
fi
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
- if [ [[ "[$]$1CC_VERSION_NUMBER" =~ (.*\.){3} ]] ]; then
- AC_MSG_WARN([C compiler version number has more than three parts (X.Y.Z): [$]$1CC_VERSION_NUMBER. Comparisons might be wrong.])
+ if [ [[ "[$]$1CC_VERSION_NUMBER" =~ (.*\.){4} ]] ]; then
+ AC_MSG_WARN([C compiler version number has more than four parts (W.X.Y.Z): [$]$1CC_VERSION_NUMBER. Comparisons might be wrong.])
fi
if [ [[ "[$]$1CC_VERSION_NUMBER" =~ [0-9]{6} ]] ]; then
AC_MSG_WARN([C compiler version number has a part larger than 99999: [$]$1CC_VERSION_NUMBER. Comparisons might be wrong.])
fi
- $2COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", [$]1, [$]2, [$]3) }' <<< "[$]$1CC_VERSION_NUMBER"`
+ $2COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", [$]1, [$]2, [$]3, [$]4) }' <<< "[$]$1CC_VERSION_NUMBER"`
])
# Check if the configured compiler (C and C++) is of a specific version or
@@ -94,8 +94,8 @@ BASIC_DEFUN_NAMED([TOOLCHAIN_CHECK_COMPILER_VERSION],
# Need to assign to a variable since m4 is blocked from modifying parts in [].
REFERENCE_VERSION=ARG_VERSION
- if [ [[ "$REFERENCE_VERSION" =~ (.*\.){3} ]] ]; then
- AC_MSG_ERROR([Internal error: Cannot compare to ARG_VERSION, only three parts (X.Y.Z) is supported])
+ if [ [[ "$REFERENCE_VERSION" =~ (.*\.){4} ]] ]; then
+ AC_MSG_ERROR([Internal error: Cannot compare to ARG_VERSION, only four parts (W.X.Y.Z) is supported])
fi
if [ [[ "$REFERENCE_VERSION" =~ [0-9]{6} ]] ]; then
@@ -103,7 +103,7 @@ BASIC_DEFUN_NAMED([TOOLCHAIN_CHECK_COMPILER_VERSION],
fi
# Version comparison method inspired by http://stackoverflow.com/a/24067243
- COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", [$]1, [$]2, [$]3) }' <<< "$REFERENCE_VERSION"`
+ COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d%05d\n", [$]1, [$]2, [$]3, [$]4) }' <<< "$REFERENCE_VERSION"`
if test [$]ARG_PREFIX[COMPARABLE_ACTUAL_VERSION] -ge $COMPARABLE_REFERENCE_VERSION ; then
:
@@ -333,9 +333,11 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION],
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
# cc -V output typically looks like
# cc: Sun C 5.12 Linux_i386 2011/11/16
+ # or
+ # cc: Studio 12.5 Sun C 5.14 SunOS_sparc 2016/05/31
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
# Check that this is likely to be the Solaris Studio cc.
- $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
+ $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.* Sun $COMPILER_NAME" > /dev/null
if test $? -ne 0; then
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
AC_MSG_NOTICE([The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler.])
@@ -827,7 +829,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_BUILD_COMPILERS],
BUILD_SYSROOT_CFLAGS="$SYSROOT_CFLAGS"
BUILD_SYSROOT_LDFLAGS="$SYSROOT_LDFLAGS"
BUILD_AR="$AR"
-
+
TOOLCHAIN_PREPARE_FOR_VERSION_COMPARISONS([], [OPENJDK_BUILD_])
fi
@@ -842,36 +844,6 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_BUILD_COMPILERS],
AC_SUBST(BUILD_AR)
])
-# Setup legacy variables that are still needed as alternative ways to refer to
-# parts of the toolchain.
-AC_DEFUN_ONCE([TOOLCHAIN_SETUP_LEGACY],
-[
- if test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
- # For hotspot, we need these in Windows mixed path,
- # so rewrite them all. Need added .exe suffix.
- HOTSPOT_CXX="$CXX.exe"
- HOTSPOT_LD="$LD.exe"
- HOTSPOT_MT="$MT.exe"
- HOTSPOT_RC="$RC.exe"
- BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(HOTSPOT_CXX)
- BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(HOTSPOT_LD)
- BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(HOTSPOT_MT)
- BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(HOTSPOT_RC)
- AC_SUBST(HOTSPOT_MT)
- AC_SUBST(HOTSPOT_RC)
- else
- HOTSPOT_CXX="$CXX"
- HOTSPOT_LD="$LD"
- fi
- AC_SUBST(HOTSPOT_CXX)
- AC_SUBST(HOTSPOT_LD)
-
- if test "x$TOOLCHAIN_TYPE" = xclang; then
- USE_CLANG=true
- fi
- AC_SUBST(USE_CLANG)
-])
-
# Do some additional checks on the detected tools.
AC_DEFUN_ONCE([TOOLCHAIN_MISC_CHECKS],
[
diff --git a/common/autoconf/toolchain_windows.m4 b/common/autoconf/toolchain_windows.m4
index 4a40fa63a91..f2304df0fe9 100644
--- a/common/autoconf/toolchain_windows.m4
+++ b/common/autoconf/toolchain_windows.m4
@@ -566,7 +566,7 @@ AC_DEFUN([TOOLCHAIN_SETUP_VS_RUNTIME_DLLS],
[path to microsoft C runtime dll (msvcr*.dll) (Windows only) @<:@probed@:>@])])
if test "x$with_msvcr_dll" != x; then
- # If given explicitely by user, do not probe. If not present, fail directly.
+ # If given explicitly by user, do not probe. If not present, fail directly.
TOOLCHAIN_CHECK_POSSIBLE_MSVC_DLL($MSVCR_NAME, [$with_msvcr_dll], [--with-msvcr-dll])
if test "x$MSVC_DLL" = x; then
AC_MSG_ERROR([Could not find a proper $MSVCR_NAME as specified by --with-msvcr-dll])
@@ -589,7 +589,7 @@ AC_DEFUN([TOOLCHAIN_SETUP_VS_RUNTIME_DLLS],
if test "x$MSVCP_NAME" != "x"; then
if test "x$with_msvcp_dll" != x; then
- # If given explicitely by user, do not probe. If not present, fail directly.
+ # If given explicitly by user, do not probe. If not present, fail directly.
TOOLCHAIN_CHECK_POSSIBLE_MSVC_DLL($MSVCP_NAME, [$with_msvcp_dll], [--with-msvcp-dll])
if test "x$MSVC_DLL" = x; then
AC_MSG_ERROR([Could not find a proper $MSVCP_NAME as specified by --with-msvcp-dll])
diff --git a/common/bin/compare.sh b/common/bin/compare.sh
index ba60485ea53..de909e0154c 100644
--- a/common/bin/compare.sh
+++ b/common/bin/compare.sh
@@ -1295,8 +1295,8 @@ if [ "$SKIP_DEFAULT" != "true" ]; then
OTHER_JDK="$OTHER/images/jdk"
OTHER_JRE="$OTHER/images/jre"
echo "Selecting jdk images for compare"
- elif [ -d "$(ls -d $THIS/licensee-src/build/*/images/jdk)" ] \
- && [ -d "$(ls -d $OTHER/licensee-src/build/*/images/jdk)" ]
+ elif [ -d "$(ls -d $THIS/licensee-src/build/*/images/jdk 2> /dev/null)" ] \
+ && [ -d "$(ls -d $OTHER/licensee-src/build/*/images/jdk 2> /dev/null)" ]
then
echo "Selecting licensee images for compare"
# Simply override the THIS and OTHER dir with the build dir from
diff --git a/common/conf/jib-profiles.js b/common/conf/jib-profiles.js
index fa3c5749f8c..a40c8b05eef 100644
--- a/common/conf/jib-profiles.js
+++ b/common/conf/jib-profiles.js
@@ -427,7 +427,7 @@ var getJibProfilesDependencies = function (input, common) {
jtreg: {
server: "javare",
revision: "4.2",
- build_number: "b03",
+ build_number: "b04",
checksum_file: "MD5_VALUES",
file: "jtreg_bin-4.2.zip",
environment_name: "JT_HOME",
diff --git a/hotspot/.hgignore b/hotspot/.hgignore
index f7628dd32dc..d3a43d0999e 100644
--- a/hotspot/.hgignore
+++ b/hotspot/.hgignore
@@ -8,7 +8,6 @@
^src/share/tools/IdealGraphVisualizer/dist/
^src/share/tools/IdealGraphVisualizer/nbplatform/
.igv.log
-^.hgtip
.DS_Store
^\.mx.jvmci/env
^\.mx.jvmci/.*\.pyc
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index c7fc0f3084c..4070057d7fa 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -549,3 +549,4 @@ d87d5d430c42342f0320ca7f5cbe0cbd1f9d62ba jdk-9+143
6187b582d02aee38341dc8ce4011906e9b364e9f jdk-9+144
61e7ea56312351657e69198c503a6f7bf865af83 jdk-9+145
a82cb5350cad96a0b4de496afebe3ded89f27efa jdk-9+146
+132a72c782071cc11ab25cc7c9ee167c3632fea4 jdk-9+147
diff --git a/hotspot/make/symbols/symbols-unix b/hotspot/make/symbols/symbols-unix
index cee0c5f71a8..90326d263dc 100644
--- a/hotspot/make/symbols/symbols-unix
+++ b/hotspot/make/symbols/symbols-unix
@@ -190,8 +190,6 @@ JVM_AddModuleExportsToAll
JVM_AddModuleExportsToAllUnnamed
JVM_AddModulePackage
JVM_AddReadsModule
-JVM_CanReadModule
JVM_DefineModule
-JVM_IsExportedToModule
JVM_SetBootLoaderUnnamedModule
JVM_GetModuleByPackageName
diff --git a/hotspot/make/test/JtregNative.gmk b/hotspot/make/test/JtregNative.gmk
index 78e78d774e5..cdead40d6d1 100644
--- a/hotspot/make/test/JtregNative.gmk
+++ b/hotspot/make/test/JtregNative.gmk
@@ -55,6 +55,9 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC := \
$(HOTSPOT_TOPDIR)/test/compiler/calls \
$(HOTSPOT_TOPDIR)/test/compiler/native \
$(HOTSPOT_TOPDIR)/test/serviceability/jvmti/GetNamedModule \
+ $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleReads \
+ $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleExportsAndOpens \
+ $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleUsesAndProvides \
$(HOTSPOT_TOPDIR)/test/testlibrary/jvmti \
$(HOTSPOT_TOPDIR)/test/compiler/jvmci/jdk.vm.ci.code.test \
$(HOTSPOT_TOPDIR)/test/serviceability/jvmti/GetModulesInfo \
@@ -81,6 +84,9 @@ ifeq ($(TOOLCHAIN_TYPE), solstudio)
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_liboverflow := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libSimpleClassFileLoadHook := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libGetNamedModuleTest := -lc
+ BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleReadsTest := -lc
+ BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleExportsAndOpensTest := -lc
+ BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleUsesAndProvidesTest := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libMAAClassFileLoadHook := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libMAAClassLoadPrepare := -lc
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libMAAThreadStart := -lc
diff --git a/hotspot/src/jdk.vm.ci/share/classes/module-info.java b/hotspot/src/jdk.vm.ci/share/classes/module-info.java
index f1a766e43f5..e62eed3da6b 100644
--- a/hotspot/src/jdk.vm.ci/share/classes/module-info.java
+++ b/hotspot/src/jdk.vm.ci/share/classes/module-info.java
@@ -30,9 +30,7 @@ module jdk.vm.ci {
uses jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory;
provides jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory with
- jdk.vm.ci.hotspot.aarch64.AArch64HotSpotJVMCIBackendFactory;
- provides jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory with
- jdk.vm.ci.hotspot.amd64.AMD64HotSpotJVMCIBackendFactory;
- provides jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory with
+ jdk.vm.ci.hotspot.aarch64.AArch64HotSpotJVMCIBackendFactory,
+ jdk.vm.ci.hotspot.amd64.AMD64HotSpotJVMCIBackendFactory,
jdk.vm.ci.hotspot.sparc.SPARCHotSpotJVMCIBackendFactory;
}
diff --git a/hotspot/src/share/vm/classfile/moduleEntry.hpp b/hotspot/src/share/vm/classfile/moduleEntry.hpp
index 82f0747a2ae..6301d4ed2b3 100644
--- a/hotspot/src/share/vm/classfile/moduleEntry.hpp
+++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp
@@ -36,6 +36,8 @@
#include "utilities/ostream.hpp"
#define UNNAMED_MODULE "Unnamed Module"
+#define JAVAPKG "java/"
+#define JAVAPKG_LEN 5
class ModuleClosure;
diff --git a/hotspot/src/share/vm/classfile/modules.cpp b/hotspot/src/share/vm/classfile/modules.cpp
index a4cdacc6035..9c4aa894f64 100644
--- a/hotspot/src/share/vm/classfile/modules.cpp
+++ b/hotspot/src/share/vm/classfile/modules.cpp
@@ -34,6 +34,7 @@
#include "classfile/packageEntry.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
@@ -44,6 +45,7 @@
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/reflection.hpp"
+#include "utilities/stringUtils.hpp"
#include "utilities/utf8.hpp"
static bool verify_module_name(char *module_name) {
@@ -290,6 +292,14 @@ void Modules::define_module(jobject module, jstring version,
const char* module_version = get_module_version(version);
+ oop loader = java_lang_reflect_Module::loader(module_handle());
+ // Make sure loader is not the jdk.internal.reflect.DelegatingClassLoader.
+ if (loader != java_lang_ClassLoader::non_reflection_class_loader(loader)) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
+ "Class loader is an invalid delegating class loader");
+ }
+ Handle h_loader = Handle(THREAD, loader);
+
objArrayOop packages_oop = objArrayOop(JNIHandles::resolve(packages));
objArrayHandle packages_h(THREAD, packages_oop);
int num_packages = (packages_h == NULL ? 0 : packages_h->length());
@@ -310,6 +320,21 @@ void Modules::define_module(jobject module, jstring version,
err_msg("Invalid package name: %s for module: %s",
package_name, module_name));
}
+
+ // Only modules defined to either the boot or platform class loader, can define a "java/" package.
+ if (!h_loader.is_null() &&
+ !SystemDictionary::is_platform_class_loader(h_loader) &&
+ strncmp(package_name, JAVAPKG, JAVAPKG_LEN) == 0) {
+ const char* class_loader_name = SystemDictionary::loader_name(h_loader());
+ StringUtils::replace_no_expand(package_name, "/", ".");
+ const char* msg_text1 = "Class loader (instance of): ";
+ const char* msg_text2 = " tried to define prohibited package name: ";
+ size_t len = strlen(msg_text1) + strlen(class_loader_name) + strlen(msg_text2) + strlen(package_name) + 1;
+ char* message = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, len);
+ jio_snprintf(message, len, "%s%s%s%s", msg_text1, class_loader_name, msg_text2, package_name);
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), message);
+ }
+
Symbol* pkg_symbol = SymbolTable::new_symbol(package_name, CHECK);
// append_if_missing() returns FALSE if entry already exists.
if (!pkg_list->append_if_missing(pkg_symbol)) {
@@ -319,20 +344,6 @@ void Modules::define_module(jobject module, jstring version,
}
}
- oop loader = java_lang_reflect_Module::loader(module_handle());
- // Make sure loader is not the sun.reflect.DelegatingClassLoader.
- if (loader != java_lang_ClassLoader::non_reflection_class_loader(loader)) {
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
- "Class loader is an invalid delegating class loader");
- }
- Handle h_loader = Handle(THREAD, loader);
-
- // Check that loader is a subclass of java.lang.ClassLoader.
- if (loader != NULL && !java_lang_ClassLoader::is_subclass(h_loader->klass())) {
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
- "Class loader is not a subclass of java.lang.ClassLoader");
- }
-
ModuleEntryTable* module_table = get_module_entry_table(h_loader, CHECK);
assert(module_table != NULL, "module entry table shouldn't be null");
@@ -595,122 +606,6 @@ void Modules::add_reads_module(jobject from_module, jobject to_module, TRAPS) {
}
}
-jboolean Modules::can_read_module(jobject asking_module, jobject target_module, TRAPS) {
- if (asking_module == NULL) {
- THROW_MSG_(vmSymbols::java_lang_NullPointerException(),
- "asking_module is null", JNI_FALSE);
- }
-
- ModuleEntry* asking_module_entry = get_module_entry(asking_module, CHECK_false);
- if (asking_module_entry == NULL) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- "asking_module is invalid", JNI_FALSE);
- }
-
- // Calling can_read_all_unnamed() with NULL tests if a module is loose.
- if (target_module == NULL) {
- return asking_module_entry->can_read_all_unnamed();
- }
-
- ModuleEntry* target_module_entry = get_module_entry(target_module, CHECK_false);
- if (target_module_entry == NULL) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- "target_module is invalid", JNI_FALSE);
- }
-
- ResourceMark rm(THREAD);
- log_debug(modules)("can_read_module(): module %s trying to read module %s, allowed = %s",
- asking_module_entry->is_named() ?
- asking_module_entry->name()->as_C_string() : UNNAMED_MODULE,
- target_module_entry->is_named() ?
- target_module_entry->name()->as_C_string() : UNNAMED_MODULE,
- BOOL_TO_STR(asking_module_entry == target_module_entry ||
- (asking_module_entry->can_read_all_unnamed() &&
- !target_module_entry->is_named()) ||
- asking_module_entry->can_read(target_module_entry)));
-
- // Return true if:
- // 1. the modules are the same, or
- // 2. the asking_module is unnamed (because unnamed modules read everybody), or
- // 3. the asking_module is loose and the target module is unnamed, or
- // 4. if can_read() returns true.
- if (asking_module_entry == target_module_entry ||
- (asking_module_entry->can_read_all_unnamed() && !target_module_entry->is_named())) {
- return true;
- }
- return asking_module_entry->can_read(target_module_entry);
-}
-
-jboolean Modules::is_exported_to_module(jobject from_module, jstring package,
- jobject to_module, TRAPS) {
- if (package == NULL) {
- THROW_MSG_(vmSymbols::java_lang_NullPointerException(),
- "package is null", JNI_FALSE);
- }
- if (from_module == NULL) {
- THROW_MSG_(vmSymbols::java_lang_NullPointerException(),
- "from_module is null", JNI_FALSE);
- }
- ModuleEntry* from_module_entry = get_module_entry(from_module, CHECK_false);
- if (from_module_entry == NULL) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- "from_module is invalid", JNI_FALSE);
- }
- ModuleEntry* to_module_entry;
- if (to_module == NULL) {
- THROW_MSG_(vmSymbols::java_lang_NullPointerException(),
- "to_module is null", JNI_FALSE);
- }
- to_module_entry = get_module_entry(to_module, CHECK_false);
- if (to_module_entry == NULL) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- "to_module is invalid", JNI_FALSE);
- }
-
- PackageEntry *package_entry = get_package_entry(from_module_entry, package,
- CHECK_false);
- ResourceMark rm(THREAD);
- if (package_entry == NULL) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- err_msg("Package not found in from_module: %s",
- from_module_entry->is_named() ?
- from_module_entry->name()->as_C_string() : UNNAMED_MODULE),
- JNI_FALSE);
- }
- if (package_entry->module() != from_module_entry) {
- THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(),
- err_msg("Package: %s found in module %s, not in from_module: %s",
- package_entry->name()->as_C_string(),
- package_entry->module()->is_named() ?
- package_entry->module()->name()->as_C_string() : UNNAMED_MODULE,
- from_module_entry->is_named() ?
- from_module_entry->name()->as_C_string() : UNNAMED_MODULE),
- JNI_FALSE);
- }
-
- log_debug(modules)("is_exported_to_module: package %s from module %s checking"
- " if exported to module %s, exported? = %s",
- package_entry->name()->as_C_string(),
- from_module_entry->is_named() ?
- from_module_entry->name()->as_C_string() : UNNAMED_MODULE,
- to_module_entry->is_named() ?
- to_module_entry->name()->as_C_string() : UNNAMED_MODULE,
- BOOL_TO_STR(!from_module_entry->is_named() ||
- package_entry->is_unqual_exported() ||
- from_module_entry == to_module_entry ||
- package_entry->is_qexported_to(to_module_entry)));
-
- // Return true if:
- // 1. from_module is unnamed because unnamed modules export all their packages (by default), or
- // 2. if the package is unqualifiedly exported, or
- // 3. if the modules are the same, or
- // 4. if the package is exported to to_module
- return (!from_module_entry->is_named() ||
- package_entry->is_unqual_exported() ||
- from_module_entry == to_module_entry ||
- package_entry->is_qexported_to(to_module_entry));
-}
-
// This method is called by JFR and JNI.
jobject Modules::get_module(jclass clazz, TRAPS) {
assert(ModuleEntryTable::javabase_defined(), "Attempt to call get_module before java.base is defined");
@@ -860,11 +755,27 @@ void Modules::add_module_package(jobject module, jstring package, TRAPS) {
err_msg("Invalid package name: %s", package_name));
}
+ ClassLoaderData *loader_data = module_entry->loader_data();
+
+ // Only modules defined to either the boot or platform class loader, can define a "java/" package.
+ if (!loader_data->is_the_null_class_loader_data() &&
+ !loader_data->is_platform_class_loader_data() &&
+ strncmp(package_name, JAVAPKG, JAVAPKG_LEN) == 0) {
+ const char* class_loader_name = SystemDictionary::loader_name(loader_data);
+ StringUtils::replace_no_expand(package_name, "/", ".");
+ const char* msg_text1 = "Class loader (instance of): ";
+ const char* msg_text2 = " tried to define prohibited package name: ";
+ size_t len = strlen(msg_text1) + strlen(class_loader_name) + strlen(msg_text2) + strlen(package_name) + 1;
+ char* message = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, len);
+ jio_snprintf(message, len, "%s%s%s%s", msg_text1, class_loader_name, msg_text2, package_name);
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), message);
+ }
+
log_debug(modules)("add_module_package(): Adding package %s to module %s",
package_name, module_entry->name()->as_C_string());
TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK);
- PackageEntryTable* package_table = module_entry->loader_data()->packages();
+ PackageEntryTable* package_table = loader_data->packages();
assert(package_table != NULL, "Missing package_table");
bool pkg_exists = false;
diff --git a/hotspot/src/share/vm/classfile/modules.hpp b/hotspot/src/share/vm/classfile/modules.hpp
index 2d0c3311a05..25d02bf845c 100644
--- a/hotspot/src/share/vm/classfile/modules.hpp
+++ b/hotspot/src/share/vm/classfile/modules.hpp
@@ -93,24 +93,6 @@ public:
// module does not exist.
static void add_reads_module(jobject from_module, jobject to_module, TRAPS);
- // can_read_module returns TRUE if module asking_module can read module target_module,
- // or if they are the same module, or if the asking_module is loose and target_module
- // is null.
- //
- // Throws IllegalArgumentException if:
- // * either asking_module or target_module is not a java.lang.reflect.Module
- static jboolean can_read_module(jobject asking_module, jobject target_module, TRAPS);
-
- // If package is valid then this returns TRUE if module from_module exports
- // package to module to_module, if from_module and to_module are the same
- // module, or if package is exported without qualification.
- //
- // IllegalArgumentException is throw if:
- // * Either to_module or from_module does not exist
- // * package is syntactically incorrect
- // * package is not in from_module
- static jboolean is_exported_to_module(jobject from_module, jstring package, jobject to_module, TRAPS);
-
// Return the java.lang.reflect.Module object for this class object.
static jobject get_module(jclass clazz, TRAPS);
diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp
index fd3b3fe4173..31e3519bfde 100644
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp
@@ -451,8 +451,6 @@
template(loader_name, "loader") \
template(module_name, "module") \
template(getModule_name, "getModule") \
- template(addReads_name, "addReads") \
- template(addReads_signature, "(Ljava/lang/reflect/Module;Ljava/lang/reflect/Module;)V") \
template(input_stream_void_signature, "(Ljava/io/InputStream;)V") \
template(definePackage_name, "definePackage") \
template(definePackage_signature, "(Ljava/lang/String;Ljava/lang/reflect/Module;)Ljava/lang/Package;") \
@@ -645,6 +643,15 @@
/* JVMTI/java.lang.instrument support and VM Attach mechanism */ \
template(jdk_internal_module_Modules, "jdk/internal/module/Modules") \
template(jdk_internal_vm_VMSupport, "jdk/internal/vm/VMSupport") \
+ template(addReads_name, "addReads") \
+ template(addReads_signature, "(Ljava/lang/reflect/Module;Ljava/lang/reflect/Module;)V") \
+ template(addExports_name, "addExports") \
+ template(addOpens_name, "addOpens") \
+ template(addExports_signature, "(Ljava/lang/reflect/Module;Ljava/lang/String;Ljava/lang/reflect/Module;)V") \
+ template(addUses_name, "addUses") \
+ template(addUses_signature, "(Ljava/lang/reflect/Module;Ljava/lang/Class;)V") \
+ template(addProvides_name, "addProvides") \
+ template(addProvides_signature, "(Ljava/lang/reflect/Module;Ljava/lang/Class;Ljava/lang/Class;)V") \
template(transformedByAgent_name, "transformedByAgent") \
template(transformedByAgent_signature, "(Ljava/lang/reflect/Module;)V") \
template(appendToClassPathForInstrumentation_name, "appendToClassPathForInstrumentation") \
diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp
index 7e6c3c809fb..32c224592b9 100644
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp
@@ -26,6 +26,7 @@
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.hpp"
+#include "classfile/moduleEntry.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp"
@@ -2383,18 +2384,17 @@ Klass* InstanceKlass::compute_enclosing_class_impl(instanceKlassHandle self,
// Only boot and platform class loaders can define classes in "java/" packages.
void InstanceKlass::check_prohibited_package(Symbol* class_name,
- Handle class_loader,
- TRAPS) {
- const char* javapkg = "java/";
+ Handle class_loader,
+ TRAPS) {
ResourceMark rm(THREAD);
if (!class_loader.is_null() &&
!SystemDictionary::is_platform_class_loader(class_loader) &&
class_name != NULL &&
- strncmp(class_name->as_C_string(), javapkg, strlen(javapkg)) == 0) {
+ strncmp(class_name->as_C_string(), JAVAPKG, JAVAPKG_LEN) == 0) {
TempNewSymbol pkg_name = InstanceKlass::package_from_name(class_name, CHECK);
assert(pkg_name != NULL, "Error in parsing package name starting with 'java/'");
char* name = pkg_name->as_C_string();
- const char* class_loader_name = InstanceKlass::cast(class_loader()->klass())->name()->as_C_string();
+ const char* class_loader_name = SystemDictionary::loader_name(class_loader());
StringUtils::replace_no_expand(name, "/", ".");
const char* msg_text1 = "Class loader (instance of): ";
const char* msg_text2 = " tried to load prohibited package name: ";
diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp
index bb67d13caa3..f519e5e2788 100644
--- a/hotspot/src/share/vm/prims/jni.cpp
+++ b/hotspot/src/share/vm/prims/jni.cpp
@@ -3475,40 +3475,6 @@ JNI_ENTRY(jobject, jni_GetModule(JNIEnv* env, jclass clazz))
JNI_END
-JNI_ENTRY(void, jni_AddModuleReads(JNIEnv* env, jobject m1, jobject m2))
- JNIWrapper("AddModuleReads");
- if (m1 == NULL || m2 == NULL) {
- THROW(vmSymbols::java_lang_NullPointerException());
- }
- JavaValue result(T_VOID);
- Handle m1_h(THREAD, JNIHandles::resolve(m1));
- if (!java_lang_reflect_Module::is_instance(m1_h())) {
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Bad m1 object");
- }
- Handle m2_h(THREAD, JNIHandles::resolve(m2));
- if (!java_lang_reflect_Module::is_instance(m2_h())) {
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Bad m2 object");
- }
- JavaCalls::call_static(&result,
- KlassHandle(THREAD, SystemDictionary::module_Modules_klass()),
- vmSymbols::addReads_name(),
- vmSymbols::addReads_signature(),
- m1_h,
- m2_h,
- THREAD);
-JNI_END
-
-
-JNI_ENTRY(jboolean, jni_CanReadModule(JNIEnv* env, jobject m1, jobject m2))
- JNIWrapper("CanReadModule");
- if (m1 == NULL || m2 == NULL) {
- THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE);
- }
- jboolean res = Modules::can_read_module(m1, m2, CHECK_false);
- return res;
-JNI_END
-
-
// Structure containing all jni functions
struct JNINativeInterface_ jni_NativeInterface = {
NULL,
@@ -3792,9 +3758,7 @@ struct JNINativeInterface_ jni_NativeInterface = {
// Module features
- jni_GetModule,
- jni_AddModuleReads,
- jni_CanReadModule
+ jni_GetModule
};
diff --git a/hotspot/src/share/vm/prims/jni.h b/hotspot/src/share/vm/prims/jni.h
index 96996885a07..d5de88d0dd8 100644
--- a/hotspot/src/share/vm/prims/jni.h
+++ b/hotspot/src/share/vm/prims/jni.h
@@ -770,12 +770,6 @@ struct JNINativeInterface_ {
jobject (JNICALL *GetModule)
(JNIEnv* env, jclass clazz);
-
- void (JNICALL *AddModuleReads)
- (JNIEnv* env, jobject m1, jobject m2);
-
- jboolean (JNICALL *CanReadModule)
- (JNIEnv* env, jobject m1, jobject m2);
};
/*
@@ -1874,14 +1868,6 @@ struct JNIEnv_ {
return functions->GetModule(this, clazz);
}
- void AddModuleReads(jobject fromModule, jobject sourceModule) {
- functions->AddModuleReads(this, fromModule, sourceModule);
- }
-
- jboolean CanReadModule(jobject askingModule, jobject sourceModule) {
- return functions->CanReadModule(this, askingModule, sourceModule);
- }
-
#endif /* __cplusplus */
};
diff --git a/hotspot/src/share/vm/prims/jniCheck.cpp b/hotspot/src/share/vm/prims/jniCheck.cpp
index d6b703574d0..ea80b5b8110 100644
--- a/hotspot/src/share/vm/prims/jniCheck.cpp
+++ b/hotspot/src/share/vm/prims/jniCheck.cpp
@@ -2001,37 +2001,6 @@ JNI_ENTRY_CHECKED(jobject,
return result;
JNI_END
-JNI_ENTRY_CHECKED(void,
- checked_jni_AddModuleReads(JNIEnv *env,
- jobject fromModule,
- jobject sourceModule))
- functionEnter(thr);
- IN_VM(
- jniCheck::validate_object(thr, fromModule);
- if (sourceModule != NULL) {
- jniCheck::validate_object(thr, sourceModule);
- }
- )
- UNCHECKED()->AddModuleReads(env,fromModule,sourceModule);
- functionExit(thr);
-JNI_END
-
-JNI_ENTRY_CHECKED(jboolean,
- checked_jni_CanReadModule(JNIEnv *env,
- jobject askingModule,
- jobject sourceModule))
- functionEnter(thr);
- IN_VM(
- jniCheck::validate_object(thr, askingModule);
- if (sourceModule != NULL) {
- jniCheck::validate_object(thr, sourceModule);
- }
- )
- jboolean result = UNCHECKED()->CanReadModule(env,askingModule,sourceModule);
- functionExit(thr);
- return result;
-JNI_END
-
/*
* Structure containing all checked jni functions
*/
@@ -2317,9 +2286,7 @@ struct JNINativeInterface_ checked_jni_NativeInterface = {
// Module Features
- checked_jni_GetModule,
- checked_jni_AddModuleReads,
- checked_jni_CanReadModule
+ checked_jni_GetModule
};
diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp
index 51bc575cf99..6a1b4dda087 100644
--- a/hotspot/src/share/vm/prims/jvm.cpp
+++ b/hotspot/src/share/vm/prims/jvm.cpp
@@ -1008,8 +1008,8 @@ JVM_END
// Module support //////////////////////////////////////////////////////////////////////////////
-JVM_ENTRY(void, JVM_DefineModule(JNIEnv *env, jobject module, jstring version, jstring location,
- jobjectArray packages))
+JVM_ENTRY(void, JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version,
+ jstring location, jobjectArray packages))
JVMWrapper("JVM_DefineModule");
Modules::define_module(module, version, location, packages, CHECK);
JVM_END
@@ -1039,16 +1039,6 @@ JVM_ENTRY (void, JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject so
Modules::add_reads_module(from_module, source_module, CHECK);
JVM_END
-JVM_ENTRY(jboolean, JVM_CanReadModule(JNIEnv *env, jobject asking_module, jobject source_module))
- JVMWrapper("JVM_CanReadModule");
- return Modules::can_read_module(asking_module, source_module, THREAD);
-JVM_END
-
-JVM_ENTRY(jboolean, JVM_IsExportedToModule(JNIEnv *env, jobject from_module, jstring package, jobject to_module))
- JVMWrapper("JVM_IsExportedToModule");
- return Modules::is_exported_to_module(from_module, package, to_module, THREAD);
-JVM_END
-
JVM_ENTRY (void, JVM_AddModulePackage(JNIEnv *env, jobject module, jstring package))
JVMWrapper("JVM_AddModulePackage");
Modules::add_module_package(module, package, CHECK);
diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h
index 47bf5a0d023..c713645024e 100644
--- a/hotspot/src/share/vm/prims/jvm.h
+++ b/hotspot/src/share/vm/prims/jvm.h
@@ -413,8 +413,8 @@ JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader,
*/
JNIEXPORT void JNICALL
-JVM_DefineModule(JNIEnv *env, jobject module, jstring version, jstring location,
- jobjectArray packages);
+JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version,
+ jstring location, jobjectArray packages);
JNIEXPORT void JNICALL
JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module);
@@ -431,12 +431,6 @@ JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package);
JNIEXPORT void JNICALL
JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module);
-JNIEXPORT jboolean JNICALL
-JVM_CanReadModule(JNIEnv *env, jobject asking_module, jobject source_module);
-
-JNIEXPORT jboolean JNICALL
-JVM_IsExportedToModule(JNIEnv *env, jobject from_module, jstring package, jobject to_module);
-
JNIEXPORT void JNICALL
JVM_AddModulePackage(JNIEnv* env, jobject module, jstring package);
diff --git a/hotspot/src/share/vm/prims/jvmti.xml b/hotspot/src/share/vm/prims/jvmti.xml
index bcbfc998567..28144da3a39 100644
--- a/hotspot/src/share/vm/prims/jvmti.xml
+++ b/hotspot/src/share/vm/prims/jvmti.xml
@@ -863,14 +863,12 @@ Agent_OnUnload_L(JavaVM *vm)
Since JDWP version 9."
- (Out
- (moduleID module "This module.")
- (moduleID sourceModule "The source module.")
- )
- (Reply
- (boolean canRead "true if this module can read the source module; false otherwise.")
- )
- (ErrorSet
- (Error INVALID_MODULE "This module or sourceModule is not the ID of a module.")
- (Error NOT_IMPLEMENTED)
- (Error VM_DEAD)
- )
- )
)
(CommandSet Event=64
(Command Composite=100
diff --git a/jdk/make/gensrc/GensrcMisc.gmk b/jdk/make/gensrc/GensrcMisc.gmk
index 475b8f1d033..6b5dcf9e6b6 100644
--- a/jdk/make/gensrc/GensrcMisc.gmk
+++ b/jdk/make/gensrc/GensrcMisc.gmk
@@ -108,3 +108,19 @@ ifeq ($(OPENJDK_TARGET_OS), solaris)
endif
################################################################################
+# Create the javax/crypto/JceSecurity.class, using the build default.
+#
+ifeq ($(UNLIMITED_CRYPTO), true)
+ JCE_DEFAULT_POLICY = unlimited
+else
+ JCE_DEFAULT_POLICY = limited
+endif
+
+$(eval $(call SetupTextFileProcessing, BUILD_JCESECURITY_JAVA, \
+ SOURCE_FILES := $(JDK_TOPDIR)/src/java.base/share/classes/javax/crypto/JceSecurity.java.template, \
+ OUTPUT_FILE := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/javax/crypto/JceSecurity.java, \
+ REPLACEMENTS := \
+ @@JCE_DEFAULT_POLICY@@ => $(JCE_DEFAULT_POLICY), \
+))
+
+GENSRC_JAVA_BASE += $(BUILD_JCESECURITY_JAVA)
diff --git a/jdk/make/launcher/Launcher-jdk.jconsole.gmk b/jdk/make/launcher/Launcher-jdk.jconsole.gmk
index aa07823c54a..6205ae63d16 100644
--- a/jdk/make/launcher/Launcher-jdk.jconsole.gmk
+++ b/jdk/make/launcher/Launcher-jdk.jconsole.gmk
@@ -27,7 +27,8 @@ include LauncherCommon.gmk
$(eval $(call SetupBuildLauncher, jconsole, \
MAIN_CLASS := sun.tools.jconsole.JConsole, \
- JAVA_ARGS := -Djconsole.showOutputViewer, \
+ JAVA_ARGS := --add-opens java.base/java.io=jdk.jconsole \
+ -Djconsole.showOutputViewer, \
CFLAGS_windows := -DJAVAW, \
LIBS_windows := user32.lib, \
))
diff --git a/jdk/make/lib/Awt2dLibraries.gmk b/jdk/make/lib/Awt2dLibraries.gmk
index ed402abecf5..09423e4d770 100644
--- a/jdk/make/lib/Awt2dLibraries.gmk
+++ b/jdk/make/lib/Awt2dLibraries.gmk
@@ -287,9 +287,8 @@ ifeq ($(findstring $(OPENJDK_TARGET_OS),windows macosx),)
$(JDK_TOPDIR)/src/java.desktop/share/native/common/awt/debug \
$(JDK_TOPDIR)/src/java.desktop/share/native/common/awt/utility \
$(JDK_TOPDIR)/src/java.desktop/share/native/common/font \
- $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d/opengl \
- $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d/opengl \
- $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d/x11 \
+ $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d \
+ $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d \
$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \
#
@@ -396,7 +395,7 @@ LCMS_CFLAGS=-DCMS_DONT_USE_FAST_FLOOR
ifeq ($(USE_EXTERNAL_LCMS), true)
# If we're using an external library, we'll just need the wrapper part.
- # By including it explicitely, all other files will be excluded.
+ # By including it explicitly, all other files will be excluded.
BUILD_LIBLCMS_INCLUDE_FILES := LCMS.c
else
BUILD_LIBLCMS_INCLUDE_FILES :=
@@ -516,24 +515,25 @@ ifeq ($(findstring $(OPENJDK_TARGET_OS), windows macosx),)
LIBAWT_HEADLESS_DIRS := $(JDK_TOPDIR)/src/java.desktop/unix/native/libawt_headless/awt \
$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \
- $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d/opengl \
- $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d/x11 \
- $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d/opengl \
+ $(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d \
+ $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d \
$(JDK_TOPDIR)/src/java.desktop/share/native/common/font \
#
LIBAWT_HEADLESS_EXCLUDES := medialib
LIBAWT_HEADLESS_CFLAGS := -I$(SUPPORT_OUTPUTDIR)/headers/java.desktop \
$(addprefix -I, $(LIBAWT_HEADLESS_DIRS)) \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/loops \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image/cvutils \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/pipe \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image/cvutils \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d \
-I$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/libawt/java2d \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/font \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/loops \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/pipe \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/common/awt/debug \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/font \
-I$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/font \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d/opengl \
+ -I$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/common/java2d/opengl \
-I$(JDK_TOPDIR)/src/java.desktop/$(OPENJDK_TARGET_OS_TYPE)/native/libsunwjdga/ \
$(LIBJAVA_HEADER_FLAGS) \
#
@@ -862,7 +862,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
ifeq ($(OPENJDK_TARGET_OS), windows)
LIBSPLASHSCREEN_DIRS += $(JDK_TOPDIR)/src/java.desktop/windows/native/common/awt/systemscale
- endif
+ endif
LIBSPLASHSCREEN_CFLAGS += -DSPLASHSCREEN -DPNG_NO_MMX_CODE -DPNG_ARM_NEON_OPT=0 \
$(addprefix -I, $(LIBSPLASHSCREEN_DIRS)) \
$(LIBJAVA_HEADER_FLAGS) \
@@ -952,26 +952,27 @@ ifeq ($(OPENJDK_TARGET_OS), macosx)
$(JDK_TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt \
$(JDK_TOPDIR)/src/java.desktop/unix/native/common/awt \
$(JDK_TOPDIR)/src/java.desktop/share/native/common/font \
- $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d/opengl \
+ $(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d \
#
LIBAWT_LWAWT_CFLAGS := \
$(addprefix -I, $(LIBAWT_LWAWT_DIRS)) \
-I$(SUPPORT_OUTPUTDIR)/headers/java.desktop \
- -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/include \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/include \
- -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl \
-I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/awt \
-I$(JDK_TOPDIR)/src/java.desktop/unix/native/libawt_xawt/awt \
-I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/font \
+ -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/java2d/opengl \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/awt/debug \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/java2d/opengl \
+ -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/include \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/include \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image/cvutils \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d \
-I$(JDK_TOPDIR)/src/java.desktop/unix/native/libawt/java2d \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libmlib_image/ \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/awt/image/cvutils \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/loops \
-I$(JDK_TOPDIR)/src/java.desktop/share/native/libawt/java2d/pipe \
- -I$(JDK_TOPDIR)/src/java.desktop/share/native/common/awt/debug \
+ -I$(JDK_TOPDIR)/src/java.desktop/share/native/libmlib_image/ \
-I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libosxapp \
$(LIBJAVA_HEADER_FLAGS) \
#
diff --git a/jdk/make/lib/CoreLibraries.gmk b/jdk/make/lib/CoreLibraries.gmk
index 345c4253389..27dad3553ee 100644
--- a/jdk/make/lib/CoreLibraries.gmk
+++ b/jdk/make/lib/CoreLibraries.gmk
@@ -33,10 +33,22 @@ $(eval $(call IncludeCustomExtension, jdk, lib/CoreLibraries.gmk))
# libfdlibm is statically linked with libjava below and not delivered into the
# product on its own.
-BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+BUILD_LIBFDLIBM_OPTIMIZATION := NONE
-ifneq ($(OPENJDK_TARGET_OS), solaris)
- BUILD_LIBFDLIBM_OPTIMIZATION := NONE
+ifeq ($(OPENJDK_TARGET_OS), solaris)
+ BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+endif
+
+ifeq ($(OPENJDK_TARGET_OS), linux)
+ ifeq ($(OPENJDK_TARGET_CPU), ppc64)
+ BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+ else ifeq ($(OPENJDK_TARGET_CPU), ppc64le)
+ BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+ else ifeq ($(OPENJDK_TARGET_CPU), s390x)
+ BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+ else ifeq ($(OPENJDK_TARGET_CPU), aarch64)
+ BUILD_LIBFDLIBM_OPTIMIZATION := HIGH
+ endif
endif
LIBFDLIBM_SRC := $(JDK_TOPDIR)/src/java.base/share/native/libfdlibm
@@ -51,6 +63,10 @@ ifneq ($(OPENJDK_TARGET_OS), macosx)
CFLAGS := $(CFLAGS_JDKLIB) $(LIBFDLIBM_CFLAGS), \
CFLAGS_windows_debug := -DLOGGING, \
CFLAGS_aix := -qfloat=nomaf, \
+ CFLAGS_linux_ppc64 := -ffp-contract=off, \
+ CFLAGS_linux_ppc64le := -ffp-contract=off, \
+ CFLAGS_linux_s390x := -ffp-contract=off, \
+ CFLAGS_linux_aarch64 := -ffp-contract=off, \
DISABLED_WARNINGS_gcc := sign-compare, \
DISABLED_WARNINGS_microsoft := 4146 4244 4018, \
ARFLAGS := $(ARFLAGS), \
diff --git a/jdk/make/src/classes/build/tools/jigsaw/AddPackagesAttribute.java b/jdk/make/src/classes/build/tools/jigsaw/AddPackagesAttribute.java
index 5d212cd70ce..7aa02f365b9 100644
--- a/jdk/make/src/classes/build/tools/jigsaw/AddPackagesAttribute.java
+++ b/jdk/make/src/classes/build/tools/jigsaw/AddPackagesAttribute.java
@@ -65,7 +65,7 @@ public class AddPackagesAttribute {
String mn = entry.getFileName().toString();
Optional This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method. Represents a module opens directive, may be qualified or
+ * unqualified. The opens directive in a module declaration declares a
+ * package to be open to allow all types in the package, and all their
+ * members, not just public types and their public members to be reflected
+ * on by APIs that support private access or a way to bypass or suppress
+ * default Java language access control checks. The hash code is based upon the modifiers, the package name,
+ * and for a qualified opens, the set of modules names to which the
+ * package is opened. It satisfies the general contract of the
+ * {@link Object#hashCode Object.hashCode} method.
+ *
+ * @return The hash-code value for this module opens
+ */
+ @Override
+ public int hashCode() {
+ int hash = mods.hashCode();
+ hash = hash * 43 + source.hashCode();
+ return hash * 43 + targets.hashCode();
+ }
+
+ /**
+ * Tests this module opens for equality with the given object.
+ *
+ * If the given object is not an {@code Opens} then this method
+ * returns {@code false}. Two {@code Opens} objects are equal if their
+ * set of modifiers is equal, the package names are equal and the set
+ * of target module names is equal. This method satisfies the general contract of the {@link
+ * java.lang.Object#equals(Object) Object.equals} method. A service that a module provides one or more implementations of. If the given object is not a {@code Provides} then this method
* returns {@code false}. Two {@code Provides} objects are equal if the
- * service type is equal and the set of providers is equal. public and in a package that is exported by its module.
- Agents can use the JNI functions CanReadModule and
- AddModuleReads to test and update a module to read another.
+ Agents can use the functions to_module is not a subclass of
+ java.lang.reflect.Module this function returns
+ to_module is not a subclass of
+ java.lang.reflect.Module this function returns
+ "));
- boolean footnote = requiresPublicNote;
+ boolean footnote = requiresTransitiveNote;
ms.descriptor().requires().stream()
.sorted(Comparator.comparing(Requires::name))
.forEach(r -> {
- boolean requiresPublic = r.modifiers().contains(Requires.Modifier.PUBLIC);
- Selector sel = requiresPublic ? REQUIRES_PUBLIC : REQUIRES;
+ boolean requiresTransitive = r.modifiers().contains(Requires.Modifier.TRANSITIVE);
+ Selector sel = requiresTransitive ? REQUIRES_PUBLIC : REQUIRES;
String req = String.format("%s",
sel, r.name(), r.name());
- if (!requiresPublicNote && requiresPublic) {
- requiresPublicNote = true;
+ if (!requiresTransitiveNote && requiresTransitive) {
+ requiresTransitiveNote = true;
req += "*";
}
sb.append(req).append("\n").append(" ");
return sb.toString();
@@ -558,11 +558,10 @@ public class ModuleSummary {
ms.descriptor().uses().stream()
.sorted()
.forEach(s -> sb.append("uses ").append(s).append("
");
@@ -534,8 +534,8 @@ public class ModuleSummary {
sb.append("
");
sb.append("+").append(indirectDeps).append(" transitive dependencies");
}
- if (footnote != requiresPublicNote) {
- sb.append("
").append("* bold denotes requires public");
+ if (footnote != requiresTransitiveNote) {
+ sb.append("
").append("* bold denotes requires transitive");
}
sb.append("
").append("\n"));
- ms.descriptor().provides().entrySet().stream()
- .sorted(Map.Entry.comparingByKey())
- .flatMap(e -> e.getValue().providers().stream()
- .map(p -> String.format("provides %s
with %s",
- e.getKey(), p)))
+ ms.descriptor().provides().stream()
+ .sorted(Comparator.comparing(Provides::service))
+ .map(p -> String.format("provides %s
with %s",
+ p.service(), p.providers()))
.forEach(p -> sb.append(p).append("
").append("\n"));
sb.append("");
return sb.toString();
diff --git a/jdk/make/src/classes/build/tools/module/GenModuleInfoSource.java b/jdk/make/src/classes/build/tools/module/GenModuleInfoSource.java
index e7d932d1141..60d0b6e3132 100644
--- a/jdk/make/src/classes/build/tools/module/GenModuleInfoSource.java
+++ b/jdk/make/src/classes/build/tools/module/GenModuleInfoSource.java
@@ -30,219 +30,594 @@ import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import static java.util.stream.Collectors.*;
/**
* A build tool to extend the module-info.java in the source tree for
- * platform-specific exports, uses, and provides and write to the specified
- * output file. Injecting platform-specific requires is not supported.
+ * platform-specific exports, opens, uses, and provides and write to
+ * the specified output file.
*
- * The extra exports, uses, provides can be specified in module-info.java.extra
- * files and GenModuleInfoSource will be invoked for each module that has
+ * GenModuleInfoSource will be invoked for each module that has
* module-info.java.extra in the source directory.
+ *
+ * The extra exports, opens, uses, provides can be specified
+ * in module-info.java.extra.
+ * Injecting platform-specific requires is not supported.
+ *
+ * @see build.tools.module.ModuleInfoExtraTest for basic testing
*/
public class GenModuleInfoSource {
private final static String USAGE =
- "Usage: GenModuleInfoSource [option] -o
This method satisfies the general contract of the {@link * java.lang.Object#equals(Object) Object.equals} method.
@@ -774,10 +970,7 @@ public class ModuleDescriptor // From module declarations private final String name; - private final SetReturns {@code true} if this is an open module.
+ * + *An open module does not declare any open packages (the {@link #opens() + * opens} method returns an empty set) but the resulting module is treated + * as if all packages are open.
+ * + * @return {@code true} if this is an open module + */ + public boolean isOpen() { + return open; + } + /** *Returns {@code true} if this is an automatic module.
* @@ -933,8 +1148,6 @@ public class ModuleDescriptor * * @return {@code true} if this module descriptor was not generated by * an explicit or implicit module declaration - * - * @jvms 4.7.8 The {@code Synthetic} Attribute */ public boolean isSynthetic() { return synthetic; @@ -949,6 +1162,33 @@ public class ModuleDescriptor return requires; } + /** + *The module exports.
+ * + * @return A possibly-empty unmodifiable set of exported packages + */ + public SetThe module opens directives.
+ * + *Each {@code Opens} object in the set represents a package (and + * the set of target module names when qualified) where all types in the + * package, and all their members, not just public types and their public + * members, can be reflected on when using APIs that bypass or suppress + * default Java language access control checks.
+ * + *This method returns an empty set when invoked on {@link #isOpen() + * open} module.
+ * + * @return A possibly-empty unmodifiable set of open packages + */ + public SetThe service dependences of this module.
* @@ -962,23 +1202,13 @@ public class ModuleDescriptor /** *The services that this module provides.
* - * @return The possibly-empty unmodifiable map of the services that this - * module provides. The map key is fully qualified class name of - * the service type. + * @return The possibly-empty unmodifiable set of the services that this + * module provides */ - public MapThe module exports.
- * - * @return A possibly-empty unmodifiable set of exported packages - */ - public SetExample usage:
+ *{@code ModuleDescriptor} defines the {@link #module module}, {@link + * #openModule openModule}, and {@link #automaticModule automaticModule} + * methods to create builders for building different kinds of modules.
* - *{@code
- * ModuleDescriptor descriptor = new ModuleDescriptor.Builder("m1")
- * .requires("m2")
+ * Example usage:
+ * {@code ModuleDescriptor descriptor = ModuleDescriptor.module("m1")
* .exports("p")
+ * .requires("m2")
* .build();
* }
*
- * @apiNote A {@code Builder} cannot be used to create an {@link
- * ModuleDescriptor#isAutomatic() automatic} or a {@link
- * ModuleDescriptor#isSynthetic() synthetic} module.
+ * @apiNote A {@code Builder} checks the components and invariants as
+ * components are added to the builder. The rational for this is to detect
+ * errors as early as possible and not defer all validation to the
+ * {@link #build build} method. A {@code Builder} cannot be used to create
+ * a {@link ModuleDescriptor#isSynthetic() synthetic} module.
*
* @since 9
*/
public static final class Builder {
-
final String name;
+ final boolean strict; // true if module names are checked
+ boolean open;
boolean automatic;
boolean synthetic;
final Map requires = new HashMap<>();
- final Set uses = new HashSet<>();
+
final Map exports = new HashMap<>();
+ final Map opens = new HashMap<>();
+ final Set concealedPackages = new HashSet<>();
+
+ final Set uses = new HashSet<>();
final Map provides = new HashMap<>();
- Set conceals = Collections.emptySet();
Version version;
String osName;
String osArch;
@@ -1113,29 +1338,30 @@ public class ModuleDescriptor
/**
* Initializes a new builder with the given module name.
*
- * @param name
- * The module name
- *
- * @throws IllegalArgumentException
- * If the module name is {@code null} or is not a legal Java
- * identifier
+ * @param strict
+ * Indicates whether module names are checked or not
*/
- public Builder(String name) {
- this.name = requireModuleName(name);
+ Builder(String name, boolean strict) {
+ this.strict = strict;
+ this.name = (strict) ? requireModuleName(name) : name;
}
- /**
- * Updates the builder so that it builds an automatic module.
- *
- * @return This builder
- *
- * @see ModuleDescriptor#isAutomatic()
- */
- /* package */ Builder automatic() {
- this.automatic = true;
+ /* package */ Builder open(boolean open) {
+ this.open = open;
return this;
}
+ /* package */ Builder automatic(boolean automatic) {
+ this.automatic = automatic;
+ return this;
+ }
+
+ /* package */ boolean isOpen() { return open; }
+
+ /* package */ boolean isAutomatic() {
+ return automatic;
+ }
+
/**
* Adds a dependence on a module.
*
@@ -1165,7 +1391,7 @@ public class ModuleDescriptor
* Adds a dependence on a module with the given (and possibly empty)
* set of modifiers.
*
- * @param mods
+ * @param ms
* The set of modifiers
* @param mn
* The module name
@@ -1179,14 +1405,10 @@ public class ModuleDescriptor
* @throws IllegalStateException
* If the dependence on the module has already been declared
*/
- public Builder requires(Set mods, String mn) {
- if (name.equals(mn))
- throw new IllegalArgumentException("Dependence on self");
- if (requires.containsKey(mn))
- throw new IllegalStateException("Dependence upon " + mn
- + " already declared");
- requires.put(mn, new Requires(mods, mn)); // checks mn
- return this;
+ public Builder requires(Set ms, String mn) {
+ if (strict)
+ mn = requireModuleName(mn);
+ return requires(new Requires(ms, mn));
}
/**
@@ -1208,62 +1430,6 @@ public class ModuleDescriptor
return requires(EnumSet.noneOf(Requires.Modifier.class), mn);
}
- /**
- * Adds a dependence on a module with the given modifier.
- *
- * @param mod
- * The modifier
- * @param mn
- * The module name
- *
- * @return This builder
- *
- * @throws IllegalArgumentException
- * If the module name is {@code null}, is not a legal Java
- * identifier, or is equal to the module name that this builder
- * was initialized to build
- * @throws IllegalStateException
- * If the dependence on the module has already been declared
- */
- public Builder requires(Requires.Modifier mod, String mn) {
- return requires(EnumSet.of(mod), mn);
- }
-
- /**
- * Adds a service dependence.
- *
- * @param st
- * The service type
- *
- * @return This builder
- *
- * @throws IllegalArgumentException
- * If the service type is {@code null} or is not a legal Java
- * identifier
- * @throws IllegalStateException
- * If a dependency on the service type has already been declared
- */
- public Builder uses(String st) {
- if (uses.contains(requireServiceTypeName(st)))
- throw new IllegalStateException("Dependence upon service "
- + st + " already declared");
- uses.add(st);
- return this;
- }
-
- /**
- * Ensures that the given package name has not been declared as an
- * exported or concealed package.
- */
- private void ensureNotExportedOrConcealed(String pn) {
- if (exports.containsKey(pn))
- throw new IllegalStateException("Export of package "
- + pn + " already declared");
- if (conceals.contains(pn))
- throw new IllegalStateException("Concealed package "
- + pn + " already declared");
- }
-
/**
* Adds an export.
*
@@ -1273,18 +1439,90 @@ public class ModuleDescriptor
* @return This builder
*
* @throws IllegalStateException
- * If the package is already declared as an exported or
- * concealed package
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method or the package is already
+ * declared as exported
*/
public Builder exports(Exports e) {
- String pn = e.source();
- ensureNotExportedOrConcealed(pn);
- exports.put(pn, e);
+ // can't be exported and concealed
+ String source = e.source();
+ if (concealedPackages.contains(source)) {
+ throw new IllegalStateException("Package " + source
+ + " already declared");
+ }
+ if (exports.containsKey(source)) {
+ throw new IllegalStateException("Exported package " + source
+ + " already declared");
+ }
+
+ exports.put(source, e);
return this;
}
/**
- * Adds an export to a set of target modules.
+ * Adds an export, with the given (and possibly empty) set of modifiers,
+ * to export a package to a set of target modules.
+ *
+ * @param ms
+ * The set of modifiers
+ * @param pn
+ * The package name
+ * @param targets
+ * The set of target modules names
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name or any of the target modules is {@code
+ * null} or is not a legal Java identifier, or the set of
+ * targets is empty
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method or the package is already
+ * declared as exported
+ */
+ public Builder exports(Set ms,
+ String pn,
+ Set targets)
+ {
+ Exports e = new Exports(ms, requirePackageName(pn), targets);
+
+ // check targets
+ targets = e.targets();
+ if (targets.isEmpty())
+ throw new IllegalArgumentException("Empty target set");
+ if (strict)
+ targets.stream().forEach(Checks::requireModuleName);
+
+ return exports(e);
+ }
+
+ /**
+ * Adds an unqualified export with the given (and possibly empty) set
+ * of modifiers.
+ *
+ * @param ms
+ * The set of modifiers
+ * @param pn
+ * The package name
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name is {@code null} or is not a legal Java
+ * identifier
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method or the package is already
+ * declared as exported
+ */
+ public Builder exports(Set ms, String pn) {
+ Exports e = new Exports(ms, requirePackageName(pn), Collections.emptySet());
+ return exports(e);
+ }
+
+ /**
+ * Adds an export to export a package to a set of target modules.
*
* @param pn
* The package name
@@ -1298,38 +1536,16 @@ public class ModuleDescriptor
* null} or is not a legal Java identifier, or the set of
* targets is empty
* @throws IllegalStateException
- * If the package is already declared as an exported or
- * concealed package
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method or the package is already
+ * declared as exported
*/
public Builder exports(String pn, Set targets) {
- ensureNotExportedOrConcealed(pn);
- exports.put(pn, new Exports(pn, targets)); // checks pn and targets
- return this;
+ return exports(Collections.emptySet(), pn, targets);
}
/**
- * Adds an export to a target module.
- *
- * @param pn
- * The package name
- * @param target
- * The target module name
- *
- * @return This builder
- *
- * @throws IllegalArgumentException
- * If the package name or target module is {@code null} or is
- * not a legal Java identifier
- * @throws IllegalStateException
- * If the package is already declared as an exported or
- * concealed package
- */
- public Builder exports(String pn, String target) {
- return exports(pn, Collections.singleton(target));
- }
-
- /**
- * Adds an export.
+ * Adds an unqualified export.
*
* @param pn
* The package name
@@ -1340,18 +1556,186 @@ public class ModuleDescriptor
* If the package name is {@code null} or is not a legal Java
* identifier
* @throws IllegalStateException
- * If the package is already declared as an exported or
- * concealed package
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method or the package is already
+ * declared as exported
*/
public Builder exports(String pn) {
- ensureNotExportedOrConcealed(pn);
- exports.put(pn, new Exports(pn)); // checks pn
+ return exports(Collections.emptySet(), pn);
+ }
+
+ /**
+ * Adds an opens directive.
+ *
+ * @param obj
+ * The {@code Opens} object
+ *
+ * @return This builder
+ *
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method, the package is already
+ * declared as open, or this is a builder for an open module
+ */
+ public Builder opens(Opens obj) {
+ if (open) {
+ throw new IllegalStateException("open modules cannot declare"
+ + " open packages");
+ }
+
+ // can't be open and concealed
+ String source = obj.source();
+ if (concealedPackages.contains(source)) {
+ throw new IllegalStateException("Package " + source
+ + " already declared");
+ }
+ if (opens.containsKey(source)) {
+ throw new IllegalStateException("Open package " + source
+ + " already declared");
+ }
+
+ opens.put(source, obj);
return this;
}
+
+ /**
+ * Adds an opens directive, with the given (and possibly empty)
+ * set of modifiers, to open a package to a set of target modules.
+ *
+ * @param ms
+ * The set of modifiers
+ * @param pn
+ * The package name
+ * @param targets
+ * The set of target modules names
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name or any of the target modules is {@code
+ * null} or is not a legal Java identifier, or the set of
+ * targets is empty
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method, the package is already
+ * declared as open, or this is a builder for an open module
+ */
+ public Builder opens(Set ms,
+ String pn,
+ Set targets)
+ {
+ Opens e = new Opens(ms, requirePackageName(pn), targets);
+
+ // check targets
+ targets = e.targets();
+ if (targets.isEmpty())
+ throw new IllegalArgumentException("Empty target set");
+ if (strict)
+ targets.stream().forEach(Checks::requireModuleName);
+
+ return opens(e);
+ }
+
+ /**
+ * Adds an opens directive to open a package with the given (and
+ * possibly empty) set of modifiers.
+ *
+ * @param ms
+ * The set of modifiers
+ * @param pn
+ * The package name
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name is {@code null} or is not a legal Java
+ * identifier
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method, the package is already
+ * declared as open, or this is a builder for an open module
+ */
+ public Builder opens(Set ms, String pn) {
+ Opens e = new Opens(ms, requirePackageName(pn), Collections.emptySet());
+ return opens(e);
+ }
+
+ /**
+ * Adds an opens directive to open a package to a set of target
+ * modules.
+ *
+ * @param pn
+ * The package name
+ * @param targets
+ * The set of target modules names
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name or any of the target modules is {@code
+ * null} or is not a legal Java identifier, or the set of
+ * targets is empty
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method, the package is already
+ * declared as open, or this is a builder for an open module
+ */
+ public Builder opens(String pn, Set targets) {
+ return opens(Collections.emptySet(), pn, targets);
+ }
+
+ /**
+ * Adds an opens directive to open a package.
+ *
+ * @param pn
+ * The package name
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the package name is {@code null} or is not a legal Java
+ * identifier
+ * @throws IllegalStateException
+ * If the package is already declared as a package with the
+ * {@link #contains contains} method, the package is already
+ * declared as open, or this is a builder for an open module
+ */
+ public Builder opens(String pn) {
+ return opens(Collections.emptySet(), pn);
+ }
+
+
// Used by ModuleInfo, after a packageFinder is invoked
- /* package */ Set exportedPackages() {
- return exports.keySet();
+ /* package */ Set exportedAndOpenPackages() {
+ if (opens.isEmpty())
+ return exports.keySet();
+ Set result = new HashSet<>();
+ result.addAll(exports.keySet());
+ result.addAll(opens.keySet());
+ return result;
+ }
+
+ /**
+ * Adds a service dependence.
+ *
+ * @param service
+ * The service type
+ *
+ * @return This builder
+ *
+ * @throws IllegalArgumentException
+ * If the service type is {@code null} or is not a legal Java
+ * identifier
+ * @throws IllegalStateException
+ * If a dependency on the service type has already been declared
+ */
+ public Builder uses(String service) {
+ if (uses.contains(requireServiceTypeName(service)))
+ throw new IllegalStateException("Dependence upon service "
+ + service + " already declared");
+ uses.add(service);
+ return this;
}
/**
@@ -1376,38 +1760,47 @@ public class ModuleDescriptor
}
/**
- * Provides service {@code st} with implementations {@code pcs}.
+ * Provides implementations of a service.
*
- * @param st
+ * @param service
* The service type
- * @param pcs
- * The set of provider class names
+ * @param providers
+ * The list of provider or provider factory class names
*
* @return This builder
*
* @throws IllegalArgumentException
* If the service type or any of the provider class names is
- * {@code null} or is not a legal Java identifier, or the set
+ * {@code null} or is not a legal Java identifier, or the list
* of provider class names is empty
* @throws IllegalStateException
* If the providers for the service type have already been
* declared
*/
- public Builder provides(String st, Set pcs) {
- if (provides.containsKey(st))
+ public Builder provides(String service, List providers) {
+ if (provides.containsKey(service))
throw new IllegalStateException("Providers of service "
- + st + " already declared");
- provides.put(st, new Provides(st, pcs)); // checks st and pcs
+ + service + " already declared by " + name);
+
+ Provides p = new Provides(requireServiceTypeName(service), providers);
+
+ // check providers after the set has been copied.
+ List providerNames = p.providers();
+ if (providerNames.isEmpty())
+ throw new IllegalArgumentException("Empty providers set");
+ providerNames.forEach(Checks::requireServiceProviderName);
+
+ provides.put(service, p);
return this;
}
/**
- * Provides service {@code st} with implementation {@code pc}.
+ * Provides an implementation of a service.
*
- * @param st
+ * @param service
* The service type
- * @param pc
- * The provider class name
+ * @param provider
+ * The provider or provider factory class name
*
* @return This builder
*
@@ -1418,15 +1811,17 @@ public class ModuleDescriptor
* If the providers for the service type have already been
* declared
*/
- public Builder provides(String st, String pc) {
- return provides(st, Collections.singleton(pc));
+ public Builder provides(String service, String provider) {
+ if (provider == null)
+ throw new IllegalArgumentException("'provider' is null");
+ return provides(service, List.of(provider));
}
/**
- * Adds a set of (possible empty) concealed packages.
+ * Adds a (possible empty) set of packages to the module
*
* @param pns
- * The set of package names of the concealed packages
+ * The set of package names
*
* @return This builder
*
@@ -1434,16 +1829,17 @@ public class ModuleDescriptor
* If any of the package names is {@code null} or is not a
* legal Java identifier
* @throws IllegalStateException
- * If any of packages are already declared as a concealed or
- * exported package
+ * If any of packages are already declared as packages in
+ * the module. This includes packages that are already
+ * declared as exported or open packages.
*/
- public Builder conceals(Set pns) {
- pns.forEach(this::conceals);
+ public Builder contains(Set pns) {
+ pns.forEach(this::contains);
return this;
}
/**
- * Adds a concealed package.
+ * Adds a package to the module.
*
* @param pn
* The package name
@@ -1454,15 +1850,25 @@ public class ModuleDescriptor
* If the package name is {@code null}, or is not a legal Java
* identifier
* @throws IllegalStateException
- * If the package is already declared as a concealed or exported
- * package
+ * If the package is already declared as a package in the
+ * module. This includes the package already declared as an
+ * exported or open package.
*/
- public Builder conceals(String pn) {
+ public Builder contains(String pn) {
Checks.requirePackageName(pn);
- ensureNotExportedOrConcealed(pn);
- if (conceals.isEmpty())
- conceals = new HashSet<>();
- conceals.add(pn);
+ if (concealedPackages.contains(pn)) {
+ throw new IllegalStateException("Package " + pn
+ + " already declared");
+ }
+ if (exports.containsKey(pn)) {
+ throw new IllegalStateException("Exported package "
+ + pn + " already declared");
+ }
+ if (opens.containsKey(pn)) {
+ throw new IllegalStateException("Open package "
+ + pn + " already declared");
+ }
+ concealedPackages.add(pn);
return this;
}
@@ -1473,13 +1879,8 @@ public class ModuleDescriptor
* The version
*
* @return This builder
- *
- * @throws IllegalStateException
- * If the module version is already set
*/
public Builder version(Version v) {
- if (version != null)
- throw new IllegalStateException("module version already set");
version = requireNonNull(v);
return this;
}
@@ -1494,16 +1895,11 @@ public class ModuleDescriptor
*
* @throws IllegalArgumentException
* If {@code v} is null or cannot be parsed as a version string
- * @throws IllegalStateException
- * If the module version is already set
*
* @see Version#parse(String)
*/
public Builder version(String v) {
- if (version != null)
- throw new IllegalStateException("module version already set");
- version = Version.parse(v);
- return this;
+ return version(Version.parse(v));
}
/**
@@ -1516,12 +1912,8 @@ public class ModuleDescriptor
*
* @throws IllegalArgumentException
* If {@code mainClass} is null or is not a legal Java identifier
- * @throws IllegalStateException
- * If the module main class is already set
*/
public Builder mainClass(String mc) {
- if (mainClass != null)
- throw new IllegalStateException("main class already set");
mainClass = requireJavaIdentifier("main class name", mc);
return this;
}
@@ -1536,12 +1928,8 @@ public class ModuleDescriptor
*
* @throws IllegalArgumentException
* If {@code name} is null or the empty String
- * @throws IllegalStateException
- * If the operating system name is already set
*/
public Builder osName(String name) {
- if (osName != null)
- throw new IllegalStateException("OS name already set");
if (name == null || name.isEmpty())
throw new IllegalArgumentException("OS name is null or empty");
osName = name;
@@ -1558,12 +1946,8 @@ public class ModuleDescriptor
*
* @throws IllegalArgumentException
* If {@code name} is null or the empty String
- * @throws IllegalStateException
- * If the operating system architecture is already set
*/
public Builder osArch(String arch) {
- if (osArch != null)
- throw new IllegalStateException("OS arch already set");
if (arch == null || arch.isEmpty())
throw new IllegalArgumentException("OS arch is null or empty");
osArch = arch;
@@ -1580,12 +1964,8 @@ public class ModuleDescriptor
*
* @throws IllegalArgumentException
* If {@code name} is null or the empty String
- * @throws IllegalStateException
- * If the operating system version is already set
*/
public Builder osVersion(String version) {
- if (osVersion != null)
- throw new IllegalStateException("OS version already set");
if (version == null || version.isEmpty())
throw new IllegalArgumentException("OS version is null or empty");
osVersion = version;
@@ -1597,7 +1977,6 @@ public class ModuleDescriptor
return this;
}
-
/* package */ Builder synthetic(boolean v) {
this.synthetic = v;
return this;
@@ -1609,16 +1988,24 @@ public class ModuleDescriptor
* @return The module descriptor
*/
public ModuleDescriptor build() {
- assert name != null;
+ Set requires = new HashSet<>(this.requires.values());
+
+ Set packages = new HashSet<>(exportedAndOpenPackages());
+ packages.addAll(concealedPackages);
+
+ Set exports = new HashSet<>(this.exports.values());
+ Set opens = new HashSet<>(this.opens.values());
+
+ Set provides = new HashSet<>(this.provides.values());
- Set packages = new HashSet<>(conceals);
- packages.addAll(exportedPackages());
return new ModuleDescriptor(name,
+ open,
automatic,
synthetic,
requires,
- uses,
exports,
+ opens,
+ uses,
provides,
version,
mainClass,
@@ -1631,7 +2018,6 @@ public class ModuleDescriptor
}
-
/**
* Compares this module descriptor to another.
*
@@ -1689,11 +2075,13 @@ public class ModuleDescriptor
return false;
ModuleDescriptor that = (ModuleDescriptor)ob;
return (name.equals(that.name)
+ && open == that.open
&& automatic == that.automatic
&& synthetic == that.synthetic
&& requires.equals(that.requires)
- && uses.equals(that.uses)
&& exports.equals(that.exports)
+ && opens.equals(that.opens)
+ && uses.equals(that.uses)
&& provides.equals(that.provides)
&& Objects.equals(version, that.version)
&& Objects.equals(mainClass, that.mainClass)
@@ -1720,11 +2108,13 @@ public class ModuleDescriptor
int hc = hash;
if (hc == 0) {
hc = name.hashCode();
+ hc = hc * 43 + Boolean.hashCode(open);
hc = hc * 43 + Boolean.hashCode(automatic);
hc = hc * 43 + Boolean.hashCode(synthetic);
hc = hc * 43 + requires.hashCode();
- hc = hc * 43 + uses.hashCode();
hc = hc * 43 + exports.hashCode();
+ hc = hc * 43 + opens.hashCode();
+ hc = hc * 43 + uses.hashCode();
hc = hc * 43 + provides.hashCode();
hc = hc * 43 + Objects.hashCode(version);
hc = hc * 43 + Objects.hashCode(mainClass);
@@ -1748,36 +2138,101 @@ public class ModuleDescriptor
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("Module { name: ").append(toNameAndVersion());
+
+ if (isOpen())
+ sb.append("open ");
+ sb.append("module { name: ").append(toNameAndVersion());
if (!requires.isEmpty())
sb.append(", ").append(requires);
if (!uses.isEmpty())
sb.append(", ").append(uses);
if (!exports.isEmpty())
sb.append(", exports: ").append(exports);
+ if (!opens.isEmpty())
+ sb.append(", opens: ").append(opens);
if (!provides.isEmpty()) {
- sb.append(", provides: [");
- for (Map.Entry entry : provides.entrySet()) {
- sb.append(entry.getKey())
- .append(" with ")
- .append(entry.getValue());
- }
- sb.append("]");
+ sb.append(", provides: ").append(provides);
}
sb.append(" }");
return sb.toString();
}
+
+ /**
+ * Instantiates a builder to build a module descriptor.
+ *
+ * @param name
+ * The module name
+ *
+ * @return A new builder
+ *
+ * @throws IllegalArgumentException
+ * If the module name is {@code null} or is not a legal Java
+ * identifier
+ */
+ public static Builder module(String name) {
+ return new Builder(name, true);
+ }
+
+ /**
+ * Instantiates a builder to build a module descriptor for an open module.
+ * An open module does not declare any open packages but the resulting
+ * module is treated as if all packages are open.
+ *
+ * As an example, the following creates a module descriptor for an open
+ * name "{@code m}" containing two packages, one of which is exported.
+ * {@code
+ * ModuleDescriptor descriptor = ModuleDescriptor.openModule("m")
+ * .requires("java.base")
+ * .exports("p")
+ * .contains("q")
+ * .build();
+ * }
+ *
+ * @param name
+ * The module name
+ *
+ * @return A new builder that builds an open module
+ *
+ * @throws IllegalArgumentException
+ * If the module name is {@code null} or is not a legal Java
+ * identifier
+ */
+ public static Builder openModule(String name) {
+ return new Builder(name, true).open(true);
+ }
+
+ /**
+ * Instantiates a builder to build a module descriptor for an automatic
+ * module. Automatic modules receive special treatment during resolution
+ * (see {@link Configuration}) so that they read all other modules. When
+ * Instantiated in the Java virtual machine as a {@link java.lang.reflect.Module}
+ * then the Module reads every unnamed module in the Java virtual machine.
+ *
+ * @param name
+ * The module name
+ *
+ * @return A new builder that builds an automatic module
+ *
+ * @throws IllegalArgumentException
+ * If the module name is {@code null} or is not a legal Java
+ * identifier
+ *
+ * @see ModuleFinder#of(Path[])
+ */
+ public static Builder automaticModule(String name) {
+ return new Builder(name, true).automatic(true);
+ }
+
+
/**
* Reads the binary form of a module declaration from an input stream
* as a module descriptor.
*
* If the descriptor encoded in the input stream does not indicate a
- * set of concealed packages then the {@code packageFinder} will be
- * invoked. The packages it returns, except for those indicated as
- * exported in the encoded descriptor, will be considered to be concealed.
- * If the {@code packageFinder} throws an {@link UncheckedIOException} then
- * {@link IOException} cause will be re-thrown.
+ * set of packages in the module then the {@code packageFinder} will be
+ * invoked. If the {@code packageFinder} throws an {@link UncheckedIOException}
+ * then {@link IOException} cause will be re-thrown.
*
* If there are bytes following the module descriptor then it is
* implementation specific as to whether those bytes are read, ignored,
@@ -1789,12 +2244,12 @@ public class ModuleDescriptor
*
* @apiNote The {@code packageFinder} parameter is for use when reading
* module descriptors from legacy module-artifact formats that do not
- * record the set of concealed packages in the descriptor itself.
+ * record the set of packages in the descriptor itself.
*
* @param in
* The input stream
* @param packageFinder
- * A supplier that can produce a set of package names
+ * A supplier that can produce the set of packages
*
* @return The module descriptor
*
@@ -1834,10 +2289,7 @@ public class ModuleDescriptor
* as a module descriptor.
*
*
If the descriptor encoded in the byte buffer does not indicate a
- * set of concealed packages then the {@code packageFinder} will be
- * invoked. The packages it returns, except for those indicated as
- * exported in the encoded descriptor, will be considered to be
- * concealed.
+ * set of packages then the {@code packageFinder} will be invoked.
*
* The module descriptor is read from the buffer stating at index
* {@code p}, where {@code p} is the buffer's {@link ByteBuffer#position()
@@ -1853,12 +2305,12 @@ public class ModuleDescriptor
*
* @apiNote The {@code packageFinder} parameter is for use when reading
* module descriptors from legacy module-artifact formats that do not
- * record the set of concealed packages in the descriptor itself.
+ * record the set of packages in the descriptor itself.
*
* @param bb
* The byte buffer
* @param packageFinder
- * A supplier that can produce a set of package names
+ * A supplier that can produce the set of packages
*
* @return The module descriptor
*
@@ -1908,6 +2360,15 @@ public class ModuleDescriptor
}
}
+ /**
+ * Returns a string containing the given set of modifiers and label.
+ */
+ private static String toString(Set mods, String what) {
+ return (Stream.concat(mods.stream().map(e -> e.toString().toLowerCase()),
+ Stream.of(what)))
+ .collect(Collectors.joining(" "));
+ }
+
static {
/**
* Setup the shared secret to allow code in other packages access
@@ -1915,25 +2376,48 @@ public class ModuleDescriptor
*/
jdk.internal.misc.SharedSecrets
.setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() {
+ @Override
+ public Builder newModuleBuilder(String mn, boolean strict) {
+ return new Builder(mn, strict);
+ }
+
+ @Override
+ public Builder newOpenModuleBuilder(String mn, boolean strict) {
+ return new Builder(mn, strict).open(true);
+ }
@Override
public Requires newRequires(Set ms, String mn) {
- return new Requires(ms, mn, false);
+ return new Requires(ms, mn, true);
}
@Override
- public Exports newExports(String source, Set targets) {
- return new Exports(source, targets, false);
+ public Exports newExports(Set ms, String source) {
+ return new Exports(ms, source, Collections.emptySet(), true);
}
@Override
- public Exports newExports(String source) {
- return new Exports(source, false);
+ public Exports newExports(Set ms,
+ String source,
+ Set targets) {
+ return new Exports(ms, source, targets, true);
}
@Override
- public Provides newProvides(String service, Set providers) {
- return new Provides(service, providers, false);
+ public Opens newOpens(Set ms,
+ String source,
+ Set targets) {
+ return new Opens(ms, source, targets, true);
+ }
+
+ @Override
+ public Opens newOpens(Set ms, String source) {
+ return new Opens(ms, source, Collections.emptySet(), true);
+ }
+
+ @Override
+ public Provides newProvides(String service, List providers) {
+ return new Provides(service, providers, true);
}
@Override
@@ -1949,25 +2433,30 @@ public class ModuleDescriptor
@Override
public ModuleDescriptor newModuleDescriptor(String name,
+ boolean open,
boolean automatic,
boolean synthetic,
Set requires,
- Set uses,
Set exports,
- Map provides,
+ Set opens,
+ Set uses,
+ Set provides,
Version version,
String mainClass,
String osName,
String osArch,
String osVersion,
Set packages,
- ModuleHashes hashes) {
+ ModuleHashes hashes,
+ int hashCode) {
return new ModuleDescriptor(name,
+ open,
automatic,
synthetic,
requires,
- uses,
exports,
+ opens,
+ uses,
provides,
version,
mainClass,
@@ -1975,7 +2464,14 @@ public class ModuleDescriptor
osArch,
osVersion,
packages,
- hashes);
+ hashes,
+ hashCode,
+ false);
+ }
+
+ @Override
+ public Optional hashes(ModuleDescriptor descriptor) {
+ return descriptor.hashes();
}
@Override
@@ -1994,11 +2490,6 @@ public class ModuleDescriptor
return new ModuleReference(descriptor, location, s, true, null);
}
- @Override
- public Optional hashes(ModuleDescriptor descriptor) {
- return descriptor.hashes();
- }
-
@Override
public ModuleFinder newModulePath(Runtime.Version version,
boolean isLinkPhase,
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java
index 4e72a52cf22..4b98bf48416 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java
@@ -233,10 +233,11 @@ public interface ModuleFinder {
* ModuleDescriptor.Version} and ignored if it cannot be parsed as
* a {@code Version}.
*
- * For the module name, then all non-alphanumeric
- * characters ({@code [^A-Za-z0-9])} are replaced with a dot
- * ({@code "."}), all repeating dots are replaced with one dot,
- * and all leading and trailing dots are removed.
+ * For the module name, then any trailing digits and dots
+ * are removed, all non-alphanumeric characters ({@code [^A-Za-z0-9]})
+ * are replaced with a dot ({@code "."}), all repeating dots are
+ * replaced with one dot, and all leading and trailing dots are
+ * removed.
*
* As an example, a JAR file named {@code foo-bar.jar} will
* derive a module name {@code foo.bar} and no version. A JAR file
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java b/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java
index 1d0e22fec11..b1c82819aa9 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,13 +31,17 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
-import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.module.ModuleDescriptor.Builder;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Opens;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
@@ -56,15 +60,12 @@ import static jdk.internal.module.ClassFileConstants.*;
final class ModuleInfo {
- // supplies the set of packages when ConcealedPackages not present
+ // supplies the set of packages when ModulePackages attribute not present
private final Supplier> packageFinder;
- // indicates if the Hashes attribute should be parsed
+ // indicates if the ModuleHashes attribute should be parsed
private final boolean parseHashes;
- // the builder, created when parsing
- private ModuleDescriptor.Builder builder;
-
private ModuleInfo(Supplier> pf, boolean ph) {
packageFinder = pf;
parseHashes = ph;
@@ -86,9 +87,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf).doRead(new DataInputStream(in));
- } catch (IllegalArgumentException iae) {
- // IllegalArgumentException means a malformed class
- throw invalidModuleDescriptor(iae.getMessage());
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
}
@@ -105,9 +105,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
- } catch (IllegalArgumentException iae) {
- // IllegalArgumentException means a malformed class
- throw invalidModuleDescriptor(iae.getMessage());
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
@@ -117,7 +116,7 @@ final class ModuleInfo {
/**
* Reads a {@code module-info.class} from the given byte buffer
- * but ignore the {@code Hashes} attribute.
+ * but ignore the {@code ModuleHashes} attribute.
*
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
@@ -127,8 +126,8 @@ final class ModuleInfo {
{
try {
return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
- } catch (IllegalArgumentException iae) {
- throw invalidModuleDescriptor(iae.getMessage());
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
@@ -164,12 +163,8 @@ final class ModuleInfo {
throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
int this_class = in.readUnsignedShort();
- String mn = cpool.getClassName(this_class);
- int suffix = mn.indexOf("/module-info");
- if (suffix < 1)
- throw invalidModuleDescriptor("this_class not of form name/module-info");
- mn = mn.substring(0, suffix).replace('/', '.');
- builder = new ModuleDescriptor.Builder(mn);
+ if (this_class != 0)
+ throw invalidModuleDescriptor("this_class must be 0");
int super_class = in.readUnsignedShort();
if (super_class > 0)
@@ -192,6 +187,13 @@ final class ModuleInfo {
// the names of the attributes found in the class file
Set attributes = new HashSet<>();
+ Builder builder = null;
+ Set packages = null;
+ String version = null;
+ String mainClass = null;
+ String[] osValues = null;
+ ModuleHashes hashes = null;
+
for (int i = 0; i < attributes_count ; i++) {
int name_index = in.readUnsignedShort();
String attribute_name = cpool.getUtf8(name_index);
@@ -206,28 +208,28 @@ final class ModuleInfo {
switch (attribute_name) {
case MODULE :
- readModuleAttribute(mn, in, cpool);
+ builder = readModuleAttribute(in, cpool);
break;
- case CONCEALED_PACKAGES :
- readConcealedPackagesAttribute(in, cpool);
+ case MODULE_PACKAGES :
+ packages = readModulePackagesAttribute(in, cpool);
break;
- case VERSION :
- readVersionAttribute(in, cpool);
+ case MODULE_VERSION :
+ version = readModuleVersionAttribute(in, cpool);
break;
- case MAIN_CLASS :
- readMainClassAttribute(in, cpool);
+ case MODULE_MAIN_CLASS :
+ mainClass = readModuleMainClassAttribute(in, cpool);
break;
- case TARGET_PLATFORM :
- readTargetPlatformAttribute(in, cpool);
+ case MODULE_TARGET :
+ osValues = readModuleTargetAttribute(in, cpool);
break;
- case HASHES :
+ case MODULE_HASHES :
if (parseHashes) {
- readHashesAttribute(in, cpool);
+ hashes = readModuleHashesAttribute(in, cpool);
} else {
in.skipBytes(length);
}
@@ -245,53 +247,91 @@ final class ModuleInfo {
}
// the Module attribute is required
- if (!attributes.contains(MODULE)) {
+ if (builder == null) {
throw invalidModuleDescriptor(MODULE + " attribute not found");
}
- // If the ConcealedPackages attribute is not present then the
- // packageFinder is used to to find any non-exported packages.
- if (!attributes.contains(CONCEALED_PACKAGES) && packageFinder != null) {
- Set pkgs;
+ // If the ModulePackages attribute is not present then the packageFinder
+ // is used to find the set of packages
+ boolean usedPackageFinder = false;
+ if (packages == null && packageFinder != null) {
try {
- pkgs = new HashSet<>(packageFinder.get());
+ packages = new HashSet<>(packageFinder.get());
} catch (UncheckedIOException x) {
throw x.getCause();
}
- pkgs.removeAll(builder.exportedPackages());
- builder.conceals(pkgs);
+ usedPackageFinder = true;
+ }
+ if (packages != null) {
+ for (String pn : builder.exportedAndOpenPackages()) {
+ if (!packages.contains(pn)) {
+ String tail;
+ if (usedPackageFinder) {
+ tail = " not found by package finder";
+ } else {
+ tail = " missing from ModulePackages attribute";
+ }
+ throw invalidModuleDescriptor("Package " + pn + tail);
+ }
+ packages.remove(pn);
+ }
+ builder.contains(packages);
}
- // Was the Synthetic attribute present?
- if (attributes.contains(SYNTHETIC))
- builder.synthetic(true);
+ if (version != null)
+ builder.version(version);
+ if (mainClass != null)
+ builder.mainClass(mainClass);
+ if (osValues != null) {
+ if (osValues[0] != null) builder.osName(osValues[0]);
+ if (osValues[1] != null) builder.osArch(osValues[1]);
+ if (osValues[2] != null) builder.osVersion(osValues[2]);
+ }
+ if (hashes != null)
+ builder.hashes(hashes);
return builder.build();
}
/**
- * Reads the Module attribute.
+ * Reads the Module attribute, returning the ModuleDescriptor.Builder to
+ * build the corresponding ModuleDescriptor.
*/
- private void readModuleAttribute(String mn, DataInput in, ConstantPool cpool)
+ private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
+ // module_name
+ int module_name_index = in.readUnsignedShort();
+ String mn = cpool.getUtf8AsBinaryName(module_name_index);
+
+ Builder builder = new ModuleDescriptor.Builder(mn, /*strict*/ false);
+
+ int module_flags = in.readUnsignedShort();
+ boolean open = ((module_flags & ACC_OPEN) != 0);
+ if (open)
+ builder.open(true);
+ if ((module_flags & ACC_SYNTHETIC) != 0)
+ builder.synthetic(true);
+
int requires_count = in.readUnsignedShort();
boolean requiresJavaBase = false;
for (int i=0; i mods;
+ String dn = cpool.getUtf8AsBinaryName(index);
+ Set mods;
if (flags == 0) {
mods = Collections.emptySet();
} else {
mods = new HashSet<>();
- if ((flags & ACC_PUBLIC) != 0)
- mods.add(Modifier.PUBLIC);
+ if ((flags & ACC_TRANSITIVE) != 0)
+ mods.add(Requires.Modifier.TRANSITIVE);
+ if ((flags & ACC_STATIC_PHASE) != 0)
+ mods.add(Requires.Modifier.STATIC);
if ((flags & ACC_SYNTHETIC) != 0)
- mods.add(Modifier.SYNTHETIC);
+ mods.add(Requires.Modifier.SYNTHETIC);
if ((flags & ACC_MANDATED) != 0)
- mods.add(Modifier.MANDATED);
+ mods.add(Requires.Modifier.MANDATED);
}
builder.requires(mods, dn);
if (dn.equals("java.base"))
@@ -311,17 +351,66 @@ final class ModuleInfo {
if (exports_count > 0) {
for (int i=0; i mods;
+ int flags = in.readUnsignedShort();
+ if (flags == 0) {
+ mods = Collections.emptySet();
+ } else {
+ mods = new HashSet<>();
+ if ((flags & ACC_SYNTHETIC) != 0)
+ mods.add(Exports.Modifier.SYNTHETIC);
+ if ((flags & ACC_MANDATED) != 0)
+ mods.add(Exports.Modifier.MANDATED);
+ }
+
int exports_to_count = in.readUnsignedShort();
if (exports_to_count > 0) {
Set targets = new HashSet<>(exports_to_count);
for (int j=0; j 0) {
+ if (open) {
+ throw invalidModuleDescriptor("The opens table for an open"
+ + " module must be 0 length");
+ }
+ for (int i=0; i mods;
+ int flags = in.readUnsignedShort();
+ if (flags == 0) {
+ mods = Collections.emptySet();
+ } else {
+ mods = new HashSet<>();
+ if ((flags & ACC_SYNTHETIC) != 0)
+ mods.add(Opens.Modifier.SYNTHETIC);
+ if ((flags & ACC_MANDATED) != 0)
+ mods.add(Opens.Modifier.MANDATED);
+ }
+
+ int open_to_count = in.readUnsignedShort();
+ if (open_to_count > 0) {
+ Set targets = new HashSet<>(open_to_count);
+ for (int j=0; j 0) {
for (int i=0; i 0) {
- Map> pm = new HashMap<>();
for (int i=0; i providers = pm.get(sn);
- if (providers == null) {
- providers = new LinkedHashSet<>(); // preserve order
- pm.put(sn, providers);
+ String sn = cpool.getClassNameAsBinaryName(index);
+ int with_count = in.readUnsignedShort();
+ List providers = new ArrayList<>(with_count);
+ for (int j=0; j> e : pm.entrySet()) {
- builder.provides(e.getKey(), e.getValue());
+ builder.provides(sn, providers);
}
}
+
+ return builder;
}
/**
- * Reads the ConcealedPackages attribute
+ * Reads the ModulePackages attribute
*/
- private void readConcealedPackagesAttribute(DataInput in, ConstantPool cpool)
+ private Set readModulePackagesAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int package_count = in.readUnsignedShort();
+ Set packages = new HashSet<>(package_count);
for (int i=0; i map = new HashMap<>(hash_count);
+ Map map = new HashMap<>(hash_count);
for (int i=0; i notAllowed = predefinedNotAllowed;
@@ -477,12 +569,11 @@ final class ModuleInfo {
"LineNumberTable",
"LocalVariableTable",
"LocalVariableTypeTable",
- "RuntimeVisibleAnnotations",
- "RuntimeInvisibleAnnotations",
"RuntimeVisibleParameterAnnotations",
"RuntimeInvisibleParameterAnnotations",
"RuntimeVisibleTypeAnnotations",
"RuntimeInvisibleTypeAnnotations",
+ "Synthetic",
"AnnotationDefault",
"BootstrapMethods",
"MethodParameters");
@@ -495,7 +586,6 @@ final class ModuleInfo {
private static volatile Set predefinedNotAllowed;
-
/**
* The constant pool in a class file.
*/
@@ -628,6 +718,11 @@ final class ModuleInfo {
return getUtf8(((IndexEntry) e).index);
}
+ String getClassNameAsBinaryName(int index) {
+ String value = getClassName(index);
+ return value.replace('/', '.'); // internal form -> binary name
+ }
+
String getUtf8(int index) {
checkIndex(index);
Entry e = pool[index];
@@ -638,6 +733,11 @@ final class ModuleInfo {
return (String) (((ValueEntry) e).value);
}
+ String getUtf8AsBinaryName(int index) {
+ String value = getUtf8(index);
+ return value.replace('/', '.'); // internal -> binary name
+ }
+
void checkIndex(int index) {
if (index < 1 || index >= pool.length)
throw invalidModuleDescriptor("Index into constant pool out of range");
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
index fa40f3b7958..15df0307180 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
@@ -40,9 +40,10 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -54,7 +55,6 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
@@ -399,8 +399,8 @@ class ModulePath implements ModuleFinder {
*
* 1. The module name (and optionally the version) is derived from the file
* name of the JAR file
- * 2. The packages of all .class files in the JAR file are exported
- * 3. It has no module-private/concealed packages
+ * 2. All packages are exported and open
+ * 3. It has no non-exported/non-open packages
* 4. The contents of any META-INF/services configuration files are mapped
* to "provides" declarations
* 5. The Main-Class attribute in the main attributes of the JAR manifest
@@ -440,8 +440,7 @@ class ModulePath implements ModuleFinder {
// Builder throws IAE if module name is empty or invalid
ModuleDescriptor.Builder builder
- = new ModuleDescriptor.Builder(mn)
- .automatic()
+ = ModuleDescriptor.automaticModule(mn)
.requires(Set.of(Requires.Modifier.MANDATED), "java.base");
if (vs != null)
builder.version(vs);
@@ -455,13 +454,12 @@ class ModulePath implements ModuleFinder {
Set resources = map.get(Boolean.FALSE);
Set configFiles = map.get(Boolean.TRUE);
-
- // all packages are exported
+ // all packages are exported and open
resources.stream()
.map(this::toPackageName)
.flatMap(Optional::stream)
.distinct()
- .forEach(builder::exports);
+ .forEach(pn -> builder.exports(pn).opens(pn));
// map names of service configuration files to service names
Set serviceNames = configFiles.stream()
@@ -472,7 +470,7 @@ class ModulePath implements ModuleFinder {
// parse each service configuration file
for (String sn : serviceNames) {
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
- Set providerClasses = new LinkedHashSet<>();
+ List providerClasses = new ArrayList<>();
try (InputStream in = jf.getInputStream(entry)) {
BufferedReader reader
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
@@ -493,7 +491,7 @@ class ModulePath implements ModuleFinder {
Attributes attrs = man.getMainAttributes();
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
if (mainClass != null)
- builder.mainClass(mainClass);
+ builder.mainClass(mainClass.replace("/", "."));
}
return builder.build();
@@ -504,6 +502,7 @@ class ModulePath implements ModuleFinder {
*/
private static class Patterns {
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
+ static final Pattern TRAILING_VERSION = Pattern.compile("(\\.|\\d)*$");
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
@@ -514,6 +513,9 @@ class ModulePath implements ModuleFinder {
* Clean up candidate module name derived from a JAR file name.
*/
private static String cleanModuleName(String mn) {
+ // drop trailing version from name
+ mn = Patterns.TRAILING_VERSION.matcher(mn).replaceAll("");
+
// replace non-alphanumeric
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java b/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java
index 34d13639736..cbf84cf938d 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java
@@ -60,8 +60,8 @@ public final class ModuleReference {
// the function that computes the hash of this module reference
private final HashSupplier hasher;
- // cached hash string to avoid needing to compute it many times
- private String cachedHash;
+ // cached hash to avoid needing to compute it many times
+ private byte[] cachedHash;
/**
@@ -183,13 +183,13 @@ public final class ModuleReference {
}
/**
- * Computes the hash of this module, returning it as a hex string.
- * Returns {@code null} if the hash cannot be computed.
+ * Computes the hash of this module. Returns {@code null} if the hash
+ * cannot be computed.
*
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
- String computeHash(String algorithm) {
- String result = cachedHash;
+ byte[] computeHash(String algorithm) {
+ byte[] result = cachedHash;
if (result != null)
return result;
if (hasher == null)
@@ -211,8 +211,11 @@ public final class ModuleReference {
public int hashCode() {
int hc = hash;
if (hc == 0) {
- hc = Objects.hash(descriptor, location, readerSupplier, hasher,
- Boolean.valueOf(patched));
+ hc = descriptor.hashCode();
+ hc = 43 * hc + readerSupplier.hashCode();
+ hc = 43 * hc + Objects.hashCode(location);
+ hc = 43 * hc + Objects.hashCode(hasher);
+ hc = 43 * hc + Boolean.hashCode(patched);
if (hc == 0)
hc = -1;
hash = hc;
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java b/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java
index 8393bd0f223..e53ba0441d3 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java
@@ -51,6 +51,7 @@ import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.ModulePatcher;
@@ -80,9 +81,8 @@ class ModuleReferences {
HashSupplier hasher) {
ModuleReference mref = new ModuleReference(md, uri, supplier, hasher);
-
if (JLA.getBootLayer() == null)
- mref = ModulePatcher.interposeIfNeeded(mref);
+ mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
return mref;
}
@@ -93,7 +93,7 @@ class ModuleReferences {
static ModuleReference newJarModule(ModuleDescriptor md, Path file) {
URI uri = file.toUri();
Supplier supplier = () -> new JarModuleReader(file, uri);
- HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
+ HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, uri, supplier, hasher);
}
@@ -103,7 +103,7 @@ class ModuleReferences {
static ModuleReference newJModModule(ModuleDescriptor md, Path file) {
URI uri = file.toUri();
Supplier supplier = () -> new JModModuleReader(file, uri);
- HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
+ HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a);
return newModule(md, file.toUri(), supplier, hasher);
}
diff --git a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java
index adc60da8892..5f716b131b7 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java
@@ -26,9 +26,12 @@
package java.lang.module;
import java.io.PrintStream;
+import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.lang.reflect.Layer;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
@@ -47,12 +50,15 @@ import jdk.internal.module.ModuleHashes;
/**
* The resolver used by {@link Configuration#resolveRequires} and
* {@link Configuration#resolveRequiresAndUses}.
+ *
+ * @implNote The resolver is used at VM startup and so deliberately avoids
+ * using lambda and stream usages in code paths used during startup.
*/
final class Resolver {
private final ModuleFinder beforeFinder;
- private final Configuration parent;
+ private final List parents;
private final ModuleFinder afterFinder;
private final PrintStream traceOutput;
@@ -61,11 +67,11 @@ final class Resolver {
Resolver(ModuleFinder beforeFinder,
- Configuration parent,
+ List parents,
ModuleFinder afterFinder,
PrintStream traceOutput) {
this.beforeFinder = beforeFinder;
- this.parent = parent;
+ this.parents = parents;
this.afterFinder = afterFinder;
this.traceOutput = traceOutput;
}
@@ -85,10 +91,12 @@ final class Resolver {
// find root module
ModuleReference mref = findWithBeforeFinder(root);
if (mref == null) {
- if (parent.findModule(root).isPresent()) {
+
+ if (findInParent(root) != null) {
// in parent, nothing to do
continue;
}
+
mref = findWithAfterFinder(root);
if (mref == null) {
fail("Module %s not found", root);
@@ -125,13 +133,21 @@ final class Resolver {
// process dependences
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
+
+ // only required at compile-time
+ if (requires.modifiers().contains(Modifier.STATIC))
+ continue;
+
String dn = requires.name();
// find dependence
ModuleReference mref = findWithBeforeFinder(dn);
if (mref == null) {
- if (parent.findModule(dn).isPresent())
+
+ if (findInParent(dn) != null) {
+ // dependence is in parent
continue;
+ }
mref = findWithAfterFinder(dn);
if (mref == null) {
@@ -174,7 +190,9 @@ final class Resolver {
ModuleDescriptor descriptor = mref.descriptor();
if (!descriptor.provides().isEmpty()) {
- for (String sn : descriptor.provides().keySet()) {
+ for (Provides provides : descriptor.provides()) {
+ String sn = provides.service();
+
// computeIfAbsent
Set providers = availableProviders.get(sn);
if (providers == null) {
@@ -191,19 +209,23 @@ final class Resolver {
Deque q = new ArrayDeque<>();
// the initial set of modules that may use services
- Set candidateConsumers = new HashSet<>();
- Configuration p = parent;
- while (p != null) {
- candidateConsumers.addAll(p.descriptors());
- p = p.parent().orElse(null);
+ Set initialConsumers;
+ if (Layer.boot() == null) {
+ initialConsumers = new HashSet<>();
+ } else {
+ initialConsumers = parents.stream()
+ .flatMap(Configuration::configurations)
+ .distinct()
+ .flatMap(c -> c.descriptors().stream())
+ .collect(Collectors.toSet());
}
for (ModuleReference mref : nameToReference.values()) {
- candidateConsumers.add(mref.descriptor());
+ initialConsumers.add(mref.descriptor());
}
-
// Where there is a consumer of a service then resolve all modules
// that provide an implementation of that service
+ Set candidateConsumers = initialConsumers;
do {
for (ModuleDescriptor descriptor : candidateConsumers) {
if (!descriptor.uses().isEmpty()) {
@@ -234,7 +256,6 @@ final class Resolver {
}
candidateConsumers = resolve(q);
-
} while (!candidateConsumers.isEmpty());
return this;
@@ -429,21 +450,21 @@ final class Resolver {
for (String dn : hashes.names()) {
ModuleReference other = nameToReference.get(dn);
if (other == null) {
- other = parent.findModule(dn)
- .map(ResolvedModule::reference)
- .orElse(null);
+ ResolvedModule resolvedModule = findInParent(dn);
+ if (resolvedModule != null)
+ other = resolvedModule.reference();
}
// skip checking the hash if the module has been patched
if (other != null && !other.isPatched()) {
- String recordedHash = hashes.hashFor(dn);
- String actualHash = other.computeHash(algorithm);
+ byte[] recordedHash = hashes.hashFor(dn);
+ byte[] actualHash = other.computeHash(algorithm);
if (actualHash == null)
fail("Unable to compute the hash of module %s", dn);
- if (!recordedHash.equals(actualHash)) {
+ if (!Arrays.equals(recordedHash, actualHash)) {
fail("Hash of %s (%s) differs to expected hash (%s)" +
- " recorded in %s", dn, actualHash, recordedHash,
- descriptor.name());
+ " recorded in %s", dn, toHexString(actualHash),
+ toHexString(recordedHash), descriptor.name());
}
}
}
@@ -451,52 +472,68 @@ final class Resolver {
}
}
+ private static String toHexString(byte[] ba) {
+ StringBuilder sb = new StringBuilder(ba.length * 2);
+ for (byte b: ba) {
+ sb.append(String.format("%02x", b & 0xff));
+ }
+ return sb.toString();
+ }
+
/**
* Computes the readability graph for the modules in the given Configuration.
*
* The readability graph is created by propagating "requires" through the
- * "public requires" edges of the module dependence graph. So if the module
- * dependence graph has m1 requires m2 && m2 requires public m3 then the
- * resulting readability graph will contain m1 reads m2, m1 reads m3, and
- * m2 reads m3.
+ * "requires transitive" edges of the module dependence graph. So if the
+ * module dependence graph has m1 requires m2 && m2 requires transitive m3
+ * then the resulting readability graph will contain m1 reads m2, m1 reads m3,
+ * and m2 reads m3.
*/
private Map> makeGraph(Configuration cf) {
+ // initial capacity of maps to avoid resizing
+ int capacity = 1 + (4 * nameToReference.size())/ 3;
+
// the "reads" graph starts as a module dependence graph and
// is iteratively updated to be the readability graph
- Map> g1 = new HashMap<>();
+ Map> g1 = new HashMap<>(capacity);
- // the "requires public" graph, contains requires public edges only
- Map> g2 = new HashMap<>();
+ // the "requires transitive" graph, contains requires transitive edges only
+ Map> g2;
-
- // need "requires public" from the modules in parent configurations as
- // there may be selected modules that have a dependency on modules in
+ // need "requires transitive" from the modules in parent configurations
+ // as there may be selected modules that have a dependency on modules in
// the parent configuration.
-
- Configuration p = parent;
- while (p != null) {
- for (ModuleDescriptor descriptor : p.descriptors()) {
- String name = descriptor.name();
- ResolvedModule m1 = p.findModule(name)
- .orElseThrow(() -> new InternalError(name + " not found"));
- for (ModuleDescriptor.Requires requires : descriptor.requires()) {
- if (requires.modifiers().contains(Modifier.PUBLIC)) {
- String dn = requires.name();
- ResolvedModule m2 = p.findModule(dn)
- .orElseThrow(() -> new InternalError(dn + " not found"));
- g2.computeIfAbsent(m1, k -> new HashSet<>()).add(m2);
- }
- }
- }
-
- p = p.parent().orElse(null);
+ if (Layer.boot() == null) {
+ g2 = new HashMap<>(capacity);
+ } else {
+ g2 = parents.stream()
+ .flatMap(Configuration::configurations)
+ .distinct()
+ .flatMap(c ->
+ c.modules().stream().flatMap(m1 ->
+ m1.descriptor().requires().stream()
+ .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE))
+ .flatMap(r -> {
+ Optional m2 = c.findModule(r.name());
+ assert m2.isPresent()
+ || r.modifiers().contains(Modifier.STATIC);
+ return m2.stream();
+ })
+ .map(m2 -> Map.entry(m1, m2))
+ )
+ )
+ // stream of m1->m2
+ .collect(Collectors.groupingBy(Map.Entry::getKey,
+ HashMap::new,
+ Collectors.mapping(Map.Entry::getValue, Collectors.toSet())
+ ));
}
// populate g1 and g2 with the dependences from the selected modules
- Map nameToResolved = new HashMap<>();
+ Map nameToResolved = new HashMap<>(capacity);
for (ModuleReference mref : nameToReference.values()) {
ModuleDescriptor descriptor = mref.descriptor();
@@ -505,20 +542,21 @@ final class Resolver {
ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
Set reads = new HashSet<>();
- Set requiresPublic = new HashSet<>();
+ Set requiresTransitive = new HashSet<>();
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
String dn = requires.name();
- ResolvedModule m2;
+ ResolvedModule m2 = null;
ModuleReference mref2 = nameToReference.get(dn);
if (mref2 != null) {
// same configuration
m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
} else {
// parent configuration
- m2 = parent.findModule(dn).orElse(null);
+ m2 = findInParent(dn);
if (m2 == null) {
+ assert requires.modifiers().contains(Modifier.STATIC);
continue;
}
}
@@ -526,9 +564,9 @@ final class Resolver {
// m1 requires m2 => m1 reads m2
reads.add(m2);
- // m1 requires public m2
- if (requires.modifiers().contains(Modifier.PUBLIC)) {
- requiresPublic.add(m2);
+ // m1 requires transitive m2
+ if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
+ requiresTransitive.add(m2);
}
}
@@ -538,7 +576,7 @@ final class Resolver {
if (descriptor.isAutomatic()) {
// reads all selected modules
- // `requires public` all selected automatic modules
+ // `requires transitive` all selected automatic modules
for (ModuleReference mref2 : nameToReference.values()) {
ModuleDescriptor descriptor2 = mref2.descriptor();
String name2 = descriptor2.name();
@@ -548,40 +586,42 @@ final class Resolver {
= computeIfAbsent(nameToResolved, name2, cf, mref2);
reads.add(m2);
if (descriptor2.isAutomatic())
- requiresPublic.add(m2);
+ requiresTransitive.add(m2);
}
}
// reads all modules in parent configurations
- // `requires public` all automatic modules in parent configurations
- p = parent;
- while (p != null) {
- for (ResolvedModule m : p.modules()) {
- reads.add(m);
- if (m.reference().descriptor().isAutomatic())
- requiresPublic.add(m);
- }
- p = p.parent().orElse(null);
+ // `requires transitive` all automatic modules in parent
+ // configurations
+ for (Configuration parent : parents) {
+ parent.configurations()
+ .map(Configuration::modules)
+ .flatMap(Set::stream)
+ .forEach(m -> {
+ reads.add(m);
+ if (m.reference().descriptor().isAutomatic())
+ requiresTransitive.add(m);
+ });
}
-
}
g1.put(m1, reads);
- g2.put(m1, requiresPublic);
+ g2.put(m1, requiresTransitive);
}
- // Iteratively update g1 until there are no more requires public to propagate
+ // Iteratively update g1 until there are no more requires transitive
+ // to propagate
boolean changed;
- Set toAdd = new HashSet<>();
+ List toAdd = new ArrayList<>();
do {
changed = false;
for (Set m1Reads : g1.values()) {
for (ResolvedModule m2 : m1Reads) {
- Set m2RequiresPublic = g2.get(m2);
- if (m2RequiresPublic != null) {
- for (ResolvedModule m3 : m2RequiresPublic) {
+ Set m2RequiresTransitive = g2.get(m2);
+ if (m2RequiresTransitive != null) {
+ for (ResolvedModule m3 : m2RequiresTransitive) {
if (!m1Reads.contains(m3)) {
- // m1 reads m2, m2 requires public m3
+ // m1 reads m2, m2 requires transitive m3
// => need to add m1 reads m3
toAdd.add(m3);
}
@@ -655,7 +695,7 @@ final class Resolver {
// source is exported to descriptor2
String source = export.source();
ModuleDescriptor other
- = packageToExporter.put(source, descriptor2);
+ = packageToExporter.putIfAbsent(source, descriptor2);
if (other != null && other != descriptor2) {
// package might be local to descriptor1
@@ -692,12 +732,8 @@ final class Resolver {
}
// provides S
- for (Map.Entry entry :
- descriptor1.provides().entrySet()) {
- String service = entry.getKey();
- ModuleDescriptor.Provides provides = entry.getValue();
-
- String pn = packageName(service);
+ for (ModuleDescriptor.Provides provides : descriptor1.provides()) {
+ String pn = packageName(provides.service());
if (!packageToExporter.containsKey(pn)) {
fail("Module %s does not read a module that exports %s",
descriptor1.name(), pn);
@@ -717,6 +753,18 @@ final class Resolver {
}
+ /**
+ * Find a module of the given name in the parent configurations
+ */
+ private ResolvedModule findInParent(String mn) {
+ for (Configuration parent : parents) {
+ Optional om = parent.findModule(mn);
+ if (om.isPresent())
+ return om.get();
+ }
+ return null;
+ }
+
/**
* Invokes the beforeFinder to find method to find the given module.
@@ -755,15 +803,18 @@ final class Resolver {
if (afterModules.isEmpty())
return beforeModules;
- if (beforeModules.isEmpty() && parent == Configuration.empty())
+ if (beforeModules.isEmpty()
+ && parents.size() == 1
+ && parents.get(0) == Configuration.empty())
return afterModules;
Set result = new HashSet<>(beforeModules);
for (ModuleReference mref : afterModules) {
String name = mref.descriptor().name();
if (!beforeFinder.find(name).isPresent()
- && !parent.findModule(name).isPresent())
+ && findInParent(name) == null) {
result.add(mref);
+ }
}
return result;
diff --git a/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java b/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java
index 9fae75cb382..6de8ce824cf 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java
@@ -39,6 +39,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -53,6 +54,7 @@ import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.misc.JavaNetUriAccess;
import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleHashes.HashSupplier;
import jdk.internal.module.SystemModules;
@@ -64,7 +66,7 @@ import jdk.internal.perf.PerfCounter;
* run-time image.
*
* The modules linked into the run-time image are assumed to have the
- * ConcealedPackages attribute.
+ * Packages attribute.
*/
class SystemModuleFinder implements ModuleFinder {
@@ -102,8 +104,11 @@ class SystemModuleFinder implements ModuleFinder {
int n = names.length;
moduleCount.add(n);
- Set mods = new HashSet<>(n);
- Map map = new HashMap<>(n);
+ ModuleReference[] mods = new ModuleReference[n];
+
+ @SuppressWarnings(value = {"rawtypes", "unchecked"})
+ Entry[] map
+ = (Entry[])new Entry[n];
for (int i = 0; i < n; i++) {
ModuleDescriptor md = descriptors[i];
@@ -111,16 +116,16 @@ class SystemModuleFinder implements ModuleFinder {
// create the ModuleReference
ModuleReference mref = toModuleReference(md, hashSupplier(i, names[i]));
- mods.add(mref);
- map.put(names[i], mref);
+ mods[i] = mref;
+ map[i] = Map.entry(names[i], mref);
// counters
packageCount.add(md.packages().size());
exportsCount.add(md.exports().size());
}
- modules = Collections.unmodifiableSet(mods);
- nameToModule = map;
+ modules = Set.of(mods);
+ nameToModule = Map.ofEntries(map);
initTime.addElapsedTimeFrom(t0);
}
@@ -190,7 +195,7 @@ class SystemModuleFinder implements ModuleFinder {
new ModuleReference(md, uri, readerSupplier, hash);
// may need a reference to a patched module if --patch-module specified
- mref = ModulePatcher.interposeIfNeeded(mref);
+ mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
return mref;
}
@@ -199,7 +204,7 @@ class SystemModuleFinder implements ModuleFinder {
if (isFastPathSupported()) {
return new HashSupplier() {
@Override
- public String generate(String algorithm) {
+ public byte[] generate(String algorithm) {
return SystemModules.MODULES_TO_HASH[index];
}
};
@@ -213,7 +218,7 @@ class SystemModuleFinder implements ModuleFinder {
* It will get the recorded hashes from module-info.class.
*/
private static class Hashes {
- static Map hashes = new HashMap<>();
+ static Map hashes = new HashMap<>();
static void add(ModuleDescriptor descriptor) {
Optional ohashes = descriptor.hashes();
@@ -228,7 +233,7 @@ class SystemModuleFinder implements ModuleFinder {
return new HashSupplier() {
@Override
- public String generate(String algorithm) {
+ public byte[] generate(String algorithm) {
return hashes.get(name);
}
};
diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java b/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
index 353870b37d8..a046a748f7c 100644
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
@@ -25,12 +25,12 @@
package java.lang.reflect;
+import java.lang.annotation.Annotation;
import java.security.AccessController;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
-import java.lang.annotation.Annotation;
/**
* The AccessibleObject class is the base class for Field, Method and
@@ -81,8 +81,10 @@ public class AccessibleObject implements AnnotatedElement {
* This method cannot be used to enable access to an object that is a
* {@link Member member} of a class in a different module to the caller and
* where the class is in a package that is not exported to the caller's
- * module. Additionally, this method cannot be used to enable access to
- * non-public members of {@code AccessibleObject} or {@link Module}.
+ * module. Additionally, if the member is non-public or its declaring
+ * class is non-public, then this method can only be used to enable access
+ * if the package is {@link Module#isOpen(String,Module) open} to at least
+ * the caller's module.
*
*
If there is a security manager, its
* {@code checkPermission} method is first called with a
@@ -126,8 +128,10 @@ public class AccessibleObject implements AnnotatedElement {
*
This method cannot be used to enable access to an object that is a
* {@link Member member} of a class in a different module to the caller and
* where the class is in a package that is not exported to the caller's
- * module. Additionally, this method cannot be used to enable access to
- * non-public members of {@code AccessibleObject} or {@link Module}.
+ * module. Additionally, if the member is non-public or its declaring
+ * class is non-public, then this method can only be used to enable access
+ * if the package is {@link Module#isOpen(String,Module) open} to at least
+ * the caller's module.
*
*
If there is a security manager, its
* {@code checkPermission} method is first called with a
@@ -138,6 +142,7 @@ public class AccessibleObject implements AnnotatedElement {
* @throws SecurityException if the request is denied
* @see SecurityManager#checkPermission
* @see ReflectPermission
+ * @see java.lang.invoke.MethodHandles#privateLookupIn
*/
public void setAccessible(boolean flag) {
AccessibleObject.checkPermission();
@@ -161,35 +166,39 @@ public class AccessibleObject implements AnnotatedElement {
Module callerModule = caller.getModule();
Module declaringModule = declaringClass.getModule();
- if (callerModule != declaringModule
- && callerModule != Object.class.getModule()) {
+ if (callerModule == declaringModule) return;
+ if (callerModule == Object.class.getModule()) return;
+ if (!declaringModule.isNamed()) return;
- // check exports to target module
- String pn = packageName(declaringClass);
- if (!declaringModule.isExported(pn, callerModule)) {
- String msg = "Unable to make member of "
- + declaringClass + " accessible: "
- + declaringModule + " does not export "
- + pn + " to " + callerModule;
- Reflection.throwInaccessibleObjectException(msg);
- }
+ // package is open to caller
+ String pn = packageName(declaringClass);
+ if (declaringModule.isOpen(pn, callerModule))
+ return;
+ // package is exported to caller and class/member is public
+ boolean isExported = declaringModule.isExported(pn, callerModule);
+ boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
+ int modifiers;
+ if (this instanceof Executable) {
+ modifiers = ((Executable) this).getModifiers();
+ } else {
+ modifiers = ((Field) this).getModifiers();
}
+ boolean isMemberPublic = Modifier.isPublic(modifiers);
+ if (isExported && isClassPublic && isMemberPublic)
+ return;
- if (declaringClass == Module.class
- || declaringClass == AccessibleObject.class) {
- int modifiers;
- if (this instanceof Executable) {
- modifiers = ((Executable) this).getModifiers();
- } else {
- modifiers = ((Field) this).getModifiers();
- }
- if (!Modifier.isPublic(modifiers)) {
- String msg = "Cannot make a non-public member of "
- + declaringClass + " accessible";
- Reflection.throwInaccessibleObjectException(msg);
- }
- }
+ // not accessible
+ String msg = "Unable to make ";
+ if (this instanceof Field)
+ msg += "field ";
+ msg += this + " accessible: " + declaringModule + " does not \"";
+ if (isClassPublic && isMemberPublic)
+ msg += "exports";
+ else
+ msg += "opens";
+ msg += " " + pn + "\" to " + callerModule;
+ Reflection.throwInaccessibleObjectException(msg);
}
/**
diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java b/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java
index 6e220a5e43c..7466c57ae22 100644
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java
@@ -27,23 +27,29 @@ package java.lang.reflect;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleDescriptor.Provides;
import java.lang.module.ResolvedModule;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.internal.loader.ClassLoaderValue;
import jdk.internal.loader.Loader;
import jdk.internal.loader.LoaderPool;
import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.Modules;
import jdk.internal.module.ServicesCatalog;
-import jdk.internal.module.ServicesCatalog.ServiceProvider;
import sun.security.util.SecurityConstants;
@@ -55,7 +61,7 @@ import sun.security.util.SecurityConstants;
* Creating a layer informs the Java virtual machine about the classes that
* may be loaded from modules so that the Java virtual machine knows which
* module that each class is a member of. Each layer, except the {@link
- * #empty() empty} layer, has a {@link #parent() parent}.
+ * #empty() empty} layer, has at least one {@link #parents() parent}.
*
* Creating a layer creates a {@link Module} object for each {@link
* ResolvedModule} in the configuration. For each resolved module that is
@@ -71,7 +77,11 @@ import sun.security.util.SecurityConstants;
* mapped to a single class loader or where each module is mapped to its own
* class loader. The {@link #defineModules defineModules} method is for more
* advanced cases where modules are mapped to custom class loaders by means of
- * a function specified to the method.
+ * a function specified to the method. Each of these methods has an instance
+ * and static variant. The instance methods create a layer with the receiver
+ * as the parent layer. The static methods are for more advanced cases where
+ * there can be more than one parent layer or a {@link Layer.Controller
+ * Controller} is needed to control modules in the layer.
*
* A Java virtual machine has at least one non-empty layer, the {@link
* #boot() boot} layer, that is created when the Java virtual machine is
@@ -80,7 +90,7 @@ import sun.security.util.SecurityConstants;
* The modules in the boot layer are mapped to the bootstrap class loader and
* other class loaders that are
* built-in into the Java virtual machine. The boot layer will often be
- * the {@link #parent() parent} when creating additional layers.
+ * the {@link #parents() parent} when creating additional layers.
*
* As when creating a {@code Configuration},
* {@link ModuleDescriptor#isAutomatic() automatic} modules receive
@@ -123,30 +133,29 @@ public final class Layer {
// the empty Layer
private static final Layer EMPTY_LAYER
- = new Layer(Configuration.empty(), null, null);
+ = new Layer(Configuration.empty(), List.of(), null);
// the configuration from which this Layer was created
private final Configuration cf;
- // parent layer, null in the case of the empty layer
- private final Layer parent;
+ // parent layers, empty in the case of the empty layer
+ private final List parents;
// maps module name to jlr.Module
private final Map nameToModule;
-
/**
* Creates a new Layer from the modules in the given configuration.
*/
private Layer(Configuration cf,
- Layer parent,
+ List parents,
Function clf)
{
this.cf = cf;
- this.parent = parent;
+ this.parents = parents; // no need to do defensive copy
Map map;
- if (parent == null) {
+ if (parents.isEmpty()) {
map = Collections.emptyMap();
} else {
map = Module.defineModules(cf, clf, this);
@@ -154,12 +163,230 @@ public final class Layer {
this.nameToModule = map; // no need to do defensive copy
}
+ /**
+ * Controls a layer. The static methods defined by {@link Layer} to create
+ * module layers return a {@code Controller} that can be used to control
+ * modules in the layer.
+ *
+ * @apiNote Care should be taken with {@code Controller} objects, they
+ * should never be shared with untrusted code.
+ *
+ * @since 9
+ */
+ public static final class Controller {
+ private final Layer layer;
+
+ Controller(Layer layer) {
+ this.layer = layer;
+ }
+
+ /**
+ * Returns the layer that this object controls.
+ *
+ * @return the layer
+ */
+ public Layer layer() {
+ return layer;
+ }
+
+ private void ensureInLayer(Module source) {
+ if (!layer.modules().contains(source))
+ throw new IllegalArgumentException(source + " not in layer");
+ }
+
+
+ /**
+ * Updates module {@code source} in the layer to read module
+ * {@code target}. This method is a no-op if {@code source} already
+ * reads {@code target}.
+ *
+ * @implNote Read edges added by this method are weak
+ * and do not prevent {@code target} from being GC'ed when {@code source}
+ * is strongly reachable.
+ *
+ * @param source
+ * The source module
+ * @param target
+ * The target module to read
+ *
+ * @return This controller
+ *
+ * @throws IllegalArgumentException
+ * If {@code source} is not in the layer
+ *
+ * @see Module#addReads
+ */
+ public Controller addReads(Module source, Module target) {
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(target);
+ ensureInLayer(source);
+ Modules.addReads(source, target);
+ return this;
+ }
+
+ /**
+ * Updates module {@code source} in the layer to open a package to
+ * module {@code target}. This method is a no-op if {@code source}
+ * already opens the package to at least {@code target}.
+ *
+ * @param source
+ * The source module
+ * @param pn
+ * The package name
+ * @param target
+ * The target module to read
+ *
+ * @return This controller
+ *
+ * @throws IllegalArgumentException
+ * If {@code source} is not in the layer or the package is not
+ * in the source module
+ *
+ * @see Module#addOpens
+ */
+ public Controller addOpens(Module source, String pn, Module target) {
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(source);
+ Objects.requireNonNull(target);
+ ensureInLayer(source);
+ Modules.addOpens(source, pn, target);
+ return this;
+ }
+ }
+
/**
* Creates a new layer, with this layer as its parent, by defining the
* modules in the given {@code Configuration} to the Java virtual machine.
* This method creates one class loader and defines all modules to that
- * class loader.
+ * class loader. The {@link ClassLoader#getParent() parent} of each class
+ * loader is the given parent class loader. This method works exactly as
+ * specified by the static {@link
+ * #defineModulesWithOneLoader(Configuration,List,ClassLoader)
+ * defineModulesWithOneLoader} method when invoked with this layer as the
+ * parent. In other words, if this layer is {@code thisLayer} then this
+ * method is equivalent to invoking:
+ * {@code
+ * Layer.defineModulesWithOneLoader(cf, List.of(thisLayer), parentLoader).layer();
+ * }
+ *
+ * @param cf
+ * The configuration for the layer
+ * @param parentLoader
+ * The parent class loader for the class loader created by this
+ * method; may be {@code null} for the bootstrap class loader
+ *
+ * @return The newly created layer
+ *
+ * @throws IllegalArgumentException
+ * If the parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If all modules cannot be defined to the same class loader for any
+ * of the reasons listed above or the layer cannot be created because
+ * the configuration contains a module named "{@code java.base}" or
+ * a module with a package name starting with "{@code java.}"
+ * @throws SecurityException
+ * If {@code RuntimePermission("createClassLoader")} or
+ * {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ *
+ * @see #findLoader
+ */
+ public Layer defineModulesWithOneLoader(Configuration cf,
+ ClassLoader parentLoader) {
+ return defineModulesWithOneLoader(cf, List.of(this), parentLoader).layer();
+ }
+
+
+ /**
+ * Creates a new layer, with this layer as its parent, by defining the
+ * modules in the given {@code Configuration} to the Java virtual machine.
+ * Each module is defined to its own {@link ClassLoader} created by this
+ * method. The {@link ClassLoader#getParent() parent} of each class loader
+ * is the given parent class loader. This method works exactly as specified
+ * by the static {@link
+ * #defineModulesWithManyLoaders(Configuration,List,ClassLoader)
+ * defineModulesWithManyLoaders} method when invoked with this layer as the
+ * parent. In other words, if this layer is {@code thisLayer} then this
+ * method is equivalent to invoking:
+ * {@code
+ * Layer.defineModulesWithManyLoaders(cf, List.of(thisLayer), parentLoader).layer();
+ * }
+ *
+ * @param cf
+ * The configuration for the layer
+ * @param parentLoader
+ * The parent class loader for each of the class loaders created by
+ * this method; may be {@code null} for the bootstrap class loader
+ *
+ * @return The newly created layer
+ *
+ * @throws IllegalArgumentException
+ * If the parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If the layer cannot be created because the configuration contains
+ * a module named "{@code java.base}" or a module with a package
+ * name starting with "{@code java.}"
+ * @throws SecurityException
+ * If {@code RuntimePermission("createClassLoader")} or
+ * {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ *
+ * @see #findLoader
+ */
+ public Layer defineModulesWithManyLoaders(Configuration cf,
+ ClassLoader parentLoader) {
+ return defineModulesWithManyLoaders(cf, List.of(this), parentLoader).layer();
+ }
+
+
+ /**
+ * Creates a new layer, with this layer as its parent, by defining the
+ * modules in the given {@code Configuration} to the Java virtual machine.
+ * Each module is mapped, by name, to its class loader by means of the
+ * given function. This method works exactly as specified by the static
+ * {@link #defineModules(Configuration,List,Function) defineModules}
+ * method when invoked with this layer as the parent. In other words, if
+ * this layer is {@code thisLayer} then this method is equivalent to
+ * invoking:
+ * {@code
+ * Layer.defineModules(cf, List.of(thisLayer), clf).layer();
+ * }
+ *
+ * @param cf
+ * The configuration for the layer
+ * @param clf
+ * The function to map a module name to a class loader
+ *
+ * @return The newly created layer
+ *
+ * @throws IllegalArgumentException
+ * If the parent of the given configuration is not the configuration
+ * for this layer
+ * @throws LayerInstantiationException
+ * If creating the {@code Layer} fails for any of the reasons
+ * listed above, the layer cannot be created because the
+ * configuration contains a module named "{@code java.base}",
+ * a module with a package name starting with "{@code java.}" is
+ * mapped to a class loader other than the {@link
+ * ClassLoader#getPlatformClassLoader() platform class loader},
+ * or the function to map a module name to a class loader returns
+ * {@code null}
+ * @throws SecurityException
+ * If {@code RuntimePermission("getClassLoader")} is denied by
+ * the security manager
+ */
+ public Layer defineModules(Configuration cf,
+ Function clf) {
+ return defineModules(cf, List.of(this), clf).layer();
+ }
+
+ /**
+ * Creates a new layer by defining the modules in the given {@code
+ * Configuration} to the Java virtual machine. This method creates one
+ * class loader and defines all modules to that class loader.
*
* The class loader created by this method implements direct
* delegation when loading types from modules. When its {@link
@@ -180,7 +407,7 @@ public final class Layer {
*
*
* Overlapping packages: Two or more modules in the
- * configuration have the same package (exported or concealed).
+ * configuration have the same package.
*
* Split delegation: The resulting class loader would
* need to delegate to more than one class loader in order to load types
@@ -194,19 +421,22 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list parent layers in search order
* @param parentLoader
* The parent class loader for the class loader created by this
* method; may be {@code null} for the bootstrap class loader
*
- * @return The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations do not match the configuration of
+ * the parent layers, including order
* @throws LayerInstantiationException
* If all modules cannot be defined to the same class loader for any
* of the reasons listed above or the layer cannot be created because
- * the configuration contains a module named "{@code java.base}"
+ * the configuration contains a module named "{@code java.base}" or
+ * a module with a package name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@@ -214,29 +444,32 @@ public final class Layer {
*
* @see #findLoader
*/
- public Layer defineModulesWithOneLoader(Configuration cf,
- ClassLoader parentLoader)
+ public static Controller defineModulesWithOneLoader(Configuration cf,
+ List parentLayers,
+ ClassLoader parentLoader)
{
- checkConfiguration(cf);
+ List parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
+
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
try {
Loader loader = new Loader(cf.modules(), parentLoader);
- loader.initRemotePackageMap(cf, this);
- return new Layer(cf, this, mn -> loader);
+ loader.initRemotePackageMap(cf, parents);
+ Layer layer = new Layer(cf, parents, mn -> loader);
+ return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
-
/**
- * Creates a new layer, with this layer as its parent, by defining the
- * modules in the given {@code Configuration} to the Java virtual machine.
- * Each module is defined to its own {@link ClassLoader} created by this
- * method. The {@link ClassLoader#getParent() parent} of each class loader
- * is the given parent class loader.
+ * Creates a new layer by defining the modules in the given {@code
+ * Configuration} to the Java virtual machine. Each module is defined to
+ * its own {@link ClassLoader} created by this method. The {@link
+ * ClassLoader#getParent() parent} of each class loader is the given parent
+ * class loader.
*
* The class loaders created by this method implement direct
* delegation when loading types from modules. When {@link
@@ -258,18 +491,21 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list parent layers in search order
* @param parentLoader
* The parent class loader for each of the class loaders created by
* this method; may be {@code null} for the bootstrap class loader
*
- * @return The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations do not match the configuration of
+ * the parent layers, including order
* @throws LayerInstantiationException
* If the layer cannot be created because the configuration contains
- * a module named "{@code java.base}"
+ * a module named "{@code java.base}" or a module with a package
+ * name starting with "{@code java.}"
* @throws SecurityException
* If {@code RuntimePermission("createClassLoader")} or
* {@code RuntimePermission("getClassLoader")} is denied by
@@ -277,37 +513,43 @@ public final class Layer {
*
* @see #findLoader
*/
- public Layer defineModulesWithManyLoaders(Configuration cf,
- ClassLoader parentLoader)
+ public static Controller defineModulesWithManyLoaders(Configuration cf,
+ List parentLayers,
+ ClassLoader parentLoader)
{
- checkConfiguration(cf);
+ List parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
+
checkCreateClassLoaderPermission();
checkGetClassLoaderPermission();
- LoaderPool pool = new LoaderPool(cf, this, parentLoader);
+ LoaderPool pool = new LoaderPool(cf, parents, parentLoader);
try {
- return new Layer(cf, this, pool::loaderFor);
+ Layer layer = new Layer(cf, parents, pool::loaderFor);
+ return new Controller(layer);
} catch (IllegalArgumentException e) {
throw new LayerInstantiationException(e.getMessage());
}
}
-
/**
- * Creates a new layer, with this layer as its parent, by defining the
- * modules in the given {@code Configuration} to the Java virtual machine.
+ * Creates a new layer by defining the modules in the given {@code
+ * Configuration} to the Java virtual machine.
* Each module is mapped, by name, to its class loader by means of the
* given function. The class loader delegation implemented by these class
- * loaders must respect module readability. In addition, the caller needs
- * to arrange that the class loaders are ready to load from these module
- * before there are any attempts to load classes or resources.
+ * loaders must respect module readability. The class loaders should be
+ * {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to
+ * avoid deadlocks during class loading. In addition, the entity creating
+ * a new layer with this method should arrange that the class loaders are
+ * ready to load from these module before there are any attempts to load
+ * classes or resources.
*
* Creating a {@code Layer} can fail for the following reasons:
*
*
*
- * Two or more modules with the same package (exported or
- * concealed) are mapped to the same class loader.
+ * Two or more modules with the same package are mapped to the
+ * same class loader.
*
* A module is mapped to a class loader that already has a
* module of the same name defined to it.
@@ -328,26 +570,35 @@ public final class Layer {
*
* @param cf
* The configuration for the layer
+ * @param parentLayers
+ * The list parent layers in search order
* @param clf
* The function to map a module name to a class loader
*
- * @return The newly created layer
+ * @return A controller that controls the newly created layer
*
* @throws IllegalArgumentException
- * If the parent of the given configuration is not the configuration
- * for this layer
+ * If the parent configurations do not match the configuration of
+ * the parent layers, including order
* @throws LayerInstantiationException
* If creating the {@code Layer} fails for any of the reasons
- * listed above or the layer cannot be created because the
- * configuration contains a module named "{@code java.base}"
+ * listed above, the layer cannot be created because the
+ * configuration contains a module named "{@code java.base}",
+ * a module with a package name starting with "{@code java.}" is
+ * mapped to a class loader other than the {@link
+ * ClassLoader#getPlatformClassLoader() platform class loader},
+ * or the function to map a module name to a class loader returns
+ * {@code null}
* @throws SecurityException
* If {@code RuntimePermission("getClassLoader")} is denied by
* the security manager
*/
- public Layer defineModules(Configuration cf,
- Function clf)
+ public static Controller defineModules(Configuration cf,
+ List parentLayers,
+ Function clf)
{
- checkConfiguration(cf);
+ List parents = new ArrayList<>(parentLayers);
+ checkConfiguration(cf, parents);
Objects.requireNonNull(clf);
checkGetClassLoaderPermission();
@@ -362,7 +613,8 @@ public final class Layer {
}
try {
- return new Layer(cf, this, clf);
+ Layer layer = new Layer(cf, parents, clf);
+ return new Controller(layer);
} catch (IllegalArgumentException iae) {
// IAE is thrown by VM when defining the module fails
throw new LayerInstantiationException(iae.getMessage());
@@ -370,13 +622,26 @@ public final class Layer {
}
- private void checkConfiguration(Configuration cf) {
+ /**
+ * Checks that the parent configurations match the configuration of
+ * the parent layers.
+ */
+ private static void checkConfiguration(Configuration cf,
+ List parentLayers)
+ {
Objects.requireNonNull(cf);
- Optional oparent = cf.parent();
- if (!oparent.isPresent() || oparent.get() != this.configuration()) {
- throw new IllegalArgumentException(
- "Parent of configuration != configuration of this Layer");
+ List parentConfigurations = cf.parents();
+ if (parentLayers.size() != parentConfigurations.size())
+ throw new IllegalArgumentException("wrong number of parents");
+
+ int index = 0;
+ for (Layer parent : parentLayers) {
+ if (parent.configuration() != parentConfigurations.get(index)) {
+ throw new IllegalArgumentException(
+ "Parent of configuration != configuration of this Layer");
+ }
+ index++;
}
}
@@ -463,18 +728,57 @@ public final class Layer {
/**
- * Returns this layer's parent unless this is the {@linkplain #empty empty
- * layer}, which has no parent.
+ * Returns the list of this layer's parents unless this is the
+ * {@linkplain #empty empty layer}, which has no parents and so an
+ * empty list is returned.
*
- * @return This layer's parent
+ * @return The list of this layer's parents
*/
- public Optional parent() {
- return Optional.ofNullable(parent);
+ public List parents() {
+ return parents;
}
/**
- * Returns a set of the modules in this layer.
+ * Returns an ordered stream of layers. The first element is is this layer,
+ * the remaining elements are the parent layers in DFS order.
+ *
+ * @implNote For now, the assumption is that the number of elements will
+ * be very low and so this method does not use a specialized spliterator.
+ */
+ Stream layers() {
+ List allLayers = this.allLayers;
+ if (allLayers != null)
+ return allLayers.stream();
+
+ allLayers = new ArrayList<>();
+ Set visited = new HashSet<>();
+ Deque stack = new ArrayDeque<>();
+ visited.add(this);
+ stack.push(this);
+
+ while (!stack.isEmpty()) {
+ Layer layer = stack.pop();
+ allLayers.add(layer);
+
+ // push in reverse order
+ for (int i = layer.parents.size() - 1; i >= 0; i--) {
+ Layer parent = layer.parents.get(i);
+ if (!visited.contains(parent)) {
+ visited.add(parent);
+ stack.push(parent);
+ }
+ }
+ }
+
+ this.allLayers = allLayers = Collections.unmodifiableList(allLayers);
+ return allLayers.stream();
+ }
+
+ private volatile List allLayers;
+
+ /**
+ * Returns the set of the modules in this layer.
*
* @return A possibly-empty unmodifiable set of the modules in this layer
*/
@@ -486,7 +790,11 @@ public final class Layer {
/**
* Returns the module with the given name in this layer, or if not in this
- * layer, the {@linkplain #parent parent} layer.
+ * layer, the {@linkplain #parents parents} layers. Finding a module in
+ * parent layers is equivalent to invoking {@code findModule} on each
+ * parent, in search order, until the module is found or all parents have
+ * been searched. In a tree of layers then this is equivalent to
+ * a depth-first search.
*
* @param name
* The name of the module to find
@@ -496,17 +804,25 @@ public final class Layer {
* parent layer
*/
public Optional findModule(String name) {
- Module m = nameToModule.get(Objects.requireNonNull(name));
+ Objects.requireNonNull(name);
+ Module m = nameToModule.get(name);
if (m != null)
return Optional.of(m);
- return parent().flatMap(l -> l.findModule(name));
+
+ return layers()
+ .skip(1) // skip this layer
+ .map(l -> l.nameToModule)
+ .filter(map -> map.containsKey(name))
+ .map(map -> map.get(name))
+ .findAny();
}
/**
* Returns the {@code ClassLoader} for the module with the given name. If
- * a module of the given name is not in this layer then the {@link #parent}
- * layer is checked.
+ * a module of the given name is not in this layer then the {@link #parents
+ * parent} layers are searched in the manner specified by {@link
+ * #findModule(String) findModule}.
*
* If there is a security manager then its {@code checkPermission}
* method is called with a {@code RuntimePermission("getClassLoader")}
@@ -527,20 +843,32 @@ public final class Layer {
* @throws SecurityException if denied by the security manager
*/
public ClassLoader findLoader(String name) {
- Module m = nameToModule.get(Objects.requireNonNull(name));
- if (m != null)
- return m.getClassLoader();
- Optional ol = parent();
- if (ol.isPresent())
- return ol.get().findLoader(name);
- throw new IllegalArgumentException("Module " + name
- + " not known to this layer");
+ Optional om = findModule(name);
+
+ // can't use map(Module::getClassLoader) as class loader can be null
+ if (om.isPresent()) {
+ return om.get().getClassLoader();
+ } else {
+ throw new IllegalArgumentException("Module " + name
+ + " not known to this layer");
+ }
}
+ /**
+ * Returns a string describing this layer.
+ *
+ * @return A possibly empty string describing this layer
+ */
+ @Override
+ public String toString() {
+ return modules().stream()
+ .map(Module::getName)
+ .collect(Collectors.joining(", "));
+ }
/**
* Returns the empty layer. There are no modules in the empty
- * layer. It has no parent.
+ * layer. It has no parents.
*
* @return The empty layer
*/
@@ -572,39 +900,12 @@ public final class Layer {
if (servicesCatalog != null)
return servicesCatalog;
- Map> map = new HashMap<>();
- for (Module m : nameToModule.values()) {
- ModuleDescriptor descriptor = m.getDescriptor();
- for (Provides provides : descriptor.provides().values()) {
- String service = provides.service();
- Set providers
- = map.computeIfAbsent(service, k -> new HashSet<>());
- for (String pn : provides.providers()) {
- providers.add(new ServiceProvider(m, pn));
- }
- }
- }
-
- ServicesCatalog catalog = new ServicesCatalog() {
- @Override
- public void register(Module module) {
- throw new UnsupportedOperationException();
- }
- @Override
- public Set findServices(String service) {
- Set providers = map.get(service);
- if (providers == null) {
- return Collections.emptySet();
- } else {
- return Collections.unmodifiableSet(providers);
- }
- }
- };
-
synchronized (this) {
servicesCatalog = this.servicesCatalog;
if (servicesCatalog == null) {
- this.servicesCatalog = servicesCatalog = catalog;
+ servicesCatalog = ServicesCatalog.create();
+ nameToModule.values().forEach(servicesCatalog::register);
+ this.servicesCatalog = servicesCatalog;
}
}
@@ -612,4 +913,36 @@ public final class Layer {
}
private volatile ServicesCatalog servicesCatalog;
+
+
+ /**
+ * Record that this layer has at least one module defined to the given
+ * class loader.
+ */
+ void bindToLoader(ClassLoader loader) {
+ // CLV.computeIfAbsent(loader, (cl, clv) -> new CopyOnWriteArrayList<>())
+ List list = CLV.get(loader);
+ if (list == null) {
+ list = new CopyOnWriteArrayList<>();
+ List previous = CLV.putIfAbsent(loader, list);
+ if (previous != null) list = previous;
+ }
+ list.add(this);
+ }
+
+ /**
+ * Returns a stream of the layers that have at least one module defined to
+ * the given class loader.
+ */
+ static Stream layers(ClassLoader loader) {
+ List list = CLV.get(loader);
+ if (list != null) {
+ return list.stream();
+ } else {
+ return Stream.empty();
+ }
+ }
+
+ // the list of layers with modules defined to a class loader
+ private static final ClassLoaderValue> CLV = new ClassLoaderValue<>();
}
diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Module.java b/jdk/src/java.base/share/classes/java/lang/reflect/Module.java
index 26e47245f38..29f21cc0108 100644
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Module.java
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Module.java
@@ -27,15 +27,18 @@ package java.lang.reflect;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Annotation;
import java.lang.module.Configuration;
import java.lang.module.ModuleReference;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
-import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleDescriptor.Opens;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -49,9 +52,17 @@ import java.util.stream.Stream;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.loader.BootLoader;
+import jdk.internal.loader.ResourceHelper;
+import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.JavaLangReflectModuleAccess;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.module.ServicesCatalog;
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import sun.security.util.SecurityConstants;
@@ -83,7 +94,7 @@ import sun.security.util.SecurityConstants;
* @see java.lang.Class#getModule
*/
-public final class Module {
+public final class Module implements AnnotatedElement {
// the layer that contains this module, can be null
private final Layer layer;
@@ -113,6 +124,10 @@ public final class Module {
// define module to VM
+ boolean isOpen = descriptor.isOpen();
+ Version version = descriptor.version().orElse(null);
+ String vs = Objects.toString(version, null);
+ String loc = Objects.toString(uri, null);
Set packages = descriptor.packages();
int n = packages.size();
String[] array = new String[n];
@@ -120,11 +135,7 @@ public final class Module {
for (String pn : packages) {
array[i++] = pn.replace('.', '/');
}
- Version version = descriptor.version().orElse(null);
- String vs = Objects.toString(version, null);
- String loc = Objects.toString(uri, null);
-
- defineModule0(this, vs, loc, array);
+ defineModule0(this, isOpen, vs, loc, array);
}
@@ -240,24 +251,24 @@ public final class Module {
// --
- // the special Module to mean reads or exported to "all unnamed modules"
+ // special Module to mean "all unnamed modules"
private static final Module ALL_UNNAMED_MODULE = new Module(null);
- // special Module to mean exported to "everyone"
+ // special Module to mean "everyone"
private static final Module EVERYONE_MODULE = new Module(null);
- // exported to all modules
- private static final Set EVERYONE = Collections.singleton(EVERYONE_MODULE);
+ // set contains EVERYONE_MODULE, used when a package is opened or
+ // exported unconditionally
+ private static final Set EVERYONE_SET = Set.of(EVERYONE_MODULE);
// -- readability --
- // the modules that this module permanently reads
- // (will be final when the modules are defined in reverse topology order)
+ // the modules that this module reads
private volatile Set reads;
// additional module (2nd key) that some module (1st key) reflectively reads
- private static final WeakPairMap transientReads
+ private static final WeakPairMap reflectivelyReads
= new WeakPairMap<>();
@@ -293,13 +304,13 @@ public final class Module {
}
// check if this module reads the other module reflectively
- if (transientReads.containsKeyPair(this, other))
+ if (reflectivelyReads.containsKeyPair(this, other))
return true;
// if other is an unnamed module then check if this module reads
// all unnamed modules
if (!other.isNamed()
- && transientReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
+ && reflectivelyReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
return true;
return false;
@@ -309,9 +320,13 @@ public final class Module {
* If the caller's module is this module then update this module to read
* the given module.
*
- * This method is a no-op if {@code other} is this module (all modules can
- * read themselves) or this module is an unnamed module (as unnamed modules
- * read all modules).
+ * This method is a no-op if {@code other} is this module (all modules read
+ * themselves), this module is an unnamed module (as unnamed modules read
+ * all modules), or this module already reads {@code other}.
+ *
+ * @implNote Read edges added by this method are weak and
+ * do not prevent {@code other} from being GC'ed when this module is
+ * strongly reachable.
*
* @param other
* The other module
@@ -381,30 +396,39 @@ public final class Module {
}
// add reflective read
- transientReads.putIfAbsent(this, other, Boolean.TRUE);
+ reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE);
}
- // -- exports --
+ // -- exported and open packages --
- // the packages that are permanently exported
- // (will be final when the modules are defined in reverse topology order)
- private volatile Map> exports;
+ // the packages are open to other modules, can be null
+ // if the value contains EVERYONE_MODULE then the package is open to all
+ private volatile Map> openPackages;
- // additional exports added at run-time
- // this module (1st key), other module (2nd key), exported packages (value)
+ // the packages that are exported, can be null
+ // if the value contains EVERYONE_MODULE then the package is exported to all
+ private volatile Map> exportedPackages;
+
+ // additional exports or opens added at run-time
+ // this module (1st key), other module (2nd key)
+ // (package name, open?) (value)
private static final WeakPairMap>
- transientExports = new WeakPairMap<>();
+ reflectivelyExports = new WeakPairMap<>();
/**
* Returns {@code true} if this module exports the given package to at
* least the given module.
*
- * This method always return {@code true} when invoked on an unnamed
+ *
This method returns {@code true} if invoked to test if a package in
+ * this module is exported to itself. It always returns {@code true} when
+ * invoked on an unnamed module. A package that is {@link #isOpen open} to
+ * the given module is considered exported to that module at run-time and
+ * so this method returns {@code true} if the package is open to the given
* module.
*
- * This method does not check if the given module reads this module
+ * This method does not check if the given module reads this module.
*
* @param pn
* The package name
@@ -413,93 +437,196 @@ public final class Module {
*
* @return {@code true} if this module exports the package to at least the
* given module
+ *
+ * @see ModuleDescriptor#exports()
+ * @see #addExports(String,Module)
*/
public boolean isExported(String pn, Module other) {
Objects.requireNonNull(pn);
Objects.requireNonNull(other);
- return implIsExported(pn, other);
+ return implIsExportedOrOpen(pn, other, /*open*/false);
+ }
+
+ /**
+ * Returns {@code true} if this module has opened a package to at
+ * least the given module.
+ *
+ * This method returns {@code true} if invoked to test if a package in
+ * this module is open to itself. It returns {@code true} when invoked on an
+ * {@link ModuleDescriptor#isOpen open} module with a package in the module.
+ * It always returns {@code true} when invoked on an unnamed module.
+ *
+ * This method does not check if the given module reads this module.
+ *
+ * @param pn
+ * The package name
+ * @param other
+ * The other module
+ *
+ * @return {@code true} if this module has opened the package
+ * to at least the given module
+ *
+ * @see ModuleDescriptor#opens()
+ * @see #addOpens(String,Module)
+ * @see AccessibleObject#setAccessible(boolean)
+ * @see java.lang.invoke.MethodHandles#privateLookupIn
+ */
+ public boolean isOpen(String pn, Module other) {
+ Objects.requireNonNull(pn);
+ Objects.requireNonNull(other);
+ return implIsExportedOrOpen(pn, other, /*open*/true);
}
/**
* Returns {@code true} if this module exports the given package
* unconditionally.
*
- * This method always return {@code true} when invoked on an unnamed
- * module.
+ * This method always returns {@code true} when invoked on an unnamed
+ * module. A package that is {@link #isOpen(String) opened} unconditionally
+ * is considered exported unconditionally at run-time and so this method
+ * returns {@code true} if the package is opened unconditionally.
*
- * This method does not check if the given module reads this module
+ * This method does not check if the given module reads this module.
*
* @param pn
* The package name
*
* @return {@code true} if this module exports the package unconditionally
+ *
+ * @see ModuleDescriptor#exports()
*/
public boolean isExported(String pn) {
Objects.requireNonNull(pn);
- return implIsExported(pn, EVERYONE_MODULE);
+ return implIsExportedOrOpen(pn, EVERYONE_MODULE, /*open*/false);
}
/**
- * Returns {@code true} if this module exports the given package to the
- * given module. If the other module is {@code EVERYONE_MODULE} then
- * this method tests if the package is exported unconditionally.
+ * Returns {@code true} if this module has opened a package
+ * unconditionally.
+ *
+ * This method always returns {@code true} when invoked on an unnamed
+ * module. Additionally, it always returns {@code true} when invoked on an
+ * {@link ModuleDescriptor#isOpen open} module with a package in the
+ * module.
+ *
+ * This method does not check if the given module reads this module.
+ *
+ * @param pn
+ * The package name
+ *
+ * @return {@code true} if this module has opened the package
+ * unconditionally
+ *
+ * @see ModuleDescriptor#opens()
*/
- private boolean implIsExported(String pn, Module other) {
+ public boolean isOpen(String pn) {
+ Objects.requireNonNull(pn);
+ return implIsExportedOrOpen(pn, EVERYONE_MODULE, /*open*/true);
+ }
- // all packages are exported by unnamed modules
+
+ /**
+ * Returns {@code true} if this module exports or opens the given package
+ * to the given module. If the other module is {@code EVERYONE_MODULE} then
+ * this method tests if the package is exported or opened unconditionally.
+ */
+ private boolean implIsExportedOrOpen(String pn, Module other, boolean open) {
+ // all packages in unnamed modules are open
if (!isNamed())
return true;
- // exported via module declaration/descriptor
- if (isExportedPermanently(pn, other))
+ // all packages are exported/open to self
+ if (other == this && containsPackage(pn))
return true;
- // exported via addExports
- if (isExportedReflectively(pn, other))
+ // all packages in open modules are open
+ if (descriptor.isOpen())
+ return containsPackage(pn);
+
+ // exported/opened via module declaration/descriptor
+ if (isStaticallyExportedOrOpen(pn, other, open))
return true;
- // not exported or not exported to other
+ // exported via addExports/addOpens
+ if (isReflectivelyExportedOrOpen(pn, other, open))
+ return true;
+
+ // not exported or open to other
return false;
}
/**
- * Returns {@code true} if this module permanently exports the given
- * package to the given module.
+ * Returns {@code true} if this module exports or opens a package to
+ * the given module via its module declaration.
*/
- private boolean isExportedPermanently(String pn, Module other) {
- Map> exports = this.exports;
- if (exports != null) {
- Set targets = exports.get(pn);
-
- if ((targets != null)
- && (targets.contains(other) || targets.contains(EVERYONE_MODULE)))
- return true;
+ private boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
+ // package is open to everyone or
+ Map> openPackages = this.openPackages;
+ if (openPackages != null) {
+ Set targets = openPackages.get(pn);
+ if (targets != null) {
+ if (targets.contains(EVERYONE_MODULE))
+ return true;
+ if (other != EVERYONE_MODULE && targets.contains(other))
+ return true;
+ }
}
+
+ if (!open) {
+ // package is exported to everyone or
+ Map> exportedPackages = this.exportedPackages;
+ if (exportedPackages != null) {
+ Set targets = exportedPackages.get(pn);
+ if (targets != null) {
+ if (targets.contains(EVERYONE_MODULE))
+ return true;
+ if (other != EVERYONE_MODULE && targets.contains(other))
+ return true;
+ }
+ }
+ }
+
return false;
}
+
/**
- * Returns {@code true} if this module reflectively exports the given
+ * Returns {@code true} if this module reflectively exports or opens given
* package package to the given module.
*/
- private boolean isExportedReflectively(String pn, Module other) {
- // exported to all modules
- Map exports = transientExports.get(this, EVERYONE_MODULE);
- if (exports != null && exports.containsKey(pn))
- return true;
+ private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean open) {
+ // exported or open to all modules
+ Map exports = reflectivelyExports.get(this, EVERYONE_MODULE);
+ if (exports != null) {
+ Boolean b = exports.get(pn);
+ if (b != null) {
+ boolean isOpen = b.booleanValue();
+ if (!open || isOpen) return true;
+ }
+ }
if (other != EVERYONE_MODULE) {
- // exported to other
- exports = transientExports.get(this, other);
- if (exports != null && exports.containsKey(pn))
- return true;
+ // exported or open to other
+ exports = reflectivelyExports.get(this, other);
+ if (exports != null) {
+ Boolean b = exports.get(pn);
+ if (b != null) {
+ boolean isOpen = b.booleanValue();
+ if (!open || isOpen) return true;
+ }
+ }
- // other is an unnamed module && exported to all unnamed
+ // other is an unnamed module && exported or open to all unnamed
if (!other.isNamed()) {
- exports = transientExports.get(this, ALL_UNNAMED_MODULE);
- if (exports != null && exports.containsKey(pn))
- return true;
+ exports = reflectivelyExports.get(this, ALL_UNNAMED_MODULE);
+ if (exports != null) {
+ Boolean b = exports.get(pn);
+ if (b != null) {
+ boolean isOpen = b.booleanValue();
+ if (!open || isOpen) return true;
+ }
+ }
}
}
@@ -510,11 +637,11 @@ public final class Module {
/**
* If the caller's module is this module then update this module to export
- * package {@code pn} to the given module.
+ * the given package to the given module.
*
- * This method has no effect if the package is already exported to the
- * given module. It also has no effect if invoked on an unnamed module (as
- * unnamed modules export all packages).
+ * This method has no effect if the package is already exported (or
+ * open) to the given module. It also has no effect if
+ * invoked on an {@link ModuleDescriptor#isOpen open} module.
*
* @param pn
* The package name
@@ -528,6 +655,8 @@ public final class Module {
* package {@code pn} is not a package in this module
* @throws IllegalStateException
* If this is a named module and the caller is not this module
+ *
+ * @see #isExported(String,Module)
*/
@CallerSensitive
public Module addExports(String pn, Module other) {
@@ -535,17 +664,65 @@ public final class Module {
throw new IllegalArgumentException("package is null");
Objects.requireNonNull(other);
- if (isNamed()) {
+ if (isNamed() && !descriptor.isOpen()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
throw new IllegalStateException(caller + " != " + this);
}
- implAddExports(pn, other, true);
+ implAddExportsOrOpens(pn, other, /*open*/false, /*syncVM*/true);
}
return this;
}
+ /**
+ * If the caller's module is this module then update this module to
+ * open the given package to the given module.
+ * Opening a package with this method allows all types in the package,
+ * and all their members, not just public types and their public members,
+ * to be reflected on by the given module when using APIs that support
+ * private access or a way to bypass or suppress default Java language
+ * access control checks.
+ *
+ * This method has no effect if the package is already open
+ * to the given module. It also has no effect if invoked on an {@link
+ * ModuleDescriptor#isOpen open} module.
+ *
+ * @param pn
+ * The package name
+ * @param other
+ * The module
+ *
+ * @return this module
+ *
+ * @throws IllegalArgumentException
+ * If {@code pn} is {@code null}, or this is a named module and the
+ * package {@code pn} is not a package in this module
+ * @throws IllegalStateException
+ * If this is a named module and the caller is not this module
+ *
+ * @see #isOpen(String,Module)
+ * @see AccessibleObject#setAccessible(boolean)
+ * @see java.lang.invoke.MethodHandles#privateLookupIn
+ */
+ @CallerSensitive
+ public Module addOpens(String pn, Module other) {
+ if (pn == null)
+ throw new IllegalArgumentException("package is null");
+ Objects.requireNonNull(other);
+
+ if (isNamed() && !descriptor.isOpen()) {
+ Module caller = Reflection.getCallerClass().getModule();
+ if (caller != this) {
+ throw new IllegalStateException(caller + " != " + this);
+ }
+ implAddExportsOrOpens(pn, other, /*open*/true, /*syncVM*/true);
+ }
+
+ return this;
+ }
+
+
/**
* Updates the exports so that package {@code pn} is exported to module
* {@code other} but without notifying the VM.
@@ -555,7 +732,7 @@ public final class Module {
void implAddExportsNoSync(String pn, Module other) {
if (other == null)
other = EVERYONE_MODULE;
- implAddExports(pn.replace('/', '.'), other, false);
+ implAddExportsOrOpens(pn.replace('/', '.'), other, false, false);
}
/**
@@ -565,25 +742,36 @@ public final class Module {
* @apiNote This method is for white-box testing.
*/
void implAddExports(String pn, Module other) {
- implAddExports(pn, other, true);
+ implAddExportsOrOpens(pn, other, false, true);
}
/**
- * Updates the exports so that package {@code pn} is exported to module
- * {@code other}.
+ * Updates the module to open package {@code pn} to module {@code other}.
+ *
+ * @apiNote This method is for white-box tests and jtreg
+ */
+ void implAddOpens(String pn, Module other) {
+ implAddExportsOrOpens(pn, other, true, true);
+ }
+
+ /**
+ * Updates a module to export or open a module to another module.
*
* If {@code syncVM} is {@code true} then the VM is notified.
*/
- private void implAddExports(String pn, Module other, boolean syncVM) {
+ private void implAddExportsOrOpens(String pn,
+ Module other,
+ boolean open,
+ boolean syncVM) {
Objects.requireNonNull(other);
Objects.requireNonNull(pn);
- // unnamed modules export all packages
- if (!isNamed())
+ // all packages are open in unnamed and open modules
+ if (!isNamed() || descriptor.isOpen())
return;
- // nothing to do if already exported to other
- if (implIsExported(pn, other))
+ // nothing to do if already exported/open to other
+ if (implIsExportedOrOpen(pn, other, open))
return;
// can only export a package in the module
@@ -604,18 +792,23 @@ public final class Module {
}
}
- // add package name to transientExports if absent
- transientExports
+ // add package name to reflectivelyExports if absent
+ Map map = reflectivelyExports
.computeIfAbsent(this, other,
- (_this, _other) -> new ConcurrentHashMap<>())
- .putIfAbsent(pn, Boolean.TRUE);
+ (m1, m2) -> new ConcurrentHashMap<>());
+
+ if (open) {
+ map.put(pn, Boolean.TRUE); // may need to promote from FALSE to TRUE
+ } else {
+ map.putIfAbsent(pn, Boolean.FALSE);
+ }
}
// -- services --
// additional service type (2nd key) that some module (1st key) uses
- private static final WeakPairMap, Boolean> transientUses
+ private static final WeakPairMap, Boolean> reflectivelyUses
= new WeakPairMap<>();
/**
@@ -624,13 +817,13 @@ public final class Module {
* for use by frameworks that invoke {@link java.util.ServiceLoader
* ServiceLoader} on behalf of other modules or where the framework is
* passed a reference to the service type by other code. This method is
- * a no-op when invoked on an unnamed module.
+ * a no-op when invoked on an unnamed module or an automatic module.
*
* This method does not cause {@link
* Configuration#resolveRequiresAndUses resolveRequiresAndUses} to be
* re-run.
*
- * @param st
+ * @param service
* The service type
*
* @return this module
@@ -642,39 +835,45 @@ public final class Module {
* @see ModuleDescriptor#uses()
*/
@CallerSensitive
- public Module addUses(Class> st) {
- Objects.requireNonNull(st);
-
- if (isNamed()) {
+ public Module addUses(Class> service) {
+ Objects.requireNonNull(service);
+ if (isNamed() && !descriptor.isAutomatic()) {
Module caller = Reflection.getCallerClass().getModule();
if (caller != this) {
throw new IllegalStateException(caller + " != " + this);
}
-
- if (!canUse(st)) {
- transientUses.putIfAbsent(this, st, Boolean.TRUE);
- }
-
+ implAddUses(service);
}
return this;
}
+ /**
+ * Update this module to add a service dependence on the given service
+ * type.
+ */
+ void implAddUses(Class> service) {
+ if (!canUse(service)) {
+ reflectivelyUses.putIfAbsent(this, service, Boolean.TRUE);
+ }
+ }
+
+
/**
* Indicates if this module has a service dependence on the given service
* type. This method always returns {@code true} when invoked on an unnamed
- * module.
+ * module or an automatic module.
*
- * @param st
+ * @param service
* The service type
*
* @return {@code true} if this module uses service type {@code st}
*
* @see #addUses(Class)
*/
- public boolean canUse(Class> st) {
- Objects.requireNonNull(st);
+ public boolean canUse(Class> service) {
+ Objects.requireNonNull(service);
if (!isNamed())
return true;
@@ -683,11 +882,11 @@ public final class Module {
return true;
// uses was declared
- if (descriptor.uses().contains(st.getName()))
+ if (descriptor.uses().contains(service.getName()))
return true;
// uses added via addUses
- return transientUses.containsKeyPair(this, st);
+ return reflectivelyUses.containsKeyPair(this, service);
}
@@ -780,8 +979,12 @@ public final class Module {
* If {@code syncVM} is {@code true} then the VM is notified.
*/
private void implAddPackage(String pn, boolean syncVM) {
- if (pn.length() == 0)
- throw new IllegalArgumentException(" package not allowed");
+ if (!isNamed())
+ throw new InternalError("adding package to unnamed module?");
+ if (descriptor.isOpen())
+ throw new InternalError("adding package to open module?");
+ if (pn.isEmpty())
+ throw new InternalError("adding package to module?");
if (descriptor.packages().contains(pn)) {
// already in module
@@ -822,28 +1025,7 @@ public final class Module {
// -- creating Module objects --
/**
- * Find the runtime Module corresponding to the given ResolvedModule
- * in the given parent Layer (or its parents).
- */
- private static Module find(ResolvedModule resolvedModule, Layer layer) {
- Configuration cf = resolvedModule.configuration();
- String dn = resolvedModule.name();
-
- Module m = null;
- while (layer != null) {
- if (layer.configuration() == cf) {
- Optional om = layer.findModule(dn);
- m = om.get();
- assert m.getLayer() == layer;
- break;
- }
- layer = layer.parent().orElse(null);
- }
- return m;
- }
-
- /**
- * Defines each of the module in the given configuration to the runtime.
+ * Defines all module in a configuration to the runtime.
*
* @return a map of module name to runtime {@code Module}
*
@@ -854,26 +1036,40 @@ public final class Module {
Function clf,
Layer layer)
{
- Map modules = new HashMap<>();
- Map loaders = new HashMap<>();
+ Map nameToModule = new HashMap<>();
+ Map moduleToLoader = new HashMap<>();
+
+ boolean isBootLayer = (Layer.boot() == null);
+ Set loaders = new HashSet<>();
+
+ // map each module to a class loader
+ for (ResolvedModule resolvedModule : cf.modules()) {
+ String name = resolvedModule.name();
+ ClassLoader loader = clf.apply(name);
+ if (loader != null) {
+ moduleToLoader.put(name, loader);
+ loaders.add(loader);
+ } else if (!isBootLayer) {
+ throw new IllegalArgumentException("loader can't be 'null'");
+ }
+ }
// define each module in the configuration to the VM
for (ResolvedModule resolvedModule : cf.modules()) {
ModuleReference mref = resolvedModule.reference();
ModuleDescriptor descriptor = mref.descriptor();
String name = descriptor.name();
- ClassLoader loader = clf.apply(name);
URI uri = mref.location().orElse(null);
-
+ ClassLoader loader = moduleToLoader.get(resolvedModule.name());
Module m;
- if (loader == null && name.equals("java.base") && Layer.boot() == null) {
+ if (loader == null && isBootLayer && name.equals("java.base")) {
+ // java.base is already defined to the VM
m = Object.class.getModule();
} else {
m = new Module(layer, loader, descriptor, uri);
}
-
- modules.put(name, m);
- loaders.put(name, loader);
+ nameToModule.put(name, m);
+ moduleToLoader.put(name, loader);
}
// setup readability and exports
@@ -882,20 +1078,24 @@ public final class Module {
ModuleDescriptor descriptor = mref.descriptor();
String mn = descriptor.name();
- Module m = modules.get(mn);
+ Module m = nameToModule.get(mn);
assert m != null;
// reads
Set reads = new HashSet<>();
- for (ResolvedModule d : resolvedModule.reads()) {
- Module m2;
- if (d.configuration() == cf) {
- String dn = d.reference().descriptor().name();
- m2 = modules.get(dn);
- assert m2 != null;
+ for (ResolvedModule other : resolvedModule.reads()) {
+ Module m2 = null;
+ if (other.configuration() == cf) {
+ String dn = other.reference().descriptor().name();
+ m2 = nameToModule.get(dn);
} else {
- m2 = find(d, layer.parent().orElse(null));
+ for (Layer parent: layer.parents()) {
+ m2 = findModule(parent, other);
+ if (m2 != null)
+ break;
+ }
}
+ assert m2 != null;
reads.add(m2);
@@ -904,64 +1104,273 @@ public final class Module {
}
m.reads = reads;
- // automatic modules reads all unnamed modules
+ // automatic modules read all unnamed modules
if (descriptor.isAutomatic()) {
m.implAddReads(ALL_UNNAMED_MODULE, true);
}
- // exports
- Map> exports = new HashMap<>();
- for (Exports export : descriptor.exports()) {
- String source = export.source();
+ // exports and opens
+ initExportsAndOpens(descriptor, nameToModule, m);
+ }
+
+ // register the modules in the boot layer
+ if (isBootLayer) {
+ for (ResolvedModule resolvedModule : cf.modules()) {
+ ModuleReference mref = resolvedModule.reference();
+ ModuleDescriptor descriptor = mref.descriptor();
+ if (!descriptor.provides().isEmpty()) {
+ String name = descriptor.name();
+ Module m = nameToModule.get(name);
+ ClassLoader loader = moduleToLoader.get(name);
+ ServicesCatalog catalog;
+ if (loader == null) {
+ catalog = BootLoader.getServicesCatalog();
+ } else {
+ catalog = ServicesCatalog.getServicesCatalog(loader);
+ }
+ catalog.register(m);
+ }
+ }
+ }
+
+ // record that there is a layer with modules defined to the class loader
+ for (ClassLoader loader : loaders) {
+ layer.bindToLoader(loader);
+ }
+
+ return nameToModule;
+ }
+
+
+ /**
+ * Find the runtime Module corresponding to the given ResolvedModule
+ * in the given parent layer (or its parents).
+ */
+ private static Module findModule(Layer parent, ResolvedModule resolvedModule) {
+ Configuration cf = resolvedModule.configuration();
+ String dn = resolvedModule.name();
+ return parent.layers()
+ .filter(l -> l.configuration() == cf)
+ .findAny()
+ .map(layer -> {
+ Optional om = layer.findModule(dn);
+ assert om.isPresent() : dn + " not found in layer";
+ Module m = om.get();
+ assert m.getLayer() == layer : m + " not in expected layer";
+ return m;
+ })
+ .orElse(null);
+ }
+
+ /**
+ * Initialize the maps of exported and open packages for module m.
+ */
+ private static void initExportsAndOpens(ModuleDescriptor descriptor,
+ Map nameToModule,
+ Module m)
+ {
+ // The VM doesn't know about open modules so need to export all packages
+ if (descriptor.isOpen()) {
+ assert descriptor.opens().isEmpty();
+ for (String source : descriptor.packages()) {
String sourceInternalForm = source.replace('.', '/');
+ addExportsToAll0(m, sourceInternalForm);
+ }
+ return;
+ }
- if (export.isQualified()) {
+ Map> openPackages = new HashMap<>();
+ Map> exportedPackages = new HashMap<>();
- // qualified export
- Set targets = new HashSet<>();
- for (String target : export.targets()) {
- // only export to modules that are in this configuration
- Module m2 = modules.get(target);
- if (m2 != null) {
- targets.add(m2);
+ // process the open packages first
+ for (Opens opens : descriptor.opens()) {
+ String source = opens.source();
+ String sourceInternalForm = source.replace('.', '/');
+
+ if (opens.isQualified()) {
+ // qualified opens
+ Set targets = new HashSet<>();
+ for (String target : opens.targets()) {
+ // only open to modules that are in this configuration
+ Module m2 = nameToModule.get(target);
+ if (m2 != null) {
+ addExports0(m, sourceInternalForm, m2);
+ targets.add(m2);
+ }
+ }
+ if (!targets.isEmpty()) {
+ openPackages.put(source, targets);
+ }
+ } else {
+ // unqualified opens
+ addExportsToAll0(m, sourceInternalForm);
+ openPackages.put(source, EVERYONE_SET);
+ }
+ }
+
+ // next the exports, skipping exports when the package is open
+ for (Exports exports : descriptor.exports()) {
+ String source = exports.source();
+ String sourceInternalForm = source.replace('.', '/');
+
+ // skip export if package is already open to everyone
+ Set openToTargets = openPackages.get(source);
+ if (openToTargets != null && openToTargets.contains(EVERYONE_MODULE))
+ continue;
+
+ if (exports.isQualified()) {
+ // qualified exports
+ Set targets = new HashSet<>();
+ for (String target : exports.targets()) {
+ // only export to modules that are in this configuration
+ Module m2 = nameToModule.get(target);
+ if (m2 != null) {
+ // skip qualified export if already open to m2
+ if (openToTargets == null || !openToTargets.contains(m2)) {
addExports0(m, sourceInternalForm, m2);
+ targets.add(m2);
}
}
- if (!targets.isEmpty()) {
- exports.put(source, targets);
- }
-
- } else {
-
- // unqualified export
- exports.put(source, EVERYONE);
- addExportsToAll0(m, sourceInternalForm);
}
- }
- m.exports = exports;
- }
-
- // register the modules in the service catalog if they provide services
- for (ResolvedModule resolvedModule : cf.modules()) {
- ModuleReference mref = resolvedModule.reference();
- ModuleDescriptor descriptor = mref.descriptor();
- Map services = descriptor.provides();
- if (!services.isEmpty()) {
- String name = descriptor.name();
- Module m = modules.get(name);
- ClassLoader loader = loaders.get(name);
- ServicesCatalog catalog;
- if (loader == null) {
- catalog = BootLoader.getServicesCatalog();
- } else {
- catalog = SharedSecrets.getJavaLangAccess()
- .createOrGetServicesCatalog(loader);
+ if (!targets.isEmpty()) {
+ exportedPackages.put(source, targets);
}
- catalog.register(m);
+
+ } else {
+ // unqualified exports
+ addExportsToAll0(m, sourceInternalForm);
+ exportedPackages.put(source, EVERYONE_SET);
}
}
- return modules;
+ if (!openPackages.isEmpty())
+ m.openPackages = openPackages;
+ if (!exportedPackages.isEmpty())
+ m.exportedPackages = exportedPackages;
+ }
+
+
+ // -- annotations --
+
+ /**
+ * {@inheritDoc}
+ * This method returns {@code null} when invoked on an unnamed module.
+ */
+ @Override
+ public T getAnnotation(Class annotationClass) {
+ return moduleInfoClass().getDeclaredAnnotation(annotationClass);
+ }
+
+ /**
+ * {@inheritDoc}
+ * This method returns an empty array when invoked on an unnamed module.
+ */
+ @Override
+ public Annotation[] getAnnotations() {
+ return moduleInfoClass().getAnnotations();
+ }
+
+ /**
+ * {@inheritDoc}
+ * This method returns an empty array when invoked on an unnamed module.
+ */
+ @Override
+ public Annotation[] getDeclaredAnnotations() {
+ return moduleInfoClass().getDeclaredAnnotations();
+ }
+
+ // cached class file with annotations
+ private volatile Class> moduleInfoClass;
+
+ private Class> moduleInfoClass() {
+ Class> clazz = this.moduleInfoClass;
+ if (clazz != null)
+ return clazz;
+
+ synchronized (this) {
+ clazz = this.moduleInfoClass;
+ if (clazz == null) {
+ if (isNamed()) {
+ PrivilegedAction> pa = this::loadModuleInfoClass;
+ clazz = AccessController.doPrivileged(pa);
+ }
+ if (clazz == null) {
+ class DummyModuleInfo { }
+ clazz = DummyModuleInfo.class;
+ }
+ this.moduleInfoClass = clazz;
+ }
+ return clazz;
+ }
+ }
+
+ private Class> loadModuleInfoClass() {
+ Class> clazz = null;
+ try (InputStream in = getResourceAsStream("module-info.class")) {
+ if (in != null)
+ clazz = loadModuleInfoClass(in);
+ } catch (Exception ignore) { }
+ return clazz;
+ }
+
+ /**
+ * Loads module-info.class as a package-private interface in a class loader
+ * that is a child of this module's class loader.
+ */
+ private Class> loadModuleInfoClass(InputStream in) throws IOException {
+ final String MODULE_INFO = "module-info";
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+ + ClassWriter.COMPUTE_FRAMES);
+
+ ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) {
+ @Override
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ cw.visit(version,
+ Opcodes.ACC_INTERFACE
+ + Opcodes.ACC_ABSTRACT
+ + Opcodes.ACC_SYNTHETIC,
+ MODULE_INFO,
+ null,
+ "java/lang/Object",
+ null);
+ }
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ // keep annotations
+ return super.visitAnnotation(desc, visible);
+ }
+ @Override
+ public void visitAttribute(Attribute attr) {
+ // drop non-annotation attributes
+ }
+ };
+
+ ClassReader cr = new ClassReader(in);
+ cr.accept(cv, 0);
+ byte[] bytes = cw.toByteArray();
+
+ ClassLoader cl = new ClassLoader(loader) {
+ @Override
+ protected Class> findClass(String cn)throws ClassNotFoundException {
+ if (cn.equals(MODULE_INFO)) {
+ return super.defineClass(cn, bytes, 0, bytes.length);
+ } else {
+ throw new ClassNotFoundException(cn);
+ }
+ }
+ };
+
+ try {
+ return cl.loadClass(MODULE_INFO);
+ } catch (ClassNotFoundException e) {
+ throw new InternalError(e);
+ }
}
@@ -969,16 +1378,35 @@ public final class Module {
/**
- * Returns an input stream for reading a resource in this module. Returns
- * {@code null} if the resource is not in this module or access to the
- * resource is denied by the security manager.
- * The {@code name} is a {@code '/'}-separated path name that identifies
- * the resource.
+ * Returns an input stream for reading a resource in this module. The
+ * {@code name} parameter is a {@code '/'}-separated path name that
+ * identifies the resource.
*
- * If this module is an unnamed module, and the {@code ClassLoader} for
- * this module is not {@code null}, then this method is equivalent to
- * invoking the {@link ClassLoader#getResourceAsStream(String)
- * getResourceAsStream} method on the class loader for this module.
+ *
A resource in a named modules may be encapsulated so that
+ * it cannot be located by code in other modules. Whether a resource can be
+ * located or not is determined as follows:
+ *
+ *
+ * - The package name of the resource is derived from the
+ * subsequence of characters that precedes the last {@code '/'} and then
+ * replacing each {@code '/'} character in the subsequence with
+ * {@code '.'}. For example, the package name derived for a resource
+ * named "{@code a/b/c/foo.properties}" is "{@code a.b.c}".
+ *
+ * - If the package name is a package in the module then the package
+ * must be {@link #isOpen open} the module of the caller of this method.
+ * If the package is not in the module then the resource is not
+ * encapsulated. Resources in the unnamed package or "{@code META-INF}",
+ * for example, are never encapsulated because they can never be
+ * packages in a named module.
+ *
+ * - As a special case, resources ending with "{@code .class}" are
+ * never encapsulated.
+ *
+ *
+ * This method returns {@code null} if the resource is not in this
+ * module, the resource is encapsulated and cannot be located by the caller,
+ * or access to the resource is denied by the security manager.
*
* @param name
* The resource name
@@ -990,36 +1418,35 @@ public final class Module {
*
* @see java.lang.module.ModuleReader#open(String)
*/
+ @CallerSensitive
public InputStream getResourceAsStream(String name) throws IOException {
Objects.requireNonNull(name);
- URL url = null;
-
- if (isNamed()) {
- String mn = this.name;
-
- // special-case built-in class loaders to avoid URL connection
- if (loader == null) {
- return BootLoader.findResourceAsStream(mn, name);
- } else if (loader instanceof BuiltinClassLoader) {
- return ((BuiltinClassLoader) loader).findResourceAsStream(mn, name);
+ if (isNamed() && !ResourceHelper.isSimpleResource(name)) {
+ Module caller = Reflection.getCallerClass().getModule();
+ if (caller != this && caller != Object.class.getModule()) {
+ // ignore packages added for proxies via addPackage
+ Set packages = getDescriptor().packages();
+ String pn = ResourceHelper.getPackageName(name);
+ if (packages.contains(pn) && !isOpen(pn, caller)) {
+ // resource is in package not open to caller
+ return null;
+ }
}
-
- // use SharedSecrets to invoke protected method
- url = SharedSecrets.getJavaLangAccess().findResource(loader, mn, name);
-
- } else {
-
- // unnamed module
- if (loader == null) {
- url = BootLoader.findResource(name);
- } else {
- return loader.getResourceAsStream(name);
- }
-
}
- // fallthrough to URL case
+ String mn = this.name;
+
+ // special-case built-in class loaders to avoid URL connection
+ if (loader == null) {
+ return BootLoader.findResourceAsStream(mn, name);
+ } else if (loader instanceof BuiltinClassLoader) {
+ return ((BuiltinClassLoader) loader).findResourceAsStream(mn, name);
+ }
+
+ // locate resource in module
+ JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+ URL url = jla.findResource(loader, mn, name);
if (url != null) {
try {
return url.openStream();
@@ -1053,6 +1480,7 @@ public final class Module {
// JVM_DefineModule
private static native void defineModule0(Module module,
+ boolean isOpen,
String version,
String location,
String[] pns);
@@ -1098,15 +1526,31 @@ public final class Module {
}
@Override
public void addExports(Module m, String pn, Module other) {
- m.implAddExports(pn, other, true);
+ m.implAddExportsOrOpens(pn, other, false, true);
+ }
+ @Override
+ public void addOpens(Module m, String pn, Module other) {
+ m.implAddExportsOrOpens(pn, other, true, true);
}
@Override
public void addExportsToAll(Module m, String pn) {
- m.implAddExports(pn, Module.EVERYONE_MODULE, true);
+ m.implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, false, true);
+ }
+ @Override
+ public void addOpensToAll(Module m, String pn) {
+ m.implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, true, true);
}
@Override
public void addExportsToAllUnnamed(Module m, String pn) {
- m.implAddExports(pn, Module.ALL_UNNAMED_MODULE, true);
+ m.implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, false, true);
+ }
+ @Override
+ public void addOpensToAllUnnamed(Module m, String pn) {
+ m.implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, true, true);
+ }
+ @Override
+ public void addUses(Module m, Class> service) {
+ m.implAddUses(service);
}
@Override
public void addPackage(Module m, String pn) {
@@ -1116,6 +1560,14 @@ public final class Module {
public ServicesCatalog getServicesCatalog(Layer layer) {
return layer.getServicesCatalog();
}
+ @Override
+ public Stream layers(Layer layer) {
+ return layer.layers();
+ }
+ @Override
+ public Stream layers(ClassLoader loader) {
+ return Layer.layers(loader);
+ }
});
}
}
diff --git a/jdk/src/java.base/share/classes/java/net/Authenticator.java b/jdk/src/java.base/share/classes/java/net/Authenticator.java
index 81a87c79987..16af2845c8c 100644
--- a/jdk/src/java.base/share/classes/java/net/Authenticator.java
+++ b/jdk/src/java.base/share/classes/java/net/Authenticator.java
@@ -25,6 +25,8 @@
package java.net;
+import sun.net.www.protocol.http.AuthenticatorKeys;
+
/**
* The class Authenticator represents an object that knows how to obtain
* authentication for a network connection. Usually, it will do this
@@ -70,6 +72,7 @@ class Authenticator {
private String requestingScheme;
private URL requestingURL;
private RequestorType requestingAuthType;
+ private final String key = AuthenticatorKeys.computeKey(this);
/**
* The type of the entity requesting authentication.
@@ -348,6 +351,75 @@ class Authenticator {
}
}
+ /**
+ * Ask the given {@code authenticator} for a password. If the given
+ * {@code authenticator} is null, the authenticator, if any, that has been
+ * registered with the system using {@link #setDefault(java.net.Authenticator)
+ * setDefault} is used.
+ *
+ * First, if there is a security manager, its {@code checkPermission}
+ * method is called with a
+ * {@code NetPermission("requestPasswordAuthentication")} permission.
+ * This may result in a java.lang.SecurityException.
+ *
+ * @param authenticator the authenticator, or {@code null}.
+ * @param host The hostname of the site requesting authentication.
+ * @param addr The InetAddress of the site requesting authorization,
+ * or null if not known.
+ * @param port the port for the requested connection
+ * @param protocol The protocol that's requesting the connection
+ * ({@link java.net.Authenticator#getRequestingProtocol()})
+ * @param prompt A prompt string for the user
+ * @param scheme The authentication scheme
+ * @param url The requesting URL that caused the authentication
+ * @param reqType The type (server or proxy) of the entity requesting
+ * authentication.
+ *
+ * @return The username/password, or {@code null} if one can't be gotten.
+ *
+ * @throws SecurityException
+ * if a security manager exists and its
+ * {@code checkPermission} method doesn't allow
+ * the password authentication request.
+ *
+ * @see SecurityManager#checkPermission
+ * @see java.net.NetPermission
+ *
+ * @since 9
+ */
+ public static PasswordAuthentication requestPasswordAuthentication(
+ Authenticator authenticator,
+ String host,
+ InetAddress addr,
+ int port,
+ String protocol,
+ String prompt,
+ String scheme,
+ URL url,
+ RequestorType reqType) {
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ NetPermission requestPermission
+ = new NetPermission("requestPasswordAuthentication");
+ sm.checkPermission(requestPermission);
+ }
+
+ Authenticator a = authenticator == null ? theAuthenticator : authenticator;
+ if (a == null) {
+ return null;
+ } else {
+ return a.requestPasswordAuthenticationInstance(host,
+ addr,
+ port,
+ protocol,
+ prompt,
+ scheme,
+ url,
+ reqType);
+ }
+ }
+
/**
* Ask this authenticator for a password.
*
@@ -493,4 +565,11 @@ class Authenticator {
protected RequestorType getRequestorType () {
return requestingAuthType;
}
+
+ static String getKey(Authenticator a) {
+ return a.key;
+ }
+ static {
+ AuthenticatorKeys.setAuthenticatorKeyAccess(Authenticator::getKey);
+ }
}
diff --git a/jdk/src/java.base/share/classes/java/net/HttpURLConnection.java b/jdk/src/java.base/share/classes/java/net/HttpURLConnection.java
index 94b537d2658..9e428e59584 100644
--- a/jdk/src/java.base/share/classes/java/net/HttpURLConnection.java
+++ b/jdk/src/java.base/share/classes/java/net/HttpURLConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -102,6 +102,53 @@ public abstract class HttpURLConnection extends URLConnection {
*/
protected long fixedContentLengthLong = -1;
+ /**
+ * Supplies an {@link java.net.Authenticator Authenticator} to be used
+ * when authentication is requested through the HTTP protocol for
+ * this {@code HttpURLConnection}.
+ * If no authenticator is supplied, the
+ * {@linkplain Authenticator#setDefault(java.net.Authenticator) default
+ * authenticator} will be used.
+ *
+ * @implSpec The default behavior of this method is to unconditionally
+ * throw {@link UnsupportedOperationException}. Concrete
+ * implementations of {@code HttpURLConnection}
+ * which support supplying an {@code Authenticator} for a
+ * specific {@code HttpURLConnection} instance should
+ * override this method to implement a different behavior.
+ *
+ * @implNote Depending on authentication schemes, an implementation
+ * may or may not need to use the provided authenticator
+ * to obtain a password. For instance, an implementation that
+ * relies on third-party security libraries may still invoke the
+ * default authenticator if these libraries are configured
+ * to do so.
+ * Likewise, an implementation that supports transparent
+ * NTLM authentication may let the system attempt
+ * to connect using the system user credentials first,
+ * before invoking the provided authenticator.
+ *
+ * However, if an authenticator is specifically provided,
+ * then the underlying connection may only be reused for
+ * {@code HttpURLConnection} instances which share the same
+ * {@code Authenticator} instance, and authentication information,
+ * if cached, may only be reused for an {@code HttpURLConnection}
+ * sharing that same {@code Authenticator}.
+ *
+ * @param auth The {@code Authenticator} that should be used by this
+ * {@code HttpURLConnection}.
+ *
+ * @throws UnsupportedOperationException if setting an Authenticator is
+ * not supported by the underlying implementation.
+ * @throws IllegalStateException if URLConnection is already connected.
+ * @throws NullPointerException if the supplied {@code auth} is {@code null}.
+ * @since 9
+ */
+ public void setAuthenticator(Authenticator auth) {
+ throw new UnsupportedOperationException("Supplying an authenticator"
+ + " is not supported by " + this.getClass());
+ }
+
/**
* Returns the key for the {@code n}th header field.
* Some implementations may treat the {@code 0}th
diff --git a/jdk/src/java.base/share/classes/java/net/URLClassLoader.java b/jdk/src/java.base/share/classes/java/net/URLClassLoader.java
index 602f9d7f61a..57f45027ea2 100644
--- a/jdk/src/java.base/share/classes/java/net/URLClassLoader.java
+++ b/jdk/src/java.base/share/classes/java/net/URLClassLoader.java
@@ -72,6 +72,10 @@ import sun.security.util.SecurityConstants;
*
* The classes that are loaded are by default granted permission only to
* access the URLs specified when the URLClassLoader was created.
+ *
+ * This class loader supports the loading of classes from the contents of a
+ * multi-release JAR file
+ * that is referred to by a given URL.
*
* @author David Connelly
* @since 1.2
diff --git a/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java b/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java
index c9d6baa3ac8..d1c3919cb63 100644
--- a/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java
+++ b/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java
@@ -151,7 +151,7 @@ import java.util.Set;
* Each chronology must define a chronology ID that is unique within the system.
* If the chronology represents a calendar system defined by the
* CLDR specification then the calendar type is the concatenation of the
- * CLDR type and, if applicable, the CLDR variant,
+ * CLDR type and, if applicable, the CLDR variant.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
@@ -177,7 +177,7 @@ public interface Chronology extends Comparable {
*
* @param temporal the temporal to convert, not null
* @return the chronology, not null
- * @throws DateTimeException if unable to convert to an {@code Chronology}
+ * @throws DateTimeException if unable to convert to a {@code Chronology}
*/
static Chronology from(TemporalAccessor temporal) {
Objects.requireNonNull(temporal, "temporal");
@@ -203,7 +203,7 @@ public interface Chronology extends Comparable {
* For example, the locale "en-JP-u-ca-japanese" represents the English
* language as used in Japan with the Japanese calendar system.
*
- * This method finds the desired calendar system by in a manner equivalent
+ * This method finds the desired calendar system in a manner equivalent
* to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}.
* If the "ca" key is not present, then {@code IsoChronology} is returned.
*
@@ -286,7 +286,7 @@ public interface Chronology extends Comparable {
*
* The calendar type is an identifier defined by the CLDR and
* Unicode Locale Data Markup Language (LDML) specifications
- * to uniquely identification a calendar.
+ * to uniquely identify a calendar.
* The {@code getCalendarType} is the concatenation of the CLDR calendar type
* and the variant, if applicable, is appended separated by "-".
* The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
diff --git a/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java
index 9beb6955c0c..0e6eb066fd3 100644
--- a/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java
+++ b/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java
@@ -119,6 +119,7 @@ import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import sun.text.spi.JavaTimeDateTimePatternProvider;
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.LocaleResources;
import sun.util.locale.provider.TimeZoneNameUtility;
@@ -212,9 +213,10 @@ public final class DateTimeFormatterBuilder {
if (dateStyle == null && timeStyle == null) {
throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null");
}
- LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale);
- String pattern = lr.getJavaTimeDateTimePattern(
- convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType());
+ LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(JavaTimeDateTimePatternProvider.class, locale);
+ JavaTimeDateTimePatternProvider provider = adapter.getJavaTimeDateTimePatternProvider();
+ String pattern = provider.getJavaTimeDateTimePattern(convertStyle(timeStyle),
+ convertStyle(dateStyle), chrono.getCalendarType(), locale);
return pattern;
}
diff --git a/jdk/src/java.base/share/classes/java/util/ArrayDeque.java b/jdk/src/java.base/share/classes/java/util/ArrayDeque.java
index 5a9b8312e30..0122f9e52e5 100644
--- a/jdk/src/java.base/share/classes/java/util/ArrayDeque.java
+++ b/jdk/src/java.base/share/classes/java/util/ArrayDeque.java
@@ -36,6 +36,8 @@ package java.util;
import java.io.Serializable;
import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
/**
* Resizable-array implementation of the {@link Deque} interface. Array
@@ -87,81 +89,89 @@ import java.util.function.Consumer;
public class ArrayDeque extends AbstractCollection
implements Deque, Cloneable, Serializable
{
+ /*
+ * VMs excel at optimizing simple array loops where indices are
+ * incrementing or decrementing over a valid slice, e.g.
+ *
+ * for (int i = start; i < end; i++) ... elements[i]
+ *
+ * Because in a circular array, elements are in general stored in
+ * two disjoint such slices, we help the VM by writing unusual
+ * nested loops for all traversals over the elements. Having only
+ * one hot inner loop body instead of two or three eases human
+ * maintenance and encourages VM loop inlining into the caller.
+ */
+
/**
* The array in which the elements of the deque are stored.
- * The capacity of the deque is the length of this array, which is
- * always a power of two. The array is never allowed to become
- * full, except transiently within an addX method where it is
- * resized (see doubleCapacity) immediately upon becoming full,
- * thus avoiding head and tail wrapping around to equal each
- * other. We also guarantee that all array cells not holding
- * deque elements are always null.
+ * All array cells not holding deque elements are always null.
+ * The array always has at least one null slot (at tail).
*/
- transient Object[] elements; // non-private to simplify nested class access
+ transient Object[] elements;
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
- * arbitrary number equal to tail if the deque is empty.
+ * arbitrary number 0 <= head < elements.length equal to tail if
+ * the deque is empty.
*/
transient int head;
/**
* The index at which the next element would be added to the tail
- * of the deque (via addLast(E), add(E), or push(E)).
+ * of the deque (via addLast(E), add(E), or push(E));
+ * elements[tail] is always null.
*/
transient int tail;
/**
- * The minimum capacity that we'll use for a newly created deque.
- * Must be a power of 2.
+ * The maximum size of array to allocate.
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * OutOfMemoryError: Requested array size exceeds VM limit
*/
- private static final int MIN_INITIAL_CAPACITY = 8;
-
- // ****** Array allocation and resizing utilities ******
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
- * Allocates empty array to hold the given number of elements.
+ * Increases the capacity of this deque by at least the given amount.
*
- * @param numElements the number of elements to hold
+ * @param needed the required minimum extra capacity; must be positive
*/
- private void allocateElements(int numElements) {
- int initialCapacity = MIN_INITIAL_CAPACITY;
- // Find the best power of two to hold elements.
- // Tests "<=" because arrays aren't kept full.
- if (numElements >= initialCapacity) {
- initialCapacity = numElements;
- initialCapacity |= (initialCapacity >>> 1);
- initialCapacity |= (initialCapacity >>> 2);
- initialCapacity |= (initialCapacity >>> 4);
- initialCapacity |= (initialCapacity >>> 8);
- initialCapacity |= (initialCapacity >>> 16);
- initialCapacity++;
-
- if (initialCapacity < 0) // Too many elements, must back off
- initialCapacity >>>= 1; // Good luck allocating 2^30 elements
+ private void grow(int needed) {
+ // overflow-conscious code
+ final int oldCapacity = elements.length;
+ int newCapacity;
+ // Double capacity if small; else grow by 50%
+ int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
+ if (jump < needed
+ || (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0)
+ newCapacity = newCapacity(needed, jump);
+ elements = Arrays.copyOf(elements, newCapacity);
+ // Exceptionally, here tail == head needs to be disambiguated
+ if (tail < head || (tail == head && elements[head] != null)) {
+ // wrap around; slide first leg forward to end of array
+ int newSpace = newCapacity - oldCapacity;
+ System.arraycopy(elements, head,
+ elements, head + newSpace,
+ oldCapacity - head);
+ Arrays.fill(elements, head, head + newSpace, null);
+ head += newSpace;
}
- elements = new Object[initialCapacity];
}
- /**
- * Doubles the capacity of this deque. Call only when full, i.e.,
- * when head and tail have wrapped around to become equal.
- */
- private void doubleCapacity() {
- assert head == tail;
- int p = head;
- int n = elements.length;
- int r = n - p; // number of elements to the right of p
- int newCapacity = n << 1;
- if (newCapacity < 0)
- throw new IllegalStateException("Sorry, deque too big");
- Object[] a = new Object[newCapacity];
- System.arraycopy(elements, p, a, 0, r);
- System.arraycopy(elements, 0, a, r, p);
- elements = a;
- head = 0;
- tail = n;
+ /** Capacity calculation for edge conditions, especially overflow. */
+ private int newCapacity(int needed, int jump) {
+ final int oldCapacity = elements.length, minCapacity;
+ if ((minCapacity = oldCapacity + needed) - MAX_ARRAY_SIZE > 0) {
+ if (minCapacity < 0)
+ throw new IllegalStateException("Sorry, deque too big");
+ return Integer.MAX_VALUE;
+ }
+ if (needed > jump)
+ return minCapacity;
+ return (oldCapacity + jump - MAX_ARRAY_SIZE < 0)
+ ? oldCapacity + jump
+ : MAX_ARRAY_SIZE;
}
/**
@@ -176,10 +186,13 @@ public class ArrayDeque extends AbstractCollection
* Constructs an empty array deque with an initial capacity
* sufficient to hold the specified number of elements.
*
- * @param numElements lower bound on initial capacity of the deque
+ * @param numElements lower bound on initial capacity of the deque
*/
public ArrayDeque(int numElements) {
- allocateElements(numElements);
+ elements =
+ new Object[(numElements < 1) ? 1 :
+ (numElements == Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+ numElements + 1];
}
/**
@@ -193,10 +206,71 @@ public class ArrayDeque extends AbstractCollection
* @throws NullPointerException if the specified collection is null
*/
public ArrayDeque(Collection extends E> c) {
- allocateElements(c.size());
+ this(c.size());
addAll(c);
}
+ /**
+ * Increments i, mod modulus.
+ * Precondition and postcondition: 0 <= i < modulus.
+ */
+ static final int inc(int i, int modulus) {
+ if (++i >= modulus) i = 0;
+ return i;
+ }
+
+ /**
+ * Decrements i, mod modulus.
+ * Precondition and postcondition: 0 <= i < modulus.
+ */
+ static final int dec(int i, int modulus) {
+ if (--i < 0) i = modulus - 1;
+ return i;
+ }
+
+ /**
+ * Circularly adds the given distance to index i, mod modulus.
+ * Precondition: 0 <= i < modulus, 0 <= distance <= modulus.
+ * @return index 0 <= i < modulus
+ */
+ static final int add(int i, int distance, int modulus) {
+ if ((i += distance) - modulus >= 0) i -= modulus;
+ return i;
+ }
+
+ /**
+ * Subtracts j from i, mod modulus.
+ * Index i must be logically ahead of index j.
+ * Precondition: 0 <= i < modulus, 0 <= j < modulus.
+ * @return the "circular distance" from j to i; corner case i == j
+ * is diambiguated to "empty", returning 0.
+ */
+ static final int sub(int i, int j, int modulus) {
+ if ((i -= j) < 0) i += modulus;
+ return i;
+ }
+
+ /**
+ * Returns element at array index i.
+ * This is a slight abuse of generics, accepted by javac.
+ */
+ @SuppressWarnings("unchecked")
+ static final E elementAt(Object[] es, int i) {
+ return (E) es[i];
+ }
+
+ /**
+ * A version of elementAt that checks for null elements.
+ * This check doesn't catch all possible comodifications,
+ * but does catch ones that corrupt traversal.
+ */
+ static final E nonNullElementAt(Object[] es, int i) {
+ @SuppressWarnings("unchecked") E e = (E) es[i];
+ if (e == null)
+ throw new ConcurrentModificationException();
+ return e;
+ }
+
// The main insertion and extraction methods are addFirst,
// addLast, pollFirst, pollLast. The other methods are defined in
// terms of these.
@@ -210,9 +284,10 @@ public class ArrayDeque extends AbstractCollection
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
- elements[head = (head - 1) & (elements.length - 1)] = e;
+ final Object[] es = elements;
+ es[head = dec(head, es.length)] = e;
if (head == tail)
- doubleCapacity();
+ grow(1);
}
/**
@@ -226,9 +301,29 @@ public class ArrayDeque extends AbstractCollection
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
- elements[tail] = e;
- if ( (tail = (tail + 1) & (elements.length - 1)) == head)
- doubleCapacity();
+ final Object[] es = elements;
+ es[tail] = e;
+ if (head == (tail = inc(tail, es.length)))
+ grow(1);
+ }
+
+ /**
+ * Adds all of the elements in the specified collection at the end
+ * of this deque, as if by calling {@link #addLast} on each one,
+ * in the order that they are returned by the collection's
+ * iterator.
+ *
+ * @param c the elements to be inserted into this deque
+ * @return {@code true} if this deque changed as a result of the call
+ * @throws NullPointerException if the specified collection or any
+ * of its elements are null
+ */
+ public boolean addAll(Collection extends E> c) {
+ final int s, needed;
+ if ((needed = (s = size()) + c.size() + 1 - elements.length) > 0)
+ grow(needed);
+ c.forEach(this::addLast);
+ return size() > s;
}
/**
@@ -259,78 +354,70 @@ public class ArrayDeque extends AbstractCollection
* @throws NoSuchElementException {@inheritDoc}
*/
public E removeFirst() {
- E x = pollFirst();
- if (x == null)
+ E e = pollFirst();
+ if (e == null)
throw new NoSuchElementException();
- return x;
+ return e;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E removeLast() {
- E x = pollLast();
- if (x == null)
+ E e = pollLast();
+ if (e == null)
throw new NoSuchElementException();
- return x;
+ return e;
}
public E pollFirst() {
- final Object[] elements = this.elements;
- final int h = head;
- @SuppressWarnings("unchecked")
- E result = (E) elements[h];
- // Element is null if deque empty
- if (result != null) {
- elements[h] = null; // Must null out slot
- head = (h + 1) & (elements.length - 1);
+ final Object[] es;
+ final int h;
+ E e = elementAt(es = elements, h = head);
+ if (e != null) {
+ es[h] = null;
+ head = inc(h, es.length);
}
- return result;
+ return e;
}
public E pollLast() {
- final Object[] elements = this.elements;
- final int t = (tail - 1) & (elements.length - 1);
- @SuppressWarnings("unchecked")
- E result = (E) elements[t];
- if (result != null) {
- elements[t] = null;
- tail = t;
- }
- return result;
+ final Object[] es;
+ final int t;
+ E e = elementAt(es = elements, t = dec(tail, es.length));
+ if (e != null)
+ es[tail = t] = null;
+ return e;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E getFirst() {
- @SuppressWarnings("unchecked")
- E result = (E) elements[head];
- if (result == null)
+ E e = elementAt(elements, head);
+ if (e == null)
throw new NoSuchElementException();
- return result;
+ return e;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
public E getLast() {
- @SuppressWarnings("unchecked")
- E result = (E) elements[(tail - 1) & (elements.length - 1)];
- if (result == null)
+ final Object[] es = elements;
+ E e = elementAt(es, dec(tail, es.length));
+ if (e == null)
throw new NoSuchElementException();
- return result;
+ return e;
}
- @SuppressWarnings("unchecked")
public E peekFirst() {
- // elements[head] is null if deque empty
- return (E) elements[head];
+ return elementAt(elements, head);
}
- @SuppressWarnings("unchecked")
public E peekLast() {
- return (E) elements[(tail - 1) & (elements.length - 1)];
+ final Object[] es;
+ return elementAt(es = elements, dec(tail, es.length));
}
/**
@@ -347,13 +434,15 @@ public class ArrayDeque extends AbstractCollection
*/
public boolean removeFirstOccurrence(Object o) {
if (o != null) {
- int mask = elements.length - 1;
- int i = head;
- for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) {
- if (o.equals(x)) {
- delete(i);
- return true;
- }
+ final Object[] es = elements;
+ for (int i = head, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ if (o.equals(es[i])) {
+ delete(i);
+ return true;
+ }
+ if (to == end) break;
}
}
return false;
@@ -373,13 +462,15 @@ public class ArrayDeque extends AbstractCollection
*/
public boolean removeLastOccurrence(Object o) {
if (o != null) {
- int mask = elements.length - 1;
- int i = (tail - 1) & mask;
- for (Object x; (x = elements[i]) != null; i = (i - 1) & mask) {
- if (o.equals(x)) {
- delete(i);
- return true;
- }
+ final Object[] es = elements;
+ for (int i = tail, end = head, to = (i >= end) ? end : 0;
+ ; i = es.length, to = end) {
+ for (i--; i > to - 1; i--)
+ if (o.equals(es[i])) {
+ delete(i);
+ return true;
+ }
+ if (to == end) break;
}
}
return false;
@@ -499,59 +590,47 @@ public class ArrayDeque extends AbstractCollection
return removeFirst();
}
- private void checkInvariants() {
- assert elements[tail] == null;
- assert head == tail ? elements[head] == null :
- (elements[head] != null &&
- elements[(tail - 1) & (elements.length - 1)] != null);
- assert elements[(head - 1) & (elements.length - 1)] == null;
- }
-
/**
- * Removes the element at the specified position in the elements array,
- * adjusting head and tail as necessary. This can result in motion of
- * elements backwards or forwards in the array.
+ * Removes the element at the specified position in the elements array.
+ * This can result in forward or backwards motion of array elements.
+ * We optimize for least element motion.
*
* This method is called delete rather than remove to emphasize
* that its semantics differ from those of {@link List#remove(int)}.
*
- * @return true if elements moved backwards
+ * @return true if elements near tail moved backwards
*/
boolean delete(int i) {
- checkInvariants();
- final Object[] elements = this.elements;
- final int mask = elements.length - 1;
- final int h = head;
- final int t = tail;
- final int front = (i - h) & mask;
- final int back = (t - i) & mask;
-
- // Invariant: head <= i < tail mod circularity
- if (front >= ((t - h) & mask))
- throw new ConcurrentModificationException();
-
- // Optimize for least element motion
+ final Object[] es = elements;
+ final int capacity = es.length;
+ final int h, t;
+ // number of elements before to-be-deleted elt
+ final int front = sub(i, h = head, capacity);
+ // number of elements after to-be-deleted elt
+ final int back = sub(t = tail, i, capacity) - 1;
if (front < back) {
+ // move front elements forwards
if (h <= i) {
- System.arraycopy(elements, h, elements, h + 1, front);
+ System.arraycopy(es, h, es, h + 1, front);
} else { // Wrap around
- System.arraycopy(elements, 0, elements, 1, i);
- elements[0] = elements[mask];
- System.arraycopy(elements, h, elements, h + 1, mask - h);
+ System.arraycopy(es, 0, es, 1, i);
+ es[0] = es[capacity - 1];
+ System.arraycopy(es, h, es, h + 1, front - (i + 1));
}
- elements[h] = null;
- head = (h + 1) & mask;
+ es[h] = null;
+ head = inc(h, capacity);
return false;
} else {
- if (i < t) { // Copy the null tail as well
- System.arraycopy(elements, i + 1, elements, i, back);
- tail = t - 1;
+ // move back elements backwards
+ tail = dec(t, capacity);
+ if (i <= tail) {
+ System.arraycopy(es, i + 1, es, i, back);
} else { // Wrap around
- System.arraycopy(elements, i + 1, elements, i, mask - i);
- elements[mask] = elements[0];
- System.arraycopy(elements, 1, elements, 0, t);
- tail = (t - 1) & mask;
+ System.arraycopy(es, i + 1, es, i, capacity - (i + 1));
+ es[capacity - 1] = es[0];
+ System.arraycopy(es, 1, es, 0, t - 1);
}
+ es[tail] = null;
return true;
}
}
@@ -564,7 +643,7 @@ public class ArrayDeque extends AbstractCollection
* @return the number of elements in this deque
*/
public int size() {
- return (tail - head) & (elements.length - 1);
+ return sub(tail, head, elements.length);
}
/**
@@ -593,101 +672,317 @@ public class ArrayDeque extends AbstractCollection
}
private class DeqIterator implements Iterator {
- /**
- * Index of element to be returned by subsequent call to next.
- */
- private int cursor = head;
+ /** Index of element to be returned by subsequent call to next. */
+ int cursor;
- /**
- * Tail recorded at construction (also in remove), to stop
- * iterator and also to check for comodification.
- */
- private int fence = tail;
+ /** Number of elements yet to be returned. */
+ int remaining = size();
/**
* Index of element returned by most recent call to next.
* Reset to -1 if element is deleted by a call to remove.
*/
- private int lastRet = -1;
+ int lastRet = -1;
- public boolean hasNext() {
- return cursor != fence;
+ DeqIterator() { cursor = head; }
+
+ public final boolean hasNext() {
+ return remaining > 0;
}
public E next() {
- if (cursor == fence)
+ if (remaining <= 0)
throw new NoSuchElementException();
- @SuppressWarnings("unchecked")
- E result = (E) elements[cursor];
- // This check doesn't catch all possible comodifications,
- // but does catch the ones that corrupt traversal
- if (tail != fence || result == null)
- throw new ConcurrentModificationException();
- lastRet = cursor;
- cursor = (cursor + 1) & (elements.length - 1);
- return result;
+ final Object[] es = elements;
+ E e = nonNullElementAt(es, cursor);
+ cursor = inc(lastRet = cursor, es.length);
+ remaining--;
+ return e;
}
- public void remove() {
+ void postDelete(boolean leftShifted) {
+ if (leftShifted)
+ cursor = dec(cursor, elements.length);
+ }
+
+ public final void remove() {
if (lastRet < 0)
throw new IllegalStateException();
- if (delete(lastRet)) { // if left-shifted, undo increment in next()
- cursor = (cursor - 1) & (elements.length - 1);
- fence = tail;
- }
+ postDelete(delete(lastRet));
lastRet = -1;
}
public void forEachRemaining(Consumer super E> action) {
Objects.requireNonNull(action);
- Object[] a = elements;
- int m = a.length - 1, f = fence, i = cursor;
- cursor = f;
- while (i != f) {
- @SuppressWarnings("unchecked") E e = (E)a[i];
- i = (i + 1) & m;
- if (e == null)
- throw new ConcurrentModificationException();
- action.accept(e);
+ int r;
+ if ((r = remaining) <= 0)
+ return;
+ remaining = 0;
+ final Object[] es = elements;
+ if (es[cursor] == null || sub(tail, cursor, es.length) != r)
+ throw new ConcurrentModificationException();
+ for (int i = cursor, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ action.accept(elementAt(es, i));
+ if (to == end) {
+ if (end != tail)
+ throw new ConcurrentModificationException();
+ lastRet = dec(end, es.length);
+ break;
+ }
+ }
+ }
+ }
+
+ private class DescendingIterator extends DeqIterator {
+ DescendingIterator() { cursor = dec(tail, elements.length); }
+
+ public final E next() {
+ if (remaining <= 0)
+ throw new NoSuchElementException();
+ final Object[] es = elements;
+ E e = nonNullElementAt(es, cursor);
+ cursor = dec(lastRet = cursor, es.length);
+ remaining--;
+ return e;
+ }
+
+ void postDelete(boolean leftShifted) {
+ if (!leftShifted)
+ cursor = inc(cursor, elements.length);
+ }
+
+ public final void forEachRemaining(Consumer super E> action) {
+ Objects.requireNonNull(action);
+ int r;
+ if ((r = remaining) <= 0)
+ return;
+ remaining = 0;
+ final Object[] es = elements;
+ if (es[cursor] == null || sub(cursor, head, es.length) + 1 != r)
+ throw new ConcurrentModificationException();
+ for (int i = cursor, end = head, to = (i >= end) ? end : 0;
+ ; i = es.length - 1, to = end) {
+ // hotspot generates faster code than for: i >= to !
+ for (; i > to - 1; i--)
+ action.accept(elementAt(es, i));
+ if (to == end) {
+ if (end != head)
+ throw new ConcurrentModificationException();
+ lastRet = end;
+ break;
+ }
}
}
}
/**
- * This class is nearly a mirror-image of DeqIterator, using tail
- * instead of head for initial cursor, and head instead of tail
- * for fence.
+ * Creates a late-binding
+ * and fail-fast {@link Spliterator} over the elements in this
+ * deque.
+ *
+ * The {@code Spliterator} reports {@link Spliterator#SIZED},
+ * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
+ * {@link Spliterator#NONNULL}. Overriding implementations should document
+ * the reporting of additional characteristic values.
+ *
+ * @return a {@code Spliterator} over the elements in this deque
+ * @since 1.8
*/
- private class DescendingIterator implements Iterator {
- private int cursor = tail;
- private int fence = head;
- private int lastRet = -1;
+ public Spliterator spliterator() {
+ return new DeqSpliterator();
+ }
- public boolean hasNext() {
- return cursor != fence;
+ final class DeqSpliterator implements Spliterator {
+ private int fence; // -1 until first use
+ private int cursor; // current index, modified on traverse/split
+
+ /** Constructs late-binding spliterator over all elements. */
+ DeqSpliterator() {
+ this.fence = -1;
}
- public E next() {
- if (cursor == fence)
- throw new NoSuchElementException();
- cursor = (cursor - 1) & (elements.length - 1);
- @SuppressWarnings("unchecked")
- E result = (E) elements[cursor];
- if (head != fence || result == null)
- throw new ConcurrentModificationException();
- lastRet = cursor;
- return result;
+ /** Constructs spliterator over the given range. */
+ DeqSpliterator(int origin, int fence) {
+ // assert 0 <= origin && origin < elements.length;
+ // assert 0 <= fence && fence < elements.length;
+ this.cursor = origin;
+ this.fence = fence;
}
- public void remove() {
- if (lastRet < 0)
- throw new IllegalStateException();
- if (!delete(lastRet)) {
- cursor = (cursor + 1) & (elements.length - 1);
- fence = head;
+ /** Ensures late-binding initialization; then returns fence. */
+ private int getFence() { // force initialization
+ int t;
+ if ((t = fence) < 0) {
+ t = fence = tail;
+ cursor = head;
}
- lastRet = -1;
+ return t;
}
+
+ public DeqSpliterator trySplit() {
+ final Object[] es = elements;
+ final int i, n;
+ return ((n = sub(getFence(), i = cursor, es.length) >> 1) <= 0)
+ ? null
+ : new DeqSpliterator(i, cursor = add(i, n, es.length));
+ }
+
+ public void forEachRemaining(Consumer super E> action) {
+ if (action == null)
+ throw new NullPointerException();
+ final int end = getFence(), cursor = this.cursor;
+ final Object[] es = elements;
+ if (cursor != end) {
+ this.cursor = end;
+ // null check at both ends of range is sufficient
+ if (es[cursor] == null || es[dec(end, es.length)] == null)
+ throw new ConcurrentModificationException();
+ for (int i = cursor, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ action.accept(elementAt(es, i));
+ if (to == end) break;
+ }
+ }
+ }
+
+ public boolean tryAdvance(Consumer super E> action) {
+ Objects.requireNonNull(action);
+ final Object[] es = elements;
+ if (fence < 0) { fence = tail; cursor = head; } // late-binding
+ final int i;
+ if ((i = cursor) == fence)
+ return false;
+ E e = nonNullElementAt(es, i);
+ cursor = inc(i, es.length);
+ action.accept(e);
+ return true;
+ }
+
+ public long estimateSize() {
+ return sub(getFence(), cursor, elements.length);
+ }
+
+ public int characteristics() {
+ return Spliterator.NONNULL
+ | Spliterator.ORDERED
+ | Spliterator.SIZED
+ | Spliterator.SUBSIZED;
+ }
+ }
+
+ public void forEach(Consumer super E> action) {
+ Objects.requireNonNull(action);
+ final Object[] es = elements;
+ for (int i = head, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ action.accept(elementAt(es, i));
+ if (to == end) {
+ if (end != tail) throw new ConcurrentModificationException();
+ break;
+ }
+ }
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public boolean removeIf(Predicate super E> filter) {
+ Objects.requireNonNull(filter);
+ return bulkRemove(filter);
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public boolean removeAll(Collection> c) {
+ Objects.requireNonNull(c);
+ return bulkRemove(e -> c.contains(e));
+ }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public boolean retainAll(Collection> c) {
+ Objects.requireNonNull(c);
+ return bulkRemove(e -> !c.contains(e));
+ }
+
+ /** Implementation of bulk remove methods. */
+ private boolean bulkRemove(Predicate super E> filter) {
+ final Object[] es = elements;
+ // Optimize for initial run of survivors
+ for (int i = head, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ if (filter.test(elementAt(es, i)))
+ return bulkRemoveModified(filter, i);
+ if (to == end) {
+ if (end != tail) throw new ConcurrentModificationException();
+ break;
+ }
+ }
+ return false;
+ }
+
+ // A tiny bit set implementation
+
+ private static long[] nBits(int n) {
+ return new long[((n - 1) >> 6) + 1];
+ }
+ private static void setBit(long[] bits, int i) {
+ bits[i >> 6] |= 1L << i;
+ }
+ private static boolean isClear(long[] bits, int i) {
+ return (bits[i >> 6] & (1L << i)) == 0;
+ }
+
+ /**
+ * Helper for bulkRemove, in case of at least one deletion.
+ * Tolerate predicates that reentrantly access the collection for
+ * read (but writers still get CME), so traverse once to find
+ * elements to delete, a second pass to physically expunge.
+ *
+ * @param beg valid index of first element to be deleted
+ */
+ private boolean bulkRemoveModified(
+ Predicate super E> filter, final int beg) {
+ final Object[] es = elements;
+ final int capacity = es.length;
+ final int end = tail;
+ final long[] deathRow = nBits(sub(end, beg, capacity));
+ deathRow[0] = 1L; // set bit 0
+ for (int i = beg + 1, to = (i <= end) ? end : es.length, k = beg;
+ ; i = 0, to = end, k -= capacity) {
+ for (; i < to; i++)
+ if (filter.test(elementAt(es, i)))
+ setBit(deathRow, i - k);
+ if (to == end) break;
+ }
+ // a two-finger traversal, with hare i reading, tortoise w writing
+ int w = beg;
+ for (int i = beg + 1, to = (i <= end) ? end : es.length, k = beg;
+ ; w = 0) { // w rejoins i on second leg
+ // In this loop, i and w are on the same leg, with i > w
+ for (; i < to; i++)
+ if (isClear(deathRow, i - k))
+ es[w++] = es[i];
+ if (to == end) break;
+ // In this loop, w is on the first leg, i on the second
+ for (i = 0, to = end, k -= capacity; i < to && w < capacity; i++)
+ if (isClear(deathRow, i - k))
+ es[w++] = es[i];
+ if (i >= to) {
+ if (w == capacity) w = 0; // "corner" case
+ break;
+ }
+ }
+ if (end != tail) throw new ConcurrentModificationException();
+ circularClear(es, tail = w, end);
+ return true;
}
/**
@@ -700,11 +995,13 @@ public class ArrayDeque extends AbstractCollection
*/
public boolean contains(Object o) {
if (o != null) {
- int mask = elements.length - 1;
- int i = head;
- for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) {
- if (o.equals(x))
- return true;
+ final Object[] es = elements;
+ for (int i = head, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ if (o.equals(es[i]))
+ return true;
+ if (to == end) break;
}
}
return false;
@@ -732,16 +1029,18 @@ public class ArrayDeque extends AbstractCollection
* The deque will be empty after this call returns.
*/
public void clear() {
- int h = head;
- int t = tail;
- if (h != t) { // clear all cells
- head = tail = 0;
- int i = h;
- int mask = elements.length - 1;
- do {
- elements[i] = null;
- i = (i + 1) & mask;
- } while (i != t);
+ circularClear(elements, head, tail);
+ head = tail = 0;
+ }
+
+ /**
+ * Nulls out slots starting at array index i, upto index end.
+ */
+ private static void circularClear(Object[] es, int i, int end) {
+ for (int to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ Arrays.fill(es, i, to, null);
+ if (to == end) break;
}
}
@@ -759,13 +1058,23 @@ public class ArrayDeque extends AbstractCollection
* @return an array containing all of the elements in this deque
*/
public Object[] toArray() {
- final int head = this.head;
- final int tail = this.tail;
- boolean wrap = (tail < head);
- int end = wrap ? tail + elements.length : tail;
- Object[] a = Arrays.copyOfRange(elements, head, end);
- if (wrap)
- System.arraycopy(elements, 0, a, elements.length - head, tail);
+ return toArray(Object[].class);
+ }
+
+ private T[] toArray(Class klazz) {
+ final Object[] es = elements;
+ final T[] a;
+ final int head = this.head, tail = this.tail, end;
+ if ((end = tail + ((head <= tail) ? 0 : es.length)) >= 0) {
+ // Uses null extension feature of copyOfRange
+ a = Arrays.copyOfRange(es, head, end, klazz);
+ } else {
+ // integer overflow!
+ a = Arrays.copyOfRange(es, 0, end - head, klazz);
+ System.arraycopy(es, head, a, 0, es.length - head);
+ }
+ if (end != tail)
+ System.arraycopy(es, 0, a, es.length - head, tail);
return a;
}
@@ -807,22 +1116,17 @@ public class ArrayDeque extends AbstractCollection
*/
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
- final int head = this.head;
- final int tail = this.tail;
- boolean wrap = (tail < head);
- int size = (tail - head) + (wrap ? elements.length : 0);
- int firstLeg = size - (wrap ? tail : 0);
- int len = a.length;
- if (size > len) {
- a = (T[]) Arrays.copyOfRange(elements, head, head + size,
- a.getClass());
- } else {
- System.arraycopy(elements, head, a, 0, firstLeg);
- if (size < len)
- a[size] = null;
+ final int size;
+ if ((size = size()) > a.length)
+ return toArray((Class) a.getClass());
+ final Object[] es = elements;
+ for (int i = head, j = 0, len = Math.min(size, es.length - i);
+ ; i = 0, len = tail) {
+ System.arraycopy(es, i, a, j, len);
+ if ((j += len) == size) break;
}
- if (wrap)
- System.arraycopy(elements, 0, a, firstLeg, tail);
+ if (size < a.length)
+ a[size] = null;
return a;
}
@@ -863,9 +1167,13 @@ public class ArrayDeque extends AbstractCollection
s.writeInt(size());
// Write out elements in order.
- int mask = elements.length - 1;
- for (int i = head; i != tail; i = (i + 1) & mask)
- s.writeObject(elements[i]);
+ final Object[] es = elements;
+ for (int i = head, end = tail, to = (i <= end) ? end : es.length;
+ ; i = 0, to = end) {
+ for (; i < to; i++)
+ s.writeObject(es[i]);
+ if (to == end) break;
+ }
}
/**
@@ -881,106 +1189,33 @@ public class ArrayDeque extends AbstractCollection
// Read in size and allocate array
int size = s.readInt();
- allocateElements(size);
- head = 0;
- tail = size;
+ elements = new Object[size + 1];
+ this.tail = size;
// Read in all elements in the proper order.
for (int i = 0; i < size; i++)
elements[i] = s.readObject();
}
- /**
- * Creates a late-binding
- * and fail-fast {@link Spliterator} over the elements in this
- * deque.
- *
- * The {@code Spliterator} reports {@link Spliterator#SIZED},
- * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
- * {@link Spliterator#NONNULL}. Overriding implementations should document
- * the reporting of additional characteristic values.
- *
- * @return a {@code Spliterator} over the elements in this deque
- * @since 1.8
- */
- public Spliterator spliterator() {
- return new DeqSpliterator<>(this, -1, -1);
- }
-
- static final class DeqSpliterator implements Spliterator {
- private final ArrayDeque deq;
- private int fence; // -1 until first use
- private int index; // current index, modified on traverse/split
-
- /** Creates new spliterator covering the given array and range. */
- DeqSpliterator(ArrayDeque deq, int origin, int fence) {
- this.deq = deq;
- this.index = origin;
- this.fence = fence;
- }
-
- private int getFence() { // force initialization
- int t;
- if ((t = fence) < 0) {
- t = fence = deq.tail;
- index = deq.head;
- }
- return t;
- }
-
- public DeqSpliterator trySplit() {
- int t = getFence(), h = index, n = deq.elements.length;
- if (h != t && ((h + 1) & (n - 1)) != t) {
- if (h > t)
- t += n;
- int m = ((h + t) >>> 1) & (n - 1);
- return new DeqSpliterator(deq, h, index = m);
- }
- return null;
- }
-
- public void forEachRemaining(Consumer super E> consumer) {
- if (consumer == null)
- throw new NullPointerException();
- Object[] a = deq.elements;
- int m = a.length - 1, f = getFence(), i = index;
- index = f;
- while (i != f) {
- @SuppressWarnings("unchecked") E e = (E)a[i];
- i = (i + 1) & m;
- if (e == null)
- throw new ConcurrentModificationException();
- consumer.accept(e);
- }
- }
-
- public boolean tryAdvance(Consumer super E> consumer) {
- if (consumer == null)
- throw new NullPointerException();
- Object[] a = deq.elements;
- int m = a.length - 1, f = getFence(), i = index;
- if (i != f) {
- @SuppressWarnings("unchecked") E e = (E)a[i];
- index = (i + 1) & m;
- if (e == null)
- throw new ConcurrentModificationException();
- consumer.accept(e);
- return true;
- }
- return false;
- }
-
- public long estimateSize() {
- int n = getFence() - index;
- if (n < 0)
- n += deq.elements.length;
- return (long) n;
- }
-
- @Override
- public int characteristics() {
- return Spliterator.ORDERED | Spliterator.SIZED |
- Spliterator.NONNULL | Spliterator.SUBSIZED;
+ /** debugging */
+ void checkInvariants() {
+ // Use head and tail fields with empty slot at tail strategy.
+ // head == tail disambiguates to "empty".
+ try {
+ int capacity = elements.length;
+ // assert 0 <= head && head < capacity;
+ // assert 0 <= tail && tail < capacity;
+ // assert capacity > 0;
+ // assert size() < capacity;
+ // assert head == tail || elements[head] != null;
+ // assert elements[tail] == null;
+ // assert head == tail || elements[dec(tail, capacity)] != null;
+ } catch (Throwable t) {
+ System.err.printf("head=%d tail=%d capacity=%d%n",
+ head, tail, elements.length);
+ System.err.printf("elements=%s%n",
+ Arrays.toString(elements));
+ throw t;
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/ArrayList.java b/jdk/src/java.base/share/classes/java/util/ArrayList.java
index d58047c8c93..732b9641a68 100644
--- a/jdk/src/java.base/share/classes/java/util/ArrayList.java
+++ b/jdk/src/java.base/share/classes/java/util/ArrayList.java
@@ -104,7 +104,6 @@ import java.util.function.UnaryOperator;
* @see Vector
* @since 1.2
*/
-
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
@@ -424,6 +423,11 @@ public class ArrayList extends AbstractList
return (E) elementData[index];
}
+ @SuppressWarnings("unchecked")
+ static E elementAt(Object[] es, int index) {
+ return (E) es[index];
+ }
+
/**
* Returns the element at the specified position in this list.
*
@@ -553,7 +557,7 @@ public class ArrayList extends AbstractList
return false;
}
- /*
+ /**
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
@@ -572,11 +576,7 @@ public class ArrayList extends AbstractList
*/
public void clear() {
modCount++;
-
- // clear to let GC do its work
- for (int i = 0; i < size; i++)
- elementData[i] = null;
-
+ Arrays.fill(elementData, 0, size, null);
size = 0;
}
@@ -665,16 +665,10 @@ public class ArrayList extends AbstractList
outOfBoundsMsg(fromIndex, toIndex));
}
modCount++;
- int numMoved = size - toIndex;
- System.arraycopy(elementData, toIndex, elementData, fromIndex,
- numMoved);
-
- // clear to let GC do its work
- int newSize = size - (toIndex-fromIndex);
- for (int i = newSize; i < size; i++) {
- elementData[i] = null;
- }
- size = newSize;
+ final Object[] es = elementData;
+ final int oldSize = size;
+ System.arraycopy(es, toIndex, es, fromIndex, oldSize - toIndex);
+ Arrays.fill(es, size -= (toIndex - fromIndex), oldSize, null);
}
/**
@@ -717,8 +711,7 @@ public class ArrayList extends AbstractList
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection> c) {
- Objects.requireNonNull(c);
- return batchRemove(c, false);
+ return batchRemove(c, false, 0, size);
}
/**
@@ -738,34 +731,35 @@ public class ArrayList extends AbstractList
* @see Collection#contains(Object)
*/
public boolean retainAll(Collection> c) {
- Objects.requireNonNull(c);
- return batchRemove(c, true);
+ return batchRemove(c, true, 0, size);
}
- private boolean batchRemove(Collection> c, boolean complement) {
- final Object[] elementData = this.elementData;
- int r = 0, w = 0;
- boolean modified = false;
- try {
- for (; r < size; r++)
- if (c.contains(elementData[r]) == complement)
- elementData[w++] = elementData[r];
- } finally {
- // Preserve behavioral compatibility with AbstractCollection,
- // even if c.contains() throws.
- if (r != size) {
- System.arraycopy(elementData, r,
- elementData, w,
- size - r);
- w += size - r;
- }
- if (w != size) {
- // clear to let GC do its work
- for (int i = w; i < size; i++)
- elementData[i] = null;
- modCount += size - w;
- size = w;
- modified = true;
+ boolean batchRemove(Collection> c, boolean complement,
+ final int from, final int end) {
+ Objects.requireNonNull(c);
+ final Object[] es = elementData;
+ final boolean modified;
+ int r;
+ // Optimize for initial run of survivors
+ for (r = from; r < end && c.contains(es[r]) == complement; r++)
+ ;
+ if (modified = (r < end)) {
+ int w = r++;
+ try {
+ for (Object e; r < end; r++)
+ if (c.contains(e = es[r]) == complement)
+ es[w++] = e;
+ } catch (Throwable ex) {
+ // Preserve behavioral compatibility with AbstractCollection,
+ // even if c.contains() throws.
+ System.arraycopy(es, r, es, w, end - r);
+ w += end - r;
+ throw ex;
+ } finally {
+ final int oldSize = size, deleted = end - w;
+ modCount += deleted;
+ System.arraycopy(es, end, es, w, oldSize - end);
+ Arrays.fill(es, size -= deleted, oldSize, null);
}
}
return modified;
@@ -912,25 +906,21 @@ public class ArrayList extends AbstractList
}
@Override
- @SuppressWarnings("unchecked")
- public void forEachRemaining(Consumer super E> consumer) {
- Objects.requireNonNull(consumer);
+ public void forEachRemaining(Consumer super E> action) {
+ Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
- if (i >= size) {
- return;
+ if (i < size) {
+ final Object[] es = elementData;
+ if (i >= es.length)
+ throw new ConcurrentModificationException();
+ for (; i < size && modCount == expectedModCount; i++)
+ action.accept(elementAt(es, i));
+ // update once at end to reduce heap write traffic
+ cursor = i;
+ lastRet = i - 1;
+ checkForComodification();
}
- final Object[] elementData = ArrayList.this.elementData;
- if (i >= elementData.length) {
- throw new ConcurrentModificationException();
- }
- while (i != size && modCount == expectedModCount) {
- consumer.accept((E) elementData[i++]);
- }
- // update once at end of iteration to reduce heap write traffic
- cursor = i;
- lastRet = i - 1;
- checkForComodification();
}
final void checkForComodification() {
@@ -1117,6 +1107,33 @@ public class ArrayList extends AbstractList
return true;
}
+ public boolean removeAll(Collection> c) {
+ return batchRemove(c, false);
+ }
+
+ public boolean retainAll(Collection> c) {
+ return batchRemove(c, true);
+ }
+
+ private boolean batchRemove(Collection> c, boolean complement) {
+ checkForComodification();
+ int oldSize = root.size;
+ boolean modified =
+ root.batchRemove(c, complement, offset, offset + size);
+ if (modified)
+ updateSizeAndModCount(root.size - oldSize);
+ return modified;
+ }
+
+ public boolean removeIf(Predicate super E> filter) {
+ checkForComodification();
+ int oldSize = root.size;
+ boolean modified = root.removeIf(filter, offset, offset + size);
+ if (modified)
+ updateSizeAndModCount(root.size - oldSize);
+ return modified;
+ }
+
public Iterator iterator() {
return listIterator();
}
@@ -1164,24 +1181,21 @@ public class ArrayList extends AbstractList
return (E) elementData[offset + (lastRet = i)];
}
- @SuppressWarnings("unchecked")
- public void forEachRemaining(Consumer super E> consumer) {
- Objects.requireNonNull(consumer);
+ public void forEachRemaining(Consumer super E> action) {
+ Objects.requireNonNull(action);
final int size = SubList.this.size;
int i = cursor;
- if (i >= size) {
- return;
+ if (i < size) {
+ final Object[] es = root.elementData;
+ if (offset + i >= es.length)
+ throw new ConcurrentModificationException();
+ for (; i < size && modCount == expectedModCount; i++)
+ action.accept(elementAt(es, offset + i));
+ // update once at end to reduce heap write traffic
+ cursor = i;
+ lastRet = i - 1;
+ checkForComodification();
}
- final Object[] elementData = root.elementData;
- if (offset + i >= elementData.length) {
- throw new ConcurrentModificationException();
- }
- while (i != size && modCount == expectedModCount) {
- consumer.accept((E) elementData[offset + (i++)]);
- }
- // update once at end of iteration to reduce heap write traffic
- lastRet = cursor = i;
- checkForComodification();
}
public int nextIndex() {
@@ -1348,15 +1362,12 @@ public class ArrayList extends AbstractList
public void forEach(Consumer super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
- @SuppressWarnings("unchecked")
- final E[] elementData = (E[]) this.elementData;
+ final Object[] es = elementData;
final int size = this.size;
- for (int i=0; modCount == expectedModCount && i < size; i++) {
- action.accept(elementData[i]);
- }
- if (modCount != expectedModCount) {
+ for (int i = 0; modCount == expectedModCount && i < size; i++)
+ action.accept(elementAt(es, i));
+ if (modCount != expectedModCount)
throw new ConcurrentModificationException();
- }
}
/**
@@ -1417,7 +1428,7 @@ public class ArrayList extends AbstractList
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
- /** Create new spliterator covering the given range */
+ /** Create new spliterator covering the given range */
ArrayListSpliterator(ArrayList list, int origin, int fence,
int expectedModCount) {
this.list = list; // OK if null unless traversed
@@ -1495,61 +1506,73 @@ public class ArrayList extends AbstractList
}
}
- @Override
- public boolean removeIf(Predicate super E> filter) {
- Objects.requireNonNull(filter);
- // figure out which elements are to be removed
- // any exception thrown from the filter predicate at this stage
- // will leave the collection unmodified
- int removeCount = 0;
- final BitSet removeSet = new BitSet(size);
- final int expectedModCount = modCount;
- final int size = this.size;
- for (int i=0; modCount == expectedModCount && i < size; i++) {
- @SuppressWarnings("unchecked")
- final E element = (E) elementData[i];
- if (filter.test(element)) {
- removeSet.set(i);
- removeCount++;
- }
- }
- if (modCount != expectedModCount) {
- throw new ConcurrentModificationException();
- }
+ // A tiny bit set implementation
- // shift surviving elements left over the spaces left by removed elements
- final boolean anyToRemove = removeCount > 0;
- if (anyToRemove) {
- final int newSize = size - removeCount;
- for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
- i = removeSet.nextClearBit(i);
- elementData[j] = elementData[i];
- }
- for (int k=newSize; k < size; k++) {
- elementData[k] = null; // Let gc do its work
- }
- this.size = newSize;
- if (modCount != expectedModCount) {
- throw new ConcurrentModificationException();
- }
- modCount++;
- }
-
- return anyToRemove;
+ private static long[] nBits(int n) {
+ return new long[((n - 1) >> 6) + 1];
+ }
+ private static void setBit(long[] bits, int i) {
+ bits[i >> 6] |= 1L << i;
+ }
+ private static boolean isClear(long[] bits, int i) {
+ return (bits[i >> 6] & (1L << i)) == 0;
+ }
+
+ @Override
+ public boolean removeIf(Predicate super E> filter) {
+ return removeIf(filter, 0, size);
+ }
+
+ /**
+ * Removes all elements satisfying the given predicate, from index
+ * i (inclusive) to index end (exclusive).
+ */
+ boolean removeIf(Predicate super E> filter, int i, final int end) {
+ Objects.requireNonNull(filter);
+ int expectedModCount = modCount;
+ final Object[] es = elementData;
+ // Optimize for initial run of survivors
+ for (; i < end && !filter.test(elementAt(es, i)); i++)
+ ;
+ // Tolerate predicates that reentrantly access the collection for
+ // read (but writers still get CME), so traverse once to find
+ // elements to delete, a second pass to physically expunge.
+ if (i < end) {
+ final int beg = i;
+ final long[] deathRow = nBits(end - beg);
+ deathRow[0] = 1L; // set bit 0
+ for (i = beg + 1; i < end; i++)
+ if (filter.test(elementAt(es, i)))
+ setBit(deathRow, i - beg);
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ expectedModCount++;
+ modCount++;
+ int w = beg;
+ for (i = beg; i < end; i++)
+ if (isClear(deathRow, i - beg))
+ es[w++] = es[i];
+ final int oldSize = size;
+ System.arraycopy(es, end, es, w, oldSize - end);
+ Arrays.fill(es, size -= (end - w), oldSize, null);
+ return true;
+ } else {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return false;
+ }
}
@Override
- @SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
+ final Object[] es = elementData;
final int size = this.size;
- for (int i=0; modCount == expectedModCount && i < size; i++) {
- elementData[i] = operator.apply((E) elementData[i]);
- }
- if (modCount != expectedModCount) {
+ for (int i = 0; modCount == expectedModCount && i < size; i++)
+ es[i] = operator.apply(elementAt(es, i));
+ if (modCount != expectedModCount)
throw new ConcurrentModificationException();
- }
modCount++;
}
@@ -1558,9 +1581,13 @@ public class ArrayList extends AbstractList
public void sort(Comparator super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
- if (modCount != expectedModCount) {
+ if (modCount != expectedModCount)
throw new ConcurrentModificationException();
- }
modCount++;
}
+
+ void checkInvariants() {
+ // assert size >= 0;
+ // assert size == elementData.length || elementData[size] == null;
+ }
}
diff --git a/jdk/src/java.base/share/classes/java/util/HashMap.java b/jdk/src/java.base/share/classes/java/util/HashMap.java
index fd4d9b11c2d..5cc36296aa8 100644
--- a/jdk/src/java.base/share/classes/java/util/HashMap.java
+++ b/jdk/src/java.base/share/classes/java/util/HashMap.java
@@ -1502,8 +1502,7 @@ public class HashMap extends AbstractMap
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
- K key = p.key;
- removeNode(hash(key), key, null, false, false);
+ removeNode(p.hash, p.key, null, false, false);
expectedModCount = modCount;
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/Iterator.java b/jdk/src/java.base/share/classes/java/util/Iterator.java
index ca05cbb8762..7dcb155c63d 100644
--- a/jdk/src/java.base/share/classes/java/util/Iterator.java
+++ b/jdk/src/java.base/share/classes/java/util/Iterator.java
@@ -76,10 +76,15 @@ public interface Iterator {
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
- * only once per call to {@link #next}. The behavior of an iterator
- * is unspecified if the underlying collection is modified while the
- * iteration is in progress in any way other than by calling this
- * method.
+ * only once per call to {@link #next}.
+ *
+ * The behavior of an iterator is unspecified if the underlying collection
+ * is modified while the iteration is in progress in any way other than by
+ * calling this method, unless an overriding class has specified a
+ * concurrent modification policy.
+ *
+ * The behavior of an iterator is unspecified if this method is called
+ * after a call to the {@link #forEachRemaining forEachRemaining} method.
*
* @implSpec
* The default implementation throws an instance of
@@ -102,6 +107,13 @@ public interface Iterator {
* have been processed or the action throws an exception. Actions are
* performed in the order of iteration, if that order is specified.
* Exceptions thrown by the action are relayed to the caller.
+ *
+ * The behavior of an iterator is unspecified if the action modifies the
+ * collection in any way (even by calling the {@link #remove remove} method),
+ * unless an overriding class has specified a concurrent modification policy.
+ *
+ * Subsequent behavior of an iterator is unspecified if the action throws an
+ * exception.
*
* @implSpec
*
The default implementation behaves as if:
diff --git a/jdk/src/java.base/share/classes/java/util/LinkedHashMap.java b/jdk/src/java.base/share/classes/java/util/LinkedHashMap.java
index 0253cd51bfd..bd21f5bf6d4 100644
--- a/jdk/src/java.base/share/classes/java/util/LinkedHashMap.java
+++ b/jdk/src/java.base/share/classes/java/util/LinkedHashMap.java
@@ -731,8 +731,7 @@ public class LinkedHashMap