OpenJDK / amber / amber
changeset 41269:5a102ee21b38
Merge
author | amurillo |
---|---|
date | Wed, 05 Oct 2016 06:28:22 -0700 |
parents | 256be8814844 4f71f07b30d1 |
children | 3995c29a8d9f |
files | jdk/src/java.base/aix/native/libnio/ch/AixNativeThread.c jdk/test/java/beans/XMLEncoder/EnumPrivate.java jdk/test/java/beans/XMLEncoder/EnumPublic.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedCollection.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedList.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedMap.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedRandomAccessList.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSet.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSortedMap.java jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSortedSet.java jdk/test/java/beans/XMLEncoder/java_util_EnumMap.java jdk/test/java/beans/XMLEncoder/java_util_JumboEnumSet.java jdk/test/java/beans/XMLEncoder/java_util_RegularEnumSet.java langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/ModuleNameReader.java make/StripBinaries.gmk |
diffstat | 245 files changed, 11245 insertions(+), 3877 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -380,3 +380,4 @@ e384420383a5b79fa0012ebcb25d8f83cff7f777 jdk-9+135 1b4b5d01aa11edf24b6fadbe3d2f3e411e3b02cd jdk-9+136 9cb87c88ed851c0575b8ead753ea238ed5b544e9 jdk-9+137 +d273dfe9a126d3bffe92072547fef2cd1361b0eb jdk-9+138
--- a/common/autoconf/buildjdk-spec.gmk.in Fri Sep 30 02:52:36 2016 -0700 +++ b/common/autoconf/buildjdk-spec.gmk.in Wed Oct 05 06:28:22 2016 -0700 @@ -33,6 +33,7 @@ CC := @BUILD_CC@ CXX := @BUILD_CXX@ LD := @BUILD_LD@ +LDCXX := @BUILD_LDCXX@ AS := @BUILD_AS@ NM := @BUILD_NM@ AR := @BUILD_AR@
--- a/common/autoconf/generated-configure.sh Fri Sep 30 02:52:36 2016 -0700 +++ b/common/autoconf/generated-configure.sh Wed Oct 05 06:28:22 2016 -0700 @@ -687,7 +687,6 @@ MSVCP_DLL MSVCR_DLL LIBCXX -STATIC_CXX_SETTING FIXPATH_DETACH_FLAG FIXPATH BUILD_GTEST @@ -5092,7 +5091,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1474894604 +DATE_WHEN_GENERATED=1475218974 ############################################################################### # @@ -53087,49 +53086,10 @@ if test "x$OPENJDK_TARGET_OS" = xlinux; then - # Test if -lstdc++ works. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if dynamic link of stdc++ is possible" >&5 -$as_echo_n "checking if dynamic link of stdc++ is possible... " >&6; } - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - OLD_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -lstdc++" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - has_dynamic_libstdcxx=yes -else - has_dynamic_libstdcxx=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CXXFLAGS="$OLD_CXXFLAGS" - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $has_dynamic_libstdcxx" >&5 -$as_echo "$has_dynamic_libstdcxx" >&6; } - # Test if stdc++ can be linked statically. { $as_echo "$as_me:${as_lineno-$LINENO}: checking if static link of stdc++ is possible" >&5 $as_echo_n "checking if static link of stdc++ is possible... " >&6; } - STATIC_STDCXX_FLAGS="-Wl,-Bstatic -lstdc++ -lgcc -Wl,-Bdynamic" + STATIC_STDCXX_FLAGS="-static-libstdc++ -static-libgcc" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -53137,9 +53097,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu OLD_LIBS="$LIBS" - OLD_CXX="$CXX" LIBS="$STATIC_STDCXX_FLAGS" - CXX="$CC" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -53159,7 +53117,6 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$OLD_LIBS" - CXX="$OLD_CXX" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -53169,59 +53126,34 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $has_static_libstdcxx" >&5 $as_echo "$has_static_libstdcxx" >&6; } - if test "x$has_static_libstdcxx" = xno && test "x$has_dynamic_libstdcxx" = xno; then - as_fn_error $? "Cannot link to stdc++, neither dynamically nor statically!" "$LINENO" 5 - fi - if test "x$with_stdc__lib" = xstatic && test "x$has_static_libstdcxx" = xno; then as_fn_error $? "Static linking of libstdc++ was not possible!" "$LINENO" 5 fi - if test "x$with_stdc__lib" = xdynamic && test "x$has_dynamic_libstdcxx" = xno; then - as_fn_error $? "Dynamic linking of libstdc++ was not possible!" "$LINENO" 5 - fi - # If dynamic was requested, it's available since it would fail above otherwise. # If dynamic wasn't requested, go with static unless it isn't available. { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libstdc++" >&5 $as_echo_n "checking how to link with libstdc++... " >&6; } - if test "x$with_stdc__lib" = xdynamic || test "x$has_static_libstdcxx" = xno || [[ " $JVM_VARIANTS " =~ " zeroshark " ]] ; then - LIBCXX="$LIBCXX -lstdc++" - # To help comparisons with old build, put stdc++ first in JVM_LIBS - JVM_LIBS="-lstdc++ $JVM_LIBS" - # Ideally, we should test stdc++ for the BUILD toolchain separately. For now - # just use the same setting as for the TARGET toolchain. - OPENJDK_BUILD_JVM_LIBS="-lstdc++ $OPENJDK_BUILD_JVM_LIBS" - LDCXX="$CXX" - STATIC_CXX_SETTING="STATIC_CXX=false" + if test "x$with_stdc__lib" = xdynamic || test "x$has_static_libstdcxx" = xno \ + || [[ " $JVM_VARIANTS " =~ " zeroshark " ]] ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: dynamic" >&5 $as_echo "dynamic" >&6; } else LIBCXX="$LIBCXX $STATIC_STDCXX_FLAGS" - JVM_LDFLAGS="$JVM_LDFLAGS -static-libgcc" - # To help comparisons with old build, put stdc++ first in JVM_LIBS - JVM_LIBS="-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic $JVM_LIBS" + JVM_LDFLAGS="$JVM_LDFLAGS $STATIC_STDCXX_FLAGS" # Ideally, we should test stdc++ for the BUILD toolchain separately. For now # just use the same setting as for the TARGET toolchain. - OPENJDK_BUILD_JVM_LDFLAGS="$OPENJDK_BUILD_JVM_LDFLAGS -static-libgcc" - OPENJDK_BUILD_JVM_LIBS="-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic $OPENJDK_BUILD_JVM_LIBS" - LDCXX="$CC" - STATIC_CXX_SETTING="STATIC_CXX=true" + OPENJDK_BUILD_JVM_LDFLAGS="$OPENJDK_BUILD_JVM_LDFLAGS $STATIC_STDCXX_FLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: static" >&5 $as_echo "static" >&6; } fi fi - # libCrun is the c++ runtime-library with SunStudio (roughly the equivalent of gcc's libstdc++.so) if test "x$TOOLCHAIN_TYPE" = xsolstudio && test "x$LIBCXX" = x; then LIBCXX="${SYSROOT}/usr/lib${OPENJDK_TARGET_CPU_ISADIR}/libCrun.so.1" fi - # TODO better (platform agnostic) test - if test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$LIBCXX" = x && test "x$TOOLCHAIN_TYPE" = xgcc; then - LIBCXX="-lstdc++" - fi # Setup Windows runtime dlls
--- a/common/autoconf/lib-std.m4 Fri Sep 30 02:52:36 2016 -0700 +++ b/common/autoconf/lib-std.m4 Wed Oct 05 06:28:22 2016 -0700 @@ -45,84 +45,44 @@ ) if test "x$OPENJDK_TARGET_OS" = xlinux; then - # Test if -lstdc++ works. - AC_MSG_CHECKING([if dynamic link of stdc++ is possible]) - AC_LANG_PUSH(C++) - OLD_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -lstdc++" - AC_LINK_IFELSE([AC_LANG_PROGRAM([], [return 0;])], - [has_dynamic_libstdcxx=yes], - [has_dynamic_libstdcxx=no]) - CXXFLAGS="$OLD_CXXFLAGS" - AC_LANG_POP(C++) - AC_MSG_RESULT([$has_dynamic_libstdcxx]) - # Test if stdc++ can be linked statically. AC_MSG_CHECKING([if static link of stdc++ is possible]) - STATIC_STDCXX_FLAGS="-Wl,-Bstatic -lstdc++ -lgcc -Wl,-Bdynamic" + STATIC_STDCXX_FLAGS="-static-libstdc++ -static-libgcc" AC_LANG_PUSH(C++) OLD_LIBS="$LIBS" - OLD_CXX="$CXX" LIBS="$STATIC_STDCXX_FLAGS" - CXX="$CC" AC_LINK_IFELSE([AC_LANG_PROGRAM([], [return 0;])], [has_static_libstdcxx=yes], [has_static_libstdcxx=no]) LIBS="$OLD_LIBS" - CXX="$OLD_CXX" AC_LANG_POP(C++) AC_MSG_RESULT([$has_static_libstdcxx]) - if test "x$has_static_libstdcxx" = xno && test "x$has_dynamic_libstdcxx" = xno; then - AC_MSG_ERROR([Cannot link to stdc++, neither dynamically nor statically!]) - fi - if test "x$with_stdc__lib" = xstatic && test "x$has_static_libstdcxx" = xno; then AC_MSG_ERROR([Static linking of libstdc++ was not possible!]) fi - if test "x$with_stdc__lib" = xdynamic && test "x$has_dynamic_libstdcxx" = xno; then - AC_MSG_ERROR([Dynamic linking of libstdc++ was not possible!]) - fi - # If dynamic was requested, it's available since it would fail above otherwise. # If dynamic wasn't requested, go with static unless it isn't available. AC_MSG_CHECKING([how to link with libstdc++]) - if test "x$with_stdc__lib" = xdynamic || test "x$has_static_libstdcxx" = xno || HOTSPOT_CHECK_JVM_VARIANT(zeroshark); then - LIBCXX="$LIBCXX -lstdc++" - # To help comparisons with old build, put stdc++ first in JVM_LIBS - JVM_LIBS="-lstdc++ $JVM_LIBS" - # Ideally, we should test stdc++ for the BUILD toolchain separately. For now - # just use the same setting as for the TARGET toolchain. - OPENJDK_BUILD_JVM_LIBS="-lstdc++ $OPENJDK_BUILD_JVM_LIBS" - LDCXX="$CXX" - STATIC_CXX_SETTING="STATIC_CXX=false" + if test "x$with_stdc__lib" = xdynamic || test "x$has_static_libstdcxx" = xno \ + || HOTSPOT_CHECK_JVM_VARIANT(zeroshark); then AC_MSG_RESULT([dynamic]) else LIBCXX="$LIBCXX $STATIC_STDCXX_FLAGS" - JVM_LDFLAGS="$JVM_LDFLAGS -static-libgcc" - # To help comparisons with old build, put stdc++ first in JVM_LIBS - JVM_LIBS="-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic $JVM_LIBS" + JVM_LDFLAGS="$JVM_LDFLAGS $STATIC_STDCXX_FLAGS" # Ideally, we should test stdc++ for the BUILD toolchain separately. For now # just use the same setting as for the TARGET toolchain. - OPENJDK_BUILD_JVM_LDFLAGS="$OPENJDK_BUILD_JVM_LDFLAGS -static-libgcc" - OPENJDK_BUILD_JVM_LIBS="-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic $OPENJDK_BUILD_JVM_LIBS" - LDCXX="$CC" - STATIC_CXX_SETTING="STATIC_CXX=true" + OPENJDK_BUILD_JVM_LDFLAGS="$OPENJDK_BUILD_JVM_LDFLAGS $STATIC_STDCXX_FLAGS" AC_MSG_RESULT([static]) fi fi - AC_SUBST(STATIC_CXX_SETTING) # libCrun is the c++ runtime-library with SunStudio (roughly the equivalent of gcc's libstdc++.so) if test "x$TOOLCHAIN_TYPE" = xsolstudio && test "x$LIBCXX" = x; then LIBCXX="${SYSROOT}/usr/lib${OPENJDK_TARGET_CPU_ISADIR}/libCrun.so.1" fi - # TODO better (platform agnostic) test - if test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$LIBCXX" = x && test "x$TOOLCHAIN_TYPE" = xgcc; then - LIBCXX="-lstdc++" - fi AC_SUBST(LIBCXX) # Setup Windows runtime dlls
--- a/common/bin/compare.sh Fri Sep 30 02:52:36 2016 -0700 +++ b/common/bin/compare.sh Wed Oct 05 06:28:22 2016 -0700 @@ -654,10 +654,10 @@ OTHER_DIZ_FILE="$OTHER/support/native/java.base/java_objs/java.diz" elif [ "$NAME" = "jimage.exe" ] \ && [ -f "$OTHER/support/native/jdk.jlink/jimage_objs/jimage.diz" ]; then - OTHER_DIZ_FILE="$OTHER/support/native/jdk.jlink/jimage_objs/jimage.diz" + OTHER_DIZ_FILE="$OTHER/support/modules_cmds/jdk.jlink/jimage.diz" elif [ "$NAME" = "javacpl.exe" ] \ && [ -f "$OTHER/support/native/jdk.plugin/javacpl/javacpl.diz" ]; then - OTHER_DIZ_FILE="$OTHER/support/native/jdk.plugin/javacpl/javacpl.diz" + OTHER_DIZ_FILE="$OTHER/support/modules_cmds/jdk.deploy.controlpanel/javacpl.diz" elif [ -f "${OTHER_FILE_BASE}.diz" ]; then OTHER_DIZ_FILE=${OTHER_FILE_BASE}.diz else @@ -686,10 +686,10 @@ THIS_DIZ_FILE="$THIS/support/native/java.base/java_objs/java.diz" elif [ "$NAME" = "jimage.exe" ] \ && [ -f "$THIS/support/native/jdk.jlink/jimage_objs/jimage.diz" ]; then - THIS_DIZ_FILE="$THIS/support/native/jdk.jlink/jimage_objs/jimage.diz" + THIS_DIZ_FILE="$THIS/support/modules_cmds/jdk.jlink/jimage.diz" elif [ "$NAME" = "javacpl.exe" ] \ && [ -f "$THIS/support/native/jdk.plugin/javacpl/javacpl.diz" ]; then - THIS_DIZ_FILE="$THIS/support/native/jdk.plugin/javacpl/javacpl.diz" + THIS_DIZ_FILE="$THIS/support/modules_cmds/jdk.deploy.controlpanel/javacpl.diz" elif [ -f "${THIS_FILE_BASE}.diz" ]; then THIS_DIZ_FILE=${THIS_FILE/.dll/}.diz else
--- a/corba/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/corba/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -380,3 +380,4 @@ 094d0db606db976045f594dba47d4593b715cc81 jdk-9+135 aa053a3faf266c12b4fd5272da431a3e08e4a3e3 jdk-9+136 258cf18fa7fc59359b874f8743b7168dc48baf73 jdk-9+137 +27bb44be32076861a0951bcefb07a1d92509a4b6 jdk-9+138
--- a/corba/make/gensrc/Gensrc-java.corba.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/corba/make/gensrc/Gensrc-java.corba.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -38,7 +38,7 @@ SRC := $(CORBA_TOPDIR)/make/src/classes, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/corba_tools_classes)) -TOOL_LOGUTIL_CMD := $(JAVA) -cp $(BUILDTOOLS_OUTPUTDIR)/corba_tools_classes \ +TOOL_LOGUTIL_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/corba_tools_classes \ build.tools.logutil.MC $(eval $(call SetupJavaCompilation,BUILD_IDLJ, \ @@ -50,7 +50,7 @@ EXCLUDE_FILES := ResourceBundleUtil.java module-info.java)) # Force the language to english for predictable source code generation. -TOOL_IDLJ_CMD := $(JAVA) -cp $(BUILDTOOLS_OUTPUTDIR)/idlj_classes \ +TOOL_IDLJ_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/idlj_classes \ -Duser.language=en com.sun.tools.corba.se.idl.toJavaPortable.Compile ################################################################################
--- a/hotspot/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -540,3 +540,4 @@ 3b1c4562953db47e36b237a500f368d5c9746d47 jdk-9+135 a20da289f646ee44440695b81abc0548330e4ca7 jdk-9+136 dfcbf839e299e7e2bba1da69bdb347617ea4c7e8 jdk-9+137 +fc0956308c7a586267c5dd35dff74f773aa9c3eb jdk-9+138
--- a/hotspot/make/lib/CompileGtest.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/make/lib/CompileGtest.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -55,7 +55,7 @@ # Disabling switch warning for clang because of test source. $(eval $(call SetupNativeCompilation, BUILD_GTEST_LIBJVM, \ - TOOLCHAIN := $(JVM_TOOLCHAIN), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ LIBRARY := jvm, \ OUTPUT_DIR := $(JVM_OUTPUTDIR)/gtest, \ OBJECT_DIR := $(JVM_OUTPUTDIR)/gtest/objs, \ @@ -95,7 +95,7 @@ ################################################################################ $(eval $(call SetupNativeCompilation, BUILD_GTEST_LAUNCHER, \ - TOOLCHAIN := $(JVM_TOOLCHAIN), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ PROGRAM := gtestLauncher, \ OUTPUT_DIR := $(JVM_OUTPUTDIR)/gtest, \ EXTRA_FILES := $(GTEST_LAUNCHER_SRC), \
--- a/hotspot/make/lib/CompileJvm.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/make/lib/CompileJvm.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -143,13 +143,6 @@ JVM_PRECOMPILED_HEADER := $(HOTSPOT_TOPDIR)/src/share/vm/precompiled/precompiled.hpp endif -ifneq ($(filter $(OPENJDK_TARGET_OS), macosx aix solaris), ) - # On macosx, aix and solaris we have to link with the C++ compiler - JVM_TOOLCHAIN := TOOLCHAIN_LINK_CXX -else - JVM_TOOLCHAIN := TOOLCHAIN_DEFAULT -endif - ifeq ($(OPENJDK_TARGET_CPU), x86) JVM_EXCLUDE_PATTERNS += x86_64 else ifeq ($(OPENJDK_TARGET_CPU), x86_64) @@ -194,7 +187,7 @@ # Now set up the actual compilation of the main hotspot native library $(eval $(call SetupNativeCompilation, BUILD_LIBJVM, \ - TOOLCHAIN := $(JVM_TOOLCHAIN), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ LIBRARY := jvm, \ OUTPUT_DIR := $(JVM_OUTPUTDIR), \ SRC := $(JVM_SRC_DIRS), \
--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java Wed Oct 05 06:28:22 2016 -0700 @@ -68,6 +68,7 @@ Type type = db.lookupType("InstanceKlass"); arrayKlasses = new MetadataField(type.getAddressField("_array_klasses"), 0); methods = type.getAddressField("_methods"); + defaultMethods = type.getAddressField("_default_methods"); methodOrdering = type.getAddressField("_method_ordering"); localInterfaces = type.getAddressField("_local_interfaces"); transitiveInterfaces = type.getAddressField("_transitive_interfaces"); @@ -128,6 +129,7 @@ private static MetadataField arrayKlasses; private static AddressField methods; + private static AddressField defaultMethods; private static AddressField methodOrdering; private static AddressField localInterfaces; private static AddressField transitiveInterfaces; @@ -335,6 +337,20 @@ // Accessors for declared fields public Klass getArrayKlasses() { return (Klass) arrayKlasses.getValue(this); } public MethodArray getMethods() { return new MethodArray(methods.getValue(getAddress())); } + + public MethodArray getDefaultMethods() { + if (defaultMethods != null) { + Address addr = defaultMethods.getValue(getAddress()); + if ((addr != null) && (addr.getAddressAt(0) != null)) { + return new MethodArray(addr); + } else { + return null; + } + } else { + return null; + } + } + public KlassArray getLocalInterfaces() { return new KlassArray(localInterfaces.getValue(getAddress())); } public KlassArray getTransitiveInterfaces() { return new KlassArray(transitiveInterfaces.getValue(getAddress())); } public int getJavaFieldsCount() { return (int) javaFieldsCount.getValue(this); }
--- a/hotspot/src/share/vm/classfile/classLoader.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/classLoader.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1358,7 +1358,7 @@ if (!Universe::is_module_initialized() && !ModuleEntryTable::javabase_defined() && mod_entry == NULL) { - mod_entry = ModuleEntryTable::javabase_module(); + mod_entry = ModuleEntryTable::javabase_moduleEntry(); } // The module must be a named module @@ -1708,7 +1708,7 @@ if (jb_module == NULL) { vm_exit_during_initialization("Unable to create ModuleEntry for java.base"); } - ModuleEntryTable::set_javabase_module(jb_module); + ModuleEntryTable::set_javabase_moduleEntry(jb_module); } }
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -773,6 +773,41 @@ InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, mirror, CHECK); } +// Set the java.lang.reflect.Module module field in the java_lang_Class mirror +void java_lang_Class::set_mirror_module_field(KlassHandle k, Handle mirror, Handle module, TRAPS) { + if (module.is_null()) { + // During startup, the module may be NULL only if java.base has not been defined yet. + // Put the class on the fixup_module_list to patch later when the java.lang.reflect.Module + // for java.base is known. + assert(!Universe::is_module_initialized(), "Incorrect java.lang.reflect.Module pre module system initialization"); + MutexLocker m1(Module_lock, THREAD); + // Keep list of classes needing java.base module fixup + if (!ModuleEntryTable::javabase_defined()) { + if (fixup_module_field_list() == NULL) { + GrowableArray<Klass*>* list = + new (ResourceObj::C_HEAP, mtModule) GrowableArray<Klass*>(500, true); + set_fixup_module_field_list(list); + } + k->class_loader_data()->inc_keep_alive(); + fixup_module_field_list()->push(k()); + } else { + // java.base was defined at some point between calling create_mirror() + // and obtaining the Module_lock, patch this particular class with java.base. + ModuleEntry *javabase_entry = ModuleEntryTable::javabase_moduleEntry(); + assert(javabase_entry != NULL && javabase_entry->module() != NULL, + "Setting class module field, java.base should be defined"); + Handle javabase_handle(THREAD, JNIHandles::resolve(javabase_entry->module())); + set_module(mirror(), javabase_handle()); + } + } else { + assert(Universe::is_module_initialized() || + (ModuleEntryTable::javabase_defined() && + (module() == JNIHandles::resolve(ModuleEntryTable::javabase_moduleEntry()->module()))), + "Incorrect java.lang.reflect.Module specification while creating mirror"); + set_module(mirror(), module()); + } +} + void java_lang_Class::create_mirror(KlassHandle k, Handle class_loader, Handle module, Handle protection_domain, TRAPS) { assert(k->java_mirror() == NULL, "should only assign mirror once"); @@ -835,25 +870,13 @@ set_class_loader(mirror(), class_loader()); // set the module field in the java_lang_Class instance - // This may be null during bootstrap but will get fixed up later on. - set_module(mirror(), module()); + set_mirror_module_field(k, mirror, module, THREAD); // Setup indirection from klass->mirror last // after any exceptions can happen during allocations. if (!k.is_null()) { k->set_java_mirror(mirror()); } - - // Keep list of classes needing java.base module fixup. - if (!ModuleEntryTable::javabase_defined()) { - if (fixup_module_field_list() == NULL) { - GrowableArray<Klass*>* list = - new (ResourceObj::C_HEAP, mtModule) GrowableArray<Klass*>(500, true); - set_fixup_module_field_list(list); - } - k->class_loader_data()->inc_keep_alive(); - fixup_module_field_list()->push(k()); - } } else { if (fixup_mirror_list() == NULL) { GrowableArray<Klass*>* list =
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -219,6 +219,7 @@ static void set_class_loader(oop java_class, oop class_loader); static void set_component_mirror(oop java_class, oop comp_mirror); static void initialize_mirror_fields(KlassHandle k, Handle mirror, Handle protection_domain, TRAPS); + static void set_mirror_module_field(KlassHandle K, Handle mirror, Handle module, TRAPS); public: static void compute_offsets();
--- a/hotspot/src/share/vm/classfile/klassFactory.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/klassFactory.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -25,12 +25,85 @@ #include "precompiled.hpp" #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" +#include "classfile/classLoader.hpp" #include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderData.inline.hpp" #include "classfile/klassFactory.hpp" +#include "classfile/sharedClassUtil.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "prims/jvmtiEnvBase.hpp" +#include "prims/jvmtiRedefineClasses.hpp" #include "trace/traceMacros.hpp" +// called during initial loading of a shared class +instanceKlassHandle KlassFactory::check_shared_class_file_load_hook( + instanceKlassHandle ik, + Symbol* class_name, + Handle class_loader, + Handle protection_domain, TRAPS) { +#if INCLUDE_CDS && INCLUDE_JVMTI + assert(ik.not_null(), "sanity"); + assert(ik()->is_shared(), "expecting a shared class"); + + if (JvmtiExport::should_post_class_file_load_hook()) { + assert(THREAD->is_Java_thread(), "must be JavaThread"); + + // Post the CFLH + JvmtiCachedClassFileData* cached_class_file = NULL; + JvmtiCachedClassFileData* archived_class_data = ik->get_archived_class_data(); + assert(archived_class_data != NULL, "shared class has no archived class data"); + unsigned char* ptr = + VM_RedefineClasses::get_cached_class_file_bytes(archived_class_data); + unsigned char* end_ptr = + ptr + VM_RedefineClasses::get_cached_class_file_len(archived_class_data); + unsigned char* old_ptr = ptr; + JvmtiExport::post_class_file_load_hook(class_name, + class_loader, + protection_domain, + &ptr, + &end_ptr, + &cached_class_file); + if (old_ptr != ptr) { + // JVMTI agent has modified class file data. + // Set new class file stream using JVMTI agent modified class file data. + ClassLoaderData* loader_data = + ClassLoaderData::class_loader_data(class_loader()); + int path_index = ik->shared_classpath_index(); + SharedClassPathEntry* ent = + (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index); + ClassFileStream* stream = new ClassFileStream(ptr, + end_ptr - ptr, + ent->_name, + ClassFileStream::verify); + ClassFileParser parser(stream, + class_name, + loader_data, + protection_domain, + NULL, + NULL, + ClassFileParser::BROADCAST, // publicity level + CHECK_NULL); + instanceKlassHandle new_ik = parser.create_instance_klass(true /* changed_by_loadhook */, + CHECK_NULL); + if (cached_class_file != NULL) { + new_ik->set_cached_class_file(cached_class_file); + } + + if (class_loader.is_null()) { + ResourceMark rm; + ClassLoader::add_package(class_name->as_C_string(), path_index, THREAD); + } + + return new_ik; + } + } +#endif + + return NULL; +} + + static ClassFileStream* check_class_file_load_hook(ClassFileStream* stream, Symbol* name, ClassLoaderData* loader_data, @@ -97,7 +170,6 @@ const InstanceKlass* host_klass, GrowableArray<Handle>* cp_patches, TRAPS) { - assert(stream != NULL, "invariant"); assert(loader_data != NULL, "invariant"); assert(THREAD->is_Java_thread(), "must be a JavaThread"); @@ -142,5 +214,27 @@ TRACE_KLASS_CREATION(result, parser, THREAD); +#if INCLUDE_CDS && INCLUDE_JVMTI + if (DumpSharedSpaces) { + assert(cached_class_file == NULL, "Sanity"); + // Archive the class stream data into the optional data section + JvmtiCachedClassFileData *p; + int len; + const unsigned char *bytes; + // event based tracing might set cached_class_file + if ((bytes = result->get_cached_class_file_bytes()) != NULL) { + len = result->get_cached_class_file_len(); + } else { + len = stream->length(); + bytes = stream->buffer(); + } + p = (JvmtiCachedClassFileData*)MetaspaceShared::optional_data_space_alloc( + offset_of(JvmtiCachedClassFileData, data) + len); + p->length = len; + memcpy(p->data, bytes, len); + result->set_archived_class_data(p); + } +#endif + return result; }
--- a/hotspot/src/share/vm/classfile/klassFactory.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/klassFactory.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -75,6 +75,12 @@ const InstanceKlass* host_klass, GrowableArray<Handle>* cp_patches, TRAPS); + public: + static instanceKlassHandle check_shared_class_file_load_hook( + instanceKlassHandle ik, + Symbol* class_name, + Handle class_loader, + Handle protection_domain, TRAPS); }; #endif // SHARE_VM_CLASSFILE_KLASSFACTORY_HPP
--- a/hotspot/src/share/vm/classfile/moduleEntry.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -92,7 +92,7 @@ // read java.base. If either of these conditions // hold, readability has been established. if (!this->is_named() || - (m == ModuleEntryTable::javabase_module())) { + (m == ModuleEntryTable::javabase_moduleEntry())) { return true; } @@ -358,16 +358,27 @@ } // Set java.lang.reflect.Module, version and location for java.base - ModuleEntry* jb_module = javabase_module(); + ModuleEntry* jb_module = javabase_moduleEntry(); assert(jb_module != NULL, "java.base ModuleEntry not defined"); - jb_module->set_module(boot_loader_data->add_handle(module_handle)); jb_module->set_version(version); jb_module->set_location(location); + // Once java.base's ModuleEntry _module field is set with the known + // java.lang.reflect.Module, java.base is considered "defined" to the VM. + jb_module->set_module(boot_loader_data->add_handle(module_handle)); + // Store pointer to the ModuleEntry for java.base in the java.lang.reflect.Module object. java_lang_reflect_Module::set_module_entry(module_handle(), jb_module); + + // Patch any previously loaded classes' module field with java.base's java.lang.reflect.Module. + patch_javabase_entries(module_handle); } +// Within java.lang.Class instances there is a java.lang.reflect.Module field +// that must be set with the defining module. During startup, prior to java.base's +// definition, classes needing their module field set are added to the fixup_module_list. +// Their module field is set once java.base's java.lang.reflect.Module is known to the VM. void ModuleEntryTable::patch_javabase_entries(Handle module_handle) { + assert(Module_lock->owned_by_self(), "should have the Module_lock"); if (module_handle.is_null()) { fatal("Unable to patch the module field of classes loaded prior to java.base's definition, invalid java.lang.reflect.Module"); } @@ -389,9 +400,7 @@ for (int i = 0; i < list_length; i++) { Klass* k = list->at(i); assert(k->is_klass(), "List should only hold classes"); - Thread* THREAD = Thread::current(); - KlassHandle kh(THREAD, k); - java_lang_Class::fixup_module_field(kh, module_handle); + java_lang_Class::fixup_module_field(KlassHandle(k), module_handle); k->class_loader_data()->dec_keep_alive(); }
--- a/hotspot/src/share/vm/classfile/moduleEntry.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -78,11 +78,11 @@ _must_walk_reads = false; } - Symbol* name() const { return literal(); } - void set_name(Symbol* n) { set_literal(n); } + Symbol* name() const { return literal(); } + void set_name(Symbol* n) { set_literal(n); } - jobject module() const { return _module; } - void set_module(jobject j) { _module = j; } + jobject module() const { return _module; } + void set_module(jobject j) { _module = j; } // The shared ProtectionDomain reference is set once the VM loads a shared class // originated from the current Module. The referenced ProtectionDomain object is @@ -217,13 +217,13 @@ // Special handling for unnamed module, one per class loader's ModuleEntryTable void create_unnamed_module(ClassLoaderData* loader_data); - ModuleEntry* unnamed_module() { return _unnamed_module; } + ModuleEntry* unnamed_module() { return _unnamed_module; } // Special handling for java.base - static ModuleEntry* javabase_module() { return _javabase_module; } - static void set_javabase_module(ModuleEntry* java_base) { _javabase_module = java_base; } - static bool javabase_defined() { return ((_javabase_module != NULL) && - (_javabase_module->module() != NULL)); } + static ModuleEntry* javabase_moduleEntry() { return _javabase_module; } + static void set_javabase_moduleEntry(ModuleEntry* java_base) { _javabase_module = java_base; } + static bool javabase_defined() { return ((_javabase_module != NULL) && + (_javabase_module->module() != NULL)); } static void finalize_javabase(Handle module_handle, Symbol* version, Symbol* location); static void patch_javabase_entries(Handle module_handle);
--- a/hotspot/src/share/vm/classfile/modules.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/modules.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -206,7 +206,7 @@ assert(pkg_list->length() == 0 || package_table != NULL, "Bad package_table"); // Ensure java.base's ModuleEntry has been created - assert(ModuleEntryTable::javabase_module() != NULL, "No ModuleEntry for java.base"); + assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "No ModuleEntry for java.base"); bool duplicate_javabase = false; { @@ -226,7 +226,7 @@ for (int x = 0; x < pkg_list->length(); x++) { // Some of java.base's packages were added early in bootstrapping, ignore duplicates. if (package_table->lookup_only(pkg_list->at(x)) == NULL) { - pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_module()); + pkg = package_table->locked_create_entry_or_null(pkg_list->at(x), ModuleEntryTable::javabase_moduleEntry()); assert(pkg != NULL, "Unable to create a java.base package entry"); } // Unable to have a GrowableArray of TempNewSymbol. Must decrement the refcount of @@ -255,9 +255,6 @@ log_trace(modules)("define_javabase_module(): creation of package %s for module java.base", (pkg_list->at(x))->as_C_string()); } - - // Patch any previously loaded classes' module field with java.base's jlr.Module. - ModuleEntryTable::patch_javabase_entries(module_handle); } void Modules::define_module(jobject module, jstring version,
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1210,16 +1210,12 @@ instanceKlassHandle SystemDictionary::load_shared_class( Symbol* class_name, Handle class_loader, TRAPS) { - // Don't load shared class when JvmtiExport::should_post_class_file_load_hook() - // is enabled since posting CFLH is not supported when loading shared class. - if (!JvmtiExport::should_post_class_file_load_hook()) { - instanceKlassHandle ik (THREAD, find_shared_class(class_name)); - // Make sure we only return the boot class for the NULL classloader. - if (ik.not_null() && - ik->is_shared_boot_class() && class_loader.is_null()) { - Handle protection_domain; - return load_shared_class(ik, class_loader, protection_domain, THREAD); - } + instanceKlassHandle ik (THREAD, find_shared_class(class_name)); + // Make sure we only return the boot class for the NULL classloader. + if (ik.not_null() && + ik->is_shared_boot_class() && class_loader.is_null()) { + Handle protection_domain; + return load_shared_class(ik, class_loader, protection_domain, THREAD); } return instanceKlassHandle(); } @@ -1303,11 +1299,6 @@ Handle class_loader, Handle protection_domain, TRAPS) { instanceKlassHandle nh = instanceKlassHandle(); // null Handle - if (JvmtiExport::should_post_class_file_load_hook()) { - // Don't load shared class when JvmtiExport::should_post_class_file_load_hook() - // is enabled since posting CFLH is not supported when loading shared class. - return nh; - } if (ik.not_null()) { Symbol* class_name = ik->name(); @@ -1358,6 +1349,14 @@ } } + instanceKlassHandle new_ik = KlassFactory::check_shared_class_file_load_hook( + ik, class_name, class_loader, protection_domain, CHECK_(nh)); + if (new_ik.not_null()) { + // The class is changed by CFLH. Return the new class. The shared class is + // not used. + return new_ik; + } + // Adjust methods to recover missing data. They need addresses for // interpreter entry points and their default native method address // must be reset.
--- a/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1366,22 +1366,25 @@ return false; } assert(prefix != NULL && prefix != BUSY, "Error"); - size_t i = 1; oop cur = prefix; - while (i < objsFromOverflow && cur->klass_or_null() != NULL) { - i++; cur = cur->list_ptr_from_klass(); + for (size_t i = 1; i < objsFromOverflow; ++i) { + oop next = cur->list_ptr_from_klass(); + if (next == NULL) break; + cur = next; } + assert(cur != NULL, "Loop postcondition"); // Reattach remaining (suffix) to overflow list - if (cur->klass_or_null() == NULL) { + oop suffix = cur->list_ptr_from_klass(); + if (suffix == NULL) { // Write back the NULL in lieu of the BUSY we wrote // above and it is still the same value. if (_overflow_list == BUSY) { (void) Atomic::cmpxchg_ptr(NULL, &_overflow_list, BUSY); } } else { - assert(cur->klass_or_null() != (Klass*)(address)BUSY, "Error"); - oop suffix = cur->list_ptr_from_klass(); // suffix will be put back on global list + assert(suffix != BUSY, "Error"); + // suffix will be put back on global list cur->set_klass_to_list_ptr(NULL); // break off suffix // It's possible that the list is still in the empty(busy) state // we left it in a short while ago; in that case we may be @@ -1401,8 +1404,10 @@ // Too bad, someone else got in in between; we'll need to do a splice. // Find the last item of suffix list oop last = suffix; - while (last->klass_or_null() != NULL) { - last = last->list_ptr_from_klass(); + while (true) { + oop next = last->list_ptr_from_klass(); + if (next == NULL) break; + last = next; } // Atomically prepend suffix to current overflow list observed_overflow_list = _overflow_list;
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1479,7 +1479,7 @@ "Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B min_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)", capacity_after_gc, used_after_gc, minimum_desired_capacity, MinHeapFreeRatio); - expand(expand_bytes); + expand(expand_bytes, _workers); // No expansion, now see if we want to shrink } else if (capacity_after_gc > maximum_desired_capacity) { @@ -1599,7 +1599,7 @@ word_size * HeapWordSize); - if (expand(expand_bytes)) { + if (expand(expand_bytes, _workers)) { _hrm.verify_optional(); _verifier->verify_region_sets_optional(); return attempt_allocation_at_safepoint(word_size, @@ -1609,7 +1609,7 @@ return NULL; } -bool G1CollectedHeap::expand(size_t expand_bytes, double* expand_time_ms) { +bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, double* expand_time_ms) { size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); aligned_expand_bytes = align_size_up(aligned_expand_bytes, HeapRegion::GrainBytes); @@ -1626,7 +1626,7 @@ uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes); assert(regions_to_expand > 0, "Must expand by at least one region"); - uint expanded_by = _hrm.expand_by(regions_to_expand); + uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers); if (expand_time_ms != NULL) { *expand_time_ms = (os::elapsedTime() - expand_heap_start_time_sec) * MILLIUNITS; } @@ -1927,7 +1927,7 @@ _cmThread = _cm->cmThread(); // Now expand into the initial heap size. - if (!expand(init_byte_size)) { + if (!expand(init_byte_size, _workers)) { vm_shutdown_during_initialization("Failed to allocate initial heap."); return JNI_ENOMEM; } @@ -3165,7 +3165,6 @@ assert(_verifier->check_cset_fast_test(), "Inconsistency in the InCSetState table."); - _cm->note_start_of_gc(); // We call this after finalize_cset() to // ensure that the CSet has been finalized. _cm->verify_no_cset_oops(); @@ -3241,7 +3240,7 @@ // No need for an ergo logging here, // expansion_amount() does this when it returns a value > 0. double expand_ms; - if (!expand(expand_bytes, &expand_ms)) { + if (!expand(expand_bytes, _workers, &expand_ms)) { // We failed to expand the heap. Cannot do anything about it. } g1_policy()->phase_times()->record_expand_heap_time(expand_ms); @@ -3251,7 +3250,6 @@ // We redo the verification but now wrt to the new CSet which // has just got initialized after the previous CSet was freed. _cm->verify_no_cset_oops(); - _cm->note_end_of_gc(); // This timing is only used by the ergonomics to handle our pause target. // It is unclear why this should not include the full pause. We will
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -557,7 +557,7 @@ // Returns true if the heap was expanded by the requested amount; // false otherwise. // (Rounds up to a HeapRegion boundary.) - bool expand(size_t expand_bytes, double* expand_time_ms = NULL); + bool expand(size_t expand_bytes, WorkGang* pretouch_workers = NULL, double* expand_time_ms = NULL); // Returns the PLAB statistics for a given destination. inline G1EvacStats* alloc_buffer_stats(InCSetState dest);
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -133,129 +133,184 @@ } G1CMMarkStack::G1CMMarkStack() : - _reserved_space(), + _max_chunk_capacity(0), _base(NULL), - _capacity(0), - _saved_index((size_t)AllBits), + _chunk_capacity(0), + _out_of_memory(false), _should_expand(false) { set_empty(); } bool G1CMMarkStack::resize(size_t new_capacity) { assert(is_empty(), "Only resize when stack is empty."); - assert(new_capacity <= MarkStackSizeMax, - "Trying to resize stack to " SIZE_FORMAT " elements when the maximum is " SIZE_FORMAT, new_capacity, MarkStackSizeMax); - - size_t reservation_size = ReservedSpace::allocation_align_size_up(new_capacity * sizeof(oop)); - - ReservedSpace rs(reservation_size); - if (!rs.is_reserved()) { - log_warning(gc)("Failed to reserve memory for new overflow mark stack with " SIZE_FORMAT " elements and size " SIZE_FORMAT "B.", new_capacity, reservation_size); + assert(new_capacity <= _max_chunk_capacity, + "Trying to resize stack to " SIZE_FORMAT " chunks when the maximum is " SIZE_FORMAT, new_capacity, _max_chunk_capacity); + + OopChunk* new_base = MmapArrayAllocator<OopChunk, mtGC>::allocate_or_null(new_capacity); + + if (new_base == NULL) { + log_warning(gc)("Failed to reserve memory for new overflow mark stack with " SIZE_FORMAT " chunks and size " SIZE_FORMAT "B.", new_capacity, new_capacity * sizeof(OopChunk)); return false; } - - VirtualSpace vs; - - if (!vs.initialize(rs, rs.size())) { - rs.release(); - log_warning(gc)("Failed to commit memory for new overflow mark stack of size " SIZE_FORMAT "B.", rs.size()); - return false; + // Release old mapping. + if (_base != NULL) { + MmapArrayAllocator<OopChunk, mtGC>::free(_base, _chunk_capacity); } - assert(vs.committed_size() == rs.size(), "Failed to commit all of the mark stack."); - - // Release old mapping. - _reserved_space.release(); - - // Save new mapping for future unmapping. - _reserved_space = rs; - - MemTracker::record_virtual_memory_type((address)_reserved_space.base(), mtGC); - - _base = (oop*) vs.low(); - _capacity = new_capacity; + _base = new_base; + _chunk_capacity = new_capacity; set_empty(); _should_expand = false; return true; } -bool G1CMMarkStack::allocate(size_t capacity) { - return resize(capacity); +size_t G1CMMarkStack::capacity_alignment() { + return (size_t)lcm(os::vm_allocation_granularity(), sizeof(OopChunk)) / sizeof(void*); +} + +bool G1CMMarkStack::initialize(size_t initial_capacity, size_t max_capacity) { + guarantee(_max_chunk_capacity == 0, "G1CMMarkStack already initialized."); + + size_t const OopChunkSizeInVoidStar = sizeof(OopChunk) / sizeof(void*); + + _max_chunk_capacity = (size_t)align_size_up(max_capacity, capacity_alignment()) / OopChunkSizeInVoidStar; + size_t initial_chunk_capacity = (size_t)align_size_up(initial_capacity, capacity_alignment()) / OopChunkSizeInVoidStar; + + guarantee(initial_chunk_capacity <= _max_chunk_capacity, + "Maximum chunk capacity " SIZE_FORMAT " smaller than initial capacity " SIZE_FORMAT, + _max_chunk_capacity, + initial_chunk_capacity); + + log_debug(gc)("Initialize mark stack with " SIZE_FORMAT " chunks, maximum " SIZE_FORMAT, + initial_chunk_capacity, _max_chunk_capacity); + + return resize(initial_chunk_capacity); } void G1CMMarkStack::expand() { // Clear expansion flag _should_expand = false; - if (_capacity == MarkStackSizeMax) { - log_debug(gc)("Can not expand overflow mark stack further, already at maximum capacity of " SIZE_FORMAT " elements.", _capacity); + if (_chunk_capacity == _max_chunk_capacity) { + log_debug(gc)("Can not expand overflow mark stack further, already at maximum capacity of " SIZE_FORMAT " chunks.", _chunk_capacity); return; } - size_t old_capacity = _capacity; + size_t old_capacity = _chunk_capacity; // Double capacity if possible - size_t new_capacity = MIN2(old_capacity * 2, MarkStackSizeMax); + size_t new_capacity = MIN2(old_capacity * 2, _max_chunk_capacity); if (resize(new_capacity)) { - log_debug(gc)("Expanded marking stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " elements", + log_debug(gc)("Expanded mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks", old_capacity, new_capacity); } else { - log_warning(gc)("Failed to expand marking stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " elements", + log_warning(gc)("Failed to expand mark stack capacity from " SIZE_FORMAT " to " SIZE_FORMAT " chunks", old_capacity, new_capacity); } } G1CMMarkStack::~G1CMMarkStack() { if (_base != NULL) { - _base = NULL; - _reserved_space.release(); - } -} - -void G1CMMarkStack::par_push_arr(oop* buffer, size_t n) { - MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag); - size_t start = _index; - size_t next_index = start + n; - if (next_index > _capacity) { - _overflow = true; - return; - } - // Otherwise. - _index = next_index; - for (size_t i = 0; i < n; i++) { - size_t ind = start + i; - assert(ind < _capacity, "By overflow test above."); - _base[ind] = buffer[i]; + MmapArrayAllocator<OopChunk, mtGC>::free(_base, _chunk_capacity); } } -bool G1CMMarkStack::par_pop_arr(oop* buffer, size_t max, size_t* n) { - MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag); - size_t index = _index; - if (index == 0) { - *n = 0; +void G1CMMarkStack::add_chunk_to_list(OopChunk* volatile* list, OopChunk* elem) { + elem->next = *list; + *list = elem; +} + +void G1CMMarkStack::add_chunk_to_chunk_list(OopChunk* elem) { + MutexLockerEx x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); + add_chunk_to_list(&_chunk_list, elem); + _chunks_in_chunk_list++; +} + +void G1CMMarkStack::add_chunk_to_free_list(OopChunk* elem) { + MutexLockerEx x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); + add_chunk_to_list(&_free_list, elem); +} + +G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_list(OopChunk* volatile* list) { + OopChunk* result = *list; + if (result != NULL) { + *list = (*list)->next; + } + return result; +} + +G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_chunk_list() { + MutexLockerEx x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); + OopChunk* result = remove_chunk_from_list(&_chunk_list); + if (result != NULL) { + _chunks_in_chunk_list--; + } + return result; +} + +G1CMMarkStack::OopChunk* G1CMMarkStack::remove_chunk_from_free_list() { + MutexLockerEx x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); + return remove_chunk_from_list(&_free_list); +} + +G1CMMarkStack::OopChunk* G1CMMarkStack::allocate_new_chunk() { + // This dirty read of _hwm is okay because we only ever increase the _hwm in parallel code. + // Further this limits _hwm to a value of _chunk_capacity + #threads, avoiding + // wraparound of _hwm. + if (_hwm >= _chunk_capacity) { + return NULL; + } + + size_t cur_idx = Atomic::add(1, &_hwm) - 1; + if (cur_idx >= _chunk_capacity) { + return NULL; + } + + OopChunk* result = ::new (&_base[cur_idx]) OopChunk; + result->next = NULL; + return result; +} + +bool G1CMMarkStack::par_push_chunk(oop* ptr_arr) { + // Get a new chunk. + OopChunk* new_chunk = remove_chunk_from_free_list(); + + if (new_chunk == NULL) { + // Did not get a chunk from the free list. Allocate from backing memory. + new_chunk = allocate_new_chunk(); + } + + if (new_chunk == NULL) { + _out_of_memory = true; return false; - } else { - size_t k = MIN2(max, index); - size_t new_ind = index - k; - for (size_t j = 0; j < k; j++) { - buffer[j] = _base[new_ind + j]; - } - _index = new_ind; - *n = k; - return true; } + + Copy::conjoint_memory_atomic(ptr_arr, new_chunk->data, OopsPerChunk * sizeof(oop)); + + add_chunk_to_chunk_list(new_chunk); + + return true; } -void G1CMMarkStack::note_start_of_gc() { - assert(_saved_index == (size_t)AllBits, "note_start_of_gc()/end_of_gc() calls bracketed incorrectly"); - _saved_index = _index; +bool G1CMMarkStack::par_pop_chunk(oop* ptr_arr) { + OopChunk* cur = remove_chunk_from_chunk_list(); + + if (cur == NULL) { + return false; + } + + Copy::conjoint_memory_atomic(cur->data, ptr_arr, OopsPerChunk * sizeof(oop)); + + add_chunk_to_free_list(cur); + return true; } -void G1CMMarkStack::note_end_of_gc() { - guarantee(!stack_modified(), "Saved index " SIZE_FORMAT " must be the same as " SIZE_FORMAT, _saved_index, _index); - - _saved_index = (size_t)AllBits; +void G1CMMarkStack::set_empty() { + _chunks_in_chunk_list = 0; + _hwm = 0; + clear_out_of_memory(); + _chunk_list = NULL; + _free_list = NULL; } G1CMRootRegions::G1CMRootRegions() : @@ -483,9 +538,8 @@ } } - if (!_global_mark_stack.allocate(MarkStackSize)) { + if (!_global_mark_stack.initialize(MarkStackSize, MarkStackSizeMax)) { vm_exit_during_initialization("Failed to allocate initial concurrent mark overflow mark stack."); - return; } _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_worker_id, mtGC); @@ -1695,10 +1749,10 @@ // oop closures will set the has_overflown flag if we overflow the // global marking stack. - assert(_global_mark_stack.overflow() || _global_mark_stack.is_empty(), - "mark stack should be empty (unless it overflowed)"); - - if (_global_mark_stack.overflow()) { + assert(_global_mark_stack.is_out_of_memory() || _global_mark_stack.is_empty(), + "Mark stack should be empty (unless it is out of memory)"); + + if (_global_mark_stack.is_out_of_memory()) { // This should have been done already when we tried to push an // entry on to the global mark stack. But let's do it again. set_has_overflown(); @@ -2343,49 +2397,54 @@ } void G1CMTask::move_entries_to_global_stack() { - // local array where we'll store the entries that will be popped - // from the local queue - oop buffer[global_stack_transfer_size]; - - int n = 0; + // Local array where we'll store the entries that will be popped + // from the local queue. + oop buffer[G1CMMarkStack::OopsPerChunk]; + + size_t n = 0; oop obj; - while (n < global_stack_transfer_size && _task_queue->pop_local(obj)) { + while (n < G1CMMarkStack::OopsPerChunk && _task_queue->pop_local(obj)) { buffer[n] = obj; ++n; } + if (n < G1CMMarkStack::OopsPerChunk) { + buffer[n] = NULL; + } if (n > 0) { - // we popped at least one entry from the local queue - - if (!_cm->mark_stack_push(buffer, n)) { + if (!_cm->mark_stack_push(buffer)) { set_has_aborted(); } } - // this operation was quite expensive, so decrease the limits + // This operation was quite expensive, so decrease the limits. decrease_limits(); } -void G1CMTask::get_entries_from_global_stack() { - // local array where we'll store the entries that will be popped +bool G1CMTask::get_entries_from_global_stack() { + // Local array where we'll store the entries that will be popped // from the global stack. - oop buffer[global_stack_transfer_size]; - size_t n; - _cm->mark_stack_pop(buffer, global_stack_transfer_size, &n); - assert(n <= global_stack_transfer_size, - "we should not pop more than the given limit"); - if (n > 0) { - // yes, we did actually pop at least one entry - for (size_t i = 0; i < n; ++i) { - bool success = _task_queue->push(buffer[i]); - // We only call this when the local queue is empty or under a - // given target limit. So, we do not expect this push to fail. - assert(success, "invariant"); + oop buffer[G1CMMarkStack::OopsPerChunk]; + + if (!_cm->mark_stack_pop(buffer)) { + return false; + } + + // We did actually pop at least one entry. + for (size_t i = 0; i < G1CMMarkStack::OopsPerChunk; ++i) { + oop elem = buffer[i]; + if (elem == NULL) { + break; } + bool success = _task_queue->push(elem); + // We only call this when the local queue is empty or under a + // given target limit. So, we do not expect this push to fail. + assert(success, "invariant"); } - // this operation was quite expensive, so decrease the limits + // This operation was quite expensive, so decrease the limits decrease_limits(); + return true; } void G1CMTask::drain_local_queue(bool partially) { @@ -2429,20 +2488,21 @@ // Decide what the target size is, depending whether we're going to // drain it partially (so that other tasks can steal if they run out - // of things to do) or totally (at the very end). Notice that, - // because we move entries from the global stack in chunks or - // because another task might be doing the same, we might in fact - // drop below the target. But, this is not a problem. - size_t target_size; + // of things to do) or totally (at the very end). + // Notice that when draining the global mark stack partially, due to the racyness + // of the mark stack size update we might in fact drop below the target. But, + // this is not a problem. + // In case of total draining, we simply process until the global mark stack is + // totally empty, disregarding the size counter. if (partially) { - target_size = _cm->partial_mark_stack_size_target(); + size_t const target_size = _cm->partial_mark_stack_size_target(); + while (!has_aborted() && _cm->mark_stack_size() > target_size) { + if (get_entries_from_global_stack()) { + drain_local_queue(partially); + } + } } else { - target_size = 0; - } - - if (_cm->mark_stack_size() > target_size) { - while (!has_aborted() && _cm->mark_stack_size() > target_size) { - get_entries_from_global_stack(); + while (!has_aborted() && get_entries_from_global_stack()) { drain_local_queue(partially); } }
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -149,42 +149,98 @@ // // Stores oops in a huge buffer in virtual memory that is always fully committed. // Resizing may only happen during a STW pause when the stack is empty. +// +// Memory is allocated on a "chunk" basis, i.e. a set of oops. For this, the mark +// stack memory is split into evenly sized chunks of oops. Users can only +// add or remove entries on that basis. +// Chunks are filled in increasing address order. Not completely filled chunks +// have a NULL element as a terminating element. +// +// Every chunk has a header containing a single pointer element used for memory +// management. This wastes some space, but is negligible (< .1% with current sizing). +// +// Memory management is done using a mix of tracking a high water-mark indicating +// that all chunks at a lower address are valid chunks, and a singly linked free +// list connecting all empty chunks. class G1CMMarkStack VALUE_OBJ_CLASS_SPEC { - ReservedSpace _reserved_space; // Space currently reserved for the mark stack. +public: + // Number of oops that can fit in a single chunk. + static const size_t OopsPerChunk = 1024 - 1 /* One reference for the next pointer */; +private: + struct OopChunk { + OopChunk* next; + oop data[OopsPerChunk]; + }; + + size_t _max_chunk_capacity; // Maximum number of OopChunk elements on the stack. + + OopChunk* _base; // Bottom address of allocated memory area. + size_t _chunk_capacity; // Current maximum number of OopChunk elements. - oop* _base; // Bottom address of allocated memory area. - size_t _capacity; // Maximum number of elements. - size_t _index; // One more than last occupied index. + char _pad0[DEFAULT_CACHE_LINE_SIZE]; + OopChunk* volatile _free_list; // Linked list of free chunks that can be allocated by users. + char _pad1[DEFAULT_CACHE_LINE_SIZE - sizeof(OopChunk*)]; + OopChunk* volatile _chunk_list; // List of chunks currently containing data. + volatile size_t _chunks_in_chunk_list; + char _pad2[DEFAULT_CACHE_LINE_SIZE - sizeof(OopChunk*) - sizeof(size_t)]; + + volatile size_t _hwm; // High water mark within the reserved space. + char _pad4[DEFAULT_CACHE_LINE_SIZE - sizeof(size_t)]; + + // Allocate a new chunk from the reserved memory, using the high water mark. Returns + // NULL if out of memory. + OopChunk* allocate_new_chunk(); - size_t _saved_index; // Value of _index saved at start of GC to detect mark stack modifications during that time. + volatile bool _out_of_memory; - bool _overflow; + // Atomically add the given chunk to the list. + void add_chunk_to_list(OopChunk* volatile* list, OopChunk* elem); + // Atomically remove and return a chunk from the given list. Returns NULL if the + // list is empty. + OopChunk* remove_chunk_from_list(OopChunk* volatile* list); + + void add_chunk_to_chunk_list(OopChunk* elem); + void add_chunk_to_free_list(OopChunk* elem); + + OopChunk* remove_chunk_from_chunk_list(); + OopChunk* remove_chunk_from_free_list(); + bool _should_expand; // Resizes the mark stack to the given new capacity. Releases any previous // memory if successful. bool resize(size_t new_capacity); - bool stack_modified() const { return _index != _saved_index; } public: G1CMMarkStack(); ~G1CMMarkStack(); - bool allocate(size_t capacity); + // Alignment and minimum capacity of this mark stack in number of oops. + static size_t capacity_alignment(); + + // Allocate and initialize the mark stack with the given number of oops. + bool initialize(size_t initial_capacity, size_t max_capacity); - // Pushes the first "n" elements of the given buffer on the stack. - void par_push_arr(oop* buffer, size_t n); + // Pushes the given buffer containing at most OopsPerChunk elements on the mark + // stack. If less than OopsPerChunk elements are to be pushed, the array must + // be terminated with a NULL. + // Returns whether the buffer contents were successfully pushed to the global mark + // stack. + bool par_push_chunk(oop* buffer); - // Moves up to max elements from the stack into the given buffer. Returns - // the number of elements pushed, and false if the array has been empty. - // Returns true if the buffer contains at least one element. - bool par_pop_arr(oop* buffer, size_t max, size_t* n); + // Pops a chunk from this mark stack, copying them into the given buffer. This + // chunk may contain up to OopsPerChunk elements. If there are less, the last + // element in the array is a NULL pointer. + bool par_pop_chunk(oop* buffer); - bool is_empty() const { return _index == 0; } - size_t capacity() const { return _capacity; } + // Return whether the chunk list is empty. Racy due to unsynchronized access to + // _chunk_list. + bool is_empty() const { return _chunk_list == NULL; } - bool overflow() const { return _overflow; } - void clear_overflow() { _overflow = false; } + size_t capacity() const { return _chunk_capacity; } + + bool is_out_of_memory() const { return _out_of_memory; } + void clear_out_of_memory() { _out_of_memory = false; } bool should_expand() const { return _should_expand; } void set_should_expand(bool value) { _should_expand = value; } @@ -192,20 +248,15 @@ // Expand the stack, typically in response to an overflow condition void expand(); - size_t size() const { return _index; } - - void set_empty() { _index = 0; clear_overflow(); } - - // Record the current index. - void note_start_of_gc(); + // Return the approximate number of oops on this mark stack. Racy due to + // unsynchronized access to _chunks_in_chunk_list. + size_t size() const { return _chunks_in_chunk_list * OopsPerChunk; } - // Make sure that we have not added any entries to the stack during GC. - void note_end_of_gc(); + void set_empty(); - // Apply fn to each oop in the mark stack, up to the bound recorded - // via one of the above "note" functions. The mark stack must not + // Apply Fn to every oop on the mark stack. The mark stack must not // be modified while iterating. - template<typename Fn> void iterate(Fn fn); + template<typename Fn> void iterate(Fn fn) const PRODUCT_RETURN; }; // Root Regions are regions that are not empty at the beginning of a @@ -278,7 +329,6 @@ friend class G1CMDrainMarkingStackClosure; friend class G1CMBitMapClosure; friend class G1CMConcurrentMarkingTask; - friend class G1CMMarkStack; friend class G1CMRemarkTask; friend class G1CMTask; @@ -479,22 +529,20 @@ public: // Manipulation of the global mark stack. // The push and pop operations are used by tasks for transfers - // between task-local queues and the global mark stack, and use - // locking for concurrency safety. - bool mark_stack_push(oop* arr, size_t n) { - _global_mark_stack.par_push_arr(arr, n); - if (_global_mark_stack.overflow()) { + // between task-local queues and the global mark stack. + bool mark_stack_push(oop* arr) { + if (!_global_mark_stack.par_push_chunk(arr)) { set_has_overflown(); return false; } return true; } - void mark_stack_pop(oop* arr, size_t max, size_t* n) { - _global_mark_stack.par_pop_arr(arr, max, n); + bool mark_stack_pop(oop* arr) { + return _global_mark_stack.par_pop_chunk(arr); } size_t mark_stack_size() { return _global_mark_stack.size(); } size_t partial_mark_stack_size_target() { return _global_mark_stack.capacity()/3; } - bool mark_stack_overflow() { return _global_mark_stack.overflow(); } + bool mark_stack_overflow() { return _global_mark_stack.is_out_of_memory(); } bool mark_stack_empty() { return _global_mark_stack.is_empty(); } G1CMRootRegions* root_regions() { return &_root_regions; } @@ -599,16 +647,6 @@ // read-only, so use this carefully! void clearRangePrevBitmap(MemRegion mr); - // Notify data structures that a GC has started. - void note_start_of_gc() { - _global_mark_stack.note_start_of_gc(); - } - - // Notify data structures that a GC is finished. - void note_end_of_gc() { - _global_mark_stack.note_end_of_gc(); - } - // Verify that there are no CSet oops on the stacks (taskqueues / // global mark stack) and fingers (global / per-task). // If marking is not in progress, it's a no-op. @@ -670,10 +708,7 @@ // references reaches this limit refs_reached_period = 384, // Initial value for the hash seed, used in the work stealing code - init_hash_seed = 17, - // How many entries will be transferred between global stack and - // local queues at once. - global_stack_transfer_size = 1024 + init_hash_seed = 17 }; uint _worker_id; @@ -858,9 +893,10 @@ // It pushes an object on the local queue. inline void push(oop obj); - // These two move entries to/from the global stack. + // Move entries to the global stack. void move_entries_to_global_stack(); - void get_entries_from_global_stack(); + // Move entries from the global stack, return true if we were successful to do so. + bool get_entries_from_global_stack(); // It pops and scans objects from the local queue. If partially is // true, then it stops when the queue size is of a given limit. If
--- a/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.inline.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1ConcurrentMark.inline.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -89,14 +89,28 @@ #undef check_mark +#ifndef PRODUCT template<typename Fn> -inline void G1CMMarkStack::iterate(Fn fn) { +inline void G1CMMarkStack::iterate(Fn fn) const { assert_at_safepoint(true); - assert(!stack_modified(), "Saved index " SIZE_FORMAT " must be the same as " SIZE_FORMAT, _saved_index, _index); - for (size_t i = 0; i < _index; ++i) { - fn(_base[i]); + + size_t num_chunks = 0; + + OopChunk* cur = _chunk_list; + while (cur != NULL) { + guarantee(num_chunks <= _chunks_in_chunk_list, "Found " SIZE_FORMAT " oop chunks which is more than there should be", num_chunks); + + for (size_t i = 0; i < OopsPerChunk; ++i) { + if (cur->data[i] == NULL) { + break; + } + fn(cur->data[i]); + } + cur = cur->next; + num_chunks++; } } +#endif // It scans an object and visits its children. inline void G1CMTask::scan_object(oop obj) { process_grey_object<true>(obj); }
--- a/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -34,7 +34,6 @@ class G1ConcurrentMark; class DirtyCardToOopClosure; class G1CMBitMap; -class G1CMMarkStack; class G1ParScanThreadState; class G1CMTask; class ReferenceProcessor;
--- a/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -24,8 +24,10 @@ #include "precompiled.hpp" #include "gc/g1/g1PageBasedVirtualSpace.hpp" +#include "gc/shared/workgroup.hpp" #include "oops/markOop.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/os.inline.hpp" #include "services/memTracker.hpp" #include "utilities/bitMap.inline.hpp" @@ -177,7 +179,7 @@ guarantee(start_page < end_page, "Given start page " SIZE_FORMAT " is larger or equal to end page " SIZE_FORMAT, start_page, end_page); - os::pretouch_memory(page_start(start_page), bounded_end_addr(end_page)); + os::pretouch_memory(page_start(start_page), bounded_end_addr(end_page), _page_size); } bool G1PageBasedVirtualSpace::commit(size_t start_page, size_t size_in_pages) { @@ -198,9 +200,6 @@ } _committed.set_range(start_page, end_page); - if (AlwaysPreTouch) { - pretouch_internal(start_page, end_page); - } return zero_filled; } @@ -227,6 +226,53 @@ _committed.clear_range(start_page, end_page); } +class G1PretouchTask : public AbstractGangTask { +private: + char* volatile _cur_addr; + char* const _start_addr; + char* const _end_addr; + size_t const _page_size; +public: + G1PretouchTask(char* start_address, char* end_address, size_t page_size) : + AbstractGangTask("G1 PreTouch", + Universe::is_fully_initialized() ? GCId::current_raw() : + // During VM initialization there is + // no GC cycle that this task can be + // associated with. + GCId::undefined()), + _cur_addr(start_address), + _start_addr(start_address), + _end_addr(end_address), + _page_size(page_size) { + } + + virtual void work(uint worker_id) { + size_t const actual_chunk_size = MAX2(chunk_size(), _page_size); + while (true) { + char* touch_addr = (char*)Atomic::add_ptr((intptr_t)actual_chunk_size, (volatile void*) &_cur_addr) - actual_chunk_size; + if (touch_addr < _start_addr || touch_addr >= _end_addr) { + break; + } + char* end_addr = touch_addr + MIN2(actual_chunk_size, pointer_delta(_end_addr, touch_addr, sizeof(char))); + os::pretouch_memory(touch_addr, end_addr, _page_size); + } + } + + static size_t chunk_size() { return PreTouchParallelChunkSize; } +}; + +void G1PageBasedVirtualSpace::pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang) { + guarantee(pretouch_gang != NULL, "No pretouch gang specified."); + + size_t num_chunks = MAX2((size_t)1, size_in_pages * _page_size / MAX2(G1PretouchTask::chunk_size(), _page_size)); + + uint num_workers = MIN2((uint)num_chunks, pretouch_gang->active_workers()); + G1PretouchTask cl(page_start(start_page), bounded_end_addr(start_page + size_in_pages), _page_size); + log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.", + cl.name(), num_workers, num_chunks, size_in_pages * _page_size); + pretouch_gang->run_task(&cl, num_workers); +} + bool G1PageBasedVirtualSpace::contains(const void* p) const { return _low_boundary <= (const char*) p && (const char*) p < _high_boundary; }
--- a/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1PageBasedVirtualSpace.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -30,6 +30,8 @@ #include "memory/virtualspace.hpp" #include "utilities/bitMap.hpp" +class WorkGang; + // Virtual space management helper for a virtual space with an OS page allocation // granularity. // (De-)Allocation requests are always OS page aligned by passing a page index @@ -117,6 +119,8 @@ // Uncommit the given area of pages starting at start being size_in_pages large. void uncommit(size_t start_page, size_t size_in_pages); + void pretouch(size_t start_page, size_t size_in_pages, WorkGang* pretouch_gang = NULL); + // Initialize the given reserved space with the given base address and the size // actually used. // Prefer to commit in page_size chunks.
--- a/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -66,8 +66,12 @@ guarantee(alloc_granularity >= page_size, "allocation granularity smaller than commit granularity"); } - virtual void commit_regions(uint start_idx, size_t num_regions) { - bool zero_filled = _storage.commit((size_t)start_idx * _pages_per_region, num_regions * _pages_per_region); + virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { + size_t const start_page = (size_t)start_idx * _pages_per_region; + bool zero_filled = _storage.commit(start_page, num_regions * _pages_per_region); + if (AlwaysPreTouch) { + _storage.pretouch(start_page, num_regions * _pages_per_region, pretouch_gang); + } _commit_map.set_range(start_idx, start_idx + num_regions); fire_on_commit(start_idx, num_regions, zero_filled); } @@ -110,19 +114,38 @@ _refcounts.initialize((HeapWord*)rs.base(), (HeapWord*)(rs.base() + align_size_up(rs.size(), page_size)), page_size); } - virtual void commit_regions(uint start_idx, size_t num_regions) { + virtual void commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { + size_t const NoPage = ~(size_t)0; + + size_t first_committed = NoPage; + size_t num_committed = 0; + + bool all_zero_filled = true; + for (uint i = start_idx; i < start_idx + num_regions; i++) { assert(!_commit_map.at(i), "Trying to commit storage at region %u that is already committed", i); size_t idx = region_idx_to_page_idx(i); uint old_refcount = _refcounts.get_by_index(idx); + bool zero_filled = false; if (old_refcount == 0) { + if (first_committed == NoPage) { + first_committed = idx; + num_committed = 1; + } else { + num_committed++; + } zero_filled = _storage.commit(idx, 1); } + all_zero_filled &= zero_filled; + _refcounts.set_by_index(idx, old_refcount + 1); _commit_map.set_bit(i); - fire_on_commit(i, 1, zero_filled); } + if (AlwaysPreTouch && num_committed > 0) { + _storage.pretouch(first_committed, num_committed, pretouch_gang); + } + fire_on_commit(start_idx, num_regions, all_zero_filled); } virtual void uncommit_regions(uint start_idx, size_t num_regions) {
--- a/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/g1RegionToSpaceMapper.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -29,6 +29,8 @@ #include "memory/allocation.hpp" #include "utilities/debug.hpp" +class WorkGang; + class G1MappingChangedListener VALUE_OBJ_CLASS_SPEC { public: // Fired after commit of the memory, i.e. the memory this listener is registered @@ -68,7 +70,7 @@ return _commit_map.at(idx); } - virtual void commit_regions(uint start_idx, size_t num_regions = 1) = 0; + virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL) = 0; virtual void uncommit_regions(uint start_idx, size_t num_regions = 1) = 0; // Creates an appropriate G1RegionToSpaceMapper for the given parameters.
--- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -353,35 +353,6 @@ } HeapWord* -HeapRegion::object_iterate_mem_careful(MemRegion mr, - ObjectClosure* cl) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // We used to use "block_start_careful" here. But we're actually happy - // to update the BOT while we do this... - HeapWord* cur = block_start(mr.start()); - mr = mr.intersection(used_region()); - if (mr.is_empty()) return NULL; - // Otherwise, find the obj that extends onto mr.start(). - - assert(cur <= mr.start() - && (oop(cur)->klass_or_null() == NULL || - cur + oop(cur)->size() > mr.start()), - "postcondition of block_start"); - oop obj; - while (cur < mr.end()) { - obj = oop(cur); - if (obj->klass_or_null() == NULL) { - // Ran into an unparseable point. - return cur; - } else if (!g1h->is_obj_dead(obj)) { - cl->do_object(obj); - } - cur += block_size(cur); - } - return NULL; -} - -HeapWord* HeapRegion:: oops_on_card_seq_iterate_careful(MemRegion mr, FilterOutOfRegionClosure* cl,
--- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -653,17 +653,6 @@ } } - // Requires that "mr" be entirely within the region. - // Apply "cl->do_object" to all objects that intersect with "mr". - // If the iteration encounters an unparseable portion of the region, - // or if "cl->abort()" is true after a closure application, - // terminate the iteration and return the address of the start of the - // subregion that isn't done. (The two can be distinguished by querying - // "cl->abort()".) Return of "NULL" indicates that the iteration - // completed. - HeapWord* - object_iterate_mem_careful(MemRegion mr, ObjectClosure* cl); - // filter_young: if true and the region is a young region then we // skip the iteration. // card_ptr: if not NULL, and we decide that the card is not young
--- a/hotspot/src/share/vm/gc/g1/heapRegionManager.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/heapRegionManager.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -72,22 +72,22 @@ return g1h->new_heap_region(hrm_index, mr); } -void HeapRegionManager::commit_regions(uint index, size_t num_regions) { +void HeapRegionManager::commit_regions(uint index, size_t num_regions, WorkGang* pretouch_gang) { guarantee(num_regions > 0, "Must commit more than zero regions"); guarantee(_num_committed + num_regions <= max_length(), "Cannot commit more than the maximum amount of regions"); _num_committed += (uint)num_regions; - _heap_mapper->commit_regions(index, num_regions); + _heap_mapper->commit_regions(index, num_regions, pretouch_gang); // Also commit auxiliary data - _prev_bitmap_mapper->commit_regions(index, num_regions); - _next_bitmap_mapper->commit_regions(index, num_regions); + _prev_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang); + _next_bitmap_mapper->commit_regions(index, num_regions, pretouch_gang); - _bot_mapper->commit_regions(index, num_regions); - _cardtable_mapper->commit_regions(index, num_regions); + _bot_mapper->commit_regions(index, num_regions, pretouch_gang); + _cardtable_mapper->commit_regions(index, num_regions, pretouch_gang); - _card_counts_mapper->commit_regions(index, num_regions); + _card_counts_mapper->commit_regions(index, num_regions, pretouch_gang); } void HeapRegionManager::uncommit_regions(uint start, size_t num_regions) { @@ -117,9 +117,9 @@ _card_counts_mapper->uncommit_regions(start, num_regions); } -void HeapRegionManager::make_regions_available(uint start, uint num_regions) { +void HeapRegionManager::make_regions_available(uint start, uint num_regions, WorkGang* pretouch_gang) { guarantee(num_regions > 0, "No point in calling this for zero regions"); - commit_regions(start, num_regions); + commit_regions(start, num_regions, pretouch_gang); for (uint i = start; i < start + num_regions; i++) { if (_regions.get_by_index(i) == NULL) { HeapRegion* new_hr = new_heap_region(i); @@ -163,11 +163,11 @@ return MemoryUsage(0, used_sz, committed_sz, committed_sz); } -uint HeapRegionManager::expand_by(uint num_regions) { - return expand_at(0, num_regions); +uint HeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) { + return expand_at(0, num_regions, pretouch_workers); } -uint HeapRegionManager::expand_at(uint start, uint num_regions) { +uint HeapRegionManager::expand_at(uint start, uint num_regions, WorkGang* pretouch_workers) { if (num_regions == 0) { return 0; } @@ -181,7 +181,7 @@ while (expanded < num_regions && (num_last_found = find_unavailable_from_idx(cur, &idx_last_found)) > 0) { uint to_expand = MIN2(num_regions - expanded, num_last_found); - make_regions_available(idx_last_found, to_expand); + make_regions_available(idx_last_found, to_expand, pretouch_workers); expanded += to_expand; cur = idx_last_found + num_last_found + 1; }
--- a/hotspot/src/share/vm/gc/g1/heapRegionManager.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/g1/heapRegionManager.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,6 +34,7 @@ class HeapRegionClosure; class HeapRegionClaimer; class FreeRegionList; +class WorkGang; class G1HeapRegionTable : public G1BiasedMappedArray<HeapRegion*> { protected: @@ -94,10 +95,10 @@ HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } HeapWord* heap_end() const {return _regions.end_address_mapped(); } - void make_regions_available(uint index, uint num_regions = 1); + void make_regions_available(uint index, uint num_regions = 1, WorkGang* pretouch_gang = NULL); // Pass down commit calls to the VirtualSpace. - void commit_regions(uint index, size_t num_regions = 1); + void commit_regions(uint index, size_t num_regions = 1, WorkGang* pretouch_gang = NULL); void uncommit_regions(uint index, size_t num_regions = 1); // Notify other data structures about change in the heap layout. @@ -209,12 +210,12 @@ // HeapRegions, or re-use existing ones. Returns the number of regions the // sequence was expanded by. If a HeapRegion allocation fails, the resulting // number of regions might be smaller than what's desired. - uint expand_by(uint num_regions); + uint expand_by(uint num_regions, WorkGang* pretouch_workers = NULL); // Makes sure that the regions from start to start+num_regions-1 are available // for allocation. Returns the number of regions that were committed to achieve // this. - uint expand_at(uint start, uint num_regions); + uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers = NULL); // Find a contiguous set of empty regions of length num. Returns the start index of // that set, or G1_NO_HRM_INDEX.
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -304,9 +304,6 @@ inline static oop array_allocate_nozero(KlassHandle klass, int size, int length, TRAPS); inline static oop class_allocate(KlassHandle klass, int size, TRAPS); - inline static void post_allocation_install_obj_klass(KlassHandle klass, - oop obj); - // Raw memory allocation facilities // The obj and array allocate methods are covers for these methods. // mem_allocate() should never be
--- a/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.inline.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -41,14 +41,22 @@ // Inline allocation implementations. void CollectedHeap::post_allocation_setup_common(KlassHandle klass, - HeapWord* obj) { - post_allocation_setup_no_klass_install(klass, obj); - post_allocation_install_obj_klass(klass, oop(obj)); + HeapWord* obj_ptr) { + post_allocation_setup_no_klass_install(klass, obj_ptr); + oop obj = (oop)obj_ptr; +#if ! INCLUDE_ALL_GCS + obj->set_klass(klass()); +#else + // Need a release store to ensure array/class length, mark word, and + // object zeroing are visible before setting the klass non-NULL, for + // concurrent collectors. + obj->release_set_klass(klass()); +#endif } void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass, - HeapWord* objPtr) { - oop obj = (oop)objPtr; + HeapWord* obj_ptr) { + oop obj = (oop)obj_ptr; assert(obj != NULL, "NULL object pointer"); if (UseBiasedLocking && (klass() != NULL)) { @@ -59,18 +67,6 @@ } } -void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass, - oop obj) { - // These asserts are kind of complicated because of klassKlass - // and the beginning of the world. - assert(klass() != NULL || !Universe::is_fully_initialized(), "NULL klass"); - assert(klass() == NULL || klass()->is_klass(), "not a klass"); - assert(obj != NULL, "NULL object pointer"); - obj->set_klass(klass()); - assert(!Universe::is_fully_initialized() || obj->klass() != NULL, - "missing klass"); -} - // Support for jvmti and dtrace inline void post_allocation_notify(KlassHandle klass, oop obj, int size) { // support low memory notifications (no-op if not enabled) @@ -88,25 +84,26 @@ } void CollectedHeap::post_allocation_setup_obj(KlassHandle klass, - HeapWord* obj, + HeapWord* obj_ptr, int size) { - post_allocation_setup_common(klass, obj); + post_allocation_setup_common(klass, obj_ptr); + oop obj = (oop)obj_ptr; assert(Universe::is_bootstrapping() || - !((oop)obj)->is_array(), "must not be an array"); + !obj->is_array(), "must not be an array"); // notify jvmti and dtrace - post_allocation_notify(klass, (oop)obj, size); + post_allocation_notify(klass, obj, size); } void CollectedHeap::post_allocation_setup_class(KlassHandle klass, - HeapWord* obj, + HeapWord* obj_ptr, int size) { - // Set oop_size field before setting the _klass field - // in post_allocation_setup_common() because the klass field - // indicates that the object is parsable by concurrent GC. - oop new_cls = (oop)obj; + // Set oop_size field before setting the _klass field because a + // non-NULL _klass field indicates that the object is parsable by + // concurrent GC. + oop new_cls = (oop)obj_ptr; assert(size > 0, "oop_size must be positive."); java_lang_Class::set_oop_size(new_cls, size); - post_allocation_setup_common(klass, obj); + post_allocation_setup_common(klass, obj_ptr); assert(Universe::is_bootstrapping() || !new_cls->is_array(), "must not be an array"); // notify jvmti and dtrace @@ -114,15 +111,15 @@ } void CollectedHeap::post_allocation_setup_array(KlassHandle klass, - HeapWord* obj, + HeapWord* obj_ptr, int length) { - // Set array length before setting the _klass field - // in post_allocation_setup_common() because the klass field - // indicates that the object is parsable by concurrent GC. + // Set array length before setting the _klass field because a + // non-NULL klass field indicates that the object is parsable by + // concurrent GC. assert(length >= 0, "length should be non-negative"); - ((arrayOop)obj)->set_length(length); - post_allocation_setup_common(klass, obj); - oop new_obj = (oop)obj; + ((arrayOop)obj_ptr)->set_length(length); + post_allocation_setup_common(klass, obj_ptr); + oop new_obj = (oop)obj_ptr; assert(new_obj->is_array(), "must be an array"); // notify jvmti and dtrace (must be after length is set for dtrace) post_allocation_notify(klass, new_obj, new_obj->size());
--- a/hotspot/src/share/vm/gc/shared/workgroup.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/shared/workgroup.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -62,7 +62,12 @@ AbstractGangTask(const char* name) : _name(name), _gc_id(GCId::current_raw()) - {} + {} + + AbstractGangTask(const char* name, const uint gc_id) : + _name(name), + _gc_id(gc_id) + {} // The abstract work method. // The argument tells you which member of the gang you are.
--- a/hotspot/src/share/vm/memory/allocation.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/allocation.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -738,6 +738,7 @@ static size_t size_for(size_t length); public: + static E* allocate_or_null(size_t length); static E* allocate(size_t length); static void free(E* addr, size_t length); };
--- a/hotspot/src/share/vm/memory/allocation.inline.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/allocation.inline.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -153,6 +153,24 @@ } template <class E, MEMFLAGS F> +E* MmapArrayAllocator<E, F>::allocate_or_null(size_t length) { + size_t size = size_for(length); + int alignment = os::vm_allocation_granularity(); + + char* addr = os::reserve_memory(size, NULL, alignment, F); + if (addr == NULL) { + return NULL; + } + + if (os::commit_memory(addr, size, !ExecMem, "Allocator (commit)")) { + return (E*)addr; + } else { + os::release_memory(addr, size); + return NULL; + } +} + +template <class E, MEMFLAGS F> E* MmapArrayAllocator<E, F>::allocate(size_t length) { size_t size = size_for(length); int alignment = os::vm_allocation_granularity();
--- a/hotspot/src/share/vm/memory/filemap.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/filemap.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -649,7 +649,7 @@ // Memory map a region in the address space. static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode", - "String1", "String2" }; + "String1", "String2", "OptionalData" }; char* FileMapInfo::map_region(int i) { assert(!MetaspaceShared::is_string_region(i), "sanity");
--- a/hotspot/src/share/vm/memory/filemap.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/filemap.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -252,10 +252,27 @@ bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false); void print_shared_spaces() NOT_CDS_RETURN; + // The ro+rw+md+mc spaces size + static size_t core_spaces_size() { + return align_size_up((SharedReadOnlySize + SharedReadWriteSize + + SharedMiscDataSize + SharedMiscCodeSize), + os::vm_allocation_granularity()); + } + + // The estimated optional space size. + // + // Currently the optional space only has archived class bytes. + // The core_spaces_size is the size of all class metadata, which is a good + // estimate of the total class bytes to be archived. Only the portion + // containing data is written out to the archive and mapped at runtime. + // There is no memory waste due to unused portion in optional space. + static size_t optional_space_size() { + return core_spaces_size(); + } + + // Total shared_spaces size includes the ro, rw, md, mc and od spaces static size_t shared_spaces_size() { - return align_size_up(SharedReadOnlySize + SharedReadWriteSize + - SharedMiscDataSize + SharedMiscCodeSize, - os::vm_allocation_granularity()); + return core_spaces_size() + optional_space_size(); } // Stop CDS sharing and unmap CDS regions.
--- a/hotspot/src/share/vm/memory/metaspace.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspace.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -3172,36 +3172,28 @@ address cds_address = NULL; FileMapInfo* mapinfo = new FileMapInfo(); - if (JvmtiExport::should_post_class_file_load_hook()) { - // Currently CDS does not support JVMTI CFLH when loading shared class. - // If JvmtiExport::should_post_class_file_load_hook is already enabled, - // just disable UseSharedSpaces. - FileMapInfo::fail_continue("Tool agent requires sharing to be disabled."); - delete mapinfo; - } else { - // Open the shared archive file, read and validate the header. If - // initialization fails, shared spaces [UseSharedSpaces] are - // disabled and the file is closed. - // Map in spaces now also - if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { - cds_total = FileMapInfo::shared_spaces_size(); - cds_address = (address)mapinfo->header()->region_addr(0); + // Open the shared archive file, read and validate the header. If + // initialization fails, shared spaces [UseSharedSpaces] are + // disabled and the file is closed. + // Map in spaces now also + if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { + cds_total = FileMapInfo::shared_spaces_size(); + cds_address = (address)mapinfo->header()->region_addr(0); #ifdef _LP64 - if (using_class_space()) { - char* cds_end = (char*)(cds_address + cds_total); - cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment); - // If UseCompressedClassPointers is set then allocate the metaspace area - // above the heap and above the CDS area (if it exists). - allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address); - // Map the shared string space after compressed pointers - // because it relies on compressed class pointers setting to work - mapinfo->map_string_regions(); - } + if (using_class_space()) { + char* cds_end = (char*)(cds_address + cds_total); + cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment); + // If UseCompressedClassPointers is set then allocate the metaspace area + // above the heap and above the CDS area (if it exists). + allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address); + // Map the shared string space after compressed pointers + // because it relies on compressed class pointers setting to work + mapinfo->map_string_regions(); + } #endif // _LP64 - } else { - assert(!mapinfo->is_open() && !UseSharedSpaces, - "archive file not closed or shared spaces not disabled."); - } + } else { + assert(!mapinfo->is_open() && !UseSharedSpaces, + "archive file not closed or shared spaces not disabled."); } } #endif // INCLUDE_CDS
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -65,6 +65,7 @@ size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0; SharedMiscRegion MetaspaceShared::_mc; SharedMiscRegion MetaspaceShared::_md; +SharedMiscRegion MetaspaceShared::_od; void SharedMiscRegion::initialize(ReservedSpace rs, size_t committed_byte_size, SharedSpaceType space_type) { _vs.initialize(rs, committed_byte_size); @@ -93,16 +94,24 @@ assert(DumpSharedSpaces, "dump time only"); _shared_rs = rs; - // Split up and initialize the misc code and data spaces + size_t core_spaces_size = FileMapInfo::core_spaces_size(); size_t metadata_size = SharedReadOnlySize + SharedReadWriteSize; - ReservedSpace shared_ro_rw = _shared_rs->first_part(metadata_size); - ReservedSpace misc_section = _shared_rs->last_part(metadata_size); + + // Split into the core and optional sections + ReservedSpace core_data = _shared_rs->first_part(core_spaces_size); + ReservedSpace optional_data = _shared_rs->last_part(core_spaces_size); - // Now split into misc sections. + // The RO/RW and the misc sections + ReservedSpace shared_ro_rw = core_data.first_part(metadata_size); + ReservedSpace misc_section = core_data.last_part(metadata_size); + + // Now split the misc code and misc data sections. ReservedSpace md_rs = misc_section.first_part(SharedMiscDataSize); ReservedSpace mc_rs = misc_section.last_part(SharedMiscDataSize); + _md.initialize(md_rs, SharedMiscDataSize, SharedMiscData); - _mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscData); + _mc.initialize(mc_rs, SharedMiscCodeSize, SharedMiscCode); + _od.initialize(optional_data, metadata_size, SharedOptional); } // Read/write a data stream for restoring/preserving metadata pointers and @@ -521,6 +530,7 @@ GrowableArray<Klass*> *_class_promote_order; VirtualSpace _md_vs; VirtualSpace _mc_vs; + VirtualSpace _od_vs; GrowableArray<MemRegion> *_string_regions; public: @@ -598,15 +608,19 @@ remove_unshareable_in_classes(); tty->print_cr("done. "); - // Set up the share data and shared code segments. + // Set up the misc data, misc code and optional data segments. _md_vs = *MetaspaceShared::misc_data_region()->virtual_space(); _mc_vs = *MetaspaceShared::misc_code_region()->virtual_space(); + _od_vs = *MetaspaceShared::optional_data_region()->virtual_space(); char* md_low = _md_vs.low(); char* md_top = MetaspaceShared::misc_data_region()->alloc_top(); char* md_end = _md_vs.high(); char* mc_low = _mc_vs.low(); char* mc_top = MetaspaceShared::misc_code_region()->alloc_top(); char* mc_end = _mc_vs.high(); + char* od_low = _od_vs.low(); + char* od_top = MetaspaceShared::optional_data_region()->alloc_top(); + char* od_end = _od_vs.high(); // Reserve space for the list of Klass*s whose vtables are used // for patching others as needed. @@ -661,28 +675,32 @@ const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType); const size_t md_alloced = md_end-md_low; const size_t mc_alloced = mc_end-mc_low; + const size_t od_alloced = od_end-od_low; const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced - + ss_bytes; + + ss_bytes + od_alloced; // Occupied size of each space. const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType); const size_t rw_bytes = rw_space->used_bytes_slow(Metaspace::NonClassType); const size_t md_bytes = size_t(md_top - md_low); const size_t mc_bytes = size_t(mc_top - mc_low); + const size_t od_bytes = size_t(od_top - od_low); // Percent of total size - const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes; + const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes + od_bytes; const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0; const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0; const double md_t_perc = md_bytes / double(total_bytes) * 100.0; const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0; const double ss_t_perc = ss_bytes / double(total_bytes) * 100.0; + const double od_t_perc = od_bytes / double(total_bytes) * 100.0; // Percent of fullness of each space const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0; const double rw_u_perc = rw_bytes / double(rw_alloced) * 100.0; const double md_u_perc = md_bytes / double(md_alloced) * 100.0; const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0; + const double od_u_perc = od_bytes / double(od_alloced) * 100.0; const double total_u_perc = total_bytes / double(total_alloced) * 100.0; #define fmt_space "%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT @@ -691,6 +709,7 @@ tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, p2i(md_low)); tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, p2i(mc_low)); tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, p2i(ss_low)); + tty->print_cr(fmt_space, "od", od_bytes, od_t_perc, od_alloced, od_u_perc, p2i(od_low)); tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]", total_bytes, total_alloced, total_u_perc); @@ -734,6 +753,10 @@ SharedMiscCodeSize, true, true); mapinfo->write_string_regions(_string_regions); + mapinfo->write_region(MetaspaceShared::od, _od_vs.low(), + pointer_delta(od_top, _od_vs.low(), sizeof(char)), + pointer_delta(od_end, _od_vs.low(), sizeof(char)), + true, false); } mapinfo->close(); @@ -1049,8 +1072,6 @@ // Map shared spaces at requested addresses and return if succeeded. -// Need to keep the bounds of the ro and rw space for the Metaspace::contains -// call, or is_in_shared_space. bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { size_t image_alignment = mapinfo->alignment(); @@ -1068,6 +1089,7 @@ char* _rw_base = NULL; char* _md_base = NULL; char* _mc_base = NULL; + char* _od_base = NULL; // Map each shared region if ((_ro_base = mapinfo->map_region(ro)) != NULL && @@ -1078,6 +1100,8 @@ mapinfo->verify_region_checksum(md) && (_mc_base = mapinfo->map_region(mc)) != NULL && mapinfo->verify_region_checksum(mc) && + (_od_base = mapinfo->map_region(od)) != NULL && + mapinfo->verify_region_checksum(od) && (image_alignment == (size_t)max_alignment()) && mapinfo->validate_classpath_entry_table()) { // Success (no need to do anything) @@ -1089,6 +1113,7 @@ if (_rw_base != NULL) mapinfo->unmap_region(rw); if (_md_base != NULL) mapinfo->unmap_region(md); if (_mc_base != NULL) mapinfo->unmap_region(mc); + if (_od_base != NULL) mapinfo->unmap_region(od); #ifndef _WINDOWS // Release the entire mapped region shared_rs.release();
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -132,6 +132,7 @@ // Used only during dumping. static SharedMiscRegion _md; static SharedMiscRegion _mc; + static SharedMiscRegion _od; public: enum { vtbl_list_size = DEFAULT_VTBL_LIST_SIZE, @@ -148,7 +149,10 @@ max_strings = 2, // max number of string regions in string space num_non_strings = 4, // number of non-string regions first_string = num_non_strings, // index of first string region - n_regions = max_strings + num_non_strings // total number of regions + // The optional data region is the last region. + // Currently it only contains class file data. + od = max_strings + num_non_strings, + n_regions = od + 1 // total number of regions }; // Accessor functions to save shared space created for metadata, which has @@ -222,9 +226,10 @@ static int count_class(const char* classlist_file); static void estimate_regions_size() NOT_CDS_RETURN; - // Allocate a block of memory from the "mc" or "md" regions. + // Allocate a block of memory from the "mc", "md", or "od" regions. static char* misc_code_space_alloc(size_t num_bytes) { return _mc.alloc(num_bytes); } static char* misc_data_space_alloc(size_t num_bytes) { return _md.alloc(num_bytes); } + static char* optional_data_space_alloc(size_t num_bytes) { return _od.alloc(num_bytes); } static address cds_i2i_entry_code_buffers(size_t total_size); @@ -243,5 +248,9 @@ assert(DumpSharedSpaces, "used during dumping only"); return &_md; } + static SharedMiscRegion* optional_data_region() { + assert(DumpSharedSpaces, "used during dumping only"); + return &_od; + } }; #endif // SHARE_VM_MEMORY_METASPACESHARED_HPP
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -41,6 +41,7 @@ #include "memory/heapInspection.hpp" #include "memory/iterator.inline.hpp" #include "memory/metadataFactory.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "oops/fieldStreams.hpp" @@ -1972,11 +1973,6 @@ m->remove_unshareable_info(); } - // cached_class_file might be pointing to a malloc'ed buffer allocated by - // event-based tracing code at CDS dump time. It's not usable at runtime - // so let's clear it. - set_cached_class_file(NULL); - // do array classes also. array_klasses_do(remove_unshareable_in_class); } @@ -2070,6 +2066,7 @@ } void InstanceKlass::release_C_heap_structures() { + assert(!this->is_shared(), "should not be called for a shared class"); // Can't release the constant pool here because the constant pool can be // deallocated separately from the InstanceKlass for default methods and @@ -2250,8 +2247,8 @@ // the java.base module. If a non-java.base package is erroneously placed // in the java.base module it will be caught later when java.base // is defined by ModuleEntryTable::verify_javabase_packages check. - assert(ModuleEntryTable::javabase_module() != NULL, "java.base module is NULL"); - _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_module()); + assert(ModuleEntryTable::javabase_moduleEntry() != NULL, "java.base module is NULL"); + _package_entry = loader_data->packages()->lookup(pkg_name, ModuleEntryTable::javabase_moduleEntry()); } else { assert(loader_data->modules()->unnamed_module() != NULL, "unnamed module is NULL"); _package_entry = loader_data->packages()->lookup(pkg_name, @@ -3653,6 +3650,15 @@ } #if INCLUDE_JVMTI +JvmtiCachedClassFileData* InstanceKlass::get_cached_class_file() { + if (MetaspaceShared::is_in_shared_space(_cached_class_file)) { + // Ignore the archived class stream data + return NULL; + } else { + return _cached_class_file; + } +} + jint InstanceKlass::get_cached_class_file_len() { return VM_RedefineClasses::get_cached_class_file_len(_cached_class_file); } @@ -3660,4 +3666,15 @@ unsigned char * InstanceKlass::get_cached_class_file_bytes() { return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file); } + +#if INCLUDE_CDS +JvmtiCachedClassFileData* InstanceKlass::get_archived_class_data() { + assert(this->is_shared(), "class should be shared"); + if (MetaspaceShared::is_in_shared_space(_cached_class_file)) { + return _cached_class_file; + } else { + return NULL; + } +} #endif +#endif
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -783,7 +783,7 @@ void set_cached_class_file(JvmtiCachedClassFileData *data) { _cached_class_file = data; } - JvmtiCachedClassFileData * get_cached_class_file() { return _cached_class_file; } + JvmtiCachedClassFileData * get_cached_class_file(); jint get_cached_class_file_len(); unsigned char * get_cached_class_file_bytes(); @@ -795,6 +795,13 @@ return _jvmti_cached_class_field_map; } +#if INCLUDE_CDS + void set_archived_class_data(JvmtiCachedClassFileData* data) { + _cached_class_file = data; + } + + JvmtiCachedClassFileData * get_archived_class_data(); +#endif // INCLUDE_CDS #else // INCLUDE_JVMTI static void purge_previous_versions(InstanceKlass* ik) { return; };
--- a/hotspot/src/share/vm/oops/klass.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/klass.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -530,7 +530,7 @@ InstanceKlass* ik = (InstanceKlass*) k; module_entry = ik->module(); } else { - module_entry = ModuleEntryTable::javabase_module(); + module_entry = ModuleEntryTable::javabase_moduleEntry(); } // Obtain java.lang.reflect.Module, if available Handle module_handle(THREAD, ((module_entry != NULL) ? JNIHandles::resolve(module_entry->module()) : (oop)NULL));
--- a/hotspot/src/share/vm/oops/oop.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/oop.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -87,6 +87,7 @@ inline narrowKlass* compressed_klass_addr(); inline void set_klass(Klass* k); + inline void release_set_klass(Klass* k); // For klass field compression inline int klass_gap() const;
--- a/hotspot/src/share/vm/oops/oop.inline.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/oop.inline.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -129,10 +129,14 @@ return &_metadata._compressed_klass; } +#define CHECK_SET_KLASS(k) \ + do { \ + assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass"); \ + assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass"); \ + } while (0) + void oopDesc::set_klass(Klass* k) { - // since klasses are promoted no store check is needed - assert(Universe::is_bootstrapping() || k != NULL, "must be a real Klass*"); - assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass*"); + CHECK_SET_KLASS(k); if (UseCompressedClassPointers) { *compressed_klass_addr() = Klass::encode_klass_not_null(k); } else { @@ -140,6 +144,18 @@ } } +void oopDesc::release_set_klass(Klass* k) { + CHECK_SET_KLASS(k); + if (UseCompressedClassPointers) { + OrderAccess::release_store(compressed_klass_addr(), + Klass::encode_klass_not_null(k)); + } else { + OrderAccess::release_store_ptr(klass_addr(), k); + } +} + +#undef CHECK_SET_KLASS + int oopDesc::klass_gap() const { return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes()); }
--- a/hotspot/src/share/vm/oops/typeArrayKlass.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -72,7 +72,7 @@ null_loader_data->add_class(ak); // Call complete_create_array_klass after all instance variables have been initialized. - complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_module(), CHECK_NULL); + complete_create_array_klass(ak, ak->super(), ModuleEntryTable::javabase_moduleEntry(), CHECK_NULL); return ak; } @@ -347,7 +347,7 @@ // A TypeArrayKlass is an array of a primitive type, its defining module is java.base ModuleEntry* TypeArrayKlass::module() const { - return ModuleEntryTable::javabase_module(); + return ModuleEntryTable::javabase_moduleEntry(); } PackageEntry* TypeArrayKlass::package() const {
--- a/hotspot/src/share/vm/prims/unsafe.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/prims/unsafe.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -272,6 +272,31 @@ // Get/PutObject must be special-cased, since it works with handles. +// We could be accessing the referent field in a reference +// object. If G1 is enabled then we need to register non-null +// referent with the SATB barrier. + +#if INCLUDE_ALL_GCS +static bool is_java_lang_ref_Reference_access(oop o, jlong offset) { + if (offset == java_lang_ref_Reference::referent_offset && o != NULL) { + Klass* k = o->klass(); + if (InstanceKlass::cast(k)->reference_type() != REF_NONE) { + assert(InstanceKlass::cast(k)->is_subclass_of(SystemDictionary::Reference_klass()), "sanity"); + return true; + } + } + return false; +} +#endif + +static void ensure_satb_referent_alive(oop o, jlong offset, oop v) { +#if INCLUDE_ALL_GCS + if (UseG1GC && v != NULL && is_java_lang_ref_Reference_access(o, offset)) { + G1SATBCardTableModRefBS::enqueue(v); + } +#endif +} + // These functions allow a null base pointer with an arbitrary address. // But if the base pointer is non-null, the offset should make some sense. // That is, it should be in the range [0, MAX_OBJECT_SIZE]. @@ -286,34 +311,9 @@ v = *(oop*)index_oop_from_field_offset_long(p, offset); } - jobject ret = JNIHandles::make_local(env, v); - -#if INCLUDE_ALL_GCS - // We could be accessing the referent field in a reference - // object. If G1 is enabled then we need to register non-null - // referent with the SATB barrier. - if (UseG1GC) { - bool needs_barrier = false; + ensure_satb_referent_alive(p, offset, v); - if (ret != NULL) { - if (offset == java_lang_ref_Reference::referent_offset && obj != NULL) { - oop o = JNIHandles::resolve(obj); - Klass* k = o->klass(); - if (InstanceKlass::cast(k)->reference_type() != REF_NONE) { - assert(InstanceKlass::cast(k)->is_subclass_of(SystemDictionary::Reference_klass()), "sanity"); - needs_barrier = true; - } - } - } - - if (needs_barrier) { - oop referent = JNIHandles::resolve(ret); - G1SATBCardTableModRefBS::enqueue(referent); - } - } -#endif // INCLUDE_ALL_GCS - - return ret; + return JNIHandles::make_local(env, v); } UNSAFE_END UNSAFE_ENTRY(void, Unsafe_PutObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) { @@ -344,6 +344,8 @@ (void)const_cast<oop&>(v = *(volatile oop*) addr); } + ensure_satb_referent_alive(p, offset, v); + OrderAccess::acquire(); return JNIHandles::make_local(env, v); } UNSAFE_END
--- a/hotspot/src/share/vm/runtime/globals.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -1596,6 +1596,10 @@ product(bool, AlwaysPreTouch, false, \ "Force all freshly committed pages to be pre-touched") \ \ + product(size_t, PreTouchParallelChunkSize, 1 * G, \ + "Per-thread chunk size for parallel memory pre-touch.") \ + range(1, SIZE_MAX / 2) \ + \ product_pd(size_t, CMSYoungGenPerWorker, \ "The maximum size of young gen chosen by default per GC worker " \ "thread available") \
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -77,6 +77,8 @@ Mutex* DirtyCardQ_FL_lock = NULL; Monitor* DirtyCardQ_CBL_mon = NULL; Mutex* Shared_DirtyCardQ_lock = NULL; +Mutex* MarkStackFreeList_lock = NULL; +Mutex* MarkStackChunkList_lock = NULL; Mutex* ParGCRareEvent_lock = NULL; Mutex* DerivedPointerTableGC_lock = NULL; Mutex* Compile_lock = NULL; @@ -194,6 +196,9 @@ def(StringDedupQueue_lock , Monitor, leaf, true, Monitor::_safepoint_check_never); def(StringDedupTable_lock , Mutex , leaf, true, Monitor::_safepoint_check_never); + + def(MarkStackFreeList_lock , Mutex , leaf , true, Monitor::_safepoint_check_never); + def(MarkStackChunkList_lock , Mutex , leaf , true, Monitor::_safepoint_check_never); } def(ParGCRareEvent_lock , Mutex , leaf , true, Monitor::_safepoint_check_sometimes); def(DerivedPointerTableGC_lock , Mutex, leaf, true, Monitor::_safepoint_check_never);
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -81,7 +81,8 @@ extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card // queue shared by // non-Java threads. - // (see option ExplicitGCInvokesConcurrent) +extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list. +extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list. extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops. extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc) extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued
--- a/hotspot/src/share/vm/runtime/os.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/os.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -1705,8 +1705,8 @@ return res; } -void os::pretouch_memory(void* start, void* end) { - for (volatile char *p = (char*)start; p < (char*)end; p += os::vm_page_size()) { +void os::pretouch_memory(void* start, void* end, size_t page_size) { + for (volatile char *p = (char*)start; p < (char*)end; p += page_size) { *p = 0; } }
--- a/hotspot/src/share/vm/runtime/os.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/os.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -324,7 +324,7 @@ // to make the OS back the memory range with actual memory. // Current implementation may not touch the last page if unaligned addresses // are passed. - static void pretouch_memory(void* start, void* end); + static void pretouch_memory(void* start, void* end, size_t page_size = vm_page_size()); enum ProtType { MEM_PROT_NONE, MEM_PROT_READ, MEM_PROT_RW, MEM_PROT_RWX }; static bool protect_memory(char* addr, size_t bytes, ProtType prot,
--- a/hotspot/src/share/vm/utilities/debug.cpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/utilities/debug.cpp Wed Oct 05 06:28:22 2016 -0700 @@ -282,6 +282,12 @@ } void report_out_of_shared_space(SharedSpaceType shared_space) { + if (shared_space == SharedOptional) { + // The estimated shared_optional_space size is large enough + // for all class bytes. It should not run out of space. + ShouldNotReachHere(); + } + static const char* name[] = { "shared read only space", "shared read write space",
--- a/hotspot/src/share/vm/utilities/debug.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/utilities/debug.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -271,7 +271,8 @@ SharedReadOnly, SharedReadWrite, SharedMiscData, - SharedMiscCode + SharedMiscCode, + SharedOptional }; void report_out_of_shared_space(SharedSpaceType space_type);
--- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -79,8 +79,8 @@ template <MEMFLAGS F> inline void HashtableBucket<F>::set_entry(BasicHashtableEntry<F>* l) { - // Warning: Preserve store ordering. The SystemDictionary is read - // without locks. The new SystemDictionaryEntry must be + // Warning: Preserve store ordering. The PackageEntryTable, ModuleEntryTable and + // SystemDictionary are read without locks. The new entry must be // complete before other threads can be allowed to see it // via a store to _buckets[index]. OrderAccess::release_store_ptr(&_entry, l); @@ -88,8 +88,8 @@ template <MEMFLAGS F> inline BasicHashtableEntry<F>* HashtableBucket<F>::get_entry() const { - // Warning: Preserve load ordering. The SystemDictionary is read - // without locks. The new SystemDictionaryEntry must be + // Warning: Preserve load ordering. The PackageEntryTable, ModuleEntryTable and + // SystemDictionary are read without locks. The new entry must be // complete before other threads can be allowed to see it // via a store to _buckets[index]. return (BasicHashtableEntry<F>*) OrderAccess::load_ptr_acquire(&_entry);
--- a/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Fri Sep 30 02:52:36 2016 -0700 +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java Wed Oct 05 06:28:22 2016 -0700 @@ -110,10 +110,6 @@ excludeTestMaxRange("OldSize"); excludeTestMaxRange("ParallelGCThreads"); - excludeTestMaxRange("CompilerThreadStackSize"); - excludeTestMaxRange("ThreadStackSize"); - excludeTestMaxRange("VMThreadStackSize"); - /* * Remove parameters controlling the code cache. As these * parameters have implications on the physical memory
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/CDSTestUtils.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import jdk.test.lib.process.OutputAnalyzer; + + +// This class contains common test utilities for CDS testing +public class CDSTestUtils { + + // check result of 'dump' operation + public static void checkDump(OutputAnalyzer output, String... extraMatches) + throws Exception { + + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + + for (String match : extraMatches) { + output.shouldContain(match); + } + } + + + // check the output for indication that mapping of the archive failed + public static boolean isUnableToMap(OutputAnalyzer output) { + String outStr = output.getOutput(); + if ((output.getExitValue() == 1) && ( + outStr.contains("Unable to reserve shared space at required address") || + outStr.contains("Unable to map ReadOnly shared space at required address") || + outStr.contains("Unable to map ReadWrite shared space at required address") || + outStr.contains("Unable to map MiscData shared space at required address") || + outStr.contains("Unable to map MiscCode shared space at required address") || + outStr.contains("Unable to map shared string space at required address") || + outStr.contains("Could not allocate metaspace at a compatible address") || + outStr.contains("Unable to allocate shared string space: range is not within java heap") )) + { + return true; + } + + return false; + } + + // check result of 'exec' operation, that is when JVM is run using the archive + public static void checkExec(OutputAnalyzer output, String... extraMatches) throws Exception { + if (isUnableToMap(output)) { + System.out.println("Unable to map shared archive: test did not complete; assumed PASS"); + return; + } + output.shouldContain("sharing"); + output.shouldHaveExitValue(0); + + for (String match : extraMatches) { + output.shouldContain(match); + } + } + + + // get the file object for the test artifact + private static File getTestArtifactFile(String prefix, String name) { + File dir = new File(System.getProperty("test.classes", ".")); + return new File(dir, prefix + name); + } + + + // create file containing the specified class list + public static File makeClassList(String testCaseName, String classes[]) + throws Exception { + + File classList = getTestArtifactFile(testCaseName, "test.classlist"); + FileOutputStream fos = new FileOutputStream(classList); + PrintStream ps = new PrintStream(fos); + + addToClassList(ps, classes); + + ps.close(); + fos.close(); + + return classList; + } + + + private static void addToClassList(PrintStream ps, String classes[]) + throws IOException + { + if (classes != null) { + for (String s : classes) { + ps.println(s); + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Implementor.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class Implementor implements Interface { + public static void main(String[] args) { + System.out.println("Implementor: entering main()"); + test(); + } + + public static void test() { + // from interface + (new Implementor()).printString(); + // from implementor + System.out.println(TransformUtil.ChildCheckPattern + + TransformUtil.BeforePattern); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/Interface.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public interface Interface { + public static final String stringToBeTransformed = + TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern; + + default void printString() { + System.out.println(stringToBeTransformed); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SubClass.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class SubClass extends SuperClazz { + public static void main(String[] args) { + System.out.println("SubClass: entering main()"); + test(); + } + + public static void test() { + // The line below will be used to check for successful class transformation + System.out.println(TransformUtil.ChildCheckPattern + + TransformUtil.BeforePattern); + (new SubClass()).callParent(); + } + + private void callParent() { + super.testParent(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/SuperClazz.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +public class SuperClazz { + public static void testParent() { + System.out.println("SuperClazz: entering testParent()"); + + // The line below will be used to check for successful class transformation + System.out.println(TransformUtil.ParentCheckPattern + TransformUtil.BeforePattern); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TestEntry.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +// Test Entry - a single entry in a test table +// that defines a test case +// See TransformRelatedClasses.java for more details +public class TestEntry { + int testCaseId; + boolean transformParent; + boolean transformChild; + boolean isParentExpectedShared; + boolean isChildExpectedShared; + + public TestEntry(int testCaseId, + boolean transformParent, boolean transformChild, + boolean isParentExpectedShared, boolean isChildExpectedShared) { + this.testCaseId = testCaseId; + this.transformParent = transformParent; + this.transformChild = transformChild; + this.isParentExpectedShared = isParentExpectedShared; + this.isChildExpectedShared = isChildExpectedShared; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Exercise initial transformation (ClassFileLoadHook) + * with CDS with Interface/Implementor pair + * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * java.instrument + * @build TransformUtil TransformerAgent Interface Implementor + * @run main/othervm TransformRelatedClasses Interface Implementor + */ + +// Clarification on @requires declarations: +// CDS is not supported w/o the use of Compressed OOPs +// JVMTI's ClassFileLoadHook is not supported under minimal VM + +// This test class uses TransformRelatedClasses to do its work. +// The goal of this test is to exercise transformation of related interface +// and its implementor in combination with CDS. +// The transformation is done via ClassFileLoadHook mechanism. +// Both superclass and subclass reside in the shared archive. +// The test consists of 4 test cases where transformation is applied +// to an interface and an implementor in a combinatorial manner. +// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformRelatedClasses.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,179 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +// This is the main test class for testing transformation of related classes +// in combination with CDS, to ensure these features work well together. +// The relationships that can be tested using this test class are: +// superclass/subclass, and interface/implementor relationships. +// +// The test uses combinatorial approach. +// For details on test table and test cases see main() method in this class. +// +// This test consists of multiple classes for better flexibility and reuse, +// and also relies on certain common utility code. +// Here are the details on the structure of the test +// +// Structure of the test: +// TransformRelatedClasses -- common main test driver +// The TransformRelatedClasses is invoked from test driver classes: +// TransformInterfaceAndImplementor, TransformSuperAndSubClasses +// It is responsible for preparing test artifacts (test jar, agent jar +// and the shared archive), running test cases and checking the results. +// The following test classes below are launched in a sub-process with use +// of shared archive: +// SuperClazz, SubClass -- super/sub class pair under test +// Interface, Implementor -- classes under test +// This test will transform these classes, based on the test case data, +// by changing a predefined unique string in each class. +// For more details, see the test classes' code and comments. +// +// Other related classes: +// TestEntry - a class representing a single test case, as test entry in the table +// TransformTestCommon - common methods for transformation test cases +// +// Other utility/helper classes and files used in this test: +// TransformerAgent - an agent that is used when JVM-under-test is executed +// to transform specific strings inside specified classes +// TransformerAgent.mf - accompanies transformer agent +// CDSTestUtils - Test Utilities common to all CDS tests + +import java.io.File; +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + + +public class TransformRelatedClasses { + static final String archiveName = "./TransformRelatedClasses.jsa"; + static String agentClasses[] = { + "TransformerAgent", + "TransformerAgent$SimpleTransformer", + "TransformUtil" + }; + + String parent; + String child; + String[] testClasses = new String[2]; + String[] testNames = new String[2]; + String testJar; + String agentJar; + + + private static void log(String msg) { + System.out.println("TransformRelatedClasses: " + msg); + } + + + // This class is intended to test 2 parent-child relationships: + // 1. Base Class (parent) and Derived Class (child) + // 2. Interface (parent) and Implementor (child) + // Parameters to main(): parent, child + public static void main(String args[]) throws Exception { + TransformRelatedClasses test = new TransformRelatedClasses(args[0], args[1]); + test.prepare(); + + // Test Table + // TestEntry: (testCaseId, transformParent, tranformChild, + // isParentExpectedShared, isChildExpectedShared) + ArrayList<TestEntry> testTable = new ArrayList<>(); + + // base case - no tranformation - all expected to be shared + testTable.add(new TestEntry(0, false, false, true, true)); + + // transform parent only - both parent and child should not be shared + testTable.add(new TestEntry(1, true, false, false, false)); + + // transform parent and child - both parent and child should not be shared + testTable.add(new TestEntry(2, true, true, false, false)); + + // transform child only - parent should still be shared, but not child + testTable.add(new TestEntry(3, false, true, true, false)); + + // run the tests + for (TestEntry entry : testTable) { + test.runTest(entry); + } + } + + + public TransformRelatedClasses(String parent, String child) { + log("Constructor: parent = " + parent + ", child = " + child); + this.parent = parent; + this.child = child; + testClasses[0] = parent; + testClasses[1] = child; + testNames[0] = parent.replace('.', '/'); + testNames[1] = child.replace('.', '/'); + } + + + // same test jar and archive can be used for all test cases + private void prepare() throws Exception { + // create agent jar + // Agent is the same for all test cases + String pathToManifest = "../../../../testlibrary/jvmti/TransformerAgent.mf"; + agentJar = ClassFileInstaller.writeJar("TransformerAgent.jar", + ClassFileInstaller.Manifest.fromSourceFile(pathToManifest), + agentClasses); + + // create a test jar + testJar = + ClassFileInstaller.writeJar(parent + "-" + child + ".jar", + testClasses); + + // create an archive + File classList = CDSTestUtils.makeClassList("transform-" + parent, + testNames); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + "-Xbootclasspath/a:" + testJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ExtraSharedClassListFile=" + + classList.getPath(), + "-XX:SharedArchiveFile=" + archiveName, + "-XX:+PrintSharedSpaces", + "-Xshare:dump"); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + CDSTestUtils.checkDump(out); + } + + + private void runTest(TestEntry entry) throws Exception { + log("runTest(): testCaseId = " + entry.testCaseId); + + // execute with archive + String agentParam = "-javaagent:" + agentJar + "=" + + TransformTestCommon.getAgentParams(entry, parent, child); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + "-Xbootclasspath/a:" + testJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=" + archiveName, + "-Xlog:class+load=info", + "-Xshare:on", "-showversion", + agentParam, child); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + + TransformTestCommon.checkResults(entry, out, parent, child); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @summary Exercise initial transformation (ClassFileLoadHook) + * with CDS with SubClass and SuperClass + * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * java.instrument + * @build TransformUtil TransformerAgent SubClass SuperClazz + * @run main/othervm TransformRelatedClasses SuperClazz SubClass +*/ + +// Clarification on @requires declarations: +// CDS is not supported w/o the use of Compressed OOPs +// JVMTI's ClassFileLoadHook is not supported under minimal VM + +// This test class uses TransformRelatedClasses to do its work. +// The goal of this test is to exercise transformation of related superclass +// and subclass in combination with CDS. +// The transformation is done via ClassFileLoadHook mechanism. +// Both superclass and subclass reside in the shared archive. +// The test consists of 4 test cases where transformation is applied +// to a parent and child in combinatorial manner. +// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @summary Exercise initial transformation (ClassFileLoadHook) + * with CDS with SubClass and SuperClass, each lives in own separate package + * @library /test/lib /runtime/SharedArchiveFile /testlibrary/jvmti + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * java.instrument + * @build TransformUtil TransformerAgent SubClass SuperClazz + * @compile myPkg2/SubClass.java myPkg1/SuperClazz.java + * @run main/othervm TransformRelatedClasses myPkg1.SuperClazz myPkg2.SubClass +*/ + +// Clarification on @requires declarations: +// CDS is not supported w/o the use of Compressed OOPs +// JVMTI's ClassFileLoadHook is not supported under minimal VM + +// This test class uses TransformRelatedClasses to do its work. +// The goal of this test is to exercise transformation of related superclass +// and subclass in combination with CDS; each class lives in its own package. +// The transformation is done via ClassFileLoadHook mechanism. +// Both superclass and subclass reside in the shared archive. +// The test consists of 4 test cases where transformation is applied +// to a parent and child in combinatorial manner. +// Please see TransformRelatedClasses.java for details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.OutputAnalyzer; + + +// This class contains methods common to all transformation test cases +public class TransformTestCommon { + + // get parameters to an agent depending on the test case + // these parameters will instruct the agent which classes should be + // transformed + public static String getAgentParams(TestEntry entry, + String parent, String child) { + + if (entry.transformParent && entry.transformChild) + return parent + "," + child; + if (entry.transformParent) + return parent; + if (entry.transformChild) + return child; + + return ""; + } + + + private static void checkTransformationResults(TestEntry entry, + OutputAnalyzer out) + throws Exception { + + if (entry.transformParent) + out.shouldContain(TransformUtil.ParentCheckPattern + + TransformUtil.AfterPattern); + + if (entry.transformChild) + out.shouldContain(TransformUtil.ChildCheckPattern + + TransformUtil.AfterPattern); + } + + + private static void checkSharingByClass(TestEntry entry, OutputAnalyzer out, + String parent, String child) + throws Exception { + + String parentSharedMatch = parent + " source: shared objects file"; + String childSharedMatch = child + " source: shared objects file"; + + if (entry.isParentExpectedShared) + out.shouldContain(parentSharedMatch); + else + out.shouldNotContain(parentSharedMatch); + + if (entry.isChildExpectedShared) + out.shouldContain(childSharedMatch); + else + out.shouldNotContain(childSharedMatch); + } + + + // Both parent and child classes should be passed to ClassFileTransformer.transform() + // exactly once. + private static void checkTransformationCounts(TestEntry entry, OutputAnalyzer out, + String parent, String child) + throws Exception { + + String patternBase = "TransformerAgent: SimpleTransformer called for: "; + + out.shouldContain(patternBase + child + "@1"); + out.shouldContain(patternBase + parent + "@1"); + + out.shouldNotContain(patternBase + child + "@2"); + out.shouldNotContain(patternBase + parent + "@2"); + } + + + public static void checkResults(TestEntry entry, OutputAnalyzer out, + String parent, String child) + throws Exception { + + // If we were not able to map an archive, + // then do not perform other checks, since + // there was no sharing at all + if (CDSTestUtils.isUnableToMap(out)) + return; + + String childVmName = child.replace('.', '/'); + String parentVmName = parent.replace('.', '/'); + + CDSTestUtils.checkExec(out); + checkTransformationCounts(entry, out, parentVmName, childVmName); + checkTransformationResults(entry, out); + checkSharingByClass(entry, out, parent, child); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg1/SuperClazz.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package myPkg1; + +public class SuperClazz { + public static void testParent() { + System.out.println("SuperClazz: entering testParent()"); + + // The line below will be used to check for successful class transformation + System.out.println("parent-transform-check: this-should-be-transformed"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/myPkg2/SubClass.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package myPkg2; + +import myPkg1.SuperClazz; + +public class SubClass extends SuperClazz { + public static void main(String[] args) { + System.out.println("SubClass: entering main()"); + test(); + } + + public static void test() { + // The line below will be used to check for successful class transformation + System.out.println("child-transform-check: this-should-be-transformed"); + (new SubClass()).callParent(); + + // Get the system packages, which should contain myPkg1 and myPkag2 + Package[] pkgs = Package.getPackages(); + for (int i = 0; i < pkgs.length; i++) { + if (pkgs[i].getName().equals("myPkg1")) { + for (int j = 0; j < pkgs.length; j++) { + if (pkgs[j].getName().equals("myPkg2")) { + return; // found myPkg1 & myPkg1 + } + } + } + } + throw new RuntimeException("Missing system package"); + } + + private void callParent() { + super.testParent(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformUtil.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +public class TransformUtil { + public static final String BeforePattern = "this-should-be-transformed"; + public static final String AfterPattern = "this-has-been--transformed"; + public static final String ParentCheckPattern = "parent-transform-check: "; + public static final String ChildCheckPattern = "child-transform-check: "; + + /** + * @return the number of occurrences of the <code>from</code> string that + * have been replaced. + */ + public static int replace(byte buff[], String from, String to) { + if (to.length() != from.length()) { + throw new RuntimeException("bad strings"); + } + byte f[] = asciibytes(from); + byte t[] = asciibytes(to); + byte f0 = f[0]; + + int numReplaced = 0; + int max = buff.length - f.length; + for (int i = 0; i < max; ) { + if (buff[i] == f0 && replace(buff, f, t, i)) { + i += f.length; + numReplaced++; + } else { + i++; + } + } + return numReplaced; + } + + public static boolean replace(byte buff[], byte f[], byte t[], int i) { + for (int x = 0; x < f.length; x++) { + if (buff[x+i] != f[x]) { + return false; + } + } + for (int x = 0; x < f.length; x++) { + buff[x+i] = t[x]; + } + return true; + } + + static byte[] asciibytes(String s) { + byte b[] = new byte[s.length()]; + for (int i = 0; i < b.length; i++) { + b[i] = (byte)s.charAt(i); + } + return b; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.HashMap; + +// This is a test utility class used to transform +// specified classes via initial transformation (ClassFileLoadHook). +// Names of classes to be transformed are supplied as arguments, +// the phrase to be transformed is a hard-coded predefined +// fairly unique phrase. + +public class TransformerAgent { + private static String[] classesToTransform; + + + private static void log(String msg) { + System.out.println("TransformerAgent: " + msg); + } + + + // arguments are comma-separated list of classes to transform + public static void premain(String agentArguments, Instrumentation instrumentation) { + log("premain() is called, arguments = " + agentArguments); + classesToTransform = agentArguments.split(","); + instrumentation.addTransformer(new SimpleTransformer(), /*canRetransform=*/true); + } + + + public static void agentmain(String args, Instrumentation inst) throws Exception { + log("agentmain() is called"); + premain(args, inst); + } + + + static class SimpleTransformer implements ClassFileTransformer { + public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined, + ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException { + + log("SimpleTransformer called for: " + name + "@" + incrCounter(name)); + if (!shouldTransform(name)) + return null; + + log("transforming: class name = " + name); + int nrOfReplacements = TransformUtil.replace(buffer, TransformUtil.BeforePattern, + TransformUtil.AfterPattern); + log("replaced the string, nrOfReplacements = " + nrOfReplacements); + return buffer; + } + + // Check class name pattern, since test should only transform certain classes + private static boolean shouldTransform(String name) { + for (String match : classesToTransform) { + if (name.matches(match)) { + log("shouldTransform: match-found, match = " + match); + return true; + } + } + + return false; + } + } + + + static HashMap<String, Integer> counterMap = new HashMap<>(); + + static Integer incrCounter(String className) { + Integer i = counterMap.get(className); + if (i == null) { + i = new Integer(1); + } else { + i = new Integer(i.intValue() + 1); + } + counterMap.put(className, i); + return i; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/jvmti/TransformerAgent.mf Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Premain-Class: TransformerAgent +Agent-Class: TransformerAgent +Can-Retransform-Classes: true +Can-Redefine-Classes: false
--- a/jaxp/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/jaxp/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -380,3 +380,4 @@ f695240370c77a25fed88225a392e7d530cb4d78 jdk-9+135 f1eafcb0eb7182b937bc93f214d8cabd01ec4d59 jdk-9+136 a8d5fe567ae72b4931040e59dd4478363f9004f5 jdk-9+137 +69c3b12ba75b2e321dee731ac545e7fbff608451 jdk-9+138
--- a/jaxws/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/jaxws/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -383,3 +383,4 @@ 22631824f55128a7ab6605493b3001a37af6a168 jdk-9+135 09ec13a99f50a4a346180d1e3b0fd8bc1ee399ce jdk-9+136 297c16d401c534cb879809d2a746d21ca99d2954 jdk-9+137 +7d3a8f52b124db26ba8425c2931b748dd9d2791b jdk-9+138
--- a/jdk/.hgtags Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/.hgtags Wed Oct 05 06:28:22 2016 -0700 @@ -380,3 +380,4 @@ 021369229cfd0b5feb76834b2ea498f47f43c0f3 jdk-9+135 54c5931849a33a363e03fdffa141503f5cc4779d jdk-9+136 e72df94364e3686e7d62059ce0d6b187b82da713 jdk-9+137 +665096863382bf23ce891307cf2a7511e77c1c88 jdk-9+138
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/CompileModuleTools.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,42 @@ +# +# Copyright (c) 2013, 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 +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include $(SPEC) +include MakeBase.gmk +include JavaCompilation.gmk +include SetupJavaCompilers.gmk + +TOOLS_CLASSES_DIR := $(BUILDTOOLS_OUTPUTDIR)/tools_jigsaw_classes + +$(eval $(call SetupJavaCompilation,BUILD_JIGSAW_TOOLS, \ + SETUP := GENERATE_USINGJDKBYTECODE, \ + SRC := $(JDK_TOPDIR)/make/src/classes, \ + INCLUDES := build/tools/deps \ + build/tools/jigsaw, \ + BIN := $(TOOLS_CLASSES_DIR), \ + ADD_JAVAC_FLAGS := \ + --add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED \ + --add-exports java.base/jdk.internal.module=ALL-UNNAMED \ +))
--- a/jdk/make/ModuleTools.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/make/ModuleTools.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 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 @@ -26,18 +26,14 @@ include $(SPEC) include MakeBase.gmk include JavaCompilation.gmk -include SetupJavaCompilers.gmk TOOLS_CLASSES_DIR := $(BUILDTOOLS_OUTPUTDIR)/tools_jigsaw_classes -$(eval $(call SetupJavaCompilation,BUILD_JIGSAW_TOOLS, \ - SETUP := GENERATE_USINGJDKBYTECODE, \ - SRC := $(JDK_TOPDIR)/make/src/classes, \ - INCLUDES := build/tools/deps \ - build/tools/jigsaw, \ - BIN := $(TOOLS_CLASSES_DIR), \ - ADD_JAVAC_FLAGS := --add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED )) - +# To avoid reevaluating the compilation setup for the tools each time this file +# is included, the actual compilation is handled by CompileModuleTools.gmk. The +# following trick is used to be able to declare a dependency on the built tools. +BUILD_TOOLS_JDK := $(call SetupJavaCompilationCompileTarget, \ + BUILD_JIGSAW_TOOLS, $(TOOLS_CLASSES_DIR)) TOOL_GENGRAPHS := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \ build.tools.jigsaw.GenGraphs @@ -45,3 +41,8 @@ TOOL_MODULESUMMARY := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \ --add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED \ build.tools.jigsaw.ModuleSummary + +TOOL_ADD_PACKAGES_ATTRIBUTE := $(BUILD_JAVA) $(JAVA_FLAGS_SMALL) \ + -cp $(TOOLS_CLASSES_DIR) \ + --add-exports java.base/jdk.internal.module=ALL-UNNAMED \ + build.tools.jigsaw.AddPackagesAttribute
--- a/jdk/make/lib/CoreLibraries.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/make/lib/CoreLibraries.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -236,10 +236,6 @@ ########################################################################################## -ifeq ($(OPENJDK_TARGET_OS), aix) - LIBJIMAGE_TOOLCHAIN := TOOLCHAIN_LINK_CXX -endif # OPENJDK_TARGET_OS aix - JIMAGELIB_CPPFLAGS := \ -I$(JDK_TOPDIR)/src/java.base/share/native/libjava \ -I$(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjava \ @@ -249,7 +245,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJIMAGE, \ LIBRARY := jimage, \ - TOOLCHAIN := $(LIBJIMAGE_TOOLCHAIN), \ + TOOLCHAIN := TOOLCHAIN_LINK_CXX, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ OPTIMIZATION := LOW, \ SRC := $(JDK_TOPDIR)/src/java.base/share/native/libjimage \
--- a/jdk/make/lib/NioLibraries.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/make/lib/NioLibraries.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -53,12 +53,7 @@ endif ifeq ($(OPENJDK_TARGET_OS), aix) - BUILD_LIBNIO_MAPFILE:=$(JDK_TOPDIR)/make/mapfiles/libnio/mapfile-$(OPENJDK_TARGET_OS) - BUILD_LIBNIO_EXFILES += \ - /NativeThread.c - # Notice: we really need the leading slash here because otherwise every - # FILE_NAME in EXCLUDE_FILES will actually match any file ending in FILE_NAME - # (e.g. 'NativeThread.c' will also exclude 'AixNativeThread.c'). + BUILD_LIBNIO_MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libnio/mapfile-$(OPENJDK_TARGET_OS) endif $(eval $(call SetupNativeCompilation,BUILD_LIBNIO, \
--- a/jdk/make/rmic/RmicCommon.gmk Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/make/rmic/RmicCommon.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -37,7 +37,7 @@ RMIC_MAIN_CLASS := sun.rmi.rmic.Main endif -RMIC := $(JAVA) $(INTERIM_OVERRIDE_MODULES_ARGS) $(RMIC_MAIN_CLASS) +RMIC := $(JAVA_SMALL) $(INTERIM_OVERRIDE_MODULES_ARGS) $(RMIC_MAIN_CLASS) CLASSES_DIR := $(JDK_OUTPUTDIR)/modules # NOTE: If the smart javac dependency management is reintroduced, these classes risk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/src/classes/build/tools/jigsaw/AddPackagesAttribute.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.jigsaw; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.Set; + +import jdk.internal.module.ModuleInfoExtender; + +/** + * Adds the Packages class file attribute to each module-info.class in an + * exploded build. + */ + +public class AddPackagesAttribute { + + public static void main(String[] args) throws IOException { + + if (args.length != 1) { + System.err.println("Usage AddPackagesAttribute exploded-java-home"); + System.exit(-1); + } + + String home = args[0]; + Path dir = Paths.get(home, "modules"); + + ModuleFinder finder = ModuleFinder.of(dir); + + try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { + for (Path entry : stream) { + Path mi = entry.resolve("module-info.class"); + if (Files.isRegularFile(mi)) { + String mn = entry.getFileName().toString(); + Optional<ModuleReference> omref = finder.find(mn); + if (omref.isPresent()) { + Set<String> packages = omref.get().descriptor().conceals(); + addPackagesAttribute(mi, packages); + } + } + } + } + } + + static void addPackagesAttribute(Path mi, Set<String> packages) throws IOException { + byte[] bytes; + try (InputStream in = Files.newInputStream(mi)) { + ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in); + extender.conceals(packages); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + extender.write(baos); + bytes = baos.toByteArray(); + } + + Files.write(mi, bytes); + } + +}
--- a/jdk/src/java.base/aix/native/libnio/ch/AixNativeThread.c Fri Sep 30 02:52:36 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#include <sys/types.h> -#include <string.h> -#include "jni.h" -#include "jni_util.h" -#include "jvm.h" -#include "jlong.h" -#include "sun_nio_ch_NativeThread.h" - -#include <pthread.h> -#include <sys/signal.h> - -/* Also defined in src/aix/native/java/net/aix_close.c */ -#define INTERRUPT_SIGNAL (SIGRTMAX - 1) - -static void -nullHandler(int sig) -{ -} - - -JNIEXPORT void JNICALL -Java_sun_nio_ch_NativeThread_init(JNIEnv *env, jclass cl) -{ - /* Install the null handler for INTERRUPT_SIGNAL. This might overwrite the - * handler previously installed by java/net/aix_close.c, but that's okay - * since neither handler actually does anything. We install our own - * handler here simply out of paranoia; ultimately the two mechanisms - * should somehow be unified, perhaps within the VM. - */ - - sigset_t ss; - struct sigaction sa, osa; - sa.sa_handler = nullHandler; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - if (sigaction(INTERRUPT_SIGNAL, &sa, &osa) < 0) - JNU_ThrowIOExceptionWithLastError(env, "sigaction"); -} - -JNIEXPORT jlong JNICALL -Java_sun_nio_ch_NativeThread_current(JNIEnv *env, jclass cl) -{ - return (long)pthread_self(); -} - -JNIEXPORT void JNICALL -Java_sun_nio_ch_NativeThread_signal(JNIEnv *env, jclass cl, jlong thread) -{ - if (pthread_kill((pthread_t)thread, INTERRUPT_SIGNAL)) - JNU_ThrowIOExceptionWithLastError(env, "Thread signal failed"); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/io/ObjectInputFilter.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,631 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Security; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + + +/** + * Filter classes, array lengths, and graph metrics during deserialization. + * If set on an {@link ObjectInputStream}, the {@link #checkInput checkInput(FilterInfo)} + * method is called to validate classes, the length of each array, + * the number of objects being read from the stream, the depth of the graph, + * and the total number of bytes read from the stream. + * <p> + * A filter can be set via {@link ObjectInputStream#setObjectInputFilter setObjectInputFilter} + * for an individual ObjectInputStream. + * A filter can be set via {@link Config#setSerialFilter(ObjectInputFilter) Config.setSerialFilter} + * to affect every {@code ObjectInputStream} that does not otherwise set a filter. + * <p> + * A filter determines whether the arguments are {@link Status#ALLOWED ALLOWED} + * or {@link Status#REJECTED REJECTED} and should return the appropriate status. + * If the filter cannot determine the status it should return + * {@link Status#UNDECIDED UNDECIDED}. + * Filters should be designed for the specific use case and expected types. + * A filter designed for a particular use may be passed a class that is outside + * of the scope of the filter. If the purpose of the filter is to black-list classes + * then it can reject a candidate class that matches and report UNDECIDED for others. + * A filter may be called with class equals {@code null}, {@code arrayLength} equal -1, + * the depth, number of references, and stream size and return a status + * that reflects only one or only some of the values. + * This allows a filter to specific about the choice it is reporting and + * to use other filters without forcing either allowed or rejected status. + * + * <p> + * Typically, a custom filter should check if a process-wide filter + * is configured and defer to it if so. For example, + * <pre>{@code + * ObjectInputFilter.Status checkInput(FilterInfo info) { + * ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter(); + * if (serialFilter != null) { + * ObjectInputFilter.Status status = serialFilter.checkInput(info); + * if (status != ObjectInputFilter.Status.UNDECIDED) { + * // The process-wide filter overrides this filter + * return status; + * } + * } + * if (info.serialClass() != null && + * Remote.class.isAssignableFrom(info.serialClass())) { + * return Status.REJECTED; // Do not allow Remote objects + * } + * return Status.UNDECIDED; + * } + *}</pre> + * <p> + * Unless otherwise noted, passing a {@code null} argument to a + * method in this interface and its nested classes will cause a + * {@link NullPointerException} to be thrown. + * + * @see ObjectInputStream#setObjectInputFilter(ObjectInputFilter) + * @since 9 + */ +@FunctionalInterface +public interface ObjectInputFilter { + + /** + * Check the class, array length, number of object references, depth, + * stream size, and other available filtering information. + * Implementations of this method check the contents of the object graph being created + * during deserialization. The filter returns {@link Status#ALLOWED Status.ALLOWED}, + * {@link Status#REJECTED Status.REJECTED}, or {@link Status#UNDECIDED Status.UNDECIDED}. + * + * @param filterInfo provides information about the current object being deserialized, + * if any, and the status of the {@link ObjectInputStream} + * @return {@link Status#ALLOWED Status.ALLOWED} if accepted, + * {@link Status#REJECTED Status.REJECTED} if rejected, + * {@link Status#UNDECIDED Status.UNDECIDED} if undecided. + * @since 9 + */ + Status checkInput(FilterInfo filterInfo); + + /** + * FilterInfo provides access to information about the current object + * being deserialized and the status of the {@link ObjectInputStream}. + * @since 9 + */ + interface FilterInfo { + /** + * The class of an object being deserialized. + * For arrays, it is the array type. + * For example, the array class name of a 2 dimensional array of strings is + * "{@code [[Ljava.lang.String;}". + * To check the array's element type, iteratively use + * {@link Class#getComponentType() Class.getComponentType} while the result + * is an array and then check the class. + * The {@code serialClass is null} in the case where a new object is not being + * created and to give the filter a chance to check the depth, number of + * references to existing objects, and the stream size. + * + * @return class of an object being deserialized; may be null + */ + Class<?> serialClass(); + + /** + * The number of array elements when deserializing an array of the class. + * + * @return the non-negative number of array elements when deserializing + * an array of the class, otherwise -1 + */ + long arrayLength(); + + /** + * The current depth. + * The depth starts at {@code 1} and increases for each nested object and + * decrements when each nested object returns. + * + * @return the current depth + */ + long depth(); + + /** + * The current number of object references. + * + * @return the non-negative current number of object references + */ + long references(); + + /** + * The current number of bytes consumed. + * @implSpec {@code streamBytes} is implementation specific + * and may not be directly related to the object in the stream + * that caused the callback. + * + * @return the non-negative current number of bytes consumed + */ + long streamBytes(); + } + + /** + * The status of a check on the class, array length, number of references, + * depth, and stream size. + * + * @since 9 + */ + enum Status { + /** + * The status is undecided, not allowed and not rejected. + */ + UNDECIDED, + /** + * The status is allowed. + */ + ALLOWED, + /** + * The status is rejected. + */ + REJECTED; + } + + /** + * A utility class to set and get the process-wide filter or create a filter + * from a pattern string. If a process-wide filter is set, it will be + * used for each {@link ObjectInputStream} that does not set its own filter. + * <p> + * When setting the filter, it should be stateless and idempotent, + * reporting the same result when passed the same arguments. + * <p> + * The filter is configured using the {@link java.security.Security} + * property {@code jdk.serialFilter} and can be overridden by + * the System property {@code jdk.serialFilter}. + * + * The syntax is the same as for the {@link #createFilter(String) createFilter} method. + * + * @since 9 + */ + final class Config { + /* No instances. */ + private Config() {} + + /** + * Lock object for process-wide filter. + */ + private final static Object serialFilterLock = new Object(); + + /** + * Debug: Logger + */ + private final static System.Logger configLog; + + /** + * Logger for debugging. + */ + static void filterLog(System.Logger.Level level, String msg, Object... args) { + if (configLog != null) { + configLog.log(level, msg, args); + } + } + + /** + * The name for the process-wide deserialization filter. + * Used as a system property and a java.security.Security property. + */ + private final static String SERIAL_FILTER_PROPNAME = "jdk.serialFilter"; + + /** + * The process-wide filter; may be null. + * Lookup the filter in java.security.Security or + * the system property. + */ + private final static ObjectInputFilter configuredFilter; + + static { + configuredFilter = AccessController + .doPrivileged((PrivilegedAction<ObjectInputFilter>) () -> { + String props = System.getProperty(SERIAL_FILTER_PROPNAME); + if (props == null) { + props = Security.getProperty(SERIAL_FILTER_PROPNAME); + } + if (props != null) { + System.Logger log = + System.getLogger("java.io.serialization"); + log.log(System.Logger.Level.INFO, + "Creating serialization filter from {0}", props); + try { + return createFilter(props); + } catch (RuntimeException re) { + log.log(System.Logger.Level.ERROR, + "Error configuring filter: {0}", re); + } + } + return null; + }); + configLog = (configuredFilter != null) ? System.getLogger("java.io.serialization") : null; + } + + /** + * Current configured filter. + */ + private static ObjectInputFilter serialFilter = configuredFilter; + + /** + * Returns the process-wide serialization filter or {@code null} if not configured. + * + * @return the process-wide serialization filter or {@code null} if not configured + */ + public static ObjectInputFilter getSerialFilter() { + synchronized (serialFilterLock) { + return serialFilter; + } + } + + /** + * Set the process-wide filter if it has not already been configured or set. + * + * @param filter the serialization filter to set as the process-wide filter; not null + * @throws SecurityException if there is security manager and the + * {@code SerializablePermission("serialFilter")} is not granted + * @throws IllegalStateException if the filter has already been set {@code non-null} + */ + public static void setSerialFilter(ObjectInputFilter filter) { + Objects.requireNonNull(filter, "filter"); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION); + } + synchronized (serialFilterLock) { + if (serialFilter != null) { + throw new IllegalStateException("Serial filter can only be set once"); + } + serialFilter = filter; + } + } + + /** + * Returns an ObjectInputFilter from a string of patterns. + * <p> + * Patterns are separated by ";" (semicolon). Whitespace is significant and + * is considered part of the pattern. + * If a pattern includes an equals assignment, "{@code =}" it sets a limit. + * If a limit appears more than once the last value is used. + * <ul> + * <li>maxdepth={@code value} - the maximum depth of a graph</li> + * <li>maxrefs={@code value} - the maximum number of internal references</li> + * <li>maxbytes={@code value} - the maximum number of bytes in the input stream</li> + * <li>maxarray={@code value} - the maximum array length allowed</li> + * </ul> + * <p> + * Other patterns match or reject class or package name + * as returned from {@link Class#getName() Class.getName()} and + * if an optional module name is present + * {@link java.lang.reflect.Module#getName() class.getModule().getName()}. + * Note that for arrays the element type is used in the pattern, + * not the array type. + * <ul> + * <li>If the pattern starts with "!", the class is rejected if the remaining pattern is matched; + * otherwise the class is allowed if the pattern matches. + * <li>If the pattern contains "/", the non-empty prefix up to the "/" is the module name; + * if the module name matches the module name of the class then + * the remaining pattern is matched with the class name. + * If there is no "/", the module name is not compared. + * <li>If the pattern ends with ".**" it matches any class in the package and all subpackages. + * <li>If the pattern ends with ".*" it matches any class in the package. + * <li>If the pattern ends with "*", it matches any class with the pattern as a prefix. + * <li>If the pattern is equal to the class name, it matches. + * <li>Otherwise, the pattern is not matched. + * </ul> + * <p> + * The resulting filter performs the limit checks and then + * tries to match the class, if any. If any of the limits are exceeded, + * the filter returns {@link Status#REJECTED Status.REJECTED}. + * If the class is an array type, the class to be matched is the element type. + * Arrays of any number of dimensions are treated the same as the element type. + * For example, a pattern of "{@code !example.Foo}", + * rejects creation of any instance or array of {@code example.Foo}. + * The first pattern that matches, working from left to right, determines + * the {@link Status#ALLOWED Status.ALLOWED} + * or {@link Status#REJECTED Status.REJECTED} result. + * If nothing matches, the result is {@link Status#UNDECIDED Status.UNDECIDED}. + * + * @param pattern the pattern string to parse; not null + * @return a filter to check a class being deserialized; may be null; + * {@code null} if no patterns + * @throws IllegalArgumentException + * if a limit is missing the name, or the long value + * is not a number or is negative, + * or the module name is missing if the pattern contains "/" + * or if the package is missing for ".*" and ".**" + */ + public static ObjectInputFilter createFilter(String pattern) { + Objects.requireNonNull(pattern, "pattern"); + return Global.createFilter(pattern); + } + + /** + * Implementation of ObjectInputFilter that performs the checks of + * the process-wide serialization filter. If configured, it will be + * used for all ObjectInputStreams that do not set their own filters. + * + */ + final static class Global implements ObjectInputFilter { + /** + * The pattern used to create the filter. + */ + private final String pattern; + /** + * The list of class filters. + */ + private final List<Function<Class<?>, Status>> filters; + /** + * Maximum allowed bytes in the stream. + */ + private long maxStreamBytes; + /** + * Maximum depth of the graph allowed. + */ + private long maxDepth; + /** + * Maximum number of references in a graph. + */ + private long maxReferences; + /** + * Maximum length of any array. + */ + private long maxArrayLength; + + /** + * Returns an ObjectInputFilter from a string of patterns. + * + * @param pattern the pattern string to parse + * @return a filter to check a class being deserialized; not null + * @throws IllegalArgumentException if the parameter is malformed + * if the pattern is missing the name, the long value + * is not a number or is negative. + */ + static ObjectInputFilter createFilter(String pattern) { + Global filter = new Global(pattern); + return filter.isEmpty() ? null : filter; + } + + /** + * Construct a new filter from the pattern String. + * + * @param pattern a pattern string of filters + * @throws IllegalArgumentException if the pattern is malformed + */ + private Global(String pattern) { + this.pattern = pattern; + + maxArrayLength = Long.MAX_VALUE; // Default values are unlimited + maxDepth = Long.MAX_VALUE; + maxReferences = Long.MAX_VALUE; + maxStreamBytes = Long.MAX_VALUE; + + String[] patterns = pattern.split(";"); + filters = new ArrayList<>(patterns.length); + for (int i = 0; i < patterns.length; i++) { + String p = patterns[i]; + int nameLen = p.length(); + if (nameLen == 0) { + continue; + } + if (parseLimit(p)) { + // If the pattern contained a limit setting, i.e. type=value + continue; + } + boolean negate = p.charAt(0) == '!'; + int poffset = negate ? 1 : 0; + + // isolate module name, if any + int slash = p.indexOf('/', poffset); + if (slash == poffset) { + throw new IllegalArgumentException("module name is missing in: \"" + pattern + "\""); + } + final String moduleName = (slash >= 0) ? p.substring(poffset, slash) : null; + poffset = (slash >= 0) ? slash + 1 : poffset; + + final Function<Class<?>, Status> patternFilter; + if (p.endsWith("*")) { + // Wildcard cases + if (p.endsWith(".*")) { + // Pattern is a package name with a wildcard + final String pkg = p.substring(poffset, nameLen - 1); + if (pkg.length() < 2) { + throw new IllegalArgumentException("package missing in: \"" + pattern + "\""); + } + if (negate) { + // A Function that fails if the class starts with the pattern, otherwise don't care + patternFilter = c -> matchesPackage(c, pkg) ? Status.REJECTED : Status.UNDECIDED; + } else { + // A Function that succeeds if the class starts with the pattern, otherwise don't care + patternFilter = c -> matchesPackage(c, pkg) ? Status.ALLOWED : Status.UNDECIDED; + } + } else if (p.endsWith(".**")) { + // Pattern is a package prefix with a double wildcard + final String pkgs = p.substring(poffset, nameLen - 2); + if (pkgs.length() < 2) { + throw new IllegalArgumentException("package missing in: \"" + pattern + "\""); + } + if (negate) { + // A Function that fails if the class starts with the pattern, otherwise don't care + patternFilter = c -> c.getName().startsWith(pkgs) ? Status.REJECTED : Status.UNDECIDED; + } else { + // A Function that succeeds if the class starts with the pattern, otherwise don't care + patternFilter = c -> c.getName().startsWith(pkgs) ? Status.ALLOWED : Status.UNDECIDED; + } + } else { + // Pattern is a classname (possibly empty) with a trailing wildcard + final String className = p.substring(poffset, nameLen - 1); + if (negate) { + // A Function that fails if the class starts with the pattern, otherwise don't care + patternFilter = c -> c.getName().startsWith(className) ? Status.REJECTED : Status.UNDECIDED; + } else { + // A Function that succeeds if the class starts with the pattern, otherwise don't care + patternFilter = c -> c.getName().startsWith(className) ? Status.ALLOWED : Status.UNDECIDED; + } + } + } else { + final String name = p.substring(poffset); + if (name.isEmpty()) { + throw new IllegalArgumentException("class or package missing in: \"" + pattern + "\""); + } + // Pattern is a class name + if (negate) { + // A Function that fails if the class equals the pattern, otherwise don't care + patternFilter = c -> c.getName().equals(name) ? Status.REJECTED : Status.UNDECIDED; + } else { + // A Function that succeeds if the class equals the pattern, otherwise don't care + patternFilter = c -> c.getName().equals(name) ? Status.ALLOWED : Status.UNDECIDED; + } + } + // If there is a moduleName, combine the module name check with the package/class check + if (moduleName == null) { + filters.add(patternFilter); + } else { + filters.add(c -> moduleName.equals(c.getModule().getName()) ? patternFilter.apply(c) : Status.UNDECIDED); + } + } + } + + /** + * Returns if this filter has any checks. + * @return {@code true} if the filter has any checks, {@code false} otherwise + */ + private boolean isEmpty() { + return filters.isEmpty() && + maxArrayLength == Long.MAX_VALUE && + maxDepth == Long.MAX_VALUE && + maxReferences == Long.MAX_VALUE && + maxStreamBytes == Long.MAX_VALUE; + } + + /** + * Parse out a limit for one of maxarray, maxdepth, maxbytes, maxreferences. + * + * @param pattern a string with a type name, '=' and a value + * @return {@code true} if a limit was parsed, else {@code false} + * @throws IllegalArgumentException if the pattern is missing + * the name, the Long value is not a number or is negative. + */ + private boolean parseLimit(String pattern) { + int eqNdx = pattern.indexOf('='); + if (eqNdx < 0) { + // not a limit pattern + return false; + } + String valueString = pattern.substring(eqNdx + 1); + if (pattern.startsWith("maxdepth=")) { + maxDepth = parseValue(valueString); + } else if (pattern.startsWith("maxarray=")) { + maxArrayLength = parseValue(valueString); + } else if (pattern.startsWith("maxrefs=")) { + maxReferences = parseValue(valueString); + } else if (pattern.startsWith("maxbytes=")) { + maxStreamBytes = parseValue(valueString); + } else { + throw new IllegalArgumentException("unknown limit: " + pattern.substring(0, eqNdx)); + } + return true; + } + + /** + * Parse the value of a limit and check that it is non-negative. + * @param string inputstring + * @return the parsed value + * @throws IllegalArgumentException if parsing the value fails or the value is negative + */ + private static long parseValue(String string) throws IllegalArgumentException { + // Parse a Long from after the '=' to the end + long value = Long.parseLong(string); + if (value < 0) { + throw new IllegalArgumentException("negative limit: " + string); + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public Status checkInput(FilterInfo filterInfo) { + if (filterInfo.references() < 0 + || filterInfo.depth() < 0 + || filterInfo.streamBytes() < 0 + || filterInfo.references() > maxReferences + || filterInfo.depth() > maxDepth + || filterInfo.streamBytes() > maxStreamBytes) { + return Status.REJECTED; + } + + Class<?> clazz = filterInfo.serialClass(); + if (clazz != null) { + if (clazz.isArray()) { + if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > maxArrayLength) { + // array length is too big + return Status.REJECTED; + } + do { + // Arrays are decided based on the component type + clazz = clazz.getComponentType(); + } while (clazz.isArray()); + } + + if (clazz.isPrimitive()) { + // Primitive types are undecided; let someone else decide + return Status.UNDECIDED; + } else { + // Find any filter that allowed or rejected the class + final Class<?> cl = clazz; + Optional<Status> status = filters.stream() + .map(f -> f.apply(cl)) + .filter(p -> p != Status.UNDECIDED) + .findFirst(); + return status.orElse(Status.UNDECIDED); + } + } + return Status.UNDECIDED; + } + + /** + * Returns {@code true} if the class is in the package. + * + * @param c a class + * @param pkg a package name (including the trailing ".") + * @return {@code true} if the class is in the package, + * otherwise {@code false} + */ + private static boolean matchesPackage(Class<?> c, String pkg) { + String n = c.getName(); + return n.startsWith(pkg) && n.lastIndexOf('.') == pkg.length() - 1; + } + + /** + * Returns the pattern used to create this filter. + * @return the pattern used to create this filter + */ + @Override + public String toString() { + return pattern; + } + } + } +}
--- a/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java Wed Oct 05 06:28:22 2016 -0700 @@ -26,6 +26,7 @@ package java.io; import java.io.ObjectStreamClass.WeakClassKey; +import java.lang.System.Logger; import java.lang.ref.ReferenceQueue; import java.lang.reflect.Array; import java.lang.reflect.Modifier; @@ -37,10 +38,12 @@ import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; + import static java.io.ObjectStreamClass.processQueue; -import jdk.internal.misc.JavaObjectInputStreamAccess; + import jdk.internal.misc.ObjectStreamClassValidator; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; @@ -172,6 +175,16 @@ * protected) or that there are get and set methods that can be used to restore * the state. * + * <p>The contents of the stream can be filtered during deserialization. + * If a {@linkplain #setObjectInputFilter(ObjectInputFilter) filter is set} + * on an ObjectInputStream, the {@link ObjectInputFilter} can check that + * the classes, array lengths, number of references in the stream, depth, and + * number of bytes consumed from the input stream are allowed and + * if not, can terminate deserialization. + * A {@linkplain ObjectInputFilter.Config#setSerialFilter(ObjectInputFilter) process-wide filter} + * can be configured that is applied to each {@code ObjectInputStream} unless replaced + * using {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter}. + * * <p>Any exception that occurs while deserializing an object will be caught by * the ObjectInputStream and abort the reading process. * @@ -240,12 +253,32 @@ new ReferenceQueue<>(); } + /* + * Separate class to defer initialization of logging until needed. + */ + private static class Logging { + /* + * Logger for ObjectInputFilter results. + * Setup the filter logger if it is set to DEBUG or TRACE. + * (Assuming it will not change). + */ + static final System.Logger filterLogger; + + static { + Logger filterLog = System.getLogger("java.io.serialization"); + filterLogger = (filterLog.isLoggable(Logger.Level.DEBUG) + || filterLog.isLoggable(Logger.Level.TRACE)) ? filterLog : null; + } + } + /** filter stream for handling block data conversion */ private final BlockDataInputStream bin; /** validation callback list */ private final ValidationList vlist; /** recursion depth */ - private int depth; + private long depth; + /** Total number of references to any type of object, class, enum, proxy, etc. */ + private long totalObjectRefs; /** whether stream is closed */ private boolean closed; @@ -269,11 +302,20 @@ private SerialCallbackContext curContext; /** + * Filter of class descriptors and classes read from the stream; + * may be null. + */ + private ObjectInputFilter serialFilter; + + /** * Creates an ObjectInputStream that reads from the specified InputStream. * A serialization stream header is read from the stream and verified. * This constructor will block until the corresponding ObjectOutputStream * has written and flushed the header. * + * <p>The serialization filter is initialized to the value of + * {@linkplain ObjectInputFilter.Config#getSerialFilter() the process-wide filter}. + * * <p>If a security manager is installed, this constructor will check for * the "enableSubclassImplementation" SerializablePermission when invoked * directly or indirectly by the constructor of a subclass which overrides @@ -295,6 +337,7 @@ bin = new BlockDataInputStream(in); handles = new HandleTable(10); vlist = new ValidationList(); + serialFilter = ObjectInputFilter.Config.getSerialFilter(); enableOverride = false; readStreamHeader(); bin.setBlockDataMode(true); @@ -305,6 +348,9 @@ * ObjectInputStream to not have to allocate private data just used by this * implementation of ObjectInputStream. * + * <p>The serialization filter is initialized to the value of + * {@linkplain ObjectInputFilter.Config#getSerialFilter() the process-wide filter}. + * * <p>If there is a security manager installed, this method first calls the * security manager's <code>checkPermission</code> method with the * <code>SerializablePermission("enableSubclassImplementation")</code> @@ -325,6 +371,7 @@ bin = null; handles = null; vlist = null; + serialFilter = ObjectInputFilter.Config.getSerialFilter(); enableOverride = true; } @@ -332,7 +379,7 @@ * Read an object from the ObjectInputStream. The class of the object, the * signature of the class, and the values of the non-transient and * non-static fields of the class and all of its supertypes are read. - * Default deserializing for a class can be overriden using the writeObject + * Default deserializing for a class can be overridden using the writeObject * and readObject methods. Objects referenced by this object are read * transitively so that a complete equivalent graph of objects is * reconstructed by readObject. @@ -343,6 +390,10 @@ * priorities. The callbacks are registered by objects (in the readObject * special methods) as they are individually restored. * + * <p>The serialization filter, when not {@code null}, is invoked for + * each object (regular or class) read to reconstruct the root object. + * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details. + * * <p>Exceptions are thrown for problems with the InputStream and for * classes that should not be deserialized. All exceptions are fatal to * the InputStream and leave it in an indeterminate state; it is up to the @@ -438,6 +489,10 @@ * invocation of readObject or readUnshared on the ObjectInputStream, * even if the underlying data stream has been manipulated. * + * <p>The serialization filter, when not {@code null}, is invoked for + * each object (regular or class) read to reconstruct the root object. + * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details. + * * <p>ObjectInputStream subclasses which override this method can only be * constructed in security contexts possessing the * "enableSubclassImplementation" SerializablePermission; any attempt to @@ -1094,6 +1149,134 @@ } /** + * Returns the serialization filter for this stream. + * The serialization filter is the most recent filter set in + * {@link #setObjectInputFilter setObjectInputFilter} or + * the initial process-wide filter from + * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter}. + * + * @return the serialization filter for the stream; may be null + * @since 9 + */ + public final ObjectInputFilter getObjectInputFilter() { + return serialFilter; + } + + /** + * Set the serialization filter for the stream. + * The filter's {@link ObjectInputFilter#checkInput checkInput} method is called + * for each class and reference in the stream. + * The filter can check any or all of the class, the array length, the number + * of references, the depth of the graph, and the size of the input stream. + * <p> + * If the filter returns {@link ObjectInputFilter.Status#REJECTED Status.REJECTED}, + * {@code null} or throws a {@link RuntimeException}, + * the active {@code readObject} or {@code readUnshared} + * throws {@link InvalidClassException}, otherwise deserialization + * continues uninterrupted. + * <p> + * The serialization filter is initialized to the value of + * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter} + * when the {@code ObjectInputStream} is constructed and can be set + * to a custom filter only once. + * + * @implSpec + * The filter, when not {@code null}, is invoked during {@link #readObject readObject} + * and {@link #readUnshared readUnshared} for each object + * (regular or class) in the stream including the following: + * <ul> + * <li>each object reference previously deserialized from the stream + * (class is {@code null}, arrayLength is -1), + * <li>each regular class (class is not {@code null}, arrayLength is -1), + * <li>each interface of a dynamic proxy and the dynamic proxy class itself + * (class is not {@code null}, arrayLength is -1), + * <li>each array is filtered using the array type and length of the array + * (class is the array type, arrayLength is the requested length), + * <li>each object replaced by its class' {@code readResolve} method + * is filtered using the replacement object's class, if not {@code null}, + * and if it is an array, the arrayLength, otherwise -1, + * <li>and each object replaced by {@link #resolveObject resolveObject} + * is filtered using the replacement object's class, if not {@code null}, + * and if it is an array, the arrayLength, otherwise -1. + * </ul> + * + * When the {@link ObjectInputFilter#checkInput checkInput} method is invoked + * it is given access to the current class, the array length, + * the current number of references already read from the stream, + * the depth of nested calls to {@link #readObject readObject} or + * {@link #readUnshared readUnshared}, + * and the implementation dependent number of bytes consumed from the input stream. + * <p> + * Each call to {@link #readObject readObject} or + * {@link #readUnshared readUnshared} increases the depth by 1 + * before reading an object and decreases by 1 before returning + * normally or exceptionally. + * The depth starts at {@code 1} and increases for each nested object and + * decrements when each nested call returns. + * The count of references in the stream starts at {@code 1} and + * is increased before reading an object. + * + * @param filter the filter, may be null + * @throws SecurityException if there is security manager and the + * {@code SerializablePermission("serialFilter")} is not granted + * @throws IllegalStateException if the {@linkplain #getObjectInputFilter() current filter} + * is not {@code null} and is not the process-wide filter + * @since 9 + */ + public final void setObjectInputFilter(ObjectInputFilter filter) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION); + } + // Allow replacement of the process-wide filter if not already set + if (serialFilter != null && + serialFilter != ObjectInputFilter.Config.getSerialFilter()) { + throw new IllegalStateException("filter can not be set more than once"); + } + this.serialFilter = filter; + } + + /** + * Invoke the serialization filter if non-null. + * If the filter rejects or an exception is thrown, throws InvalidClassException. + * + * @param clazz the class; may be null + * @param arrayLength the array length requested; use {@code -1} if not creating an array + * @throws InvalidClassException if it rejected by the filter or + * a {@link RuntimeException} is thrown + */ + private void filterCheck(Class<?> clazz, int arrayLength) + throws InvalidClassException { + if (serialFilter != null) { + RuntimeException ex = null; + ObjectInputFilter.Status status; + try { + status = serialFilter.checkInput(new FilterValues(clazz, arrayLength, + totalObjectRefs, depth, bin.getBytesRead())); + } catch (RuntimeException e) { + // Preventive interception of an exception to log + status = ObjectInputFilter.Status.REJECTED; + ex = e; + } + if (Logging.filterLogger != null) { + // Debug logging of filter checks that fail; Tracing for those that succeed + Logging.filterLogger.log(status == null || status == ObjectInputFilter.Status.REJECTED + ? Logger.Level.DEBUG + : Logger.Level.TRACE, + "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}", + status, clazz, arrayLength, totalObjectRefs, depth, bin.getBytesRead(), + Objects.toString(ex, "n/a")); + } + if (status == null || + status == ObjectInputFilter.Status.REJECTED) { + InvalidClassException ice = new InvalidClassException("filter status: " + status); + ice.initCause(ex); + throw ice; + } + } + } + + /** * Provide access to the persistent fields read from the input stream. */ public abstract static class GetField { @@ -1280,7 +1463,7 @@ */ private static Boolean auditSubclass(Class<?> subcl) { return AccessController.doPrivileged( - new PrivilegedAction<>() { + new PrivilegedAction<Boolean>() { public Boolean run() { for (Class<?> cl = subcl; cl != ObjectInputStream.class; @@ -1340,6 +1523,7 @@ } depth++; + totalObjectRefs++; try { switch (tc) { case TC_NULL: @@ -1416,6 +1600,15 @@ } Object rep = resolveObject(obj); if (rep != obj) { + // The type of the original object has been filtered but resolveObject + // may have replaced it; filter the replacement's type + if (rep != null) { + if (rep.getClass().isArray()) { + filterCheck(rep.getClass(), Array.getLength(rep)); + } else { + filterCheck(rep.getClass(), -1); + } + } handles.setObject(passHandle, rep); } return rep; @@ -1486,6 +1679,7 @@ throw new InvalidObjectException( "cannot read back reference to unshared object"); } + filterCheck(null, -1); // just a check for number of references, depth, no class return obj; } @@ -1590,6 +1784,10 @@ ReflectUtil.checkProxyPackageAccess( getClass().getClassLoader(), cl.getInterfaces()); + // Filter the interfaces + for (Class<?> clazz : cl.getInterfaces()) { + filterCheck(clazz, -1); + } } } catch (ClassNotFoundException ex) { resolveEx = ex; @@ -1598,6 +1796,9 @@ desc.initProxy(cl, resolveEx, readClassDesc(false)); + // Call filterCheck on the definition + filterCheck(desc.forClass(), -1); + handles.finish(descHandle); passHandle = descHandle; return desc; @@ -1645,8 +1846,12 @@ desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); + // Call filterCheck on the definition + filterCheck(desc.forClass(), -1); + handles.finish(descHandle); passHandle = descHandle; + return desc; } @@ -1687,6 +1892,8 @@ ObjectStreamClass desc = readClassDesc(false); int len = bin.readInt(); + filterCheck(desc.forClass(), len); + Object array = null; Class<?> cl, ccl = null; if ((cl = desc.forClass()) != null) { @@ -1835,6 +2042,14 @@ rep = cloneArray(rep); } if (rep != obj) { + // Filter the replacement object + if (rep != null) { + if (rep.getClass().isArray()) { + filterCheck(rep.getClass(), Array.getLength(rep)); + } else { + filterCheck(rep.getClass(), -1); + } + } handles.setObject(passHandle, obj = rep); } } @@ -2360,7 +2575,7 @@ try { while (list != null) { AccessController.doPrivileged( - new PrivilegedExceptionAction<>() + new PrivilegedExceptionAction<Void>() { public Void run() throws InvalidObjectException { list.obj.validateObject(); @@ -2384,6 +2599,51 @@ } /** + * Hold a snapshot of values to be passed to an ObjectInputFilter. + */ + static class FilterValues implements ObjectInputFilter.FilterInfo { + final Class<?> clazz; + final long arrayLength; + final long totalObjectRefs; + final long depth; + final long streamBytes; + + public FilterValues(Class<?> clazz, long arrayLength, long totalObjectRefs, + long depth, long streamBytes) { + this.clazz = clazz; + this.arrayLength = arrayLength; + this.totalObjectRefs = totalObjectRefs; + this.depth = depth; + this.streamBytes = streamBytes; + } + + @Override + public Class<?> serialClass() { + return clazz; + } + + @Override + public long arrayLength() { + return arrayLength; + } + + @Override + public long references() { + return totalObjectRefs; + } + + @Override + public long depth() { + return depth; + } + + @Override + public long streamBytes() { + return streamBytes; + } + } + + /** * Input stream supporting single-byte peek operations. */ private static class PeekInputStream extends InputStream { @@ -2392,6 +2652,8 @@ private final InputStream in; /** peeked byte */ private int peekb = -1; + /** total bytes read from the stream */ + private long totalBytesRead = 0; /** * Creates new PeekInputStream on top of given underlying stream. @@ -2405,7 +2667,12 @@ * that it does not consume the read value. */ int peek() throws IOException { - return (peekb >= 0) ? peekb : (peekb = in.read()); + if (peekb >= 0) { + return peekb; + } + peekb = in.read(); + totalBytesRead += peekb >= 0 ? 1 : 0; + return peekb; } public int read() throws IOException { @@ -2414,21 +2681,27 @@ peekb = -1; return v; } else { - return in.read(); + int nbytes = in.read(); + totalBytesRead += nbytes >= 0 ? 1 : 0; + return nbytes; } } public int read(byte[] b, int off, int len) throws IOException { + int nbytes; if (len == 0) { return 0; } else if (peekb < 0) { - return in.read(b, off, len); + nbytes = in.read(b, off, len); + totalBytesRead += nbytes >= 0 ? nbytes : 0; + return nbytes; } else { b[off++] = (byte) peekb; len--; peekb = -1; - int n = in.read(b, off, len); - return (n >= 0) ? (n + 1) : 1; + nbytes = in.read(b, off, len); + totalBytesRead += nbytes >= 0 ? nbytes : 0; + return (nbytes >= 0) ? (nbytes + 1) : 1; } } @@ -2453,7 +2726,9 @@ skipped++; n--; } - return skipped + in.skip(n); + n = skipped + in.skip(n); + totalBytesRead += n; + return n; } public int available() throws IOException { @@ -2463,6 +2738,10 @@ public void close() throws IOException { in.close(); } + + public long getBytesRead() { + return totalBytesRead; + } } private static final Unsafe UNSAFE = Unsafe.getUnsafe(); @@ -3346,6 +3625,14 @@ throw new UTFDataFormatException(); } } + + /** + * Returns the number of bytes read from the input stream. + * @return the number of bytes read from the input stream + */ + long getBytesRead() { + return in.getBytesRead(); + } } /**
--- a/jdk/src/java.base/share/classes/java/io/ObjectStreamConstants.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/io/ObjectStreamConstants.java Wed Oct 05 06:28:22 2016 -0700 @@ -199,6 +199,16 @@ */ static final SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new SerializablePermission("enableSubclassImplementation"); + + /** + * Enable setting the process-wide serial filter. + * + * @see java.io.ObjectInputFilter.Config#setSerialFilter(ObjectInputFilter) + * @since 9 + */ + static final SerializablePermission SERIAL_FILTER_PERMISSION = + new SerializablePermission("serialFilter"); + /** * A Stream Protocol Version. <p> *
--- a/jdk/src/java.base/share/classes/java/io/SerializablePermission.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/io/SerializablePermission.java Wed Oct 05 06:28:22 2016 -0700 @@ -40,7 +40,7 @@ * The target name is the name of the Serializable permission (see below). * * <P> - * The following table lists all the possible SerializablePermission target names, + * The following table lists the standard {@code SerializablePermission} target names, * and for each provides a description of what the permission allows * and a discussion of the risks of granting code the permission. * @@ -73,6 +73,13 @@ * malignant data.</td> * </tr> * + * <tr> + * <td>serialFilter</td> + * <td>Setting a filter for ObjectInputStreams.</td> + * <td>Code could remove a configured filter and remove protections + * already established.</td> + * </tr> + * * </table> * * @see java.security.BasicPermission
--- a/jdk/src/java.base/share/classes/java/lang/String.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/String.java Wed Oct 05 06:28:22 2016 -0700 @@ -1516,11 +1516,12 @@ * @return a hash code value for this object. */ public int hashCode() { - if (hash == 0 && value.length > 0) { - hash = isLatin1() ? StringLatin1.hashCode(value) - : StringUTF16.hashCode(value); + int h = hash; + if (h == 0 && value.length > 0) { + hash = h = isLatin1() ? StringLatin1.hashCode(value) + : StringUTF16.hashCode(value); } - return hash; + return h; } /**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Oct 05 06:28:22 2016 -0700 @@ -1962,12 +1962,12 @@ * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops}. * + * @param limit the upper bound of the parameter, statically bound at loop creation time. * @param counter the counter parameter, passed in during loop execution. - * @param limit the upper bound of the parameter, statically bound at loop creation time. * * @return whether the counter has reached the limit. */ - static boolean countedLoopPredicate(int counter, int limit) { + static boolean countedLoopPredicate(int limit, int counter) { return counter < limit; } @@ -1975,27 +1975,16 @@ * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, * MethodHandle) counting loops} to increment the counter. * + * @param limit the upper bound of the loop counter (ignored). * @param counter the loop counter. * * @return the loop counter incremented by 1. */ - static int countedLoopStep(int counter, int limit) { + static int countedLoopStep(int limit, int counter) { return counter + 1; } /** - * This method is bound as a filter in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle, - * MethodHandle) counting loops} to pass the correct counter value to the body. - * - * @param counter the loop counter. - * - * @return the loop counter decremented by 1. - */ - static int decrementCounter(int counter) { - return counter - 1; - } - - /** * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}. * * @param it the {@link Iterable} over which the loop iterates. @@ -2164,12 +2153,11 @@ MH_arrayIdentity = 5, MH_countedLoopPred = 6, MH_countedLoopStep = 7, - MH_iteratePred = 8, - MH_initIterator = 9, + MH_initIterator = 8, + MH_iteratePred = 9, MH_iterateNext = 10, - MH_decrementCounter = 11, - MH_Array_newInstance = 12, - MH_LIMIT = 13; + MH_Array_newInstance = 11, + MH_LIMIT = 12; static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; @@ -2220,18 +2208,15 @@ case MH_countedLoopStep: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep", MethodType.methodType(int.class, int.class, int.class)); - case MH_iteratePred: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", - MethodType.methodType(boolean.class, Iterator.class)); case MH_initIterator: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator", MethodType.methodType(Iterator.class, Iterable.class)); + case MH_iteratePred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", + MethodType.methodType(boolean.class, Iterator.class)); case MH_iterateNext: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext", MethodType.methodType(Object.class, Iterator.class)); - case MH_decrementCounter: - return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter", - MethodType.methodType(int.class, int.class)); case MH_Array_newInstance: return IMPL_LOOKUP.findStatic(Array.class, "newInstance", MethodType.methodType(Object.class, Class.class, int.class));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Oct 05 06:28:22 2016 -0700 @@ -44,8 +44,6 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ReflectPermission; import java.nio.ByteOrder; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -3114,7 +3112,7 @@ * @see MethodHandles#explicitCastArguments * @since 9 */ - public static MethodHandle zero(Class<?> type) { + public static MethodHandle zero(Class<?> type) { Objects.requireNonNull(type); return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); } @@ -3403,7 +3401,8 @@ throw newIllegalArgumentException("illegal pos", pos, newTypes); } addTypes = addTypes.subList(pos, add); - add -= pos; assert(addTypes.size() == add); + add -= pos; + assert(addTypes.size() == add); } // Do not add types which already match the existing arguments. if (match > add || !oldTypes.equals(addTypes.subList(0, match))) { @@ -3413,7 +3412,8 @@ throw newIllegalArgumentException("argument lists do not match", oldTypes, newTypes); } addTypes = addTypes.subList(match, add); - add -= match; assert(addTypes.size() == add); + add -= match; + assert(addTypes.size() == add); // newTypes: ( P*[pos], M*[match], A*[add] ) // target: ( S*[skip], M*[match] ) MethodHandle adapter = target; @@ -3423,26 +3423,37 @@ // adapter: (S*[skip], M*[match], A*[add] ) if (pos > 0) { adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos)); - } + } // adapter: (S*[skip], P*[pos], M*[match], A*[add] ) return adapter; } /** - * Adapts a target method handle to match the given parameter type list, if necessary, by adding dummy arguments. - * Some leading parameters are first skipped; they will be left unchanged and are otherwise ignored. - * The remaining types in the target's parameter type list must be contained as a sub-list of the given type list, - * at the given position. - * Any non-matching parameter types (before or after the matching sub-list) are inserted in corresponding - * positions of the target method handle's parameters, as if by {@link #dropArguments}. - * (More precisely, elements in the new list before {@code pos} are inserted into the target list at {@code skip}, - * while elements in the new list after the match beginning at {@code pos} are inserted at the end of the - * target list.) - * The target's return type will be unchanged. + * Adapts a target method handle to match the given parameter type list. If necessary, adds dummy arguments. Some + * leading parameters can be skipped before matching begins. The remaining types in the {@code target}'s parameter + * type list must be a sub-list of the {@code newTypes} type list at the starting position {@code pos}. The + * resulting handle will have the target handle's parameter type list, with any non-matching parameter types (before + * or after the matching sub-list) inserted in corresponding positions of the target's original parameters, as if by + * {@link #dropArguments(MethodHandle, int, Class[])}. + * <p> + * The resulting handle will have the same return type as the target handle. + * <p> + * In more formal terms, assume these two type lists:<ul> + * <li>The target handle has the parameter type list {@code S..., M...}, with as many types in {@code S} as + * indicated by {@code skip}. The {@code M} types are those that are supposed to match part of the given type list, + * {@code newTypes}. + * <li>The {@code newTypes} list contains types {@code P..., M..., A...}, with as many types in {@code P} as + * indicated by {@code pos}. The {@code M} types are precisely those that the {@code M} types in the target handle's + * parameter type list are supposed to match. The types in {@code A} are additional types found after the matching + * sub-list. + * </ul> + * Given these assumptions, the result of an invocation of {@code dropArgumentsToMatch} will have the parameter type + * list {@code S..., P..., M..., A...}, with the {@code P} and {@code A} types inserted as if by + * {@link #dropArguments(MethodHandle, int, Class[])}. + * <p> * @apiNote - * Two method handles whose argument lists are "effectively identical" (i.e., identical - * in a common prefix) may be mutually converted to a common type - * by two calls to {@code dropArgumentsToMatch}, as follows: + * Two method handles whose argument lists are "effectively identical" (i.e., identical in a common prefix) may be + * mutually converted to a common type by two calls to {@code dropArgumentsToMatch}, as follows: * <blockquote><pre>{@code import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodType.*; @@ -3461,14 +3472,15 @@ * }</pre></blockquote> * @param target the method handle to adapt * @param skip number of targets parameters to disregard (they will be unchanged) - * @param newTypes the desired argument list of the method handle + * @param newTypes the list of types to match {@code target}'s parameter type list to * @param pos place in {@code newTypes} where the non-skipped target parameters must occur * @return a possibly adapted method handle * @throws NullPointerException if either argument is null * @throws IllegalArgumentException if any element of {@code newTypes} is {@code void.class}, * or if {@code skip} is negative or greater than the arity of the target, * or if {@code pos} is negative or greater than the newTypes list size, - * or if the non-skipped target parameter types match the new types at {@code pos} + * or if {@code newTypes} does not contain the {@code target}'s non-skipped parameter types at position + * {@code pos}. * @since 9 */ public static @@ -3922,6 +3934,113 @@ return foldArguments(target, 0, combiner); } + /** + * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then + * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just + * before the folded arguments. + * <p> + * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the + * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a + * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position + * 0. + * <p> + * @apiNote Example: + * <blockquote><pre>{@code + import static java.lang.invoke.MethodHandles.*; + import static java.lang.invoke.MethodType.*; + ... + MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class, + "println", methodType(void.class, String.class)) + .bindTo(System.out); + MethodHandle cat = lookup().findVirtual(String.class, + "concat", methodType(String.class, String.class)); + assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); + MethodHandle catTrace = foldArguments(cat, 1, trace); + // also prints "jum": + assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); + * }</pre></blockquote> + * <p>Here is pseudocode for the resulting adapter. In the code, {@code T} + * represents the result type of the {@code target} and resulting adapter. + * {@code V}/{@code v} represent the type and value of the parameter and argument + * of {@code target} that precedes the folding position; {@code V} also is + * the result type of the {@code combiner}. {@code A}/{@code a} denote the + * types and values of the {@code N} parameters and arguments at the folding + * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types + * and values of the {@code target} parameters and arguments that precede and + * follow the folded parameters and arguments starting at {@code pos}, + * respectively. + * <blockquote><pre>{@code + * // there are N arguments in A... + * T target(Z..., V, A[N]..., B...); + * V combiner(A...); + * T adapter(Z... z, A... a, B... b) { + * V v = combiner(a...); + * return target(z..., v, a..., b...); + * } + * // and if the combiner has a void return: + * T target2(Z..., A[N]..., B...); + * void combiner2(A...); + * T adapter2(Z... z, A... a, B... b) { + * combiner2(a...); + * return target2(z..., a..., b...); + * } + * }</pre></blockquote> + * <p> + * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector + * variable-arity method handle}, even if the original target method handle was. + * + * @param target the method handle to invoke after arguments are combined + * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code + * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}. + * @param combiner method handle to call initially on the incoming arguments + * @return method handle which incorporates the specified argument folding logic + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if either of the following two conditions holds: + * (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position + * {@code pos} of the target signature; + * (2) the {@code N} argument types at position {@code pos} of the target signature (skipping one matching + * the {@code combiner}'s return type) are not identical with the argument types of {@code combiner}. + * + * @see #foldArguments(MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType()); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + + /** + * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the + * added capability of selecting the arguments from the targets parameters + * to call the combiner with. This allows us to avoid some simple cases of + * permutations and padding the combiner with dropArguments to select the + * right argument, which may ultimately produce fewer intermediaries. + */ + static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { int foldArgs = combinerType.parameterCount(); Class<?> rtype = combinerType.returnType(); @@ -4125,32 +4244,69 @@ * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and * delivers the loop's result, which is the return value of the resulting handle. * <p> - * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * Intuitively, every loop is formed by one or more "clauses", each specifying a local <em>iteration variable</em> and/or a loop * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in - * terms of method handles, each clause will determine four actions:<ul> - * <li>Before the loop executes, the initialization of an iteration variable or loop invariant local. - * <li>When a clause executes, an update step for the iteration variable. - * <li>When a clause executes, a predicate execution to test for loop exit. - * <li>If a clause causes a loop exit, a finalizer execution to compute the loop's return value. + * terms of method handles, each clause will specify up to four independent actions:<ul> + * <li><em>init:</em> Before the loop executes, the initialization of an iteration variable {@code v} of type {@code V}. + * <li><em>step:</em> When a clause executes, an update step for the iteration variable {@code v}. + * <li><em>pred:</em> When a clause executes, a predicate execution to test for loop exit. + * <li><em>fini:</em> If a clause causes a loop exit, a finalizer execution to compute the loop's return value. * </ul> + * The full sequence of all iteration variable types, in clause order, will be notated as {@code (V...)}. + * The values themselves will be {@code (v...)}. When we speak of "parameter lists", we will usually + * be referring to types, but in some contexts (describing execution) the lists will be of actual values. * <p> * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in * this case. See below for a detailed description. * <p> - * Each clause function, with the exception of clause initializers, is able to observe the entire loop state, - * because it will be passed <em>all</em> current iteration variable values, as well as all incoming loop - * parameters. Most clause functions will not need all of this information, but they will be formally connected as - * if by {@link #dropArguments}. + * <em>Parameters optional everywhere:</em> + * Each clause function is allowed but not required to accept a parameter for each iteration variable {@code v}. + * As an exception, the init functions cannot take any {@code v} parameters, + * because those values are not yet computed when the init functions are executed. + * Any clause function may neglect to take any trailing subsequence of parameters it is entitled to take. + * In fact, any clause function may take no arguments at all. * <p> + * <em>Loop parameters:</em> + * A clause function may take all the iteration variable values it is entitled to, in which case + * it may also take more trailing parameters. Such extra values are called <em>loop parameters</em>, + * with their types and values notated as {@code (A...)} and {@code (a...)}. + * These become the parameters of the resulting loop handle, to be supplied whenever the loop is executed. + * (Since init functions do not accept iteration variables {@code v}, any parameter to an + * init function is automatically a loop parameter {@code a}.) + * As with iteration variables, clause functions are allowed but not required to accept loop parameters. + * These loop parameters act as loop-invariant values visible across the whole loop. + * <p> + * <em>Parameters visible everywhere:</em> + * Each non-init clause function is permitted to observe the entire loop state, because it can be passed the full + * list {@code (v... a...)} of current iteration variable values and incoming loop parameters. + * The init functions can observe initial pre-loop state, in the form {@code (a...)}. + * Most clause functions will not need all of this information, but they will be formally connected to it + * as if by {@link #dropArguments}. + * <a name="astar"></a> + * More specifically, we shall use the notation {@code (V*)} to express an arbitrary prefix of a full + * sequence {@code (V...)} (and likewise for {@code (v*)}, {@code (A*)}, {@code (a*)}). + * In that notation, the general form of an init function parameter list + * is {@code (A*)}, and the general form of a non-init function parameter list is {@code (V*)} or {@code (V... A*)}. + * <p> + * <em>Checking clause structure:</em> * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must" - * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met - * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means - * that they must be identical, or else one list must be a proper prefix of the other. + * corresponds to a place where {@link IllegalArgumentException} will be thrown if the required constraint is not + * met by the inputs to the loop combinator. + * <p> + * <em>Effectively identical sequences:</em> + * <a name="effid"></a> + * A parameter list {@code A} is defined to be <em>effectively identical</em> to another parameter list {@code B} + * if {@code A} and {@code B} are identical, or if {@code A} is shorter and is identical with a proper prefix of {@code B}. + * When speaking of an unordered set of parameter lists, we say they the set is "effectively identical" + * as a whole if the set contains a longest list, and all members of the set are effectively identical to + * that longest list. + * For example, any set of type sequences of the form {@code (V*)} is effectively identical, + * and the same is true if more sequences of the form {@code (V... A*)} are added. * <p> * <em>Step 0: Determine clause structure.</em><ol type="a"> - * <li>The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element. + * <li>The clause array (of type {@code MethodHandle[][]}) must be non-{@code null} and contain at least one element. * <li>The clause array may not contain {@code null}s or sub-arrays longer than four elements. * <li>Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length * four. Padding takes place by appending elements to the array. @@ -4158,30 +4314,35 @@ * <li>Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini". * </ol> * <p> - * <em>Step 1A: Determine iteration variables.</em><ol type="a"> - * <li>Examine init and step function return types, pairwise, to determine each clause's iteration variable type. - * <li>If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else - * use the common return type (they must be identical). + * <em>Step 1A: Determine iteration variable types {@code (V...)}.</em><ol type="a"> + * <li>The iteration variable type for each clause is determined using the clause's init and step return types. + * <li>If both functions are omitted, there is no iteration variable for the corresponding clause ({@code void} is + * used as the type to indicate that). If one of them is omitted, the other's return type defines the clause's + * iteration variable type. If both are given, the common return type (they must be identical) defines the clause's + * iteration variable type. * <li>Form the list of return types (in clause order), omitting all occurrences of {@code void}. - * <li>This list of types is called the "common prefix". + * <li>This list of types is called the "iteration variable types" ({@code (V...)}). * </ol> * <p> - * <em>Step 1B: Determine loop parameters.</em><ul> - * <li><b>If at least one init function is given,</b><ol type="a"> - * <li>Examine init function parameter lists. - * <li>Omitted init functions are deemed to have {@code null} parameter lists. - * <li>All init function parameter lists must be effectively identical. - * <li>The longest parameter list (which is necessarily unique) is called the "common suffix". - * </ol> - * <li><b>If no init function is given,</b><ol type="a"> - * <li>Examine the suffixes of the step, pred, and fini parameter lists, after removing the "common prefix". - * <li>The longest of these suffixes is taken as the "common suffix". - * </ol></ul> + * <em>Step 1B: Determine loop parameters {@code (A...)}.</em><ul> + * <li>Examine and collect init function parameter lists (which are of the form {@code (A*)}). + * <li>Examine and collect the suffixes of the step, pred, and fini parameter lists, after removing the iteration variable types. + * (They must have the form {@code (V... A*)}; collect the {@code (A*)} parts only.) + * <li>Do not collect suffixes from step, pred, and fini parameter lists that do not begin with all the iteration variable types. + * (These types will checked in step 2, along with all the clause function types.) + * <li>Omitted clause functions are ignored. (Equivalently, they are deemed to have empty parameter lists.) + * <li>All of the collected parameter lists must be effectively identical. + * <li>The longest parameter list (which is necessarily unique) is called the "external parameter list" ({@code (A...)}). + * <li>If there is no such parameter list, the external parameter list is taken to be the empty sequence. + * <li>The combined list consisting of iteration variable types followed by the external parameter types is called + * the "internal parameter list". + * </ul> * <p> * <em>Step 1C: Determine loop return type.</em><ol type="a"> * <li>Examine fini function return types, disregarding omitted fini functions. - * <li>If there are no fini functions, use {@code void} as the loop return type. - * <li>Otherwise, use the common return type of the fini functions; they must all be identical. + * <li>If there are no fini functions, the loop return type is {@code void}. + * <li>Otherwise, the common return type {@code R} of the fini functions (their return types must be identical) defines the loop return + * type. * </ol> * <p> * <em>Step 1D: Check other types.</em><ol type="a"> @@ -4190,69 +4351,107 @@ * </ol> * <p> * <em>Step 2: Determine parameter lists.</em><ol type="a"> - * <li>The parameter list for the resulting loop handle will be the "common suffix". - * <li>The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter - * lists are already effectively identical to the common suffix.) - * <li>The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix - * followed by the common suffix, called the "common parameter sequence". - * <li>Every non-init, non-omitted function parameter list must be effectively identical to the common parameter - * sequence. + * <li>The parameter list for the resulting loop handle will be the external parameter list {@code (A...)}. + * <li>The parameter list for init functions will be adjusted to the external parameter list. + * (Note that their parameter lists are already effectively identical to this list.) + * <li>The parameter list for every non-omitted, non-init (step, pred, and fini) function must be + * effectively identical to the internal parameter list {@code (V... A...)}. * </ol> * <p> * <em>Step 3: Fill in omitted functions.</em><ol type="a"> - * <li>If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate - * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a - * function which does nothing and returns {@code void}; it can be obtained from another constant function by - * {@linkplain MethodHandle#asType type conversion}.) + * <li>If an init function is omitted, use a {@linkplain #empty default value} for the clause's iteration variable + * type. * <li>If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void} * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.) - * <li>If a pred function is omitted, the corresponding fini function must also be omitted. * <li>If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far - * as this clause is concerned.) - * <li>If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the + * as this clause is concerned. Note that in such cases the corresponding fini function is unreachable.) + * <li>If a fini function is omitted, use a {@linkplain #empty default value} for the * loop return type. * </ol> * <p> * <em>Step 4: Fill in missing parameter types.</em><ol type="a"> - * <li>At this point, every init function parameter list is effectively identical to the common suffix, but some - * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by - * {@linkplain #dropArguments dropping arguments}. - * <li>At this point, every non-init function parameter list is effectively identical to the common parameter - * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end - * of the list by {@linkplain #dropArguments dropping arguments}. + * <li>At this point, every init function parameter list is effectively identical to the external parameter list {@code (A...)}, + * but some lists may be shorter. For every init function with a short parameter list, pad out the end of the list. + * <li>At this point, every non-init function parameter list is effectively identical to the internal parameter + * list {@code (V... A...)}, but some lists may be shorter. For every non-init function with a short parameter list, + * pad out the end of the list. + * <li>Argument lists are padded out by {@linkplain #dropArgumentsToMatch dropping unused trailing arguments}. * </ol> * <p> * <em>Final observations.</em><ol type="a"> * <li>After these steps, all clauses have been adjusted by supplying omitted functions and arguments. - * <li>All init functions have a common parameter type list, which the final loop handle will also have. - * <li>All fini functions have a common return type, which the final loop handle will also have. - * <li>All non-init functions have a common parameter type list, which is the common parameter sequence, of - * (non-{@code void}) iteration variables followed by loop parameters. - * <li>Each pair of init and step functions agrees in their return types. - * <li>Each non-init function will be able to observe the current values of all iteration variables, by means of the - * common prefix. + * <li>All init functions have a common parameter type list {@code (A...)}, which the final loop handle will also have. + * <li>All fini functions have a common return type {@code R}, which the final loop handle will also have. + * <li>All non-init functions have a common parameter type list {@code (V... A...)}, of + * (non-{@code void}) iteration variables {@code V} followed by loop parameters. + * <li>Each pair of init and step functions agrees in their return type {@code V}. + * <li>Each non-init function will be able to observe the current values {@code (v...)} of all iteration variables. + * <li>Every function will be able to observe the incoming values {@code (a...)} of all loop parameters. * </ol> * <p> + * <em>Example.</em> As a consequence of step 1A above, the {@code loop} combinator has the following property: + * <ul> + * <li>Given {@code N} clauses {@code Cn = {null, Sn, Pn}} with {@code n = 1..N}. + * <li>Suppose predicate handles {@code Pn} are either {@code null} or have no parameters. + * (Only one {@code Pn} has to be non-{@code null}.) + * <li>Suppose step handles {@code Sn} have signatures {@code (B1..BX)Rn}, for some constant {@code X>=N}. + * <li>Suppose {@code Q} is the count of non-void types {@code Rn}, and {@code (V1...VQ)} is the sequence of those types. + * <li>It must be that {@code Vn == Bn} for {@code n = 1..min(X,Q)}. + * <li>The parameter types {@code Vn} will be interpreted as loop-local state elements {@code (V...)}. + * <li>Any remaining types {@code BQ+1..BX} (if {@code Q<X}) will determine + * the resulting loop handle's parameter types {@code (A...)}. + * </ul> + * In this example, the loop handle parameters {@code (A...)} were derived from the step functions, + * which is natural if most of the loop computation happens in the steps. For some loops, + * the burden of computation might be heaviest in the pred functions, and so the pred functions + * might need to accept the loop parameter values. For loops with complex exit logic, the fini + * functions might need to accept loop parameters, and likewise for loops with complex entry logic, + * where the init functions will need the extra parameters. For such reasons, the rules for + * determining these parameters are as symmetric as possible, across all clause parts. + * In general, the loop parameters function as common invariant values across the whole + * loop, while the iteration variables function as common variant values, or (if there is + * no step function) as internal loop invariant temporaries. + * <p> * <em>Loop execution.</em><ol type="a"> - * <li>When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to + * <li>When the loop is called, the loop input values are saved in locals, to be passed to * every clause function. These locals are loop invariant. - * <li>Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values - * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity - * functions, as noted above). - * <li>All function executions (except init functions) will be passed the common parameter sequence, consisting of - * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order). + * <li>Each init function is executed in clause order (passing the external arguments {@code (a...)}) + * and the non-{@code void} values are saved (as the iteration variables {@code (v...)}) into locals. + * These locals will be loop varying (unless their steps behave as identity functions, as noted above). + * <li>All function executions (except init functions) will be passed the internal parameter list, consisting of + * the non-{@code void} iteration values {@code (v...)} (in clause order) and then the loop inputs {@code (a...)} + * (in argument order). * <li>The step and pred functions are then executed, in clause order (step before pred), until a pred function * returns {@code false}. - * <li>The non-{@code void} result from a step function call is used to update the corresponding loop variable. The - * updated value is immediately visible to all subsequent function calls. + * <li>The non-{@code void} result from a step function call is used to update the corresponding value in the + * sequence {@code (v...)} of loop variables. + * The updated value is immediately visible to all subsequent function calls. * <li>If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value - * is returned from the loop as a whole. + * (of type {@code R}) is returned from the loop as a whole. + * <li>If all the pred functions always return true, no fini function is ever invoked, and the loop cannot exit + * except by throwing an exception. * </ol> * <p> - * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the types / values - * of loop variables; {@code A}/{@code a}, those of arguments passed to the resulting loop; and {@code R}, the - * result types of finalizers as well as of the resulting loop. + * <em>Usage tips.</em> + * <ul> + * <li>Although each step function will receive the current values of <em>all</em> the loop variables, + * sometimes a step function only needs to observe the current value of its own variable. + * In that case, the step function may need to explicitly {@linkplain #dropArguments drop all preceding loop variables}. + * This will require mentioning their types, in an expression like {@code dropArguments(step, 0, V0.class, ...)}. + * <li>Loop variables are not required to vary; they can be loop invariant. A clause can create + * a loop invariant by a suitable init function with no step, pred, or fini function. This may be + * useful to "wire" an incoming loop argument into the step or pred function of an adjacent loop variable. + * <li>If some of the clause functions are virtual methods on an instance, the instance + * itself can be conveniently placed in an initial invariant loop "variable", using an initial clause + * like {@code new MethodHandle[]{identity(ObjType.class)}}. In that case, the instance reference + * will be the first iteration variable value, and it will be easy to use virtual + * methods as clause parts, since all of them will take a leading instance reference matching that value. + * </ul> + * <p> + * Here is pseudocode for the resulting loop handle. As above, {@code V} and {@code v} represent the types + * and values of loop variables; {@code A} and {@code a} represent arguments passed to the whole loop; + * and {@code R} is the common result type of all finalizers as well as of the resulting loop. * <blockquote><pre>{@code * V... init...(A...); * boolean pred...(V..., A...); @@ -4270,6 +4469,9 @@ * } * } * }</pre></blockquote> + * Note that the parameter type lists {@code (V...)} and {@code (A...)} have been expanded + * to their full length, even though individual clause functions may neglect to take them all. + * As noted above, missing parameters are filled in as if by {@link #dropArgumentsToMatch}. * <p> * @apiNote Example: * <blockquote><pre>{@code @@ -4286,6 +4488,43 @@ * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); * assertEquals(120, loop.invoke(5)); * }</pre></blockquote> + * The same example, dropping arguments and using combinators: + * <blockquote><pre>{@code + * // simplified implementation of the factorial function as a loop handle + * static int inc(int i) { return i + 1; } // drop acc, k + * static int mult(int i, int acc) { return i * acc; } //drop k + * static boolean cmp(int i, int k) { return i < k; } + * // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods + * // null initializer for counter, should initialize to 0 + * MethodHandle MH_one = MethodHandles.constant(int.class, 1); + * MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc + * MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i + * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; + * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; + * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + * assertEquals(720, loop.invoke(6)); + * }</pre></blockquote> + * A similar example, using a helper object to hold a loop parameter: + * <blockquote><pre>{@code + * // instance-based implementation of the factorial function as a loop handle + * static class FacLoop { + * final int k; + * FacLoop(int k) { this.k = k; } + * int inc(int i) { return i + 1; } + * int mult(int i, int acc) { return i * acc; } + * boolean pred(int i) { return i < k; } + * int fin(int i, int acc) { return acc; } + * } + * // assume MH_FacLoop is a handle to the constructor + * // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods + * // null initializer for counter, should initialize to 0 + * MethodHandle MH_one = MethodHandles.constant(int.class, 1); + * MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop}; + * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc}; + * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin}; + * MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause); + * assertEquals(5040, loop.invoke(7)); + * }</pre></blockquote> * * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above. * @@ -4301,7 +4540,7 @@ */ public static MethodHandle loop(MethodHandle[]... clauses) { // Step 0: determine clause structure. - checkLoop0(clauses); + loopChecks0(clauses); List<MethodHandle> init = new ArrayList<>(); List<MethodHandle> step = new ArrayList<>(); @@ -4318,7 +4557,7 @@ assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1; final int nclauses = init.size(); - // Step 1A: determine iteration variables. + // Step 1A: determine iteration variables (V...). final List<Class<?>> iterationVariableTypes = new ArrayList<>(); for (int i = 0; i < nclauses; ++i) { MethodHandle in = init.get(i); @@ -4326,7 +4565,7 @@ if (in == null && st == null) { iterationVariableTypes.add(void.class); } else if (in != null && st != null) { - checkLoop1a(i, in, st); + loopChecks1a(i, in, st); iterationVariableTypes.add(in.type().returnType()); } else { iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType()); @@ -4335,20 +4574,20 @@ final List<Class<?>> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class). collect(Collectors.toList()); - // Step 1B: determine loop parameters. + // Step 1B: determine loop parameters (A...). final List<Class<?>> commonSuffix = buildCommonSuffix(init, step, pred, fini, commonPrefix.size()); - checkLoop1b(init, commonSuffix); + loopChecks1b(init, commonSuffix); // Step 1C: determine loop return type. // Step 1D: check other types. final Class<?> loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type). map(MethodType::returnType).findFirst().orElse(void.class); - checkLoop1cd(pred, fini, loopReturnType); + loopChecks1cd(pred, fini, loopReturnType); // Step 2: determine parameter lists. final List<Class<?>> commonParameterSequence = new ArrayList<>(commonPrefix); commonParameterSequence.addAll(commonSuffix); - checkLoop2(step, pred, fini, commonParameterSequence); + loopChecks2(step, pred, fini, commonParameterSequence); // Step 3: fill in omitted functions. for (int i = 0; i < nclauses; ++i) { @@ -4382,6 +4621,79 @@ return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, finit, fstep, fpred, ffini); } + private static void loopChecks0(MethodHandle[][] clauses) { + if (clauses == null || clauses.length == 0) { + throw newIllegalArgumentException("null or no clauses passed"); + } + if (Stream.of(clauses).anyMatch(Objects::isNull)) { + throw newIllegalArgumentException("null clauses are not allowed"); + } + if (Stream.of(clauses).anyMatch(c -> c.length > 4)) { + throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements."); + } + } + + private static void loopChecks1a(int i, MethodHandle in, MethodHandle st) { + if (in.type().returnType() != st.type().returnType()) { + throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(), + st.type().returnType()); + } + } + + private static List<Class<?>> longestParameterList(Stream<MethodHandle> mhs, int skipSize) { + final List<Class<?>> empty = List.of(); + final List<Class<?>> longest = mhs.filter(Objects::nonNull). + // take only those that can contribute to a common suffix because they are longer than the prefix + map(MethodHandle::type). + filter(t -> t.parameterCount() > skipSize). + map(MethodType::parameterList). + reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty); + return longest.size() == 0 ? empty : longest.subList(skipSize, longest.size()); + } + + private static List<Class<?>> longestParameterList(List<List<Class<?>>> lists) { + final List<Class<?>> empty = List.of(); + return lists.stream().reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty); + } + + private static List<Class<?>> buildCommonSuffix(List<MethodHandle> init, List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, int cpSize) { + final List<Class<?>> longest1 = longestParameterList(Stream.of(step, pred, fini).flatMap(List::stream), cpSize); + final List<Class<?>> longest2 = longestParameterList(init.stream(), 0); + return longestParameterList(Arrays.asList(longest1, longest2)); + } + + private static void loopChecks1b(List<MethodHandle> init, List<Class<?>> commonSuffix) { + if (init.stream().filter(Objects::nonNull).map(MethodHandle::type). + anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonSuffix))) { + throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init + + " (common suffix: " + commonSuffix + ")"); + } + } + + private static void loopChecks1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) { + if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != loopReturnType)) { + throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " + + loopReturnType + ")"); + } + + if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) { + throw newIllegalArgumentException("no predicate found", pred); + } + if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != boolean.class)) { + throw newIllegalArgumentException("predicates must have boolean return type", pred); + } + } + + private static void loopChecks2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) { + if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type). + anyMatch(t -> !t.effectivelyIdenticalParameters(0, commonParameterSequence))) { + throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step + + "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")"); + } + } + private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) { return hs.stream().map(h -> { int pc = h.type().parameterCount(); @@ -4395,26 +4707,60 @@ } /** - * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for - * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * Constructs a {@code while} loop from an initializer, a body, and a predicate. + * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * <p> + * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this + * method will, in each iteration, first evaluate the predicate and then execute its body (if the predicate + * evaluates to {@code true}). + * The loop will terminate once the predicate evaluates to {@code false} (the body will not be executed in this case). + * <p> + * The {@code init} handle describes the initial value of an additional optional loop-local variable. + * In each iteration, this loop-local variable, if present, will be passed to the {@code body} + * and updated with the value returned from its invocation. The result of loop execution will be + * the final value of the additional loop-local variable (if present). * <p> - * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. - * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle - * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code - * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) - * generic loop combinator}. + * The following rules hold for these argument handles:<ul> + * <li>The {@code body} handle must not be {@code null}; its type must be of the form + * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}. + * (In the {@code void} case, we assign the type {@code void} to the name {@code V}, + * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V} + * is quietly dropped from the parameter list, leaving {@code (A...)V}.) + * <li>The parameter list {@code (V A...)} of the body is called the <em>internal parameter list</em>. + * It will constrain the parameter lists of the other loop parts. + * <li>If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter + * list {@code (A...)} is called the <em>external parameter list</em>. + * <li>The body return type {@code V}, if non-{@code void}, determines the type of an + * additional state variable of the loop. + * The body must both accept and return a value of this type {@code V}. + * <li>If {@code init} is non-{@code null}, it must have return type {@code V}. + * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be + * <a href="MethodHandles.html#effid">effectively identical</a> + * to the external parameter list {@code (A...)}. + * <li>If {@code init} is {@code null}, the loop variable will be initialized to its + * {@linkplain #empty default value}. + * <li>The {@code pred} handle must not be {@code null}. It must have {@code boolean} as its return type. + * Its parameter list (either empty or of the form {@code (V A*)}) must be + * effectively identical to the internal parameter list. + * </ul> + * <p> + * The resulting loop handle's result type and parameter signature are determined as follows:<ul> + * <li>The loop handle's result type is the result type {@code V} of the body. + * <li>The loop handle's parameter types are the types {@code (A...)}, + * from the external parameter list. + * </ul> * <p> * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument * passed to the loop. * <blockquote><pre>{@code - * V init(A); - * boolean pred(V, A); - * V body(V, A); - * V whileLoop(A a) { - * V v = init(a); - * while (pred(v, a)) { - * v = body(v, a); + * V init(A...); + * boolean pred(V, A...); + * V body(V, A...); + * V whileLoop(A... a...) { + * V v = init(a...); + * while (pred(v, a...)) { + * v = body(v, a...); * } * return v; * } @@ -4439,58 +4785,96 @@ * }</pre></blockquote> * * <p> - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: * <blockquote><pre>{@code * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { + * MethodHandle fini = (body.type().returnType() == void.class + * ? null : identity(body.type().returnType())); * MethodHandle[] - * checkExit = {null, null, pred, identity(init.type().returnType())}, - * varBody = {init, body}; + * checkExit = { null, null, pred, fini }, + * varBody = { init, body }; * return loop(checkExit, varBody); * } * }</pre></blockquote> * - * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's - * result type. Passing {@code null} or a {@code void} init function will make the loop's result type - * {@code void}. - * @param pred condition for the loop, which may not be {@code null}. - * @param body body of the loop, which may not be {@code null}. + * @param init optional initializer, providing the initial value of the loop variable. + * May be {@code null}, implying a default initial value. See above for other constraints. + * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See + * above for other constraints. + * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type. + * See above for other constraints. * - * @return the value of the loop variable as the loop terminates. - * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * @return a method handle implementing the {@code while} loop as described by the arguments. + * @throws IllegalArgumentException if the rules for the arguments are violated. + * @throws NullPointerException if {@code pred} or {@code body} are {@code null}. * - * @see MethodHandles#loop(MethodHandle[][]) + * @see #loop(MethodHandle[][]) + * @see #doWhileLoop(MethodHandle, MethodHandle, MethodHandle) * @since 9 */ public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { - MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) : - identity(init.type().returnType()); - MethodHandle[] checkExit = {null, null, pred, fin}; - MethodHandle[] varBody = {init, body}; + whileLoopChecks(init, pred, body); + MethodHandle fini = identityOrVoid(body.type().returnType()); + MethodHandle[] checkExit = { null, null, pred, fini }; + MethodHandle[] varBody = { init, body }; return loop(checkExit, varBody); } /** - * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper - * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. + * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * <p> + * The {@code pred} handle describes the loop condition; and {@code body}, its body. The loop resulting from this + * method will, in each iteration, first execute its body and then evaluate the predicate. + * The loop will terminate once the predicate evaluates to {@code false} after an execution of the body. + * <p> + * The {@code init} handle describes the initial value of an additional optional loop-local variable. + * In each iteration, this loop-local variable, if present, will be passed to the {@code body} + * and updated with the value returned from its invocation. The result of loop execution will be + * the final value of the additional loop-local variable (if present). * <p> - * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. - * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle - * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code - * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) - * generic loop combinator}. + * The following rules hold for these argument handles:<ul> + * <li>The {@code body} handle must not be {@code null}; its type must be of the form + * {@code (V A...)V}, where {@code V} is non-{@code void}, or else {@code (A...)void}. + * (In the {@code void} case, we assign the type {@code void} to the name {@code V}, + * and we will write {@code (V A...)V} with the understanding that a {@code void} type {@code V} + * is quietly dropped from the parameter list, leaving {@code (A...)V}.) + * <li>The parameter list {@code (V A...)} of the body is called the <em>internal parameter list</em>. + * It will constrain the parameter lists of the other loop parts. + * <li>If the iteration variable type {@code V} is dropped from the internal parameter list, the resulting shorter + * list {@code (A...)} is called the <em>external parameter list</em>. + * <li>The body return type {@code V}, if non-{@code void}, determines the type of an + * additional state variable of the loop. + * The body must both accept and return a value of this type {@code V}. + * <li>If {@code init} is non-{@code null}, it must have return type {@code V}. + * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be + * <a href="MethodHandles.html#effid">effectively identical</a> + * to the external parameter list {@code (A...)}. + * <li>If {@code init} is {@code null}, the loop variable will be initialized to its + * {@linkplain #empty default value}. + * <li>The {@code pred} handle must not be {@code null}. It must have {@code boolean} as its return type. + * Its parameter list (either empty or of the form {@code (V A*)}) must be + * effectively identical to the internal parameter list. + * </ul> + * <p> + * The resulting loop handle's result type and parameter signature are determined as follows:<ul> + * <li>The loop handle's result type is the result type {@code V} of the body. + * <li>The loop handle's parameter types are the types {@code (A...)}, + * from the external parameter list. + * </ul> * <p> * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument * passed to the loop. * <blockquote><pre>{@code - * V init(A); - * boolean pred(V, A); - * V body(V, A); - * V doWhileLoop(A a) { - * V v = init(a); + * V init(A...); + * boolean pred(V, A...); + * V body(V, A...); + * V doWhileLoop(A... a...) { + * V v = init(a...); * do { - * v = body(v, a); - * } while (pred(v, a)); + * v = body(v, a...); + * } while (pred(v, a...)); * return v; * } * }</pre></blockquote> @@ -4507,59 +4891,491 @@ * }</pre></blockquote> * * <p> - * @implSpec The implementation of this method is equivalent to: + * @apiNote The implementation of this method can be expressed as follows: * <blockquote><pre>{@code * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { - * MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) }; + * MethodHandle fini = (body.type().returnType() == void.class + * ? null : identity(body.type().returnType())); + * MethodHandle[] clause = { init, body, pred, fini }; * return loop(clause); * } * }</pre></blockquote> * + * @param init optional initializer, providing the initial value of the loop variable. + * May be {@code null}, implying a default initial value. See above for other constraints. + * @param body body of the loop, which may not be {@code null}. It controls the loop parameters and result type. + * See above for other constraints. + * @param pred condition for the loop, which may not be {@code null}. Its result type must be {@code boolean}. See + * above for other constraints. * - * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's - * result type. Passing {@code null} or a {@code void} init function will make the loop's result type - * {@code void}. - * @param pred condition for the loop, which may not be {@code null}. - * @param body body of the loop, which may not be {@code null}. + * @return a method handle implementing the {@code while} loop as described by the arguments. + * @throws IllegalArgumentException if the rules for the arguments are violated. + * @throws NullPointerException if {@code pred} or {@code body} are {@code null}. * - * @return the value of the loop variable as the loop terminates. - * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure - * - * @see MethodHandles#loop(MethodHandle[][]) + * @see #loop(MethodHandle[][]) + * @see #whileLoop(MethodHandle, MethodHandle, MethodHandle) * @since 9 */ public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { - MethodHandle fin = init == null || init.type().returnType() == void.class ? zero(void.class) : - identity(init.type().returnType()); - MethodHandle[] clause = {init, body, pred, fin}; + whileLoopChecks(init, pred, body); + MethodHandle fini = identityOrVoid(body.type().returnType()); + MethodHandle[] clause = {init, body, pred, fini }; return loop(clause); } + private static void whileLoopChecks(MethodHandle init, MethodHandle pred, MethodHandle body) { + Objects.requireNonNull(pred); + Objects.requireNonNull(body); + MethodType bodyType = body.type(); + Class<?> returnType = bodyType.returnType(); + List<Class<?>> innerList = bodyType.parameterList(); + List<Class<?>> outerList = innerList; + if (returnType == void.class) { + // OK + } else if (innerList.size() == 0 || innerList.get(0) != returnType) { + // leading V argument missing => error + MethodType expected = bodyType.insertParameterTypes(0, returnType); + throw misMatchedTypes("body function", bodyType, expected); + } else { + outerList = innerList.subList(1, innerList.size()); + } + MethodType predType = pred.type(); + if (predType.returnType() != boolean.class || + !predType.effectivelyIdenticalParameters(0, innerList)) { + throw misMatchedTypes("loop predicate", predType, methodType(boolean.class, innerList)); + } + if (init != null) { + MethodType initType = init.type(); + if (initType.returnType() != returnType || + !initType.effectivelyIdenticalParameters(0, outerList)) { + throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList)); + } + } + } + + /** + * Constructs a loop that runs a given number of iterations. + * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * <p> + * The number of iterations is determined by the {@code iterations} handle evaluation result. + * The loop counter {@code i} is an extra loop iteration variable of type {@code int}. + * It will be initialized to 0 and incremented by 1 in each iteration. + * <p> + * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + * <p> + * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + * <p> + * The following rules hold for the argument handles:<ul> + * <li>The {@code iterations} handle must not be {@code null}, and must return + * the type {@code int}, referred to here as {@code I} in parameter type lists. + * <li>The {@code body} handle must not be {@code null}; its type must be of the form + * {@code (V I A...)V}, where {@code V} is non-{@code void}, or else {@code (I A...)void}. + * (In the {@code void} case, we assign the type {@code void} to the name {@code V}, + * and we will write {@code (V I A...)V} with the understanding that a {@code void} type {@code V} + * is quietly dropped from the parameter list, leaving {@code (I A...)V}.) + * <li>The parameter list {@code (V I A...)} of the body contributes to a list + * of types called the <em>internal parameter list</em>. + * It will constrain the parameter lists of the other loop parts. + * <li>As a special case, if the body contributes only {@code V} and {@code I} types, + * with no additional {@code A} types, then the internal parameter list is extended by + * the argument types {@code A...} of the {@code iterations} handle. + * <li>If the iteration variable types {@code (V I)} are dropped from the internal parameter list, the resulting shorter + * list {@code (A...)} is called the <em>external parameter list</em>. + * <li>The body return type {@code V}, if non-{@code void}, determines the type of an + * additional state variable of the loop. + * The body must both accept a leading parameter and return a value of this type {@code V}. + * <li>If {@code init} is non-{@code null}, it must have return type {@code V}. + * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be + * <a href="MethodHandles.html#effid">effectively identical</a> + * to the external parameter list {@code (A...)}. + * <li>If {@code init} is {@code null}, the loop variable will be initialized to its + * {@linkplain #empty default value}. + * <li>The parameter list of {@code iterations} (of some form {@code (A*)}) must be + * effectively identical to the external parameter list {@code (A...)}. + * </ul> + * <p> + * The resulting loop handle's result type and parameter signature are determined as follows:<ul> + * <li>The loop handle's result type is the result type {@code V} of the body. + * <li>The loop handle's parameter types are the types {@code (A...)}, + * from the external parameter list. + * </ul> + * <p> + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. + * <blockquote><pre>{@code + * int iterations(A...); + * V init(A...); + * V body(V, int, A...); + * V countedLoop(A... a...) { + * int end = iterations(a...); + * V v = init(a...); + * for (int i = 0; i < end; ++i) { + * v = body(v, i, a...); + * } + * return v; + * } + * }</pre></blockquote> + * <p> + * @apiNote Example with a fully conformant body method: + * <blockquote><pre>{@code + * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; + * // => a variation on a well known theme + * static String step(String v, int counter, String init) { return "na " + v; } + * // assume MH_step is a handle to the method above + * MethodHandle fit13 = MethodHandles.constant(int.class, 13); + * MethodHandle start = MethodHandles.identity(String.class); + * MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step); + * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + * }</pre></blockquote> + * <p> + * @apiNote Example with the simplest possible body method type, + * and passing the number of iterations to the loop invocation: + * <blockquote><pre>{@code + * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; + * // => a variation on a well known theme + * static String step(String v, int counter ) { return "na " + v; } + * // assume MH_step is a handle to the method above + * MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class); + * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class); + * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i) -> "na " + v + * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!")); + * }</pre></blockquote> + * <p> + * @apiNote Example that treats the number of iterations, string to append to, and string to append + * as loop parameters: + * <blockquote><pre>{@code + * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s; + * // => a variation on a well known theme + * static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } + * // assume MH_step is a handle to the method above + * MethodHandle count = MethodHandles.identity(int.class); + * MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class); + * MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i, _, pre, _) -> pre + " " + v + * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!")); + * }</pre></blockquote> + * <p> + * @apiNote Example that illustrates the usage of {@link #dropArgumentsToMatch(MethodHandle, int, List, int)} + * to enforce a loop type: + * <blockquote><pre>{@code + * // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s; + * // => a variation on a well known theme + * static String step(String v, int counter, String pre) { return pre + " " + v; } + * // assume MH_step is a handle to the method above + * MethodType loopType = methodType(String.class, String.class, int.class, String.class); + * MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class), 0, loopType.parameterList(), 1); + * MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2); + * MethodHandle body = MethodHandles.dropArgumentsToMatch(MH_step, 2, loopType.parameterList(), 0); + * MethodHandle loop = MethodHandles.countedLoop(count, start, body); // (v, i, pre, _, _) -> pre + " " + v + * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!")); + * }</pre></blockquote> + * <p> + * @apiNote The implementation of this method can be expressed as follows: + * <blockquote><pre>{@code + * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + * return countedLoop(empty(iterations.type()), iterations, init, body); + * } + * }</pre></blockquote> + * + * @param iterations a non-{@code null} handle to return the number of iterations this loop should run. The handle's + * result type must be {@code int}. See above for other constraints. + * @param init optional initializer, providing the initial value of the loop variable. + * May be {@code null}, implying a default initial value. See above for other constraints. + * @param body body of the loop, which may not be {@code null}. + * It controls the loop parameters and result type in the standard case (see above for details). + * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter), + * and may accept any number of additional types. + * See above for other constraints. + * + * @return a method handle representing the loop. + * @throws NullPointerException if either of the {@code iterations} or {@code body} handles is {@code null}. + * @throws IllegalArgumentException if any argument violates the rules formulated above. + * + * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + return countedLoop(empty(iterations.type()), iterations, init, body); + } + /** - * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the - * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must - * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional - * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop - * combinator}. + * Constructs a loop that counts over a range of numbers. + * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * <p> + * The loop counter {@code i} is a loop iteration variable of type {@code int}. + * The {@code start} and {@code end} handles determine the start (inclusive) and end (exclusive) + * values of the loop counter. + * The loop counter will be initialized to the {@code int} value returned from the evaluation of the + * {@code start} handle and run to the value returned from {@code end} (exclusively) with a step width of 1. + * <p> + * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + * <p> + * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). * <p> - * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code - * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code - * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter, - * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described - * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + * The following rules hold for the argument handles:<ul> + * <li>The {@code start} and {@code end} handles must not be {@code null}, and must both return + * the common type {@code int}, referred to here as {@code I} in parameter type lists. + * <li>The {@code body} handle must not be {@code null}; its type must be of the form + * {@code (V I A...)V}, where {@code V} is non-{@code void}, or else {@code (I A...)void}. + * (In the {@code void} case, we assign the type {@code void} to the name {@code V}, + * and we will write {@code (V I A...)V} with the understanding that a {@code void} type {@code V} + * is quietly dropped from the parameter list, leaving {@code (I A...)V}.) + * <li>The parameter list {@code (V I A...)} of the body contributes to a list + * of types called the <em>internal parameter list</em>. + * It will constrain the parameter lists of the other loop parts. + * <li>As a special case, if the body contributes only {@code V} and {@code I} types, + * with no additional {@code A} types, then the internal parameter list is extended by + * the argument types {@code A...} of the {@code end} handle. + * <li>If the iteration variable types {@code (V I)} are dropped from the internal parameter list, the resulting shorter + * list {@code (A...)} is called the <em>external parameter list</em>. + * <li>The body return type {@code V}, if non-{@code void}, determines the type of an + * additional state variable of the loop. + * The body must both accept a leading parameter and return a value of this type {@code V}. + * <li>If {@code init} is non-{@code null}, it must have return type {@code V}. + * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be + * <a href="MethodHandles.html#effid">effectively identical</a> + * to the external parameter list {@code (A...)}. + * <li>If {@code init} is {@code null}, the loop variable will be initialized to its + * {@linkplain #empty default value}. + * <li>The parameter list of {@code start} (of some form {@code (A*)}) must be + * effectively identical to the external parameter list {@code (A...)}. + * <li>Likewise, the parameter list of {@code end} must be effectively identical + * to the external parameter list. + * </ul> + * <p> + * The resulting loop handle's result type and parameter signature are determined as follows:<ul> + * <li>The loop handle's result type is the result type {@code V} of the body. + * <li>The loop handle's parameter types are the types {@code (A...)}, + * from the external parameter list. + * </ul> * <p> * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. + * the second loop variable as well as the result type of the loop; and {@code A...}/{@code a...} represent + * arguments passed to the loop. + * <blockquote><pre>{@code + * int start(A...); + * int end(A...); + * V init(A...); + * V body(V, int, A...); + * V countedLoop(A... a...) { + * int e = end(a...); + * int s = start(a...); + * V v = init(a...); + * for (int i = s; i < e; ++i) { + * v = body(v, i, a...); + * } + * return v; + * } + * }</pre></blockquote> + * + * <p> + * @apiNote The implementation of this method can be expressed as follows: * <blockquote><pre>{@code - * int iterations(A); - * V init(A); - * V body(int, V, A); - * V countedLoop(A a) { - * int end = iterations(a); - * V v = init(a); - * for (int i = 0; i < end; ++i) { - * v = body(i, v, a); + * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class); + * // assume MH_increment and MH_predicate are handles to implementation-internal methods with + * // the following semantics: + * // MH_increment: (int limit, int counter) -> counter + 1 + * // MH_predicate: (int limit, int counter) -> counter < limit + * Class<?> counterType = start.type().returnType(); // int + * Class<?> returnType = body.type().returnType(); + * MethodHandle incr = MH_increment, pred = MH_predicate, retv = null; + * if (returnType != void.class) { // ignore the V variable + * incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i) + * pred = dropArguments(pred, 1, returnType); // ditto + * retv = dropArguments(identity(returnType), 0, counterType); // ignore limit + * } + * body = dropArguments(body, 0, counterType); // ignore the limit variable + * MethodHandle[] + * loopLimit = { end, null, pred, retv }, // limit = end(); i < limit || return v + * bodyClause = { init, body }, // v = init(); v = body(v, i) + * indexVar = { start, incr }; // i = start(); i = i + 1 + * return loop(loopLimit, bodyClause, indexVar); + * } + * }</pre></blockquote> + * + * @param start a non-{@code null} handle to return the start value of the loop counter, which must be {@code int}. + * See above for other constraints. + * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to + * {@code end-1}). The result type must be {@code int}. See above for other constraints. + * @param init optional initializer, providing the initial value of the loop variable. + * May be {@code null}, implying a default initial value. See above for other constraints. + * @param body body of the loop, which may not be {@code null}. + * It controls the loop parameters and result type in the standard case (see above for details). + * It must accept its own return type (if non-void) plus an {@code int} parameter (for the counter), + * and may accept any number of additional types. + * See above for other constraints. + * + * @return a method handle representing the loop. + * @throws NullPointerException if any of the {@code start}, {@code end}, or {@code body} handles is {@code null}. + * @throws IllegalArgumentException if any argument violates the rules formulated above. + * + * @see #countedLoop(MethodHandle, MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + countedLoopChecks(start, end, init, body); + Class<?> counterType = start.type().returnType(); // int, but who's counting? + Class<?> limitType = end.type().returnType(); // yes, int again + Class<?> returnType = body.type().returnType(); + MethodHandle incr = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep); + MethodHandle pred = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred); + MethodHandle retv = null; + if (returnType != void.class) { + incr = dropArguments(incr, 1, returnType); // (limit, v, i) => (limit, i) + pred = dropArguments(pred, 1, returnType); // ditto + retv = dropArguments(identity(returnType), 0, counterType); + } + body = dropArguments(body, 0, counterType); // ignore the limit variable + MethodHandle[] + loopLimit = { end, null, pred, retv }, // limit = end(); i < limit || return v + bodyClause = { init, body }, // v = init(); v = body(v, i) + indexVar = { start, incr }; // i = start(); i = i + 1 + return loop(loopLimit, bodyClause, indexVar); + } + + private static void countedLoopChecks(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + Objects.requireNonNull(start); + Objects.requireNonNull(end); + Objects.requireNonNull(body); + Class<?> counterType = start.type().returnType(); + if (counterType != int.class) { + MethodType expected = start.type().changeReturnType(int.class); + throw misMatchedTypes("start function", start.type(), expected); + } else if (end.type().returnType() != counterType) { + MethodType expected = end.type().changeReturnType(counterType); + throw misMatchedTypes("end function", end.type(), expected); + } + MethodType bodyType = body.type(); + Class<?> returnType = bodyType.returnType(); + List<Class<?>> innerList = bodyType.parameterList(); + // strip leading V value if present + int vsize = (returnType == void.class ? 0 : 1); + if (vsize != 0 && (innerList.size() == 0 || innerList.get(0) != returnType)) { + // argument list has no "V" => error + MethodType expected = bodyType.insertParameterTypes(0, returnType); + throw misMatchedTypes("body function", bodyType, expected); + } else if (innerList.size() <= vsize || innerList.get(vsize) != counterType) { + // missing I type => error + MethodType expected = bodyType.insertParameterTypes(vsize, counterType); + throw misMatchedTypes("body function", bodyType, expected); + } + List<Class<?>> outerList = innerList.subList(vsize + 1, innerList.size()); + if (outerList.isEmpty()) { + // special case; take lists from end handle + outerList = end.type().parameterList(); + innerList = bodyType.insertParameterTypes(vsize + 1, outerList).parameterList(); + } + MethodType expected = methodType(counterType, outerList); + if (!start.type().effectivelyIdenticalParameters(0, outerList)) { + throw misMatchedTypes("start parameter types", start.type(), expected); + } + if (end.type() != start.type() && + !end.type().effectivelyIdenticalParameters(0, outerList)) { + throw misMatchedTypes("end parameter types", end.type(), expected); + } + if (init != null) { + MethodType initType = init.type(); + if (initType.returnType() != returnType || + !initType.effectivelyIdenticalParameters(0, outerList)) { + throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList)); + } + } + } + + /** + * Constructs a loop that ranges over the values produced by an {@code Iterator<T>}. + * This is a convenience wrapper for the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + * <p> + * The iterator itself will be determined by the evaluation of the {@code iterator} handle. + * Each value it produces will be stored in a loop iteration variable of type {@code T}. + * <p> + * If the {@code body} handle returns a non-{@code void} type {@code V}, a leading loop iteration variable + * of that type is also present. This variable is initialized using the optional {@code init} handle, + * or to the {@linkplain #empty default value} of type {@code V} if that handle is {@code null}. + * <p> + * In each iteration, the iteration variables are passed to an invocation of the {@code body} handle. + * A non-{@code void} value returned from the body (of type {@code V}) updates the leading + * iteration variable. + * The result of the loop handle execution will be the final {@code V} value of that variable + * (or {@code void} if there is no {@code V} variable). + * <p> + * The following rules hold for the argument handles:<ul> + * <li>The {@code body} handle must not be {@code null}; its type must be of the form + * {@code (V T A...)V}, where {@code V} is non-{@code void}, or else {@code (T A...)void}. + * (In the {@code void} case, we assign the type {@code void} to the name {@code V}, + * and we will write {@code (V T A...)V} with the understanding that a {@code void} type {@code V} + * is quietly dropped from the parameter list, leaving {@code (T A...)V}.) + * <li>The parameter list {@code (V T A...)} of the body contributes to a list + * of types called the <em>internal parameter list</em>. + * It will constrain the parameter lists of the other loop parts. + * <li>As a special case, if the body contributes only {@code V} and {@code T} types, + * with no additional {@code A} types, then the internal parameter list is extended by + * the argument types {@code A...} of the {@code iterator} handle; if it is {@code null} the + * single type {@code Iterable} is added and constitutes the {@code A...} list. + * <li>If the iteration variable types {@code (V T)} are dropped from the internal parameter list, the resulting shorter + * list {@code (A...)} is called the <em>external parameter list</em>. + * <li>The body return type {@code V}, if non-{@code void}, determines the type of an + * additional state variable of the loop. + * The body must both accept a leading parameter and return a value of this type {@code V}. + * <li>If {@code init} is non-{@code null}, it must have return type {@code V}. + * Its parameter list (of some <a href="MethodHandles.html#astar">form {@code (A*)}</a>) must be + * <a href="MethodHandles.html#effid">effectively identical</a> + * to the external parameter list {@code (A...)}. + * <li>If {@code init} is {@code null}, the loop variable will be initialized to its + * {@linkplain #empty default value}. + * <li>If the {@code iterator} handle is non-{@code null}, it must have the return + * type {@code java.util.Iterator} or a subtype thereof. + * The iterator it produces when the loop is executed will be assumed + * to yield values which can be converted to type {@code T}. + * <li>The parameter list of an {@code iterator} that is non-{@code null} (of some form {@code (A*)}) must be + * effectively identical to the external parameter list {@code (A...)}. + * <li>If {@code iterator} is {@code null} it defaults to a method handle which behaves + * like {@link java.lang.Iterable#iterator()}. In that case, the internal parameter list + * {@code (V T A...)} must have at least one {@code A} type, and the default iterator + * handle parameter is adjusted to accept the leading {@code A} type, as if by + * the {@link MethodHandle#asType asType} conversion method. + * The leading {@code A} type must be {@code Iterable} or a subtype thereof, or an array type. + * This conversion step, done at loop construction time, must not throw a {@code WrongMethodTypeException}. + * </ul> + * <p> + * The type {@code T} may be either a primitive or reference. + * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type {@code Iterator}, + * the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body} to {@code Object} + * as if by the {@link MethodHandle#asType asType} conversion method. + * Therefore, if an iterator of the wrong type appears as the loop is executed, runtime exceptions may occur + * as the result of dynamic conversions performed by {@link MethodHandle#asType(MethodType)}. + * <p> + * The resulting loop handle's result type and parameter signature are determined as follows:<ul> + * <li>The loop handle's result type is the result type {@code V} of the body. + * <li>The loop handle's parameter types are the types {@code (A...)}, + * from the external parameter list. + * </ul> + * <p> + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the + * structure the loop iterates over, and {@code A...}/{@code a...} represent arguments passed to the loop. + * <blockquote><pre>{@code + * Iterator<T> iterator(A...); // defaults to Iterable::iterator + * V init(A...); + * V body(V,T,A...); + * V iteratedLoop(A... a...) { + * Iterator<T> it = iterator(a...); + * V v = init(a...); + * for (T t : it) { + * v = body(v, t, a...); * } * return v; * } @@ -4567,243 +5383,164 @@ * <p> * @apiNote Example: * <blockquote><pre>{@code - * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; - * // => a variation on a well known theme - * static String start(String arg) { return arg; } - * static String step(int counter, String v, String arg) { return "na " + v; } - * // assume MH_start and MH_step are handles to the two methods above - * MethodHandle fit13 = MethodHandles.constant(int.class, 13); - * MethodHandle loop = MethodHandles.countedLoop(fit13, MH_start, MH_step); - * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); - * }</pre></blockquote> - * - * <p> - * @implSpec The implementation of this method is equivalent to: - * <blockquote><pre>{@code - * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { - * return countedLoop(null, iterations, init, body); // null => constant zero - * } - * }</pre></blockquote> - * - * @param iterations a handle to return the number of iterations this loop should run. - * @param init initializer for additional loop state. This determines the loop's result type. - * Passing {@code null} or a {@code void} init function will make the loop's result type - * {@code void}. - * @param body the body of the loop, which must not be {@code null}. - * It must accept an initial {@code int} parameter (for the counter), and then any - * additional loop-local variable plus loop parameters. - * - * @return a method handle representing the loop. - * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure - * - * @since 9 - */ - public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { - return countedLoop(null, iterations, init, body); - } - - /** - * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be - * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the - * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code - * body} function in each iteration; it has to accept an initial {@code int} parameter - * for that. The result of the loop execution is the final value of the additional local state - * obtained by running {@code init}. - * This is a - * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. - * <p> - * The constraints for the {@code init} and {@code body} handles are the same as for {@link - * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles - * must return an {@code int} and accept the same parameters as {@code init}. - * <p> - * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument - * passed to the loop. - * <blockquote><pre>{@code - * int start(A); - * int end(A); - * V init(A); - * V body(int, V, A); - * V countedLoop(A a) { - * int s = start(a); - * int e = end(a); - * V v = init(a); - * for (int i = s; i < e; ++i) { - * v = body(i, v, a); - * } - * return v; - * } - * }</pre></blockquote> - * - * <p> - * @implSpec The implementation of this method is equivalent to: - * <blockquote><pre>{@code - * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { - * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class); - * // assume MH_increment and MH_lessThan are handles to x+1 and x<y of type int, - * // assume MH_decrement is a handle to x-1 of type int - * MethodHandle[] - * indexVar = {start, MH_increment}, // i = start; i = i+1 - * loopLimit = {end, null, - * filterArgument(MH_lessThan, 0, MH_decrement), returnVar}, // i-1<end - * bodyClause = {init, - * filterArgument(dropArguments(body, 1, int.class), 0, MH_decrement}; // v = body(i-1, v) - * return loop(indexVar, loopLimit, bodyClause); - * } - * }</pre></blockquote> - * - * @param start a handle to return the start value of the loop counter. - * If it is {@code null}, a constant zero is assumed. - * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}). - * @param init initializer for additional loop state. This determines the loop's result type. - * Passing {@code null} or a {@code void} init function will make the loop's result type - * {@code void}. - * @param body the body of the loop, which must not be {@code null}. - * It must accept an initial {@code int} parameter (for the counter), and then any - * additional loop-local variable plus loop parameters. - * - * @return a method handle representing the loop. - * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure - * - * @since 9 - */ - public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { - Class<?> resultType; - MethodHandle actualInit; - if (init == null) { - resultType = body == null ? void.class : body.type().returnType(); - actualInit = empty(methodType(resultType)); - } else { - resultType = init.type().returnType(); - actualInit = init; - } - MethodHandle defaultResultHandle = resultType == void.class ? zero(void.class) : identity(resultType); - MethodHandle actualBody = body == null ? dropArguments(defaultResultHandle, 0, int.class) : body; - MethodHandle returnVar = dropArguments(defaultResultHandle, 0, int.class, int.class); - MethodHandle actualEnd = end == null ? constant(int.class, 0) : end; - MethodHandle decr = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_decrementCounter); - MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)}; - MethodHandle[] loopLimit = {actualEnd, null, - filterArgument(MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), 0, decr), - returnVar}; - MethodHandle[] bodyClause = {actualInit, filterArgument(dropArguments(actualBody, 1, int.class), 0, decr)}; - return loop(indexVar, loopLimit, bodyClause); - } - - /** - * Constructs a loop that ranges over the elements produced by an {@code Iterator<T>}. - * The iterator will be produced by the evaluation of the {@code iterator} handle. - * This handle must have {@link java.util.Iterator} as its return type. - * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead, - * and will be applied to a leading argument of the loop handle. - * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter. - * The result of the loop execution is the final value of the additional local state - * obtained by running {@code init}. - * <p> - * This is a convenience wrapper for the - * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body} - * handle follow directly from those described for the latter. - * <p> - * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of - * the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the - * structure the loop iterates over, and {@code A}/{@code a}, that of the argument passed to the loop. - * <blockquote><pre>{@code - * Iterator<T> iterator(A); // defaults to Iterable::iterator - * V init(A); - * V body(T,V,A); - * V iteratedLoop(A a) { - * Iterator<T> it = iterator(a); - * V v = init(a); - * for (T t : it) { - * v = body(t, v, a); - * } - * return v; - * } - * }</pre></blockquote> - * <p> - * The type {@code T} may be either a primitive or reference. - * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type - * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body} - * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method. - * Therefore, if an iterator of the wrong type appears as the loop is executed, - * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}. - * <p> - * @apiNote Example: - * <blockquote><pre>{@code - * // reverse a list - * static List<String> reverseStep(String e, List<String> r, List<String> l) { + * // get an iterator from a list + * static List<String> reverseStep(List<String> r, String e) { * r.add(0, e); * return r; * } - * static List<String> newArrayList(List<String> l) { return new ArrayList<>(); } - * // assume MH_reverseStep, MH_newArrayList are handles to the above methods + * static List<String> newArrayList() { return new ArrayList<>(); } + * // assume MH_reverseStep and MH_newArrayList are handles to the above methods * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep); * List<String> list = Arrays.asList("a", "b", "c", "d", "e"); * List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a"); * assertEquals(reversedList, (List<String>) loop.invoke(list)); * }</pre></blockquote> * <p> - * @implSpec The implementation of this method is equivalent to (excluding error handling): + * @apiNote The implementation of this method can be expressed approximately as follows: * <blockquote><pre>{@code * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) { - * // assume MH_next and MH_hasNext are handles to methods of Iterator - * Class<?> itype = iterator.type().returnType(); - * Class<?> ttype = body.type().parameterType(0); - * MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype); + * // assume MH_next, MH_hasNext, MH_startIter are handles to methods of Iterator/Iterable + * Class<?> returnType = body.type().returnType(); + * Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1); * MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype)); + * MethodHandle retv = null, step = body, startIter = iterator; + * if (returnType != void.class) { + * // the simple thing first: in (I V A...), drop the I to get V + * retv = dropArguments(identity(returnType), 0, Iterator.class); + * // body type signature (V T A...), internal loop types (I V A...) + * step = swapArguments(body, 0, 1); // swap V <-> T + * } + * if (startIter == null) startIter = MH_getIter; * MethodHandle[] - * iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext) - * bodyClause = {init, filterArgument(body, 0, nextVal)}; // v = body(t, v, a); + * iterVar = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext()) + * bodyClause = { init, filterArguments(step, 0, nextVal) }; // v = body(v, t, a) * return loop(iterVar, bodyClause); * } * }</pre></blockquote> * - * @param iterator a handle to return the iterator to start the loop. - * The handle must have {@link java.util.Iterator} as its return type. - * Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first - * incoming value. - * @param init initializer for additional loop state. This determines the loop's result type. - * Passing {@code null} or a {@code void} init function will make the loop's result type - * {@code void}. - * @param body the body of the loop, which must not be {@code null}. - * It must accept an initial {@code T} parameter (for the iterated values), and then any - * additional loop-local variable plus loop parameters. + * @param iterator an optional handle to return the iterator to start the loop. + * If non-{@code null}, the handle must return {@link java.util.Iterator} or a subtype. + * See above for other constraints. + * @param init optional initializer, providing the initial value of the loop variable. + * May be {@code null}, implying a default initial value. See above for other constraints. + * @param body body of the loop, which may not be {@code null}. + * It controls the loop parameters and result type in the standard case (see above for details). + * It must accept its own return type (if non-void) plus a {@code T} parameter (for the iterated values), + * and may accept any number of additional types. + * See above for other constraints. * * @return a method handle embodying the iteration loop functionality. - * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * @throws NullPointerException if the {@code body} handle is {@code null}. + * @throws IllegalArgumentException if any argument violates the above requirements. * * @since 9 */ public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) { - checkIteratedLoop(iterator, body); - Class<?> resultType = init == null ? - body == null ? void.class : body.type().returnType() : - init.type().returnType(); - boolean voidResult = resultType == void.class; - - MethodHandle initIterator; - if (iterator == null) { - MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator); - initIterator = initit.asType(initit.type().changeParameterType(0, - body.type().parameterType(voidResult ? 1 : 2))); - } else { - initIterator = iterator.asType(iterator.type().changeReturnType(Iterator.class)); + Class<?> iterableType = iteratedLoopChecks(iterator, init, body); + Class<?> returnType = body.type().returnType(); + MethodHandle hasNext = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred); + MethodHandle nextRaw = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext); + MethodHandle startIter; + MethodHandle nextVal; + { + MethodType iteratorType; + if (iterator == null) { + // derive argument type from body, if available, else use Iterable + startIter = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator); + iteratorType = startIter.type().changeParameterType(0, iterableType); + } else { + // force return type to the internal iterator class + iteratorType = iterator.type().changeReturnType(Iterator.class); + startIter = iterator; + } + Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1); + MethodType nextValType = nextRaw.type().changeReturnType(ttype); + + // perform the asType transforms under an exception transformer, as per spec.: + try { + startIter = startIter.asType(iteratorType); + nextVal = nextRaw.asType(nextValType); + } catch (WrongMethodTypeException ex) { + throw new IllegalArgumentException(ex); + } + } + + MethodHandle retv = null, step = body; + if (returnType != void.class) { + // the simple thing first: in (I V A...), drop the I to get V + retv = dropArguments(identity(returnType), 0, Iterator.class); + // body type signature (V T A...), internal loop types (I V A...) + step = swapArguments(body, 0, 1); // swap V <-> T } - Class<?> ttype = body.type().parameterType(0); - - MethodHandle returnVar = - dropArguments(voidResult ? zero(void.class) : identity(resultType), 0, Iterator.class); - MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext); - MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype)); - - MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred), - returnVar}; - MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)}; - + MethodHandle[] + iterVar = { startIter, null, hasNext, retv }, + bodyClause = { init, filterArgument(step, 0, nextVal) }; return loop(iterVar, bodyClause); } + private static Class<?> iteratedLoopChecks(MethodHandle iterator, MethodHandle init, MethodHandle body) { + Objects.requireNonNull(body); + MethodType bodyType = body.type(); + Class<?> returnType = bodyType.returnType(); + List<Class<?>> innerList = bodyType.parameterList(); + // strip leading V value if present + int vsize = (returnType == void.class ? 0 : 1); + if (vsize != 0 && (innerList.size() == 0 || innerList.get(0) != returnType)) { + // argument list has no "V" => error + MethodType expected = bodyType.insertParameterTypes(0, returnType); + throw misMatchedTypes("body function", bodyType, expected); + } else if (innerList.size() <= vsize) { + // missing T type => error + MethodType expected = bodyType.insertParameterTypes(vsize, Object.class); + throw misMatchedTypes("body function", bodyType, expected); + } + //Class<?> elementType = innerList.get(vsize); // do not need this + List<Class<?>> outerList = innerList.subList(vsize + 1, innerList.size()); + if (outerList.isEmpty()) { + // special case; take lists from iterator handle + outerList = ((iterator != null) + ? iterator.type().parameterList() + : Arrays.asList(Iterable.class)); + innerList = bodyType.insertParameterTypes(vsize + 1, outerList).parameterList(); + } + if (iterator != null) { + MethodType itype = iterator.type(); + if (!Iterator.class.isAssignableFrom(itype.returnType())) { + throw newIllegalArgumentException("iteratedLoop first argument must have Iterator return type"); + } + if (!itype.effectivelyIdenticalParameters(0, outerList)) { + MethodType expected = methodType(itype.returnType(), outerList); + throw misMatchedTypes("iterator parameters", itype, expected); + } + } + if (init != null) { + MethodType initType = init.type(); + if (initType.returnType() != returnType || + !initType.effectivelyIdenticalParameters(0, outerList)) { + throw misMatchedTypes("loop initializer", initType, methodType(returnType, outerList)); + } + } + Class<?> iterableType = outerList.isEmpty() ? null : outerList.get(0); + if (iterableType != null && !Iterable.class.isAssignableFrom(iterableType) && !iterableType.isArray()) { + throw newIllegalArgumentException( + "inferred first loop argument must be an array or inherit from Iterable: " + iterableType); + } + return iterableType; // help the caller a bit + } + + /*non-public*/ static MethodHandle swapArguments(MethodHandle mh, int i, int j) { + // there should be a better way to uncross my wires + int arity = mh.type().parameterCount(); + int[] order = new int[arity]; + for (int k = 0; k < arity; k++) order[k] = k; + order[i] = j; order[j] = i; + Class<?>[] types = mh.type().parameterArray(); + Class<?> ti = types[i]; types[i] = types[j]; types[j] = ti; + MethodType swapType = methodType(mh.type().returnType(), types); + return permuteArguments(mh, swapType, order); + } + /** * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block. * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception @@ -4885,7 +5622,7 @@ List<Class<?>> cleanupParamTypes = cleanup.type().parameterList(); Class<?> rtype = target.type().returnType(); - checkTryFinally(target, cleanup); + tryFinallyChecks(target, cleanup); // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments. // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the @@ -4896,210 +5633,22 @@ return MethodHandleImpl.makeTryFinally(target.asFixedArity(), cleanup.asFixedArity(), rtype, targetParamTypes); } - /** - * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then - * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just - * before the folded arguments. - * <p> - * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the - * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a - * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position - * 0. - * <p> - * @apiNote Example: - * <blockquote><pre>{@code - import static java.lang.invoke.MethodHandles.*; - import static java.lang.invoke.MethodType.*; - ... - MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class, - "println", methodType(void.class, String.class)) - .bindTo(System.out); - MethodHandle cat = lookup().findVirtual(String.class, - "concat", methodType(String.class, String.class)); - assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); - MethodHandle catTrace = foldArguments(cat, 1, trace); - // also prints "jum": - assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); - * }</pre></blockquote> - * <p>Here is pseudocode for the resulting adapter. In the code, {@code T} - * represents the result type of the {@code target} and resulting adapter. - * {@code V}/{@code v} represent the type and value of the parameter and argument - * of {@code target} that precedes the folding position; {@code V} also is - * the result type of the {@code combiner}. {@code A}/{@code a} denote the - * types and values of the {@code N} parameters and arguments at the folding - * position. {@code Z}/{@code z} and {@code B}/{@code b} represent the types - * and values of the {@code target} parameters and arguments that precede and - * follow the folded parameters and arguments starting at {@code pos}, - * respectively. - * <blockquote><pre>{@code - * // there are N arguments in A... - * T target(Z..., V, A[N]..., B...); - * V combiner(A...); - * T adapter(Z... z, A... a, B... b) { - * V v = combiner(a...); - * return target(z..., v, a..., b...); - * } - * // and if the combiner has a void return: - * T target2(Z..., A[N]..., B...); - * void combiner2(A...); - * T adapter2(Z... z, A... a, B... b) { - * combiner2(a...); - * return target2(z..., a..., b...); - * } - * }</pre></blockquote> - * <p> - * <em>Note:</em> The resulting adapter is never a {@linkplain MethodHandle#asVarargsCollector - * variable-arity method handle}, even if the original target method handle was. - * - * @param target the method handle to invoke after arguments are combined - * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code - * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}. - * @param combiner method handle to call initially on the incoming arguments - * @return method handle which incorporates the specified argument folding logic - * @throws NullPointerException if either argument is null - * @throws IllegalArgumentException if {@code combiner}'s return type - * is non-void and not the same as the argument type at position {@code pos} of - * the target signature, or if the {@code N} argument types at position {@code pos} - * of the target signature - * (skipping one matching the {@code combiner}'s return type) - * are not identical with the argument types of {@code combiner} - * - * @see #foldArguments(MethodHandle, MethodHandle) - * @since 9 - */ - public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) { - MethodType targetType = target.type(); - MethodType combinerType = combiner.type(); - Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType); - BoundMethodHandle result = target.rebind(); - boolean dropResult = rtype == void.class; - LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType()); - MethodType newType = targetType; - if (!dropResult) { - newType = newType.dropParameterTypes(pos, pos + 1); - } - result = result.copyWithExtendL(newType, lform, combiner); - return result; - } - - /** - * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the - * added capability of selecting the arguments from the targets parameters - * to call the combiner with. This allows us to avoid some simple cases of - * permutations and padding the combiner with dropArguments to select the - * right argument, which may ultimately produce fewer intermediaries. - */ - static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) { - MethodType targetType = target.type(); - MethodType combinerType = combiner.type(); - Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions); - BoundMethodHandle result = target.rebind(); - boolean dropResult = rtype == void.class; - LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions); - MethodType newType = targetType; - if (!dropResult) { - newType = newType.dropParameterTypes(pos, pos + 1); - } - result = result.copyWithExtendL(newType, lform, combiner); - return result; - } - - private static void checkLoop0(MethodHandle[][] clauses) { - if (clauses == null || clauses.length == 0) { - throw newIllegalArgumentException("null or no clauses passed"); - } - if (Stream.of(clauses).anyMatch(Objects::isNull)) { - throw newIllegalArgumentException("null clauses are not allowed"); - } - if (Stream.of(clauses).anyMatch(c -> c.length > 4)) { - throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements."); - } - } - - private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) { - if (in.type().returnType() != st.type().returnType()) { - throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(), - st.type().returnType()); - } - } - - private static List<Class<?>> buildCommonSuffix(List<MethodHandle> init, List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, int cpSize) { - final List<Class<?>> empty = List.of(); - final List<MethodHandle> nonNullInits = init.stream().filter(Objects::nonNull).collect(Collectors.toList()); - if (nonNullInits.isEmpty()) { - final List<Class<?>> longest = Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull). - // take only those that can contribute to a common suffix because they are longer than the prefix - map(MethodHandle::type).filter(t -> t.parameterCount() > cpSize).map(MethodType::parameterList). - reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty); - return longest.size() == 0 ? empty : longest.subList(cpSize, longest.size()); - } else { - return nonNullInits.stream().map(MethodHandle::type).map(MethodType::parameterList). - reduce((p, q) -> p.size() >= q.size() ? p : q).get(); - } - } - - private static void checkLoop1b(List<MethodHandle> init, List<Class<?>> commonSuffix) { - if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList). - anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) { - throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init + - " (common suffix: " + commonSuffix + ")"); - } - } - - private static void checkLoop1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) { - if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). - anyMatch(t -> t != loopReturnType)) { - throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " + - loopReturnType + ")"); - } - - if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) { - throw newIllegalArgumentException("no predicate found", pred); - } - if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). - anyMatch(t -> t != boolean.class)) { - throw newIllegalArgumentException("predicates must have boolean return type", pred); - } - } - - private static void checkLoop2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) { - final int cpSize = commonParameterSequence.size(); - if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type). - map(MethodType::parameterList). - anyMatch(pl -> pl.size() > cpSize || !pl.equals(commonParameterSequence.subList(0, pl.size())))) { - throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step + - "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")"); - } - } - - private static void checkIteratedLoop(MethodHandle iterator, MethodHandle body) { - if (null != iterator && !Iterator.class.isAssignableFrom(iterator.type().returnType())) { - throw newIllegalArgumentException("iteratedLoop first argument must have Iterator return type"); - } - if (null == body) { - throw newIllegalArgumentException("iterated loop body must not be null"); - } - } - - private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) { + private static void tryFinallyChecks(MethodHandle target, MethodHandle cleanup) { Class<?> rtype = target.type().returnType(); if (rtype != cleanup.type().returnType()) { throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype); } - List<Class<?>> cleanupParamTypes = cleanup.type().parameterList(); - if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) { + MethodType cleanupType = cleanup.type(); + if (!Throwable.class.isAssignableFrom(cleanupType.parameterType(0))) { throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class); } - if (rtype != void.class && cleanupParamTypes.get(1) != rtype) { + if (rtype != void.class && cleanupType.parameterType(1) != rtype) { throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype); } // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the // target parameter list. int cleanupArgIndex = rtype == void.class ? 1 : 2; - List<Class<?>> cleanupArgSuffix = cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()); - List<Class<?>> targetParamTypes = target.type().parameterList(); - if (targetParamTypes.size() < cleanupArgSuffix.size() || - !cleanupArgSuffix.equals(targetParamTypes.subList(0, cleanupParamTypes.size() - cleanupArgIndex))) { + if (!cleanupType.effectivelyIdenticalParameters(cleanupArgIndex, target.type().parameterList())) { throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix", cleanup.type(), target.type()); }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Oct 05 06:28:22 2016 -0700 @@ -809,6 +809,28 @@ return sj.toString(); } + /** True if my parameter list is effectively identical to the given full list, + * after skipping the given number of my own initial parameters. + * In other words, after disregarding {@code skipPos} parameters, + * my remaining parameter list is no longer than the {@code fullList}, and + * is equal to the same-length initial sublist of {@code fullList}. + */ + /*non-public*/ + boolean effectivelyIdenticalParameters(int skipPos, List<Class<?>> fullList) { + int myLen = ptypes.length, fullLen = fullList.size(); + if (skipPos > myLen || myLen - skipPos > fullLen) + return false; + List<Class<?>> myList = Arrays.asList(ptypes); + if (skipPos != 0) { + myList = myList.subList(skipPos, myLen); + myLen -= skipPos; + } + if (fullLen == myLen) + return myList.equals(fullList); + else + return myList.equals(fullList.subList(0, myLen)); + } + /** True if the old return type can always be viewed (w/o casting) under new return type, * and the new parameters can be viewed (w/o casting) under the old parameter types. */
--- a/jdk/src/java.base/share/classes/java/net/MulticastSocket.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/net/MulticastSocket.java Wed Oct 05 06:28:22 2016 -0700 @@ -93,23 +93,19 @@ /** * Create a multicast socket. * - * <p>If there is a security manager, - * its {@code checkListen} method is first called - * with 0 as its argument to ensure the operation is allowed. - * This could result in a SecurityException. + * <p> + * If there is a security manager, its {@code checkListen} method is first + * called with 0 as its argument to ensure the operation is allowed. This + * could result in a SecurityException. * <p> * When the socket is created the - * {@link DatagramSocket#setReuseAddress(boolean)} method is - * called to enable the SO_REUSEADDR socket option. When - * {@link StandardSocketOptions#SO_REUSEPORT SO_REUSEPORT} is - * supported then - * {@link DatagramSocketImpl#setOption(SocketOption, Object)} - * is called to enable the socket option. + * {@link DatagramSocket#setReuseAddress(boolean)} method is called to + * enable the SO_REUSEADDR socket option. * - * @exception IOException if an I/O exception occurs - * while creating the MulticastSocket - * @exception SecurityException if a security manager exists and its - * {@code checkListen} method doesn't allow the operation. + * @exception IOException if an I/O exception occurs while creating the + * MulticastSocket + * @exception SecurityException if a security manager exists and its + * {@code checkListen} method doesn't allow the operation. * @see SecurityManager#checkListen * @see java.net.DatagramSocket#setReuseAddress(boolean) * @see java.net.DatagramSocketImpl#setOption(SocketOption, Object) @@ -174,17 +170,13 @@ // Enable SO_REUSEADDR before binding setReuseAddress(true); - // Enable SO_REUSEPORT if supported before binding - if (supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { - this.setOption(StandardSocketOptions.SO_REUSEPORT, true); - } - if (bindaddr != null) { try { bind(bindaddr); } finally { - if (!isBound()) + if (!isBound()) { close(); + } } } }
--- a/jdk/src/java.base/share/classes/java/text/DecimalFormat.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/text/DecimalFormat.java Wed Oct 05 06:28:22 2016 -0700 @@ -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 @@ -968,7 +968,7 @@ * Decimal : min = 0. max = 3. * */ - private void checkAndSetFastPathStatus() { + private boolean checkAndSetFastPathStatus() { boolean fastPathWasOn = isFastPath; @@ -998,12 +998,27 @@ } else isFastPath = false; + resetFastPathData(fastPathWasOn); + fastPathCheckNeeded = false; + + /* + * Returns true after successfully checking the fast path condition and + * setting the fast path data. The return value is used by the + * fastFormat() method to decide whether to call the resetFastPathData + * method to reinitialize fast path data or is it already initialized + * in this method. + */ + return true; + } + + private void resetFastPathData(boolean fastPathWasOn) { // Since some instance properties may have changed while still falling // in the fast-path case, we need to reinitialize fastPathData anyway. if (isFastPath) { // We need to instantiate fastPathData if not already done. - if (fastPathData == null) + if (fastPathData == null) { fastPathData = new FastPathData(); + } // Sets up the locale specific constants used when formatting. // '0' is our default representation of zero. @@ -1011,22 +1026,27 @@ fastPathData.groupingChar = symbols.getGroupingSeparator(); // Sets up fractional constants related to currency/decimal pattern. - fastPathData.fractionalMaxIntBound = (isCurrencyFormat) ? 99 : 999; - fastPathData.fractionalScaleFactor = (isCurrencyFormat) ? 100.0d : 1000.0d; + fastPathData.fractionalMaxIntBound = (isCurrencyFormat) + ? 99 : 999; + fastPathData.fractionalScaleFactor = (isCurrencyFormat) + ? 100.0d : 1000.0d; // Records the need for adding prefix or suffix - fastPathData.positiveAffixesRequired = - (positivePrefix.length() != 0) || (positiveSuffix.length() != 0); - fastPathData.negativeAffixesRequired = - (negativePrefix.length() != 0) || (negativeSuffix.length() != 0); + fastPathData.positiveAffixesRequired + = (positivePrefix.length() != 0) + || (positiveSuffix.length() != 0); + fastPathData.negativeAffixesRequired + = (negativePrefix.length() != 0) + || (negativeSuffix.length() != 0); // Creates a cached char container for result, with max possible size. int maxNbIntegralDigits = 10; int maxNbGroups = 3; - int containerSize = - Math.max(positivePrefix.length(), negativePrefix.length()) + - maxNbIntegralDigits + maxNbGroups + 1 + maximumFractionDigits + - Math.max(positiveSuffix.length(), negativeSuffix.length()); + int containerSize + = Math.max(positivePrefix.length(), negativePrefix.length()) + + maxNbIntegralDigits + maxNbGroups + 1 + + maximumFractionDigits + + Math.max(positiveSuffix.length(), negativeSuffix.length()); fastPathData.fastPathContainer = new char[containerSize]; @@ -1038,17 +1058,18 @@ // Sets up fixed index positions for integral and fractional digits. // Sets up decimal point in cached result container. - int longestPrefixLength = - Math.max(positivePrefix.length(), negativePrefix.length()); - int decimalPointIndex = - maxNbIntegralDigits + maxNbGroups + longestPrefixLength; - - fastPathData.integralLastIndex = decimalPointIndex - 1; + int longestPrefixLength + = Math.max(positivePrefix.length(), + negativePrefix.length()); + int decimalPointIndex + = maxNbIntegralDigits + maxNbGroups + longestPrefixLength; + + fastPathData.integralLastIndex = decimalPointIndex - 1; fastPathData.fractionalFirstIndex = decimalPointIndex + 1; - fastPathData.fastPathContainer[decimalPointIndex] = - isCurrencyFormat ? - symbols.getMonetaryDecimalSeparator() : - symbols.getDecimalSeparator(); + fastPathData.fastPathContainer[decimalPointIndex] + = isCurrencyFormat + ? symbols.getMonetaryDecimalSeparator() + : symbols.getDecimalSeparator(); } else if (fastPathWasOn) { // Previous state was fast-path and is no more. @@ -1059,8 +1080,6 @@ fastPathData.charsPositivePrefix = null; fastPathData.charsNegativePrefix = null; } - - fastPathCheckNeeded = false; } /** @@ -1554,9 +1573,11 @@ * @return the formatted result for {@code d} as a string. */ String fastFormat(double d) { + boolean isDataSet = false; // (Re-)Evaluates fast-path status if needed. - if (fastPathCheckNeeded) - checkAndSetFastPathStatus(); + if (fastPathCheckNeeded) { + isDataSet = checkAndSetFastPathStatus(); + } if (!isFastPath ) // DecimalFormat instance is not in a fast-path state. @@ -1580,9 +1601,21 @@ if (d > MAX_INT_AS_DOUBLE) // Filters out values that are outside expected fast-path range return null; - else + else { + if (!isDataSet) { + /* + * If the fast path data is not set through + * checkAndSetFastPathStatus() and fulfil the + * fast path conditions then reset the data + * directly through resetFastPathData() + */ + resetFastPathData(isFastPath); + } fastDoubleFormat(d, negative); + } + + // Returns a new string from updated fastPathContainer. return new String(fastPathData.fastPathContainer, fastPathData.firstUsedIndex,
--- a/jdk/src/java.base/share/classes/java/util/ArrayList.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ArrayList.java Wed Oct 05 06:28:22 2016 -0700 @@ -876,6 +876,8 @@ int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; + Itr() {} + public boolean hasNext() { return cursor != size; }
--- a/jdk/src/java.base/share/classes/java/util/Locale.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Locale.java Wed Oct 05 06:28:22 2016 -0700 @@ -1027,7 +1027,7 @@ * not contain ALL valid codes that can be used to create Locales. * </ul> * - * @return Am array of ISO 639 two-letter language codes. + * @return An array of ISO 639 two-letter language codes. */ public static String[] getISOLanguages() { if (isoLanguages == null) {
--- a/jdk/src/java.base/share/classes/sun/security/rsa/RSASignature.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/rsa/RSASignature.java Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -182,14 +182,15 @@ } // verify the data and return the result. See JCA doc + // should be reset to the state after engineInitVerify call. protected boolean engineVerify(byte[] sigBytes) throws SignatureException { - if (sigBytes.length != RSACore.getByteLength(publicKey)) { - throw new SignatureException("Signature length not correct: got " + + try { + if (sigBytes.length != RSACore.getByteLength(publicKey)) { + throw new SignatureException("Signature length not correct: got " + sigBytes.length + " but was expecting " + RSACore.getByteLength(publicKey)); - } - byte[] digest = getDigestValue(); - try { + } + byte[] digest = getDigestValue(); byte[] decrypted = RSACore.rsa(sigBytes, publicKey); byte[] unpadded = padding.unpad(decrypted); byte[] decodedDigest = decodeSignature(digestOID, unpadded); @@ -202,6 +203,8 @@ return false; } catch (IOException e) { throw new SignatureException("Signature encoding error", e); + } finally { + resetDigest(); } }
--- a/jdk/src/java.base/share/conf/security/java.security Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/share/conf/security/java.security Wed Oct 05 06:28:22 2016 -0700 @@ -894,3 +894,44 @@ disallowReferenceUriSchemes file http https,\ noDuplicateIds,\ noRetrievalMethodLoops + +# +# Serialization process-wide filter +# +# A filter, if configured, is used by java.io.ObjectInputStream during +# deserialization to check the contents of the stream. +# A filter is configured as a sequence of patterns, each pattern is either +# matched against the name of a class in the stream or defines a limit. +# Patterns are separated by ";" (semicolon). +# Whitespace is significant and is considered part of the pattern. +# +# If a pattern includes a "=", it sets a limit. +# If a limit appears more than once the last value is used. +# Limits are checked before classes regardless of the order in the sequence of patterns. +# If any of the limits are exceeded, the filter status is REJECTED. +# +# maxdepth=value - the maximum depth of a graph +# maxrefs=value - the maximum number of internal references +# maxbytes=value - the maximum number of bytes in the input stream +# maxarray=value - the maximum array length allowed +# +# Other patterns, from left to right, match the class or package name as +# returned from Class.getName. +# If the class is an array type, the class or package to be matched is the element type. +# Arrays of any number of dimensions are treated the same as the element type. +# For example, a pattern of "!example.Foo", rejects creation of any instance or +# array of example.Foo. +# +# If the pattern starts with "!", the status is REJECTED if the remaining pattern +# is matched; otherwise the status is ALLOWED if the pattern matches. +# If the pattern contains "/", the non-empty prefix up to the "/" is the module name; +# if the module name matches the module name of the class then +# the remaining pattern is matched with the class name. +# If there is no "/", the module name is not compared. +# If the pattern ends with ".**" it matches any class in the package and all subpackages. +# If the pattern ends with ".*" it matches any class in the package. +# If the pattern ends with "*", it matches any class with the pattern as a prefix. +# If the pattern is equal to the class name, it matches. +# Otherwise, the status is UNDECIDED. +# +#jdk.serialFilter=pattern;pattern
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -116,7 +116,7 @@ synchronized (this) { if (iterator != null) throw new IllegalStateException("Iterator already obtained"); - iterator = new UnixDirectoryIterator(ds); + iterator = new UnixDirectoryIterator(); return iterator; } } @@ -130,17 +130,14 @@ * Iterator implementation */ private class UnixDirectoryIterator implements Iterator<Path> { - private final DirectoryStream<Path> stream; - // true when at EOF private boolean atEof; // next entry to return private Path nextEntry; - UnixDirectoryIterator(DirectoryStream<Path> stream) { + UnixDirectoryIterator() { atEof = false; - this.stream = stream; } // Return true if file name is "." or ".."
--- a/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -1302,7 +1302,7 @@ * Sets the multicast loopback mode. */ static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd, - jint opt, jobject value) { + jint opt, jobject value) { #ifdef AF_INET6 #ifdef __linux__ mcast_set_loop_v4(env, this, fd, value); @@ -1330,10 +1330,9 @@ * Signature: (ILjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv *env, - jobject this, - jint opt, - jobject value) { +Java_java_net_PlainDatagramSocketImpl_socketSetOption0 + (JNIEnv *env, jobject this, jint opt, jobject value) +{ int fd; int level, optname, optlen; int optval; @@ -1380,7 +1379,7 @@ * level and option name. */ if (NET_MapSocketOption(opt, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } @@ -1699,8 +1698,9 @@ * Signature: (I)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL -Java_java_net_PlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this, - jint opt) { +Java_java_net_PlainDatagramSocketImpl_socketGetOption + (JNIEnv *env, jobject this, jint opt) +{ int fd; int level, optname, optlen; union { @@ -1751,7 +1751,7 @@ * level and option name. */ if (NET_MapSocketOption(opt, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return NULL; }
--- a/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -855,9 +855,9 @@ * Signature: (IZLjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_PlainSocketImpl_socketSetOption0(JNIEnv *env, jobject this, - jint cmd, jboolean on, - jobject value) { +Java_java_net_PlainSocketImpl_socketSetOption0 + (JNIEnv *env, jobject this, jint cmd, jboolean on, jobject value) +{ int fd; int level, optname, optlen; union { @@ -887,7 +887,7 @@ * level and option name. */ if (NET_MapSocketOption(cmd, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } @@ -951,9 +951,9 @@ * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_java_net_PlainSocketImpl_socketGetOption(JNIEnv *env, jobject this, - jint cmd, jobject iaContainerObj) { - +Java_java_net_PlainSocketImpl_socketGetOption + (JNIEnv *env, jobject this, jint cmd, jobject iaContainerObj) +{ int fd; int level, optname, optlen; union { @@ -1004,7 +1004,7 @@ * level and option name. */ if (NET_MapSocketOption(cmd, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return -1; }
--- a/jdk/src/java.base/unix/native/libnet/SocketInputStream.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/unix/native/libnet/SocketInputStream.c Wed Oct 05 06:28:22 2016 -0700 @@ -58,15 +58,15 @@ result = NET_TimeoutWithCurrentTime(fd, timeout, prevtime); if (result <= 0) { if (result == 0) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", "Read timed out"); + JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out"); } else if (result == -1) { if (errno == EBADF) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); } else if (errno == ENOMEM) { JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed"); } else { JNU_ThrowByNameWithMessageAndLastError - (env, JNU_JAVANETPKG "SocketException", "select/poll failed"); + (env, "java/net/SocketException", "select/poll failed"); } } return -1; @@ -100,19 +100,14 @@ jint fd, nread; if (IS_NULL(fdObj)) { - /* shouldn't this be a NullPointerException? -br */ - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); return -1; - } else { - fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); - /* Bug 4086704 - If the Socket associated with this file descriptor - * was closed (sysCloseFD), then the file descriptor is set to -1. - */ - if (fd == -1) { - JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); - return -1; - } + } + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return -1; } /* @@ -154,17 +149,17 @@ break; case EBADF: - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); break; case EINTR: - JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + JNU_ThrowByName(env, "java/io/InterruptedIOException", "Operation interrupted"); break; default: JNU_ThrowByNameWithMessageAndLastError - (env, JNU_JAVANETPKG "SocketException", "Read failed"); + (env, "java/net/SocketException", "Read failed"); } } } else {
--- a/jdk/src/java.base/unix/native/libnio/ch/NativeThread.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/unix/native/libnio/ch/NativeThread.c Wed Oct 05 06:28:22 2016 -0700 @@ -37,6 +37,11 @@ #include <sys/signal.h> /* Also defined in net/linux_close.c */ #define INTERRUPT_SIGNAL (__SIGRTMAX - 2) +#elif _AIX + #include <pthread.h> + #include <sys/signal.h> + /* Also defined in net/aix_close.c */ + #define INTERRUPT_SIGNAL (SIGRTMAX - 1) #elif __solaris__ #include <thread.h> #include <signal.h> @@ -59,7 +64,7 @@ Java_sun_nio_ch_NativeThread_init(JNIEnv *env, jclass cl) { /* Install the null handler for INTERRUPT_SIGNAL. This might overwrite the - * handler previously installed by java/net/linux_close.c, but that's okay + * handler previously installed by <platform>_close.c, but that's okay * since neither handler actually does anything. We install our own * handler here simply out of paranoia; ultimately the two mechanisms * should somehow be unified, perhaps within the VM.
--- a/jdk/src/java.base/windows/native/libnet/DualStackPlainDatagramSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/DualStackPlainDatagramSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -475,13 +475,14 @@ * Method: socketSetIntOption * Signature: (III)V */ -JNIEXPORT void JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption - (JNIEnv *env, jclass clazz, jint fd , jint cmd, jint value) { +JNIEXPORT void JNICALL +Java_java_net_DualStackPlainDatagramSocketImpl_socketSetIntOption + (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) +{ int level = 0, opt = 0; if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", - "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } @@ -495,14 +496,15 @@ * Method: socketGetIntOption * Signature: (II)I */ -JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption - (JNIEnv *env, jclass clazz, jint fd, jint cmd) { - int level = 0, opt = 0, result=0; +JNIEXPORT jint JNICALL +Java_java_net_DualStackPlainDatagramSocketImpl_socketGetIntOption + (JNIEnv *env, jclass clazz, jint fd, jint cmd) +{ + int level = 0, opt = 0, result = 0; int result_len = sizeof(result); if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", - "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return -1; } @@ -519,8 +521,10 @@ * Method: dataAvailable * Signature: ()I */ -JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable -(JNIEnv *env, jobject this) { +JNIEXPORT jint JNICALL +Java_java_net_DualStackPlainDatagramSocketImpl_dataAvailable + (JNIEnv *env, jobject this) +{ SOCKET fd; int rv = -1; jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
--- a/jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -369,18 +369,17 @@ * Method: setIntOption * Signature: (III)V */ -JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_setIntOption - (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) { - +JNIEXPORT void JNICALL +Java_java_net_DualStackPlainSocketImpl_setIntOption + (JNIEnv *env, jclass clazz, jint fd, jint cmd, jint value) +{ int level = 0, opt = 0; struct linger linger = {0, 0}; char *parg; int arglen; if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } @@ -410,8 +409,8 @@ * Signature: (II)I */ JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_getIntOption - (JNIEnv *env, jclass clazz, jint fd, jint cmd) { - + (JNIEnv *env, jclass clazz, jint fd, jint cmd) +{ int level = 0, opt = 0; int result=0; struct linger linger = {0, 0}; @@ -419,9 +418,7 @@ int arglen; if (NET_MapSocketOption(cmd, &level, &opt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return -1; }
--- a/jdk/src/java.base/windows/native/libnet/SocketInputStream.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/SocketInputStream.c Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -62,18 +62,18 @@ jobject fdObj, jbyteArray data, jint off, jint len, jint timeout) { + char BUF[MAX_BUFFER_LEN]; char *bufP; - char BUF[MAX_BUFFER_LEN]; - jint fd, newfd; - jint nread; + jint fd, newfd, nread; if (IS_NULL(fdObj)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + JNU_ThrowByName(env, "java/net/SocketException", + "Socket closed"); return -1; } fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); if (fd == -1) { - NET_ThrowSocketException(env, "Socket closed"); + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); return -1; } @@ -103,10 +103,10 @@ if (ret <= 0) { if (ret == 0) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out"); } else if (ret == -1) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + JNU_ThrowByName(env, "java/net/SocketException", "socket closed"); } if (bufP != BUF) { free(bufP); @@ -117,7 +117,7 @@ /*check if the socket has been closed while we were in timeout*/ newfd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); if (newfd == -1) { - NET_ThrowSocketException(env, "Socket Closed"); + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); if (bufP != BUF) { free(bufP); } @@ -134,11 +134,11 @@ // Check if the socket has been closed since we last checked. // This could be a reason for recv failing. if ((*env)->GetIntField(env, fdObj, IO_fd_fdID) == -1) { - NET_ThrowSocketException(env, "Socket closed"); + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); } else { switch (WSAGetLastError()) { case WSAEINTR: - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + JNU_ThrowByName(env, "java/net/SocketException", "socket closed"); break; @@ -153,7 +153,7 @@ break; case WSAETIMEDOUT : - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out"); break;
--- a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -1795,9 +1795,9 @@ * Signature: (ILjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption(JNIEnv *env,jobject this, - jint opt,jobject value) { - +Java_java_net_TwoStacksPlainDatagramSocketImpl_socketNativeSetOption + (JNIEnv *env,jobject this, jint opt,jobject value) +{ int fd=-1, fd1=-1; int levelv4 = 0, levelv6 = 0, optnamev4 = 0, optnamev6 = 0, optlen = 0; union { @@ -1828,13 +1828,13 @@ */ if (fd1 != -1) { if (NET_MapSocketOptionV6(opt, &levelv6, &optnamev6)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } } if (fd != -1) { if (NET_MapSocketOption(opt, &levelv4, &optnamev4)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } } @@ -2163,9 +2163,9 @@ * Signature: (I)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL -Java_java_net_TwoStacksPlainDatagramSocketImpl_socketGetOption(JNIEnv *env, jobject this, - jint opt) { - +Java_java_net_TwoStacksPlainDatagramSocketImpl_socketGetOption + (JNIEnv *env, jobject this, jint opt) +{ int fd=-1, fd1=-1; int level, optname, optlen; union { @@ -2197,13 +2197,13 @@ * level and option name. */ if (NET_MapSocketOption(opt, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return NULL; } if (fd == -1) { if (NET_MapSocketOptionV6(opt, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return NULL; } fd = fd1; /* must be IPv6 only */
--- a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -838,10 +838,9 @@ * Signature: (IZLjava/lang/Object;)V */ JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainSocketImpl_socketNativeSetOption(JNIEnv *env, - jobject this, - jint cmd, jboolean on, - jobject value) { +Java_java_net_TwoStacksPlainSocketImpl_socketNativeSetOption + (JNIEnv *env, jobject this, jint cmd, jboolean on, jobject value) +{ int fd, fd1; int level = 0, optname = 0, optlen = 0; union { @@ -923,11 +922,10 @@ /* * Map the Java level socket option to the platform specific - * level + * level and option name. */ if (NET_MapSocketOption(cmd, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return; } @@ -1006,15 +1004,16 @@ * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_java_net_TwoStacksPlainSocketImpl_socketGetOption(JNIEnv *env, jobject this, - jint opt, jobject iaContainerObj) { - +Java_java_net_TwoStacksPlainSocketImpl_socketGetOption + (JNIEnv *env, jobject this, jint opt, jobject iaContainerObj) +{ int fd, fd1; int level = 0, optname = 0, optlen = 0; union { int i; struct linger ling; } optval; + /* * Get SOCKET and check it hasn't been closed */ @@ -1073,7 +1072,7 @@ * level and option name. */ if (NET_MapSocketOption(opt, &level, &optname)) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Invalid option"); + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); return -1; }
--- a/jdk/src/java.base/windows/native/libnet/net_util_md.c Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/net_util_md.c Wed Oct 05 06:28:22 2016 -0700 @@ -203,19 +203,6 @@ } void -NET_ThrowSocketException(JNIEnv *env, char* msg) -{ - static jclass cls = NULL; - if (cls == NULL) { - cls = (*env)->FindClass(env, "java/net/SocketException"); - CHECK_NULL(cls); - cls = (*env)->NewGlobalRef(env, cls); - CHECK_NULL(cls); - } - (*env)->ThrowNew(env, cls, msg); -} - -void NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, const char *defaultDetail) { char errmsg[255];
--- a/jdk/src/java.base/windows/native/libnet/net_util_md.h Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.base/windows/native/libnet/net_util_md.h Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -299,8 +299,6 @@ void NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, const char *defaultDetail); -void NET_ThrowSocketException(JNIEnv *env, char* msg); - /* * differs from NET_Timeout() as follows: *
--- a/jdk/src/java.desktop/share/classes/java/beans/MetaData.java Fri Sep 30 02:52:36 2016 -0700 +++ b/jdk/src/java.desktop/share/classes/java/beans/MetaData.java Wed Oct 05 06:28:22 2016 -0700 @@ -510,102 +510,6 @@ return new Expression(oldInstance, Collections.class, "synchronizedSortedMap", new Object[]{map}); } } - - static final class CheckedCollection_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); - List<?> list = new ArrayList<>((Collection<?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedCollection", new Object[]{list, type}); - } - } - - static final class CheckedList_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); - List<?> list = new LinkedList<>((Collection<?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type}); - } - } - - static final class CheckedRandomAccessList_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); - List<?> list = new ArrayList<>((Collection<?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type}); - } - } - - static final class CheckedSet_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); - Set<?> set = new HashSet<>((Set<?>) oldInstance);