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 Maprequires = 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, + Functionclf) { + 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 { *
* *
- + * configuration have the same package.
Overlapping packages: Two or more modules in the - * configuration have the same package (exported or concealed).
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.
- @@ -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
A module is mapped to a class loader that already has a * module of the same name defined to it.
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 + Mapmap = 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