OpenJDK / amber / amber
changeset 41344:11390434331a
Merge
author | amurillo |
---|---|
date | Wed, 05 Oct 2016 06:28:22 -0700 |
parents | e42e78cd7c6f 65200988b2ee |
children | d65794815f60 |
files | hotspot/make/Dist.gmk hotspot/make/gensrc/GensrcJvmti.gmk 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 |
diffstat | 177 files changed, 9288 insertions(+), 3422 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Fri Sep 30 02:52:38 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/.hgtags-top-repo Fri Sep 30 02:52:38 2016 -0700 +++ b/.hgtags-top-repo Wed Oct 05 06:28:22 2016 -0700 @@ -380,3 +380,4 @@ 82b94cb5f342319d2cda77f9fa59703ad7fde576 jdk-9+135 3ec350f5f32af249b59620d7e37b54bdcd77b233 jdk-9+136 d7f519b004254b19e384131d9f0d0e40e31a0fd3 jdk-9+137 +67c4388142bdf58aec8fefa4475faaa8a5d7380c jdk-9+138
--- a/common/autoconf/buildjdk-spec.gmk.in Fri Sep 30 02:52:38 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:38 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:38 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/corba/.hgtags Fri Sep 30 02:52:38 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:38 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/make/BuildHotspot.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/BuildHotspot.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -45,10 +45,7 @@ jsig: +$(MAKE) -f lib/CompileLibjsig.gmk -dist: $(VARIANT_TARGETS) jsig - +$(MAKE) -f Dist.gmk - -all: dist +all: $(VARIANT_TARGETS) jsig .PHONY: $(VARIANT_TARGETS) $(VARIANT_GENSRC_TARGETS) $(VARIANT_LIBS_TARGETS) \ - jsig dist all + jsig all
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/make/CopyToExplodedJdk.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,55 @@ +# +# 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. +# + +# Copy all built libraries into exploded jdk +LIB_TARGETS := $(filter $(LIB_OUTPUTDIR)/%, $(TARGETS)) +ifeq ($(OPENJDK_TARGET_OS), windows) + $(eval $(call SetupCopyFiles, COPY_LIBS_BIN, \ + SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base, \ + DEST := $(JDK_OUTPUTDIR)/bin, \ + FILES := $(filter-out %.lib, $(LIB_TARGETS)), \ + )) + + $(eval $(call SetupCopyFiles, COPY_LIBS_LIB, \ + SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base, \ + DEST := $(JDK_OUTPUTDIR)/lib, \ + FILES := $(filter %.lib, $(LIB_TARGETS)))) + + TARGETS += $(COPY_LIBS_BIN) $(COPY_LIBS_LIB) +else + $(eval $(call SetupCopyFiles, COPY_LIBS, \ + SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base, \ + DEST := $(JDK_OUTPUTDIR)/lib, \ + FILES := $(filter %$(SHARED_LIBRARY_SUFFIX), $(LIB_TARGETS)), \ + )) + $(eval $(call SetupCopyFiles, LINK_LIBS, \ + SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base, \ + DEST := $(JDK_OUTPUTDIR)/lib, \ + FILES := $(filter-out %$(SHARED_LIBRARY_SUFFIX), $(LIB_TARGETS)), \ + MACRO := link-file-relative, \ + )) + + TARGETS += $(COPY_LIBS) $(LINK_LIBS) +endif
--- a/hotspot/make/Dist.gmk Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -# -# 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. -# - -################################################################################ -# Copy the generated output into well-defined places in the dist directory. - -# This must be the first rule -default: all - -include $(SPEC) -include MakeBase.gmk - -$(eval $(call IncludeCustomExtension, hotspot, Dist.gmk)) - -DIST_OUTPUTDIR := $(HOTSPOT_OUTPUTDIR)/dist - -# Unfortunately, all platforms have different target subdirs. -ifeq ($(OPENJDK_TARGET_OS), windows) - LIB_SUBDIR := bin -else ifeq ($(OPENJDK_TARGET_OS), macosx) - LIB_SUBDIR := lib -else - LIB_SUBDIR := lib$(OPENJDK_TARGET_CPU_LIBDIR) -endif - -################################################################################ -# Setup make rules to copy a native library and associated data. -# -# Parameter 1 is the name of the rule. This name is used as variable prefix, -# and the targets generated are listed in a variable by that name. -# -# Remaining parameters are named arguments. These include: -# NAME -- The base name of the native library (e.g. 'jvm') -# VARIANT -- The variant to copy from -# VARIANT_TARGET_DIR -- The variant target sub dir, with trailing slash, optional -SetupDistLibFile = $(NamedParamsMacroTemplate) -define SetupDistLibFileBody - ifneq ($$($1_VARIANT), ) - $1_SRC_DIR := $$(HOTSPOT_OUTPUTDIR)/variant-$$($1_VARIANT)/lib$$($1_NAME) - else - $1_SRC_DIR := $$(HOTSPOT_OUTPUTDIR)/lib$$($1_NAME) - endif - $1_LIB_NAME := $(LIBRARY_PREFIX)$$($1_NAME) - $1_TARGET_DIR := $$(DIST_OUTPUTDIR)/$$(LIB_SUBDIR)/$$($1_VARIANT_TARGET_DIR) - - # Copy the the native library. - $$(eval $$(call SetupCopyFiles, $1_COPY_LIB, \ - DEST := $$($1_TARGET_DIR), \ - FILES := $$(wildcard \ - $$($1_SRC_DIR)/$$($1_LIB_NAME)$(SHARED_LIBRARY_SUFFIX)), \ - )) - - TARGETS += $$($1_COPY_LIB) - - # Copy related data (debug symbols, static-build symbols file etc) - $$(eval $$(call SetupCopyFiles, $1_COPY_FILES, \ - DEST := $$($1_TARGET_DIR), \ - FILES := $$(wildcard \ - $$(addprefix $$($1_SRC_DIR)/$$($1_LIB_NAME), \ - .diz .debuginfo .pdb .map .symbols)), \ - )) - - TARGETS += $$($1_COPY_FILES) - - ifeq ($(OPENJDK_TARGET_OS), macosx) - # Debug symbols on macosx is a directory, not a single file, per library. - $1_DSYM_SRC := $$($1_SRC_DIR)/$$($1_LIB_NAME)$(SHARED_LIBRARY_SUFFIX).dSYM) - ifneq ($$(wildcard $$($1_DSYM_SRC)), ) - $$(eval $$(call SetupCopyFiles, $1_COPY_DSYM_DIR, \ - DEST := $$($1_TARGET_DIR), \ - SRC := $$($1_SRC_DIR), \ - FILES := $$(shell $(FIND) $$($1_DSYM_SRC) -type f), \ - )) - TARGETS += $$($1_COPY_DSYM_DIR) - endif - endif -endef - -################################################################################ -# Copy common files, which are independent on the jvm variant(s) being built. -# For files that were generated during the build, we assume all versions of -# these files are identical, and just pick one arbitrarily to use as source. - -ANY_JVM_VARIANT := $(firstword $(JVM_VARIANTS)) -JVM_VARIANT_OUTPUTDIR := $(HOTSPOT_OUTPUTDIR)/variant-$(ANY_JVM_VARIANT) - -### Copy platform-independent .h files -INCLUDE_FILES_SRC_DIR := $(HOTSPOT_TOPDIR)/src/share/vm -$(eval $(call SetupCopyFiles, COPY_INCLUDE, \ - SRC := $(INCLUDE_FILES_SRC_DIR), \ - DEST := $(DIST_OUTPUTDIR)/include, \ - FLATTEN := true, \ - FILES := $(INCLUDE_FILES_SRC_DIR)/prims/jni.h \ - $(INCLUDE_FILES_SRC_DIR)/code/jvmticmlr.h \ - $(INCLUDE_FILES_SRC_DIR)/services/jmm.h)) - -TARGETS += $(COPY_INCLUDE) - -### Copy jni_md.h - -# This might have been defined in a custom extension -ifeq ($(JNI_MD_H_SRC), ) - JNI_MD_H_SRC := $(HOTSPOT_TOPDIR)/src/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/vm/jni_$(HOTSPOT_TARGET_CPU_ARCH).h -endif - -ifeq ($(OPENJDK_TARGET_OS), macosx) - # NOTE: This should most likely be darwin, but the old hotspot build uses bsd - JNI_MD_SUBDIR := bsd -else ifeq ($(OPENJDK_TARGET_OS), windows) - JNI_MD_SUBDIR := win32 -else - JNI_MD_SUBDIR := $(OPENJDK_TARGET_OS) -endif - -# SetupCopyFiles is not used here since it's non-trivial to copy a single -# file with a different target name. -$(DIST_OUTPUTDIR)/include/$(JNI_MD_SUBDIR)/jni_md.h: $(JNI_MD_H_SRC) - $(call LogInfo, Copying hotspot/dist/include/$(JNI_MD_SUBDIR)/jni_md.h) - $(install-file) - -TARGETS += $(DIST_OUTPUTDIR)/include/$(JNI_MD_SUBDIR)/jni_md.h - -$(eval $(call SetupCopyFiles, COPY_JVMTI_H, \ - DEST := $(DIST_OUTPUTDIR)/include, \ - FLATTEN := true, \ - FILES := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jvmtifiles/jvmti.h)) - -TARGETS += $(COPY_JVMTI_H) - -# NOTE: In the old build, this file was not copied on Windows. -ifneq ($(OPENJDK_TARGET_OS), windows) - $(eval $(call SetupCopyFiles, COPY_JVMTI_HTML, \ - DEST := $(DIST_OUTPUTDIR)/docs/platform/jvmti, \ - FILES := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jvmtifiles/jvmti.html)) -endif - -TARGETS += $(COPY_JVMTI_HTML) - -ifeq ($(OPENJDK_TARGET_OS), windows) - $(eval $(call SetupCopyFiles, COPY_JVM_LIB, \ - DEST := $(DIST_OUTPUTDIR)/lib, \ - FILES :=$(JVM_VARIANT_OUTPUTDIR)/libjvm/objs/jvm.lib)) - - TARGETS += $(COPY_JVM_LIB) -endif - -# Copy libjsig, if it exists -$(eval $(call SetupDistLibFile, DIST_jsig, \ - NAME := jsig, \ -)) - -################################################################################ -# Copy variant-specific files - -# Setup make rules to copy a single variant to dist. -# $1: The name of the variant -define SetupDistForVariant - ifneq ($$(filter client minimal, $1), ) - VARIANT_TARGET_DIR := $1 - else - # Use 'server' as default target directory name for all other variants. - VARIANT_TARGET_DIR := server - endif - - $$(eval $$(call SetupDistLibFile, DIST_$(strip $1)_jvm, \ - NAME := jvm, \ - VARIANT := $1, \ - VARIANT_TARGET_DIR := $$(VARIANT_TARGET_DIR)/, \ - )) - - # Copy the dtrace libraries, if they exist - $$(eval $$(call SetupDistLibFile, DIST_$(strip $1)_jvm_db, \ - NAME := jvm_db, \ - VARIANT := $1, \ - VARIANT_TARGET_DIR := $$(VARIANT_TARGET_DIR)/, \ - )) - - $$(eval $$(call SetupDistLibFile, DIST_$(strip $1)_jvm_dtrace, \ - NAME := jvm_dtrace, \ - VARIANT := $1, \ - VARIANT_TARGET_DIR := $$(VARIANT_TARGET_DIR)/, \ - )) - - # Copy the Xusage.txt file - $$(eval $$(call SetupCopyFiles, DIST_$(strip $1)_Xusage, \ - DEST := $$(DIST_OUTPUTDIR)/$$(LIB_SUBDIR)/$(strip $1), \ - FILES := $$(HOTSPOT_OUTPUTDIR)/variant-$(strip $1)/support/misc/Xusage.txt, \ - )) - - TARGETS += $$(DIST_$(strip $1)_Xusage) -endef - -$(foreach variant, $(JVM_VARIANTS), \ - $(eval $(call SetupDistForVariant, $(variant))) \ -) - -################################################################################ - -all: $(TARGETS) - -.PHONY: all
--- a/hotspot/make/HotspotCommon.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/HotspotCommon.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -33,6 +33,15 @@ DTRACE_SUPPORT_DIR := $(JVM_SUPPORT_DIR)/dtrace +LIB_OUTPUTDIR := $(call FindLibDirForModule, java.base) +ifneq ($(filter client minimal, $(JVM_VARIANT)), ) + JVM_VARIANT_SUBDIR := $(JVM_VARIANT) +else + # Use 'server' as default target directory name for all other variants. + JVM_VARIANT_SUBDIR := server +endif +JVM_LIB_OUTPUTDIR := $(LIB_OUTPUTDIR)/$(JVM_VARIANT_SUBDIR) + ################################################################################ # Test if a feature is available in the present build of JVM_VARIANT. Will return
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/make/copy/Copy-java.base.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,67 @@ +# +# 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. +# + +# These include files are currently being copied from the jdk repository for +# historical reasons. Disable copying from here until this has been cleaned up. +# The files in hotspot differ slightly from the corresponding files in jdk. +# See JDK-8167078. + +INCLUDE_DST_DIR := $(SUPPORT_OUTPUTDIR)/modules_include/$(MODULE) + +################################################################################ +# Copy platform-independent .h files +$(eval $(call SetupCopyFiles, COPY_INCLUDE_FILES, \ + SRC := $(HOTSPOT_TOPDIR)/src/share/vm, \ + DEST := $(INCLUDE_DST_DIR), \ + FLATTEN := true, \ + FILES := prims/jni.h code/jvmticmlr.h \ +)) + +#TARGETS += $(COPY_INCLUDE_FILES) + +################################################################################ +# Copy jni_md.h + +# This might have been defined in a custom extension +JNI_MD_H_SRC ?= $(HOTSPOT_TOPDIR)/src/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/vm/jni_$(HOTSPOT_TARGET_CPU_ARCH).h + +ifeq ($(OPENJDK_TARGET_OS), macosx) + # NOTE: This should most likely be darwin, but the old hotspot build uses bsd + JNI_MD_SUBDIR := bsd +else ifeq ($(OPENJDK_TARGET_OS), windows) + JNI_MD_SUBDIR := win32 +else + JNI_MD_SUBDIR := $(OPENJDK_TARGET_OS) +endif + +# SetupCopyFiles is not used here since it's non-trivial to copy a single +# file with a different target name. +$(INCLUDE_DST_DIR)/$(JNI_MD_SUBDIR)/jni_md.h: $(JNI_MD_H_SRC) + $(call LogInfo, Copying hotspot/dist/include/$(JNI_MD_SUBDIR)/jni_md.h) + $(install-file) + +#TARGETS += $(INCLUDE_DST_DIR)/$(JNI_MD_SUBDIR)/jni_md.h + +################################################################################
--- a/hotspot/make/gensrc/GenerateSources.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/gensrc/GenerateSources.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -47,7 +47,7 @@ # The Xusage.txt file needs to have platform specific path separator $(eval $(call SetupTextFileProcessing, CREATE_XUSAGE, \ SOURCE_FILES := $(HOTSPOT_TOPDIR)/src/share/vm/Xusage.txt, \ - OUTPUT_FILE := $(JVM_SUPPORT_DIR)/misc/Xusage.txt, \ + OUTPUT_FILE := $(JVM_LIB_OUTPUTDIR)/Xusage.txt, \ REPLACEMENTS := separated by ;> => separated by $(PATH_SEP)> ; , \ ))
--- a/hotspot/make/gensrc/GensrcJvmti.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/gensrc/GensrcJvmti.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -130,6 +130,21 @@ TARGETS += $(JVMTI_OUTPUTDIR)/jvmtiEnvRecommended.cpp ################################################################################ +# Disable copy of jvmti.h from hotspot until this has been cleared up. The file +# is currently being copied from the jdk repository. See JDK-8167078. +# Copy jvmti.h to include dir + +# The file is the same regardless of jvm variant. Only let one do the copy. +#ifeq ($(JVM_VARIANT), $(firstword $(JVM_VARIANTS))) +# $(eval $(call SetupCopyFiles, COPY_JVMTI_H, \ +# DEST := $(SUPPORT_OUTPUTDIR)/modules_include/java.base, \ +# FILES := $(JVMTI_OUTPUTDIR)/jvmti.h, \ +# )) + +# TARGETS += $(COPY_JVMTI_H) +#endif + +################################################################################ # Create trace files in gensrc/tracefiles TRACE_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/tracefiles
--- a/hotspot/make/lib/CompileDtracePostJvm.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/lib/CompileDtracePostJvm.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -180,7 +180,7 @@ $(eval $(call SetupNativeCompilation, BUILD_LIBJVM_DTRACE, \ LIBRARY := jvm_dtrace, \ - OUTPUT_DIR := $(LIBJVM_DTRACE_OUTPUTDIR), \ + OUTPUT_DIR := $(JVM_LIB_OUTPUTDIR), \ SRC := $(HOTSPOT_TOPDIR)/src/os/solaris/dtrace, \ INCLUDE_FILES := jvm_dtrace.c, \ CFLAGS := -m64 -G -mt -KPIC, \ @@ -197,7 +197,7 @@ # the old build. $(eval $(call SetupNativeCompilation, BUILD_LIBJVM_DB, \ LIBRARY := jvm_db, \ - OUTPUT_DIR := $(LIBJVM_DB_OUTPUTDIR), \ + OUTPUT_DIR := $(JVM_LIB_OUTPUTDIR), \ SRC := $(HOTSPOT_TOPDIR)/src/os/solaris/dtrace, \ INCLUDE_FILES := libjvm_db.c, \ CFLAGS := -I$(JVM_VARIANT_OUTPUTDIR)/gensrc -I$(DTRACE_SUPPORT_DIR) \
--- a/hotspot/make/lib/CompileGtest.gmk Fri Sep 30 02:52:38 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:38 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) @@ -181,22 +174,15 @@ JVM_RCFLAGS += -D"HS_FILEDESC=$(HOTSPOT_VM_DISTRO) $(RC_DESC)$(JVM_VARIANT) VM" endif -ifeq ($(OPENJDK_TARGET_OS), macosx) - # NOTE: The old build did not strip binaries on macosx. - JVM_STRIP_SYMBOLS := false -else - JVM_STRIP_SYMBOLS := true -endif - JVM_OPTIMIZATION ?= HIGHEST_JVM ################################################################################ # 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), \ + OUTPUT_DIR := $(JVM_LIB_OUTPUTDIR), \ SRC := $(JVM_SRC_DIRS), \ EXCLUDES := $(JVM_EXCLUDES), \ EXCLUDE_FILES := $(JVM_EXCLUDE_FILES), \ @@ -218,7 +204,6 @@ OBJECT_DIR := $(JVM_OUTPUTDIR)/objs, \ MAPFILE := $(JVM_MAPFILE), \ USE_MAPFILE_FOR_SYMBOLS := true, \ - STRIP_SYMBOLS := $(JVM_STRIP_SYMBOLS), \ EMBED_MANIFEST := true, \ RC_FLAGS := $(JVM_RCFLAGS), \ VERSIONINFO_RESOURCE := $(HOTSPOT_TOPDIR)/src/os/windows/vm/version.rc, \ @@ -226,6 +211,18 @@ PRECOMPILED_HEADER_EXCLUDE := $(JVM_PRECOMPILED_HEADER_EXCLUDE), \ )) +ifeq ($(OPENJDK_TARGET_OS), windows) + # It doesn't matter which jvm.lib file gets exported, but we need + # to pick just one. + ifeq ($(JVM_VARIANT), $(firstword $(JVM_VARIANTS))) + $(eval $(call SetupCopyFiles, COPY_JVM_LIB, \ + DEST := $(LIB_OUTPUTDIR), \ + FILES :=$(JVM_VARIANT_OUTPUTDIR)/libjvm/objs/jvm.lib, \ + )) + TARGETS += $(COPY_JVM_LIB) + endif +endif + # AIX warning explanation: # 1500-010 : (W) WARNING in ...: Infinite loop. Program may not stop. # There are several infinite loops in the vm, so better suppress.
--- a/hotspot/make/lib/CompileLibjsig.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/lib/CompileLibjsig.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -34,7 +34,6 @@ ifneq ($(OPENJDK_TARGET_OS), windows) ifeq ($(STATIC_BUILD), false) - LIBJSIG_STRIP_SYMBOLS := true ifeq ($(OPENJDK_TARGET_OS), linux) LIBJSIG_CFLAGS := -fPIC -D_GNU_SOURCE -D_REENTRANT $(EXTRA_CFLAGS) LIBJSIG_LDFLAGS := $(LDFLAGS_HASH_STYLE) $(EXTRA_CFLAGS) @@ -72,8 +71,6 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx) LIBJSIG_CFLAGS := -m64 -D_GNU_SOURCE -pthread -mno-omit-leaf-frame-pointer -mstack-alignment=16 -fPIC LIBJSIG_LDFLAGS := $(LDFLAGS_HASH_STYLE) - # NOTE: This lib is not stripped on macosx in old build. Looks like a mistake. - LIBJSIG_STRIP_SYMBOLS := false else $(error Unknown target OS $(OPENJDK_TARGET_OS) in CompileLibjsig.gmk) endif @@ -84,20 +81,71 @@ LIBJSIG_LDFLAGS += $(SHARED_LIBRARY_FLAGS) + LIB_OUTPUTDIR := $(call FindLibDirForModule, java.base) + $(eval $(call SetupNativeCompilation, BUILD_LIBJSIG, \ LIBRARY := jsig, \ EXTRA_FILES := $(LIBJSIG_SRC_FILE), \ - OUTPUT_DIR := $(LIBJSIG_OUTPUTDIR), \ + OUTPUT_DIR := $(LIB_OUTPUTDIR), \ LANG := C, \ CFLAGS := $(LIBJSIG_CFLAGS) $(LIBJSIG_CPU_FLAGS), \ LDFLAGS := $(LIBJSIG_LDFLAGS) $(LIBJSIG_CPU_FLAGS), \ LIBS := $(LIBJSIG_LIBS), \ MAPFILE := $(LIBJSIG_MAPFILE), \ OBJECT_DIR := $(LIBJSIG_OUTPUTDIR)/objs, \ - STRIP_SYMBOLS := $(LIBJSIG_STRIP_SYMBOLS), \ )) TARGETS += $(BUILD_LIBJSIG) + + ############################################################################ + # Create symlinks in each variant sub dir + ifeq ($(OPENJDK_TARGET_OS), macosx) + DEBUG_INFO_SUFFIX := $(SHARED_LIBRARY_SUFFIX).dSYM + else + DEBUG_INFO_SUFFIX := .debuginfo + endif + + # $1 variant subdir + define CreateSymlinks + # Always symlink from libdir/variant/libjsig.so -> ../libjsig.so and + # the corresponding debuginfo. + $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig): \ + $(LIB_OUTPUTDIR)/$(call SHARED_LIBRARY,jsig) + $$(call MakeDir, $$(@D)) + $(RM) $$@ + $(LN) -s ../$$(@F) $$@ + + TARGETS += $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig) + + ifeq ($(COPY_DEBUG_SYMBOLS), true) + $(LIB_OUTPUTDIR)/$1/$(LIBRARY_PREFIX)jsig$(DEBUG_INFO_SUFFIX): \ + $(LIB_OUTPUTDIR)/$(call SHARED_LIBRARY,jsig) + $$(call MakeDir, $$(@D)) + $(RM) $$@ + $(LN) -s ../$$(@F) $$@ + + TARGETS += $(LIB_OUTPUTDIR)/$1/$(LIBRARY_PREFIX)jsig$(DEBUG_INFO_SUFFIX) + + ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), true) + $(LIB_OUTPUTDIR)/$1/$(LIBRARY_PREFIX)jsig.diz: \ + $(LIB_OUTPUTDIR)/$1/$(LIBRARY_PREFIX)jsig$(DEBUG_INFO_SUFFIX) + $(CD) $$(@D) && $(ZIP) -q -y $$@ $$(basename $$(@F))$(DEBUG_INFO_SUFFIX) + + TARGETS += $(LIB_OUTPUTDIR)/$1/$(LIBRARY_PREFIX)jsig.diz + endif + endif + endef + + # The subdir is the same as the variant for client and minimal, for all + # others it's server. + VARIANT_SUBDIRS := $(filter client minimal, $(JVM_VARIANTS)) \ + $(if $(filter-out client minimal, $(JVM_VARIANTS)), server) + $(foreach v, $(VARIANT_SUBDIRS), $(eval $(call CreateSymlinks,$v))) + + ############################################################################ + + include CopyToExplodedJdk.gmk + endif endif
--- a/hotspot/make/lib/CompileLibraries.gmk Fri Sep 30 02:52:38 2016 -0700 +++ b/hotspot/make/lib/CompileLibraries.gmk Wed Oct 05 06:28:22 2016 -0700 @@ -41,6 +41,8 @@ include lib/CompileGtest.gmk endif +include CopyToExplodedJdk.gmk + all: $(TARGETS) .PHONY: all
--- a/jaxp/.hgtags Fri Sep 30 02:52:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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:38 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); - return new Expression(oldInstance, Collections.class, "checkedSet", new Object[]{set, type}); - } - } - - static final class CheckedSortedSet_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type"); - SortedSet<?> set = new TreeSet<>((SortedSet<?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedSortedSet", new Object[]{set, type}); - } - } - - static final class CheckedMap_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType"); - Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType"); - Map<?,?> map = new HashMap<>((Map<?,?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedMap", new Object[]{map, keyType, valueType}); - } - } - - static final class CheckedSortedMap_PersistenceDelegate extends java_util_Collections { - protected Expression instantiate(Object oldInstance, Encoder out) { - Object keyType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType"); - Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType"); - SortedMap<?,?> map = new TreeMap<>((SortedMap<?,?>) oldInstance); - return new Expression(oldInstance, Collections.class, "checkedSortedMap", new Object[]{map, keyType, valueType}); - } - } -} - -/** - * The persistence delegate for {@code java.util.EnumMap} classes. - * - * @author Sergey A. Malenkov - */ -static final class java_util_EnumMap_PersistenceDelegate extends PersistenceDelegate { - protected boolean mutatesTo(Object oldInstance, Object newInstance) { - return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance)); - } - - protected Expression instantiate(Object oldInstance, Encoder out) { - return new Expression(oldInstance, EnumMap.class, "new", new Object[] {getType(oldInstance)}); - } - - private static Object getType(Object instance) { - return MetaData.getPrivateFieldValue(instance, "java.util.EnumMap.keyType"); - } -} - -/** - * The persistence delegate for {@code java.util.EnumSet} classes. - * - * @author Sergey A. Malenkov - */ -static final class java_util_EnumSet_PersistenceDelegate extends PersistenceDelegate { - protected boolean mutatesTo(Object oldInstance, Object newInstance) { - return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance)); - } - - protected Expression instantiate(Object oldInstance, Encoder out) { - return new Expression(oldInstance, EnumSet.class, "noneOf", new Object[] {getType(oldInstance)}); - } - - private static Object getType(Object instance) { - return MetaData.getPrivateFieldValue(instance, "java.util.EnumSet.elementType"); - } } // Collection @@ -1313,9 +1217,6 @@ internalPersistenceDelegates.put("java.sql.Date", new java_util_Date_PersistenceDelegate()); internalPersistenceDelegates.put("java.sql.Time", new java_util_Date_PersistenceDelegate()); - - internalPersistenceDelegates.put("java.util.JumboEnumSet", new java_util_EnumSet_PersistenceDelegate()); - internalPersistenceDelegates.put("java.util.RegularEnumSet", new java_util_EnumSet_PersistenceDelegate()); } @SuppressWarnings("rawtypes")
--- a/jdk/src/java.rmi/share/classes/java/rmi/server/UnicastRemoteObject.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/java/rmi/server/UnicastRemoteObject.java Wed Oct 05 06:28:22 2016 -0700 @@ -24,9 +24,11 @@ */ package java.rmi.server; +import java.io.ObjectInputFilter; import java.rmi.*; import sun.rmi.server.UnicastServerRef; import sun.rmi.server.UnicastServerRef2; +import sun.rmi.transport.LiveRef; /** * Used for exporting a remote object with JRMP and obtaining a stub @@ -38,11 +40,11 @@ * generated stubs is deprecated. This includes the API in this class that * requires the use of static stubs, as well as the runtime support for * loading static stubs. Generating stubs dynamically is preferred, using one - * of the five non-deprecated ways of exporting objects as listed below. Do + * of the non-deprecated ways of exporting objects as listed below. Do * not run {@code rmic} to generate static stub classes. It is unnecessary, and * it is also deprecated.</em> * - * <p>There are six ways to export remote objects: + * <p>There are eight ways to export remote objects: * * <ol> * @@ -67,12 +69,19 @@ * {@link #exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory) * exportObject(Remote, port, csf, ssf)} method. * + * <li>Calling the + * {@link #exportObject(Remote, int, ObjectInputFilter) exportObject(Remote, port, filter)} method. + * + * <li>Calling the + * {@link #exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter) + * exportObject(Remote, port, csf, ssf, filter)} method. + * * </ol> * * <p>The fourth technique, {@link #exportObject(Remote)}, * always uses statically generated stubs and is deprecated. * - * <p>The other five techniques all use the following approach: if the + * <p>The other techniques all use the following approach: if the * {@code java.rmi.server.ignoreStubClasses} property is {@code true} * (case insensitive) or if a static stub cannot be found, stubs are generated * dynamically using {@link java.lang.reflect.Proxy Proxy} objects. Otherwise, @@ -130,6 +139,22 @@ * * </ul> * + * <p> + * Exported remote objects receive method invocations from the stubs + * as described in the RMI specification. Each invocation's operation and + * parameters are unmarshaled using a custom {@link java.io.ObjectInputStream}. + * If an {@link ObjectInputFilter} is provided and is not {@code null} when the object + * is exported, it is used to filter the parameters as they are unmarshaled from the stream. + * The filter is used for all invocations and all parameters regardless of + * the method being invoked or the parameter values. + * If no filter is provided or is {@code null} for the exported object then the + * {@code ObjectInputStream} default filter, if any, is used. The default filter is + * configured with {@link ObjectInputFilter.Config#setSerialFilter(ObjectInputFilter) + * ObjectInputFilter.Config.setSerialFilter}. + * If the filter rejects any of the parameters, the {@code InvalidClassException} + * thrown by {@code ObjectInputStream} is reported as the cause of an + * {@link UnmarshalException}. + * * @implNote * Depending upon which constructor or static method is used for exporting an * object, {@link RMISocketFactory} may be used for creating sockets. @@ -347,6 +372,58 @@ } /** + * Exports the remote object to make it available to receive incoming + * calls, using the particular supplied port + * and {@linkplain ObjectInputFilter filter}. + * + * <p>The object is exported with a server socket + * created using the {@link RMISocketFactory} class. + * + * @param obj the remote object to be exported + * @param port the port to export the object on + * @param filter an ObjectInputFilter applied when deserializing invocation arguments; + * may be {@code null} + * @return remote object stub + * @exception RemoteException if export fails + * @since 9 + */ + public static Remote exportObject(Remote obj, int port, + ObjectInputFilter filter) + throws RemoteException + { + return exportObject(obj, new UnicastServerRef(new LiveRef(port), filter)); + } + + /** + * Exports the remote object to make it available to receive incoming + * calls, using a transport specified by the given socket factory + * and {@linkplain ObjectInputFilter filter}. + * + * <p>Either socket factory may be {@code null}, in which case + * the corresponding client or server socket creation method of + * {@link RMISocketFactory} is used instead. + * + * @param obj the remote object to be exported + * @param port the port to export the object on + * @param csf the client-side socket factory for making calls to the + * remote object + * @param ssf the server-side socket factory for receiving remote calls + * @param filter an ObjectInputFilter applied when deserializing invocation arguments; + * may be {@code null} + * @return remote object stub + * @exception RemoteException if export fails + * @since 9 + */ + public static Remote exportObject(Remote obj, int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf, + ObjectInputFilter filter) + throws RemoteException + { + return exportObject(obj, new UnicastServerRef2(port, csf, ssf, filter)); + } + + /** * Removes the remote object, obj, from the RMI runtime. If * successful, the object can no longer accept incoming RMI calls. * If the force parameter is true, the object is forcibly unexported
--- a/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java Wed Oct 05 06:28:22 2016 -0700 @@ -27,6 +27,8 @@ import java.io.IOException; import java.io.ObjectInput; +import java.io.ObjectInputFilter; +import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectStreamClass; import java.lang.reflect.InvocationTargetException; @@ -62,6 +64,10 @@ * UnicastServerRef implements the remote reference layer server-side * behavior for remote objects exported with the "UnicastRef" reference * type. + * If an {@link ObjectInputFilter ObjectInputFilter} is supplied it is + * invoked during deserialization to filter the arguments, + * otherwise the default filter of {@link ObjectInputStream ObjectInputStream} + * applies. * * @author Ann Wollrath * @author Roger Riggs @@ -103,6 +109,9 @@ */ private transient Skeleton skel; + // The ObjectInputFilter for checking the invocation arguments + private final transient ObjectInputFilter filter; + /** maps method hash to Method object for each remote method */ private transient Map<Long,Method> hashToMethod_Map = null; @@ -121,16 +130,29 @@ /** * Create a new (empty) Unicast server remote reference. + * The filter is null to defer to the default ObjectInputStream filter, if any. */ public UnicastServerRef() { + this.filter = null; } /** * Construct a Unicast server remote reference for a specified * liveRef. + * The filter is null to defer to the default ObjectInputStream filter, if any. */ public UnicastServerRef(LiveRef ref) { super(ref); + this.filter = null; + } + + /** + * Construct a Unicast server remote reference for a specified + * liveRef and filter. + */ + public UnicastServerRef(LiveRef ref, ObjectInputFilter filter) { + super(ref); + this.filter = filter; } /** @@ -139,6 +161,7 @@ */ public UnicastServerRef(int port) { super(new LiveRef(port)); + this.filter = null; } /** @@ -363,9 +386,23 @@ } } + /** + * Sets a filter for invocation arguments, if a filter has been set. + * Called by dispatch before the arguments are read. + */ protected void unmarshalCustomCallData(ObjectInput in) - throws IOException, ClassNotFoundException - {} + throws IOException, ClassNotFoundException { + if (filter != null && + in instanceof ObjectInputStream) { + // Set the filter on the stream + ObjectInputStream ois = (ObjectInputStream) in; + + AccessController.doPrivileged((PrivilegedAction<Void>)() -> { + ois.setObjectInputFilter(filter); + return null; + }); + } + } /** * Handle server-side dispatch using the RMI 1.1 stub/skeleton
--- a/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef2.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef2.java Wed Oct 05 06:28:22 2016 -0700 @@ -25,12 +25,13 @@ package sun.rmi.server; -import java.io.IOException; +import java.io.ObjectInputFilter; import java.io.ObjectOutput; -import java.rmi.*; -import java.rmi.server.*; -import sun.rmi.transport.*; -import sun.rmi.transport.tcp.*; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RemoteRef; + +import sun.rmi.transport.LiveRef; /** * Server-side ref for a remote impl that uses a custom socket factory. @@ -59,6 +60,16 @@ } /** + * Construct a Unicast server remote reference for a specified + * liveRef and filter. + */ + public UnicastServerRef2(LiveRef ref, + ObjectInputFilter filter) + { + super(ref, filter); + } + + /** * Construct a Unicast server remote reference to be exported * on the specified port. */ @@ -70,6 +81,18 @@ } /** + * Construct a Unicast server remote reference to be exported + * on the specified port. + */ + public UnicastServerRef2(int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf, + ObjectInputFilter filter) + { + super(new LiveRef(port, csf, ssf), filter); + } + + /** * Returns the class of the ref type to be serialized */ public String getRefClass(ObjectOutput out)
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.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 @@ -344,7 +344,7 @@ private void implInit(int opmode, Key key, byte[] iv, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - cancelOperation(); + reset(true); if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) { throw new InvalidKeyException("Key size is invalid"); } @@ -404,23 +404,26 @@ if (initialized == false) { return; } - initialized = false; + if ((session == null) || (token.explicitCancel == false)) { return; } - // cancel operation by finishing it - int bufLen = doFinalLength(0); - byte[] buffer = new byte[bufLen]; try { - if (encrypt) { - token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen); + if (session.hasObjects() == false) { + session = token.killSession(session); + return; } else { - token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen); + // cancel operation by finishing it + int bufLen = doFinalLength(0); + byte[] buffer = new byte[bufLen]; + if (encrypt) { + token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen); + } else { + token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen); + } } } catch (PKCS11Exception e) { throw new ProviderException("Cancel failed", e); - } finally { - reset(); } } @@ -483,7 +486,9 @@ } // reset the states to the pre-initialized values - private void reset() { + private void reset(boolean doCancel) { + if (doCancel) cancelOperation(); + initialized = false; bytesBuffered = 0; padBufferLen = 0; @@ -610,7 +615,7 @@ throw (ShortBufferException) (new ShortBufferException().initCause(e)); } - reset(); + reset(false); throw new ProviderException("update() failed", e); } } @@ -728,7 +733,7 @@ throw (ShortBufferException) (new ShortBufferException().initCause(e)); } - reset(); + reset(false); throw new ProviderException("update() failed", e); } } @@ -740,6 +745,7 @@ if (outLen < requiredOutLen) { throw new ShortBufferException(); } + boolean doCancel = true; try { ensureInitialized(); int k = 0; @@ -753,7 +759,12 @@ } k += token.p11.C_EncryptFinal(session.id(), 0, out, (outOfs + k), (outLen - k)); + doCancel = false; } else { + // Special handling to match SunJCE provider behavior + if (bytesBuffered == 0 && padBufferLen == 0) { + return 0; + } if (paddingObj != null) { if (padBufferLen != 0) { k = token.p11.C_DecryptUpdate(session.id(), 0, @@ -762,20 +773,24 @@ } k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k); + doCancel = false; + int actualPadLen = paddingObj.unpad(padBuffer, k); k -= actualPadLen; System.arraycopy(padBuffer, 0, out, outOfs, k); } else { k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs, outLen); + doCancel = false; } } return k; } catch (PKCS11Exception e) { + doCancel = false; handleException(e); throw new ProviderException("doFinal() failed", e); } finally { - reset(); + reset(doCancel); } } @@ -788,6 +803,7 @@ throw new ShortBufferException(); } + boolean doCancel = true; try { ensureInitialized(); @@ -818,7 +834,13 @@ } k += token.p11.C_EncryptFinal(session.id(), outAddr, outArray, (outOfs + k), (outLen - k)); + doCancel = false; } else { + // Special handling to match SunJCE provider behavior + if (bytesBuffered == 0 && padBufferLen == 0) { + return 0; + } + if (paddingObj != null) { if (padBufferLen != 0) { k = token.p11.C_DecryptUpdate(session.id(), @@ -828,6 +850,8 @@ } k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k); + doCancel = false; + int actualPadLen = paddingObj.unpad(padBuffer, k); k -= actualPadLen; outArray = padBuffer; @@ -835,6 +859,7 @@ } else { k = token.p11.C_DecryptFinal(session.id(), outAddr, outArray, outOfs, outLen); + doCancel = false; } } if ((!encrypt && paddingObj != null) || @@ -846,10 +871,11 @@ } return k; } catch (PKCS11Exception e) { + doCancel = false; handleException(e); throw new ProviderException("doFinal() failed", e); } finally { - reset(); + reset(doCancel); } }
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Signature.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Signature.java Wed Oct 05 06:28:22 2016 -0700 @@ -616,8 +616,11 @@ return dsaToASN1(signature); } } - } catch (PKCS11Exception e) { - throw new ProviderException(e); + } catch (PKCS11Exception pe) { + throw new ProviderException(pe); + } catch (SignatureException | ProviderException e) { + cancelOperation(); + throw e; } finally { initialized = false; session = token.releaseSession(session); @@ -669,8 +672,8 @@ } } return true; - } catch (PKCS11Exception e) { - long errorCode = e.getErrorCode(); + } catch (PKCS11Exception pe) { + long errorCode = pe.getErrorCode(); if (errorCode == CKR_SIGNATURE_INVALID) { return false; } @@ -682,10 +685,11 @@ if (errorCode == CKR_DATA_LEN_RANGE) { return false; } - throw new ProviderException(e); + throw new ProviderException(pe); + } catch (SignatureException | ProviderException e) { + cancelOperation(); + throw e; } finally { - // XXX we should not release the session if we abort above - // before calling C_Verify initialized = false; session = token.releaseSession(session); }
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/Secmod.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/Secmod.java Wed Oct 05 06:28:22 2016 -0700 @@ -743,6 +743,7 @@ Map<Bytes,TrustAttributes> trustMap = new HashMap<Bytes,TrustAttributes>(); Token token = provider.getToken(); Session session = null; + boolean exceptionOccurred = true; try { session = token.getOpSession(); int MAX_NUM = 8192; @@ -762,8 +763,13 @@ // skip put on pkcs11 error } } + exceptionOccurred = false; } finally { - token.releaseSession(session); + if (exceptionOccurred) { + token.killSession(session); + } else { + token.releaseSession(session); + } } return trustMap; }
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeCipherWithJavaPadding.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeCipherWithJavaPadding.java Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -160,8 +160,11 @@ ShortBufferException { int tbSize = (trailingBytes == null? 0:trailingBytes.position()); int dataLen = tbSize + lastData.length; - // check total length - if ((dataLen < 1) || (dataLen % blockSize != 0)) { + + // Special handling to match SunJCE provider behavior + if (dataLen <= 0) { + return 0; + } else if (dataLen % blockSize != 0) { UcryptoProvider.debug("PKCS5Padding: unpad, buffered " + tbSize + " bytes, last block " + lastData.length + " bytes"); @@ -402,7 +405,6 @@ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { int estimatedOutLen = engineGetOutputSize(inLen); - if (out.length - outOfs < estimatedOutLen) { throw new ShortBufferException("Actual: " + (out.length - outOfs) + ". Estimated Out Length: " + estimatedOutLen);
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeRSASignature.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeRSASignature.java Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -258,27 +258,38 @@ @Override protected synchronized byte[] engineSign() throws SignatureException { - byte[] sig = new byte[sigLength]; - int rv = doFinal(sig, 0, sigLength); - if (rv < 0) { - throw new SignatureException(new UcryptoException(-rv)); + try { + byte[] sig = new byte[sigLength]; + int rv = doFinal(sig, 0, sigLength); + if (rv < 0) { + throw new SignatureException(new UcryptoException(-rv)); + } + return sig; + } finally { + // doFinal should already be called, no need to cancel + reset(false); } - return sig; } @Override protected synchronized int engineSign(byte[] outbuf, int offset, int len) throws SignatureException { - if (outbuf == null || (offset < 0) || (outbuf.length < (offset + sigLength)) - || (len < sigLength)) { - throw new SignatureException("Invalid output buffer. offset: " + - offset + ". len: " + len + ". sigLength: " + sigLength); + boolean doCancel = true; + try { + if (outbuf == null || (offset < 0) || (outbuf.length < (offset + sigLength)) + || (len < sigLength)) { + throw new SignatureException("Invalid output buffer. offset: " + + offset + ". len: " + len + ". sigLength: " + sigLength); + } + int rv = doFinal(outbuf, offset, sigLength); + doCancel = false; + if (rv < 0) { + throw new SignatureException(new UcryptoException(-rv)); + } + return sigLength; + } finally { + reset(doCancel); } - int rv = doFinal(outbuf, offset, sigLength); - if (rv < 0) { - throw new SignatureException(new UcryptoException(-rv)); - } - return sigLength; } @Override @@ -329,19 +340,25 @@ @Override protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen) throws SignatureException { - if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength)) - || (sigLen != this.sigLength)) { - throw new SignatureException("Invalid signature length: got " + - sigLen + " but was expecting " + this.sigLength); - } + boolean doCancel = true; + try { + if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength)) + || (sigLen != this.sigLength)) { + throw new SignatureException("Invalid signature length: got " + + sigLen + " but was expecting " + this.sigLength); + } - int rv = doFinal(sigBytes, sigOfs, sigLen); - if (rv == 0) { - return true; - } else { - UcryptoProvider.debug("Signature: " + mech + " verification error " + + int rv = doFinal(sigBytes, sigOfs, sigLen); + doCancel = false; + if (rv == 0) { + return true; + } else { + UcryptoProvider.debug("Signature: " + mech + " verification error " + new UcryptoException(-rv).getMessage()); - return false; + return false; + } + } finally { + reset(doCancel); } } @@ -432,13 +449,9 @@ // returns 0 (success) or negative (ucrypto error occurred) private int doFinal(byte[] sigBytes, int sigOfs, int sigLen) { - try { - ensureInitialized(); - int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen); - return k; - } finally { - reset(false); - } + ensureInitialized(); + int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen); + return k; } // check and return RSA key size in number of bytes
--- a/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/extra/EditingHistory.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/extra/EditingHistory.java Wed Oct 05 06:28:22 2016 -0700 @@ -48,7 +48,9 @@ private History currentDelegate; protected EditingHistory(ConsoleReader in, Iterable<? extends String> originalHistory) { - this.fullHistory = new MemoryHistory(); + MemoryHistory fullHistory = new MemoryHistory(); + fullHistory.setIgnoreDuplicates(false); + this.fullHistory = fullHistory; this.currentDelegate = fullHistory; bind(in, CTRL_UP, (Runnable) () -> moveHistoryToSnippet(in, ((EditingHistory) in.getHistory())::previousSnippet));
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Wed Oct 05 06:28:22 2016 -0700 @@ -103,6 +103,18 @@ basename = en.baseName; entryname = en.entryName; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Entry)) return false; + return this.file.equals(((Entry)o).file); + } + + @Override + public int hashCode() { + return file.hashCode(); + } } class EntryName { @@ -124,10 +136,10 @@ if (name.startsWith("./")) { name = name.substring(2); } - this.baseName = name; - this.entryName = (version > BASE_VERSION) - ? VERSIONS_DIR + version + "/" + this.baseName - : this.baseName; + baseName = name; + entryName = (version > BASE_VERSION) + ? VERSIONS_DIR + version + "/" + baseName + : baseName; } } @@ -137,7 +149,7 @@ Map<String, Entry> entryMap = new HashMap<>(); // All entries need to be added/updated. - Map<String, Entry> entries = new LinkedHashMap<>(); + Set<Entry> entries = new LinkedHashSet<>(); // All packages. Set<String> packages = new HashSet<>(); @@ -855,8 +867,7 @@ moduleInfoPaths.put(entryName, f.toPath()); if (isUpdate) entryMap.put(entryName, entry); - } else if (!entries.containsKey(entryName)) { - entries.put(entryName, entry); + } else if (entries.add(entry)) { jarEntries.add(entryName); if (entry.basename.endsWith(".class") && !entryName.startsWith(VERSIONS_DIR)) packages.add(toPackageName(entry.basename)); @@ -864,8 +875,7 @@ entryMap.put(entryName, entry); } } else if (f.isDirectory()) { - if (!entries.containsKey(entryName)) { - entries.put(entryName, entry); + if (entries.add(entry)) { if (isUpdate) { entryMap.put(entryName, entry); } @@ -923,8 +933,7 @@ in.transferTo(zos); zos.closeEntry(); } - for (String entryname : entries.keySet()) { - Entry entry = entries.get(entryname); + for (Entry entry : entries) { addFile(zos, entry); } zos.close(); @@ -1049,7 +1058,7 @@ Entry ent = entryMap.get(name); addFile(zos, ent); entryMap.remove(name); - entries.remove(name); + entries.remove(ent); } jarEntries.add(name); @@ -1059,8 +1068,8 @@ } // add the remaining new files - for (String entryname : entries.keySet()) { - addFile(zos, entries.get(entryname)); + for (Entry entry : entries) { + addFile(zos, entry); } if (!foundManifest) { if (newManifest != null) { @@ -1248,6 +1257,9 @@ * Adds a new file entry to the ZIP output stream. */ void addFile(ZipOutputStream zos, Entry entry) throws IOException { + // skip the generation of directory entries for META-INF/versions/*/ + if (entry.basename.isEmpty()) return; + File file = entry.file; String name = entry.entryname; boolean isDir = entry.isDir;
--- a/jdk/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c Wed Oct 05 06:28:22 2016 -0700 @@ -211,6 +211,62 @@ return error; } +/* + * Delete saved global references - if any - for: + * - a potentially thrown Exception + * - a returned refernce/array value + * See invoker_doInvoke() and invoke* methods where global references + * are being saved. + */ +static void +deletePotentiallySavedGlobalRefs(JNIEnv *env, InvokeRequest *request) +{ + /* Delete potentially saved return value */ + if ((request->invokeType == INVOKE_CONSTRUCTOR) || + (returnTypeTag(request->methodSignature) == JDWP_TAG(OBJECT)) || + (returnTypeTag(request->methodSignature) == JDWP_TAG(ARRAY))) { + if (request->returnValue.l != NULL) { + tossGlobalRef(env, &(request->returnValue.l)); + } + } + /* Delete potentially saved exception */ + if (request->exception != NULL) { + tossGlobalRef(env, &(request->exception)); + } +} + +/* + * Delete global argument references from the request which got put there before a + * invoke request was carried out. See fillInvokeRequest(). + */ +static void +deleteGlobalArgumentRefs(JNIEnv *env, InvokeRequest *request) +{ + void *cursor; + jint argIndex = 0; + jvalue *argument = request->arguments; + jbyte argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor); + + if (request->clazz != NULL) { + tossGlobalRef(env, &(request->clazz)); + } + if (request->instance != NULL) { + tossGlobalRef(env, &(request->instance)); + } + /* Delete global argument references */ + while (argIndex < request->argumentCount) { + if ((argumentTag == JDWP_TAG(OBJECT)) || + (argumentTag == JDWP_TAG(ARRAY))) { + if (argument->l != NULL) { + tossGlobalRef(env, &(argument->l)); + } + } + argument++; + argIndex++; + argumentTag = nextArgumentTypeTag(&cursor); + } +} + static jvmtiError fillInvokeRequest(JNIEnv *env, InvokeRequest *request, jbyte invokeType, jbyte options, jint id, @@ -322,6 +378,8 @@ invokeConstructor(JNIEnv *env, InvokeRequest *request) { jobject object; + + JDI_ASSERT_MSG(request->clazz, "Request clazz null"); object = JNI_FUNC_PTR(env,NewObjectA)(env, request->clazz, request->method, request->arguments); @@ -338,6 +396,7 @@ case JDWP_TAG(OBJECT): case JDWP_TAG(ARRAY): { jobject object; + JDI_ASSERT_MSG(request->clazz, "Request clazz null"); object = JNI_FUNC_PTR(env,CallStaticObjectMethodA)(env, request->clazz, request->method, @@ -426,6 +485,7 @@ case JDWP_TAG(OBJECT): case JDWP_TAG(ARRAY): { jobject object; + JDI_ASSERT_MSG(request->instance, "Request instance null"); object = JNI_FUNC_PTR(env,CallObjectMethodA)(env, request->instance, request->method, @@ -513,6 +573,8 @@ case JDWP_TAG(OBJECT): case JDWP_TAG(ARRAY): { jobject object; + JDI_ASSERT_MSG(request->clazz, "Request clazz null"); + JDI_ASSERT_MSG(request->instance, "Request instance null"); object = JNI_FUNC_PTR(env,CallNonvirtualObjectMethodA)(env, request->instance, request->clazz, @@ -609,6 +671,8 @@ JNIEnv *env; jboolean startNow; InvokeRequest *request; + jbyte options; + jbyte invokeType; JDI_ASSERT(thread); @@ -625,6 +689,9 @@ if (startNow) { request->started = JNI_TRUE; } + options = request->options; + invokeType = request->invokeType; + debugMonitorExit(invokerLock); if (!startNow) { @@ -639,7 +706,7 @@ JNI_FUNC_PTR(env,ExceptionClear)(env); - switch (request->invokeType) { + switch (invokeType) { case INVOKE_CONSTRUCTOR: invokeConstructor(env, request); break; @@ -647,7 +714,7 @@ invokeStatic(env, request); break; case INVOKE_INSTANCE: - if (request->options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) { + if (options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) { invokeNonvirtual(env, request); } else { invokeVirtual(env, request); @@ -725,12 +792,23 @@ } /* + * At this time, there's no need to retain global references on + * arguments since the reply is processed. No one will deal with + * this request ID anymore, so we must call deleteGlobalArgumentRefs(). + * + * We cannot delete saved exception or return value references + * since otherwise a deleted handle would escape when writing + * the response to the stream. Instead, we clean those refs up + * after writing the respone. + */ + deleteGlobalArgumentRefs(env, request); + + /* * Give up the lock before I/O operation */ debugMonitorExit(invokerLock); eventHandler_unlock(); - if (!detached) { outStream_initReply(&out, id); (void)outStream_writeValue(env, &out, tag, returnValue); @@ -738,6 +816,16 @@ (void)outStream_writeObjectRef(env, &out, exc); outStream_sendReply(&out); } + + /* + * Delete potentially saved global references of return value + * and exception + */ + eventHandler_lock(); // for proper lock order + debugMonitorEnter(invokerLock); + deletePotentiallySavedGlobalRefs(env, request); + debugMonitorExit(invokerLock); + eventHandler_unlock(); } jboolean
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Wed Oct 05 06:28:22 2016 -0700 @@ -86,6 +86,9 @@ task.options.help = true; }, "--help", "-h"), new Option<JlinkTask>(true, (task, opt, arg) -> { + // if used multiple times, the last one wins! + // So, clear previous values, if any. + task.options.modulePath.clear(); String[] dirs = arg.split(File.pathSeparator); int i = 0; Arrays.stream(dirs) @@ -93,6 +96,9 @@ .forEach(task.options.modulePath::add); }, "--module-path", "-p"), new Option<JlinkTask>(true, (task, opt, arg) -> { + // if used multiple times, the last one wins! + // So, clear previous values, if any. + task.options.limitMods.clear(); for (String mn : arg.split(",")) { if (mn.isEmpty()) { throw taskHelper.newBadArgs("err.mods.must.be.specified",
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Wed Oct 05 06:28:22 2016 -0700 @@ -307,9 +307,10 @@ private boolean filterOutUnsupportedTags(byte[] b) { List<Locale> locales; + List<String> originalTags = Arrays.asList(new String(b).split(" ")); try { - locales = Arrays.asList(new String(b).split(" ")).stream() + locales = originalTags.stream() .filter(tag -> !tag.isEmpty()) .map(IncludeLocalesPlugin::tagToLocale) .collect(Collectors.toList()); @@ -319,6 +320,9 @@ } byte[] filteredBytes = filterLocales(locales).stream() + // Make sure the filtered language tags do exist in the + // original supported tags for compatibility codes, e.g., "iw" + .filter(originalTags::contains) .collect(Collectors.joining(" ")) .getBytes(); @@ -331,6 +335,11 @@ return true; } + /* + * Filter list of locales according to the secified priorityList. Note + * that returned list of language tags may include extra ones, such as + * compatibility ones (e.g., "iw" -> "iw", "he"). + */ private List<String> filterLocales(List<Locale> locales) { List<String> ret = Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()
--- a/jdk/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, 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 @@ -45,6 +45,13 @@ return jmm_interface->GetDiagnosticCommands(env); } +#define EXCEPTION_CHECK_AND_FREE(x) do { \ + if ((*env)->ExceptionCheck(env)) { \ + free(x); \ + return NULL; \ + } \ + } while(0) + jobject getDiagnosticCommandArgumentInfoArray(JNIEnv *env, jstring command, int num_arg) { int i; @@ -59,6 +66,7 @@ dcmd_arg_info_array = (dcmdArgInfo*) malloc(num_arg * sizeof(dcmdArgInfo)); /* According to ISO C it is perfectly legal for malloc to return zero if called with a zero argument */ if (dcmd_arg_info_array == NULL && num_arg != 0) { + JNU_ThrowOutOfMemoryError(env, 0); return NULL; } jmm_interface->GetDiagnosticCommandArgumentsInfo(env, command, @@ -76,14 +84,24 @@ return NULL; } for (i=0; i<num_arg; i++) { + jstring jname, jdesc,jtype,jdefStr; + + jname = (*env)->NewStringUTF(env,dcmd_arg_info_array[i].name); + EXCEPTION_CHECK_AND_FREE(dcmd_arg_info_array); + + jdesc = (*env)->NewStringUTF(env,dcmd_arg_info_array[i].description); + EXCEPTION_CHECK_AND_FREE(dcmd_arg_info_array); + + jtype = (*env)->NewStringUTF(env,dcmd_arg_info_array[i].type); + EXCEPTION_CHECK_AND_FREE(dcmd_arg_info_array); + + jdefStr = (*env)->NewStringUTF(env, dcmd_arg_info_array[i].default_string); + EXCEPTION_CHECK_AND_FREE(dcmd_arg_info_array); obj = JNU_NewObjectByName(env, "com/sun/management/internal/DiagnosticCommandArgumentInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZI)V", - (*env)->NewStringUTF(env,dcmd_arg_info_array[i].name), - (*env)->NewStringUTF(env,dcmd_arg_info_array[i].description), - (*env)->NewStringUTF(env,dcmd_arg_info_array[i].type), - dcmd_arg_info_array[i].default_string == NULL ? NULL: - (*env)->NewStringUTF(env, dcmd_arg_info_array[i].default_string), + jname, jdesc, jtype, + dcmd_arg_info_array[i].default_string == NULL ? NULL: jdefStr, dcmd_arg_info_array[i].mandatory, dcmd_arg_info_array[i].option, dcmd_arg_info_array[i].multiple, @@ -93,6 +111,7 @@ return NULL; } (*env)->SetObjectArrayElement(env, result, i, obj); + EXCEPTION_CHECK_AND_FREE(dcmd_arg_info_array); } free(dcmd_arg_info_array); arraysCls = (*env)->FindClass(env, "java/util/Arrays"); @@ -125,6 +144,7 @@ jint ret = jmm_interface->GetOptionalSupport(env, &mos); jsize num_commands; dcmdInfo* dcmd_info_array; + jstring jname, jdesc, jimpact; if (commands == NULL) { JNU_ThrowNullPointerException(env, "Invalid String Array"); @@ -139,7 +159,6 @@ result = (*env)->NewObjectArray(env, num_commands, dcmdInfoCls, NULL); if (result == NULL) { - JNU_ThrowOutOfMemoryError(env, 0); return NULL; } if (num_commands == 0) { @@ -159,15 +178,22 @@ dcmd_info_array[i].num_arguments); if (args == NULL) { free(dcmd_info_array); - JNU_ThrowOutOfMemoryError(env, 0); return NULL; } + + jname = (*env)->NewStringUTF(env,dcmd_info_array[i].name); + EXCEPTION_CHECK_AND_FREE(dcmd_info_array); + + jdesc = (*env)->NewStringUTF(env,dcmd_info_array[i].description); + EXCEPTION_CHECK_AND_FREE(dcmd_info_array); + + jimpact = (*env)->NewStringUTF(env,dcmd_info_array[i].impact); + EXCEPTION_CHECK_AND_FREE(dcmd_info_array); + obj = JNU_NewObjectByName(env, "com/sun/management/internal/DiagnosticCommandInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;)V", - (*env)->NewStringUTF(env,dcmd_info_array[i].name), - (*env)->NewStringUTF(env,dcmd_info_array[i].description), - (*env)->NewStringUTF(env,dcmd_info_array[i].impact), + jname, jdesc, jimpact, dcmd_info_array[i].permission_class==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_class), dcmd_info_array[i].permission_name==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_name), dcmd_info_array[i].permission_action==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_action), @@ -175,10 +201,11 @@ args); if (obj == NULL) { free(dcmd_info_array); - JNU_ThrowOutOfMemoryError(env, 0); return NULL; } + (*env)->SetObjectArrayElement(env, result, i, obj); + EXCEPTION_CHECK_AND_FREE(dcmd_info_array); } free(dcmd_info_array); return result;
--- a/jdk/test/ProblemList.txt Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/test/ProblemList.txt Wed Oct 05 06:28:22 2016 -0700 @@ -136,6 +136,8 @@ java/lang/instrument/DaemonThread/TestDaemonThread.java 8161225 generic-all +java/lang/instrument/DaemonThread/TestDaemonThread.java 8167001 generic-all + java/lang/management/MemoryMXBean/Pending.java 8158837 generic-all java/lang/management/MemoryMXBean/PendingAllGC.sh 8158760 generic-all @@ -275,8 +277,6 @@ java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java 8062512 generic-all -java/util/Arrays/ParallelPrefix.java 8080165,8085982 generic-all - java/util/BitSet/BitSetStreamTest.java 8079538 generic-all ############################################################################
--- a/jdk/test/com/sun/jdi/InvokeHangTest.java Fri Sep 30 02:52:38 2016 -0700 +++ b/jdk/test/com/sun/jdi/InvokeHangTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -29,6 +29,7 @@ * @author jjh * * @modules jdk.jdi + * @library /test/lib * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g InvokeHangTest.java * @run driver InvokeHangTest @@ -133,7 +134,7 @@ BreakpointRequest request2; static volatile int bkpts = 0; Thread timerThread; - static int waitTime = 20000; + static long waitTime = jdk.test.lib.Utils.adjustTimeout(20000); InvokeHangTest (String args[]) { super(args);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/com/sun/jdi/OomDebugTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2016 Red Hat Inc. + * + * 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 + * @bug 8153711 + * @summary JDWP: Memory Leak (global references not deleted after invokeMethod). + * + * @author Severin Gehwolf <sgehwolf@redhat.com> + * + * @library .. + * @run build TestScaffold VMConnection TargetListener TargetAdapter + * @run compile -g OomDebugTest.java + * @run main OomDebugTest OomDebugTestTarget test1 + * @run main OomDebugTest OomDebugTestTarget test2 + * @run main OomDebugTest OomDebugTestTarget test3 + * @run main OomDebugTest OomDebugTestTarget test4 + * @run main OomDebugTest OomDebugTestTarget test5 + */ +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import com.sun.jdi.ArrayReference; +import com.sun.jdi.ArrayType; +import com.sun.jdi.ClassType; +import com.sun.jdi.Field; +import com.sun.jdi.InvocationException; +import com.sun.jdi.Method; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.StackFrame; +import com.sun.jdi.VMOutOfMemoryException; +import com.sun.jdi.Value; +import com.sun.jdi.event.BreakpointEvent; +import com.sun.jdi.event.ExceptionEvent; + +/***************** Target program **********************/ + +class OomDebugTestTarget { + + OomDebugTestTarget() { + System.out.println("DEBUG: invoked constructor"); + } + static class FooCls { + @SuppressWarnings("unused") + private byte[] bytes = new byte[3000000]; + }; + + FooCls fooCls = new FooCls(); + byte[] byteArray = new byte[0]; + + void testMethod(FooCls foo) { + System.out.println("DEBUG: invoked 'void testMethod(FooCls)', foo == " + foo); + } + + void testPrimitive(byte[] foo) { + System.out.println("DEBUG: invoked 'void testPrimitive(byte[])', foo == " + foo); + } + + byte[] testPrimitiveArrRetval() { + System.out.println("DEBUG: invoked 'byte[] testPrimitiveArrRetval()'"); + return new byte[3000000]; + } + + FooCls testFooClsRetval() { + System.out.println("DEBUG: invoked 'FooCls testFooClsRetval()'"); + return new FooCls(); + } + + public void entry() {} + + public static void main(String[] args){ + System.out.println("DEBUG: OomDebugTestTarget.main"); + new OomDebugTestTarget().entry(); + } +} + +/***************** Test program ************************/ + +public class OomDebugTest extends TestScaffold { + + private static final String[] ALL_TESTS = new String[] { + "test1", "test2", "test3", "test4", "test5" + }; + private static final Set<String> ALL_TESTS_SET = new HashSet<String>(); + static { + ALL_TESTS_SET.addAll(Arrays.asList(ALL_TESTS)); + } + private static final String TEST_CLASSES = System.getProperty("test.classes", "."); + private static final File RESULT_FILE = new File(TEST_CLASSES, "results.properties"); + private static final String LAST_TEST = ALL_TESTS[ALL_TESTS.length - 1]; + private ReferenceType targetClass; + private ObjectReference thisObject; + private int failedTests; + private final String testMethod; + + public OomDebugTest(String[] args) { + super(args); + if (args.length != 2) { + throw new RuntimeException("Test failed unexpectedly."); + } + this.testMethod = args[1]; + } + + @Override + protected void runTests() throws Exception { + try { + addListener(new TargetAdapter() { + + @Override + public void exceptionThrown(ExceptionEvent event) { + String name = event.exception().referenceType().name(); + System.err.println("DEBUG: Exception thrown in debuggee was: " + name); + } + }); + /* + * Get to the top of entry() + * to determine targetClass and mainThread + */ + BreakpointEvent bpe = startTo("OomDebugTestTarget", "entry", "()V"); + targetClass = bpe.location().declaringType(); + + mainThread = bpe.thread(); + + StackFrame frame = mainThread.frame(0); + thisObject = frame.thisObject(); + java.lang.reflect.Method m = findTestMethod(); + m.invoke(this); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + failure(); + } catch (SecurityException e) { + e.printStackTrace(); + failure(); + } + /* + * resume the target, listening for events + */ + listenUntilVMDisconnect(); + } + + private java.lang.reflect.Method findTestMethod() + throws NoSuchMethodException, SecurityException { + return OomDebugTest.class.getDeclaredMethod(testMethod); + } + + private void failure() { + failedTests++; + } + + /* + * Test case: Object reference as method parameter. + */ + @SuppressWarnings("unused") // called via reflection + private void test1() throws Exception { + System.out.println("DEBUG: ------------> Running test1"); + try { + Field field = targetClass.fieldByName("fooCls"); + ClassType clsType = (ClassType)field.type(); + Method constructor = getConstructorForClass(clsType); + for (int i = 0; i < 15; i++) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + ObjectReference objRef = clsType.newInstance(mainThread, + constructor, + new ArrayList(0), + ObjectReference.INVOKE_NONVIRTUAL); + if (objRef.isCollected()) { + System.out.println("DEBUG: Object got GC'ed before we can use it. NO-OP."); + continue; + } + invoke("testMethod", "(LOomDebugTestTarget$FooCls;)V", objRef); + } + } catch (InvocationException e) { + handleFailure(e); + } + } + + /* + * Test case: Array reference as method parameter. + */ + @SuppressWarnings("unused") // called via reflection + private void test2() throws Exception { + System.out.println("DEBUG: ------------> Running test2"); + try { + Field field = targetClass.fieldByName("byteArray"); + ArrayType arrType = (ArrayType)field.type(); + + for (int i = 0; i < 15; i++) { + ArrayReference byteArrayVal = arrType.newInstance(3000000); + if (byteArrayVal.isCollected()) { + System.out.println("DEBUG: Object got GC'ed before we can use it. NO-OP."); + continue; + } + invoke("testPrimitive", "([B)V", byteArrayVal); + } + } catch (VMOutOfMemoryException e) { + defaultHandleOOMFailure(e); + } + } + + /* + * Test case: Array reference as return value. + */ + @SuppressWarnings("unused") // called via reflection + private void test3() throws Exception { + System.out.println("DEBUG: ------------> Running test3"); + try { + for (int i = 0; i < 15; i++) { + invoke("testPrimitiveArrRetval", + "()[B", + Collections.EMPTY_LIST, + vm().mirrorOfVoid()); + } + } catch (InvocationException e) { + handleFailure(e); + } + } + + /* + * Test case: Object reference as return value. + */ + @SuppressWarnings("unused") // called via reflection + private void test4() throws Exception { + System.out.println("DEBUG: ------------> Running test4"); + try { + for (int i = 0; i < 15; i++) { + invoke("testFooClsRetval", + "()LOomDebugTestTarget$FooCls;", + Collections.EMPTY_LIST, + vm().mirrorOfVoid()); + } + } catch (InvocationException e) { + handleFailure(e); + } + } + + /* + * Test case: Constructor + */ + @SuppressWarnings({ "unused", "unchecked", "rawtypes" }) // called via reflection + private void test5() throws Exception { + System.out.println("DEBUG: ------------> Running test5"); + try { + ClassType type = (ClassType)thisObject.type(); + for (int i = 0; i < 15; i++) { + type.newInstance(mainThread, + findMethod(targetClass, "<init>", "()V"), + new ArrayList(0), + ObjectReference.INVOKE_NONVIRTUAL); + } + } catch (InvocationException e) { + handleFailure(e); + } + } + + private Method getConstructorForClass(ClassType clsType) { + List<Method> methods = clsType.methodsByName("<init>"); + if (methods.size() != 1) { + throw new RuntimeException("FAIL. Expected only one, the default, constructor"); + } + return methods.get(0); + } + + private void handleFailure(InvocationException e) { + // There is no good way to see the OOME diagnostic message in the target since the + // TestScaffold might throw an exception while trying to print the stack trace. I.e + // it might get a a VMDisconnectedException before the stack trace printing finishes. + System.err.println("FAILURE: InvocationException thrown. Trying to determine cause..."); + defaultHandleOOMFailure(e); + } + + private void defaultHandleOOMFailure(Exception e) { + e.printStackTrace(); + failure(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + void invoke(String methodName, String methodSig, Value value) + throws Exception { + List args = new ArrayList(1); + args.add(value); + invoke(methodName, methodSig, args, value); + } + + void invoke(String methodName, + String methodSig, + @SuppressWarnings("rawtypes") List args, + Value value) throws Exception { + Method method = findMethod(targetClass, methodName, methodSig); + if ( method == null) { + failure("FAILED: Can't find method: " + + methodName + " for class = " + targetClass); + return; + } + invoke(method, args, value); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + void invoke(Method method, List args, Value value) throws Exception { + thisObject.invokeMethod(mainThread, method, args, 0); + System.out.println("DEBUG: Done invoking method via debugger."); + } + + Value fieldValue(String fieldName) { + Field field = targetClass.fieldByName(fieldName); + return thisObject.getValue(field); + } + + // Determine the pass/fail status on some heuristic and don't fail the + // test if < 3 of the total number of tests (currently 5) fail. This also + // has the nice side effect that all tests are first attempted and only + // all tests ran an overall pass/fail status is determined. + private static void determineOverallTestStatus(OomDebugTest oomTest) + throws IOException, FileNotFoundException { + Properties resultProps = new Properties(); + if (!RESULT_FILE.exists()) { + RESULT_FILE.createNewFile(); + } + FileInputStream fin = null; + try { + fin = new FileInputStream(RESULT_FILE); + resultProps.load(fin); + resultProps.put(oomTest.testMethod, + Integer.toString(oomTest.failedTests)); + } finally { + if (fin != null) { + fin.close(); + } + } + System.out.println("DEBUG: Finished running test '" + + oomTest.testMethod + "'."); + if (LAST_TEST.equals(oomTest.testMethod)) { + System.out.println("DEBUG: Determining overall test status."); + Set<String> actualTestsRun = new HashSet<String>(); + int totalTests = ALL_TESTS.length; + int failedTests = 0; + for (Object key: resultProps.keySet()) { + actualTestsRun.add((String)key); + Object propVal = resultProps.get(key); + int value = Integer.parseInt((String)propVal); + failedTests += value; + } + if (!ALL_TESTS_SET.equals(actualTestsRun)) { + String errorMsg = "Test failed! Expected to run tests '" + + ALL_TESTS_SET + "', but only these were run '" + + actualTestsRun + "'"; + throw new RuntimeException(errorMsg); + } + if (failedTests >= 3) { + String errorMsg = "Test failed. Expected < 3 sub-tests to fail " + + "for a pass. Got " + failedTests + + " failed tests out of " + totalTests + "."; + throw new RuntimeException(errorMsg); + } + RESULT_FILE.delete(); + System.out.println("All " + totalTests + " tests passed."); + } else { + System.out.println("DEBUG: More tests to run. Coninuing."); + FileOutputStream fout = null; + try { + fout = new FileOutputStream(RESULT_FILE); + resultProps.store(fout, "Storing results after test " + + oomTest.testMethod); + } finally { + if (fout != null) { + fout.close(); + } + } + } + } + + public static void main(String[] args) throws Exception { + System.setProperty("test.vm.opts", "-Xmx40m"); // Set debuggee VM option + OomDebugTest oomTest = new OomDebugTest(args); + try { + oomTest.startTests(); + } catch (Throwable e) { + System.out.println("DEBUG: Got exception for test run. " + e); + e.printStackTrace(); + oomTest.failure(); + } + determineOverallTestStatus(oomTest); + } + +}
--- a/jdk/test/java/beans/XMLEncoder/EnumPrivate.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2007, 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. - */ - -enum EnumPrivate { - A0,B0,C0,D0,E0,F0,G0,H0,I0,J0,K0,L0,M0,N0,O0,P0,Q0,R0,S0,T0,U0,V0,W0,X0,Y0,Z0, - A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1, - A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2,V2,W2,X2,Y2,Z2, - A3,B3,C3,D3,E3,F3,G3,H3,I3,J3,K3,L3,M3,N3,O3,P3,Q3,R3,S3,T3,U3,V3,W3,X3,Y3,Z3, - A4,B4,C4,D4,E4,F4,G4,H4,I4,J4,K4,L4,M4,N4,O4,P4,Q4,R4,S4,T4,U4,V4,W4,X4,Y4,Z4, - A5,B5,C5,D5,E5,F5,G5,H5,I5,J5,K5,L5,M5,N5,O5,P5,Q5,R5,S5,T5,U5,V5,W5,X5,Y5,Z5, - A6,B6,C6,D6,E6,F6,G6,H6,I6,J6,K6,L6,M6,N6,O6,P6,Q6,R6,S6,T6,U6,V6,W6,X6,Y6,Z6, - A7,B7,C7,D7,E7,F7,G7,H7,I7,J7,K7,L7,M7,N7,O7,P7,Q7,R7,S7,T7,U7,V7,W7,X7,Y7,Z7, - A8,B8,C8,D8,E8,F8,G8,H8,I8,J8,K8,L8,M8,N8,O8,P8,Q8,R8,S8,T8,U8,V8,W8,X8,Y8,Z8, - A9,B9,C9,D9,E9,F9,G9,H9,I9,J9,K9,L9,M9,N9,O9,P9,Q9,R9,S9,T9,U9,V9,W9,X9,Y9,Z9, -}
--- a/jdk/test/java/beans/XMLEncoder/EnumPublic.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2007, 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 enum EnumPublic {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedCollection.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedCollection encoding - * @author Sergey Malenkov - */ - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public final class java_util_Collections_CheckedCollection extends AbstractTest<Collection<String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedCollection().test(true); - } - - protected Collection<String> getObject() { - List<String> list = Collections.singletonList("string"); - return Collections.checkedCollection(list, String.class); - } - - protected Collection<String> getAnotherObject() { - List<String> list = Collections.emptyList(); - return Collections.checkedCollection(list, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedList.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedList encoding - * @author Sergey Malenkov - */ - -import java.util.Collections; -import java.util.List; - -public final class java_util_Collections_CheckedList extends AbstractTest<List<String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedList().test(true); - } - - protected List<String> getObject() { - List<String> list = Collections.singletonList("string"); - return Collections.checkedList(list, String.class); - } - - protected List<String> getAnotherObject() { - List<String> list = Collections.emptyList(); - return Collections.checkedList(list, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedMap.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedMap encoding - * @author Sergey Malenkov - */ - -import java.util.Collections; -import java.util.Map; - -public final class java_util_Collections_CheckedMap extends AbstractTest<Map<String, String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedMap().test(true); - } - - protected Map<String, String> getObject() { - Map<String, String> map = Collections.singletonMap("key", "value"); - return Collections.checkedMap(map, String.class, String.class); - } - - protected Map<String, String> getAnotherObject() { - Map<String, String> map = Collections.emptyMap(); - return Collections.checkedMap(map, String.class, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedRandomAccessList.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedRandomAccessList encoding - * @author Sergey Malenkov - */ - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public final class java_util_Collections_CheckedRandomAccessList extends AbstractTest<List<String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedRandomAccessList().test(true); - } - - protected List<String> getObject() { - List<String> list = new ArrayList<String>(); - list.add("string"); - return Collections.checkedList(list, String.class); - } - - protected List<String> getAnotherObject() { - List<String> list = new ArrayList<String>(); - return Collections.checkedList(list, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSet.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedSet encoding - * @author Sergey Malenkov - */ - -import java.util.Collections; -import java.util.Set; - -public final class java_util_Collections_CheckedSet extends AbstractTest<Set<String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedSet().test(true); - } - - protected Set<String> getObject() { - Set<String> set = Collections.singleton("string"); - return Collections.checkedSet(set, String.class); - } - - protected Set<String> getAnotherObject() { - Set<String> set = Collections.emptySet(); - return Collections.checkedSet(set, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSortedMap.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedSortedMap encoding - * @author Sergey Malenkov - */ - -import java.util.Collections; -import java.util.SortedMap; -import java.util.TreeMap; - -public final class java_util_Collections_CheckedSortedMap extends AbstractTest<SortedMap<String, String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedSortedMap().test(true); - } - - protected SortedMap<String, String> getObject() { - SortedMap<String, String> map = new TreeMap<String, String>(); - map.put("key", "value"); - return Collections.checkedSortedMap(map, String.class, String.class); - } - - protected SortedMap<String, String> getAnotherObject() { - SortedMap<String, String> map = new TreeMap<String, String>(); - return Collections.checkedSortedMap(map, String.class, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_Collections_CheckedSortedSet.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6505888 - * @summary Tests CheckedSortedSet encoding - * @author Sergey Malenkov - */ - -import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; - -public final class java_util_Collections_CheckedSortedSet extends AbstractTest<SortedSet<String>> { - public static void main(String[] args) { - new java_util_Collections_CheckedSortedSet().test(true); - } - - protected SortedSet<String> getObject() { - SortedSet<String> set = new TreeSet<String>(); - set.add("string"); - return Collections.checkedSortedSet(set, String.class); - } - - protected SortedSet<String> getAnotherObject() { - SortedSet<String> set = new TreeSet<String>(); - return Collections.checkedSortedSet(set, String.class); - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_EnumMap.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6536295 - * @summary Tests EnumMap encoding - * @author Sergey Malenkov - */ - -import java.util.EnumMap; -import java.util.Map; - -public final class java_util_EnumMap extends AbstractTest<Map<EnumPublic, String>> { - public static void main(String[] args) { - new java_util_EnumMap().test(true); - } - - protected Map<EnumPublic, String> getObject() { - return new EnumMap<EnumPublic, String>(EnumPublic.class); - } - - protected Map<EnumPublic, String> getAnotherObject() { - Map<EnumPublic, String> map = new EnumMap<EnumPublic, String>(EnumPublic.class); - map.put(EnumPublic.A, "value"); - map.put(EnumPublic.Z, null); - return map; - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_JumboEnumSet.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6536295 - * @summary Tests JumboEnumSet encoding - * @author Sergey Malenkov - */ - -import java.util.EnumSet; -import java.util.Set; - -public final class java_util_JumboEnumSet extends AbstractTest<Set<EnumPrivate>> { - public static void main(String[] args) { - new java_util_JumboEnumSet().test(true); - } - - protected Set<EnumPrivate> getObject() { - return EnumSet.noneOf(EnumPrivate.class); - } - - protected Set<EnumPrivate> getAnotherObject() { - Set<EnumPrivate> set = EnumSet.noneOf(EnumPrivate.class); - set.add(EnumPrivate.A0); - set.add(EnumPrivate.Z9); - return set; - } -}
--- a/jdk/test/java/beans/XMLEncoder/java_util_RegularEnumSet.java Fri Sep 30 02:52:38 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2007, 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 - * @bug 6536295 - * @summary Tests RegularEnumSet encoding - * @author Sergey Malenkov - */ - -import java.util.EnumSet; -import java.util.Set; - -public final class java_util_RegularEnumSet extends AbstractTest<Set<EnumPublic>> { - public static void main(String[] args) { - new java_util_RegularEnumSet().test(true); - } - - protected Set<EnumPublic> getObject() { - return EnumSet.noneOf(EnumPublic.class); - } - - protected Set<EnumPublic> getAnotherObject() { - Set<EnumPublic> set = EnumSet.noneOf(EnumPublic.class); - set.add(EnumPublic.A); - set.add(EnumPublic.Z); - return set; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/io/Serializable/serialFilter/CheckInputOrderTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,93 @@ +/* + * 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.ByteArrayInputStream; +import java.io.InvalidClassException; +import java.io.ObjectInputFilter; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.security.Security; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; + +/* @test + * @build CheckInputOrderTest SerialFilterTest + * @run testng/othervm CheckInputOrderTest + * + * @summary Test that when both global filter and specific filter are set, + * global filter will not affect specific filter. + */ + +public class CheckInputOrderTest implements Serializable { + private static final long serialVersionUID = 12345678901L; + + @DataProvider(name="Patterns") + Object[][] patterns() { + return new Object[][] { + new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long;maxarray=0", false }, + new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long", true }, + new Object[] { Long.MAX_VALUE, "java.**;java.lang.*;java.lang.Long;maxdepth=0", false }, + new Object[] { Long.MAX_VALUE, "java.**;java.lang.*;java.lang.Long;maxbytes=0", false }, + new Object[] { Long.MAX_VALUE, "java.**;java.lang.*;java.lang.Long;maxrefs=0", false }, + + new Object[] { Long.MAX_VALUE, "java.**;java.lang.*;java.lang.Long", true }, + + new Object[] { Long.MAX_VALUE, "!java.**;java.lang.*;java.lang.Long", false }, + new Object[] { Long.MAX_VALUE, "java.**;!java.lang.*;java.lang.Long", true }, + + new Object[] { Long.MAX_VALUE, "!java.lang.*;java.**;java.lang.Long", false }, + new Object[] { Long.MAX_VALUE, "java.lang.*;!java.**;java.lang.Long", true }, + + new Object[] { Long.MAX_VALUE, "!java.lang.Long;java.**;java.lang.*", false }, + new Object[] { Long.MAX_VALUE, "java.lang.Long;java.**;!java.lang.*", true }, + + new Object[] { Long.MAX_VALUE, "java.lang.Long;!java.**;java.lang.*", false }, + new Object[] { Long.MAX_VALUE, "java.lang.Long;java.lang.Number;!java.**;java.lang.*", true }, + }; + } + + /** + * Test: + * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject + * "global filter reject" + "specific ObjectInputStream filter allow" => should allow + */ + @Test(dataProvider="Patterns") + public void testRejectedInGlobal(Object toDeserialized, String pattern, boolean allowed) throws Exception { + byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); + ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); + + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ois.setObjectInputFilter(filter); + Object o = ois.readObject(); + assertTrue(allowed, "filter should have thrown an exception"); + } catch (InvalidClassException ice) { + assertFalse(allowed, "filter should have thrown an exception"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/io/Serializable/serialFilter/FilterWithSecurityManagerTest.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.io.ByteArrayInputStream; +import java.io.ObjectInputFilter; +import java.io.ObjectInputStream; +import java.security.AccessControlException; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/* @test + * @build FilterWithSecurityManagerTest SerialFilterTest + * @run testng/othervm FilterWithSecurityManagerTest + * @run testng/othervm/policy=security.policy.without.globalFilter + * -Djava.security.manager=default FilterWithSecurityManagerTest + * @run testng/othervm/policy=security.policy + * -Djava.security.manager=default + * -Djdk.serialFilter=java.lang.Integer FilterWithSecurityManagerTest + * + * @summary Test that setting specific filter is checked by security manager, + * setting process-wide filter is checked by security manager. + */ + +@Test +public class FilterWithSecurityManagerTest { + + byte[] bytes; + boolean setSecurityManager; + ObjectInputFilter filter; + + @BeforeClass + public void setup() throws Exception { + setSecurityManager = System.getSecurityManager() != null; + Object toDeserialized = Long.MAX_VALUE; + bytes = SerialFilterTest.writeObjects(toDeserialized); + filter = ObjectInputFilter.Config.createFilter("java.lang.Long"); + } + + /** + * Test that setting process-wide filter is checked by security manager. + */ + @Test + public void testGlobalFilter() throws Exception { + if (ObjectInputFilter.Config.getSerialFilter() == null) { + return; + } + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ObjectInputFilter.Config.setSerialFilter(filter); + assertFalse(setSecurityManager, + "When SecurityManager exists, without " + + "java.security.SerializablePermission(serialFilter) Exception should be thrown"); + Object o = ois.readObject(); + } catch (AccessControlException ex) { + assertTrue(setSecurityManager); + assertTrue(ex.getMessage().contains("java.io.SerializablePermission")); + assertTrue(ex.getMessage().contains("serialFilter")); + } + } + + /** + * Test that setting specific filter is checked by security manager. + */ + @Test(dependsOnMethods = { "testGlobalFilter" }) + public void testSpecificFilter() throws Exception { + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ois.setObjectInputFilter(filter); + Object o = ois.readObject(); + } catch (AccessControlException ex) { + assertTrue(setSecurityManager); + assertTrue(ex.getMessage().contains("java.io.SerializablePermission")); + assertTrue(ex.getMessage().contains("serialFilter")); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/io/Serializable/serialFilter/GlobalFilterTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,218 @@ +/* + * 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 static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInputFilter; +import java.io.ObjectInputStream; + +import java.io.SerializablePermission; +import java.security.Security; +import java.util.Objects; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +/* @test + * @build GlobalFilterTest SerialFilterTest + * @run testng/othervm GlobalFilterTest + * @run testng/othervm -Djdk.serialFilter=java.** GlobalFilterTest + * @run testng/othervm/policy=security.policy GlobalFilterTest + * @run testng/othervm/policy=security.policy + * -Djava.security.properties=${test.src}/java.security-extra1 + * -Djava.security.debug=properties GlobalFilterTest + * + * @summary Test Global Filters + */ +@Test +public class GlobalFilterTest { + + /** + * DataProvider of patterns and objects derived from the configured process-wide filter. + * @return Array of arrays of pattern, object, allowed boolean, and API factory + */ + @DataProvider(name="globalPatternElements") + Object[][] globalPatternElements() { + String globalFilter = + System.getProperty("jdk.serialFilter", + Security.getProperty("jdk.serialFilter")); + if (globalFilter == null) { + return new Object[0][]; + } + + String[] patterns = globalFilter.split(";"); + Object[][] objects = new Object[patterns.length][]; + + for (int i = 0; i < patterns.length; i++) { + Object o; + boolean allowed; + String pattern = patterns[i].trim(); + if (pattern.contains("=")) { + allowed = false; + o = SerialFilterTest.genTestObject(pattern, false); + } else { + allowed = !pattern.startsWith("!"); + o = (allowed) + ? SerialFilterTest.genTestObject(pattern, true) + : SerialFilterTest.genTestObject(pattern.substring(1), false); + + Assert.assertNotNull(o, "fail generation failed"); + } + objects[i] = new Object[3]; + objects[i][0] = pattern; + objects[i][1] = allowed; + objects[i][2] = o; + } + return objects; + } + + /** + * Test that the process-wide filter is set when the properties are set + * and has the toString matching the configured pattern. + */ + @Test() + static void globalFilter() { + String pattern = + System.getProperty("jdk.serialFilter", + Security.getProperty("jdk.serialFilter")); + ObjectInputFilter filter = ObjectInputFilter.Config.getSerialFilter(); + System.out.printf("global pattern: %s, filter: %s%n", pattern, filter); + Assert.assertEquals(pattern, Objects.toString(filter, null), + "process-wide filter pattern does not match"); + } + + /** + * If the Global filter is already set, it should always refuse to be + * set again. + * If there is a security manager, setting the serialFilter should fail + * without the appropriate permission. + * If there is no security manager then setting it should work. + */ + @Test() + static void setGlobalFilter() { + SecurityManager sm = System.getSecurityManager(); + ObjectInputFilter filter = new SerialFilterTest.Validator(); + ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); + if (global != null) { + // once set, can never be re-set + try { + ObjectInputFilter.Config.setSerialFilter(filter); + Assert.fail("set only once process-wide filter"); + } catch (IllegalStateException ise) { + if (sm != null) { + Assert.fail("wrong exception when security manager is set", ise); + } + } catch (SecurityException se) { + if (sm == null) { + Assert.fail("wrong exception when security manager is not set", se); + } + } + } else { + if (sm == null) { + // no security manager + try { + ObjectInputFilter.Config.setSerialFilter(filter); + // Note once set, it can not be reset; so other tests + System.out.printf("Global Filter set to Validator%n"); + } catch (SecurityException se) { + Assert.fail("setGlobalFilter should not get SecurityException", se); + } + try { + // Try to set it again, expecting it to throw + ObjectInputFilter.Config.setSerialFilter(filter); + Assert.fail("set only once process-wide filter"); + } catch (IllegalStateException ise) { + // Normal case + } + } else { + // Security manager + SecurityException expectSE = null; + try { + sm.checkPermission(new SerializablePermission("serialFilter")); + } catch (SecurityException se1) { + expectSE = se1; + } + SecurityException actualSE = null; + try { + ObjectInputFilter.Config.setSerialFilter(filter); + } catch (SecurityException se2) { + actualSE = se2; + } + if (expectSE == null | actualSE == null) { + Assert.assertEquals(expectSE, actualSE, "SecurityException"); + } else { + Assert.assertEquals(expectSE.getClass(), actualSE.getClass(), + "SecurityException class"); + } + } + } + } + + /** + * For each pattern in the process-wide filter test a generated object + * against the default process-wide filter. + * + * @param pattern a pattern extracted from the configured global pattern + */ + @Test(dataProvider = "globalPatternElements") + static void globalFilterElements(String pattern, boolean allowed,Object obj) { + testGlobalPattern(pattern, obj, allowed); + } + + /** + * Serialize and deserialize an object using the default process-wide filter + * and check allowed or reject. + * + * @param pattern the pattern + * @param object the test object + * @param allowed the expected result from ObjectInputStream (exception or not) + */ + static void testGlobalPattern(String pattern, Object object, boolean allowed) { + try { +// System.out.printf("global %s pattern: %s, obj: %s%n", (allowed ? "allowed" : "not allowed"), pattern, object); + byte[] bytes = SerialFilterTest.writeObjects(object); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + Object o = ois.readObject(); + } catch (EOFException eof) { + // normal completion + } catch (ClassNotFoundException cnf) { + Assert.fail("Deserializing", cnf); + } + Assert.assertTrue(allowed, "filter should have thrown an exception"); + } catch (IllegalArgumentException iae) { + Assert.fail("bad format pattern", iae); + } catch (InvalidClassException ice) { + Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); + } catch (IOException ioe) { + Assert.fail("Unexpected IOException", ioe); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/io/Serializable/serialFilter/MixedFiltersTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,137 @@ +/* + * 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.ByteArrayInputStream; +import java.io.InvalidClassException; +import java.io.ObjectInputFilter; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.security.Security; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* @test + * @build MixedFiltersTest SerialFilterTest + * @run testng/othervm -Djdk.serialFilter=!java.**;!java.lang.Long;maxdepth=5;maxarray=5;maxbytes=90;maxrefs=5 MixedFiltersTest + * @run testng/othervm -Djdk.serialFilter=java.**;java.lang.Long;maxdepth=1000;maxarray=1000;maxbytes=1000;maxrefs=1000 MixedFiltersTest + * + * @summary Test that when both global filter and specific filter are set, + * global filter will not affect specific filter. + */ + +public class MixedFiltersTest implements Serializable { + + private static final long serialVersionUID = 1234567890L; + + + boolean globalRejected; + + @BeforeClass + public void setup() { + String pattern = System.getProperty("jdk.serialFilter", + Security.getProperty("jdk.serialFilter")); + globalRejected = pattern.startsWith("!"); + } + + @DataProvider(name="RejectedInGlobal") + Object[][] rejectedInGlobal() { + if (!globalRejected) { + return new Object[0][]; + } + return new Object[][] { + new Object[] { Long.MAX_VALUE, "java.**" }, + new Object[] { Long.MAX_VALUE, "java.lang.Long" }, + new Object[] { SerialFilterTest.genTestObject("java.lang.**", true), "java.lang.**" }, + new Object[] { SerialFilterTest.genTestObject("maxdepth=10", true), "maxdepth=100" }, + new Object[] { SerialFilterTest.genTestObject("maxarray=10", true), "maxarray=100" }, + new Object[] { SerialFilterTest.genTestObject("maxbytes=100", true), "maxbytes=1000" }, + new Object[] { SerialFilterTest.genTestObject("maxrefs=10", true), "maxrefs=100" }, + }; + } + + /** + * Test: + * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject + * "global filter reject" + "specific ObjectInputStream filter allow" => should allow + */ + @Test(dataProvider="RejectedInGlobal") + public void testRejectedInGlobal(Object toDeserialized, String pattern) throws Exception { + byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + Object o = ois.readObject(); + fail("filter should have thrown an exception"); + } catch (InvalidClassException expected) { } + + ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ois.setObjectInputFilter(filter); + Object o = ois.readObject(); + } + } + + @DataProvider(name="AllowedInGlobal") + Object[][] allowedInGlobal() { + if (globalRejected) { + return new Object[0][]; + } + + return new Object[][] { + new Object[] { Long.MAX_VALUE, "!java.**" }, + new Object[] { Long.MAX_VALUE, "!java.lang.Long" }, + new Object[] { SerialFilterTest.genTestObject("java.lang.**", true), "!java.lang.**" }, + new Object[] { SerialFilterTest.genTestObject("maxdepth=10", true), "maxdepth=5" }, + new Object[] { SerialFilterTest.genTestObject("maxarray=10", true), "maxarray=5" }, + new Object[] { SerialFilterTest.genTestObject("maxbytes=100", true), "maxbytes=5" }, + new Object[] { SerialFilterTest.genTestObject("maxrefs=10", true), "maxrefs=5" }, + }; + } + + /** + * Test: + * "global filter allow" + "specific ObjectInputStream filter is empty" => should allow + * "global filter allow" + "specific ObjectInputStream filter reject" => should reject + */ + @Test(dataProvider="AllowedInGlobal") + public void testAllowedInGlobal(Object toDeserialized, String pattern) throws Exception { + byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + Object o = ois.readObject(); + } + + ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ois.setObjectInputFilter(filter); + Object o = ois.readObject(); + assertTrue(false, "filter should have thrown an exception"); + } catch (InvalidClassException expected) { } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/io/Serializable/serialFilter/SerialFilterTest.java Wed Oct 05 06:28:22 2016 -0700 @@ -0,0 +1,752 @@ +/* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectInputFilter; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import java.util.concurrent.atomic.LongAdder; + +import javax.lang.model.SourceVersion; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +/* @test + * @build SerialFilterTest + * @run testng/othervm SerialFilterTest + * + * @summary Test ObjectInputFilters + */ +@Test +public class SerialFilterTest implements Serializable { + + private static final long serialVersionUID = -6999613679881262446L; + + /** + * Enable three arg lambda. + * @param <T> The pattern + * @param <U> The test object + * @param <V> Boolean for if the filter should allow or reject + */ + interface TriConsumer< T, U, V> { + void accept(T t, U u, V v); + } + + /** + * Misc object to use that should always be accepted. + */ + private static final Object otherObject = Integer.valueOf(0); + + /** + * DataProvider for the individual patterns to test. + * Expand the patterns into cases for each of the Std and Compatibility APIs. + * @return an array of arrays of the parameters including factories + */ + @DataProvider(name="Patterns") + static Object[][] patterns() { + Object[][] patterns = new Object[][]{ + {"java.util.Hashtable"}, + {"java.util.Hash*"}, + {"javax.lang.model.*"}, + {"javax.lang.**"}, + {"*"}, + {"maxarray=47"}, + {"maxdepth=5"}, + {"maxrefs=10"}, + {"maxbytes=100"}, + {"maxbytes=72"}, + {"maxbytes=+1024"}, + {"java.base/java.util.Hashtable"}, + }; + return patterns; + } + + @DataProvider(name="InvalidPatterns") + static Object[][] invalidPatterns() { + return new Object [][] { + {"maxrefs=-1"}, + {"maxdepth=-1"}, + {"maxbytes=-1"}, + {"maxarray=-1"}, + {"xyz=0"}, + {"xyz=-1"}, + {"maxrefs=0xabc"}, + {"maxrefs=abc"}, + {"maxrefs="}, + {"maxrefs=+"}, + {".*"}, + {".**"}, + {"!"}, + {"/java.util.Hashtable"}, + {"java.base/"}, + {"/"}, + }; + } + + @DataProvider(name="Limits") + static Object[][] limits() { + // The numbers are arbitrary > 1 + return new Object[][]{ + {"maxrefs", 10}, + {"maxdepth", 5}, + {"maxbytes", 100}, + {"maxarray", 16}, + }; + } + + /** + * DataProvider of individual objects. Used to check the information + * available to the filter. + * @return Arrays of parameters with objects + */ + @DataProvider(name="Objects") + static Object[][] objects() { + byte[] byteArray = new byte[0]; + Object[] objArray = new Object[7]; + objArray[objArray.length - 1] = objArray; + + Class<?> serClass = null; + String className = "java.util.concurrent.atomic.LongAdder$SerializationProxy"; + try { + serClass = Class.forName(className); + } catch (Exception e) { + Assert.fail("missing class: " + className, e); + } + + Class<?>[] interfaces = {Runnable.class}; + Runnable proxy = (Runnable) Proxy.newProxyInstance(null, + interfaces, (p, m, args) -> p); + + Runnable runnable = (Runnable & Serializable) SerialFilterTest::noop; + Object[][] objects = { + { null, 0, -1, 0, 0, 0, + new HashSet<>()}, // no callback, no values + { objArray, 3, 7, 8, 2, 55, + new HashSet<>(Arrays.asList(objArray.getClass()))}, + { Object[].class, 1, -1, 1, 1, 40, + new HashSet<>(Arrays.asList(Object[].class))}, + { new SerialFilterTest(), 1, -1, 1, 1, 37, + new HashSet<>(Arrays.asList(SerialFilterTest.class))}, + { new LongAdder(), 2, -1, 1, 1, 93, + new HashSet<>(Arrays.asList(LongAdder.class, serClass))}, + { new byte[14], 2, 14, 1, 1, 27, + new HashSet<>(Arrays.asList(byteArray.getClass()))}, + { runnable, 13, 0, 10, 2, 514, + new HashSet<>(Arrays.asList(java.lang.invoke.SerializedLambda.class, + SerialFilterTest.class, + objArray.getClass()))}, + { deepHashSet(10), 48, -1, 49, 11, 619, + new HashSet<>(Arrays.asList(HashSet.class))}, + { proxy.getClass(), 3, -1, 1, 1, 114, + new HashSet<>(Arrays.asList(Runnable.class, + java.lang.reflect.Proxy.class))}, + }; + return objects; + } + + @DataProvider(name="Arrays") + static Object[][] arrays() { + return new Object[][]{ + {new Object[16], 16}, + {new boolean[16], 16}, + {new byte[16], 16}, + {new char[16], 16}, + {new int[16], 16}, + {new long[16], 16}, + {new short[16], 16}, + {new float[16], 16}, + {new double[16], 16}, + }; + } + + + /** + * Test each object and verify the classes identified by the filter, + * the count of calls to the filter, the max array size, max refs, max depth, + * max bytes. + * This test ignores/is not dependent on the global filter settings. + * + * @param object a Serializable object + * @param count the expected count of calls to the filter + * @param maxArray the maximum array size + * @param maxRefs the maximum references + * @param maxDepth the maximum depth + * @param maxBytes the maximum stream size + * @param classes the expected (unique) classes + * @throws IOException + */ + @Test(dataProvider="Objects") + public static void t1(Object object, + long count, long maxArray, long maxRefs, long maxDepth, long maxBytes, + Set<Class<?>> classes) throws IOException { + byte[] bytes = writeObjects(object); + Validator validator = new Validator(); + validate(bytes, validator); + System.out.printf("v: %s%n", validator); + Assert.assertEquals(validator.count, count, "callback count wrong"); + Assert.assertEquals(validator.classes, classes, "classes mismatch"); + Assert.assertEquals(validator.maxArray, maxArray, "maxArray mismatch"); + Assert.assertEquals(validator.maxRefs, maxRefs, "maxRefs wrong"); + Assert.assertEquals(validator.maxDepth, maxDepth, "depth wrong"); + Assert.assertEquals(validator.maxBytes, maxBytes, "maxBytes wrong"); + } + + /** + * Test each pattern with an appropriate object. + * A filter is created from the pattern and used to serialize and + * deserialize a generated object with both the positive and negative case. + * This test ignores/is not dependent on the global filter settings. + * + * @param pattern a pattern + */ + @Test(dataProvider="Patterns") + static void testPatterns(String pattern) { + evalPattern(pattern, (p, o, neg) -> testPatterns(p, o, neg)); + } + + /** + * Test that the filter on a OIS can be set only on a fresh OIS, + * before deserializing any objects. + * This test is agnostic the global filter being set or not. + */ + @Test + static void nonResettableFilter() { + Validator validator1 = new Validator(); + Validator validator2 = new Validator(); + + try { + byte[] bytes = writeObjects("text1"); // an object + + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + // Check the initial filter is the global filter; may be null + ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); + ObjectInputFilter initial = ois.getObjectInputFilter(); + Assert.assertEquals(global, initial, "initial filter should be the global filter"); + + // Check if it can be set to null + ois.setObjectInputFilter(null); + ObjectInputFilter filter = ois.getObjectInputFilter(); + Assert.assertNull(filter, "set to null should be null"); + + ois.setObjectInputFilter(validator1); + Object o = ois.readObject(); + try { + ois.setObjectInputFilter(validator2); + Assert.fail("Should not be able to set filter twice"); + } catch (IllegalStateException ise) { + // success, the exception was expected + } + } catch (EOFException eof) { + Assert.fail("Should not reach end-of-file", eof); + } catch (ClassNotFoundException cnf) { + Assert.fail("Deserializing", cnf); + } + } catch (IOException ex) { + Assert.fail("Unexpected IOException", ex); + } + } + + /** + * Test that if an Objects readReadResolve method returns an array + * that the callback to the filter includes the proper array length. + * @throws IOException if an error occurs + */ + @Test(dataProvider="Arrays") + static void testReadResolveToArray(Object array, int length) throws IOException { + ReadResolveToArray object = new ReadResolveToArray(array, length); + byte[] bytes = writeObjects(object); + Object o = validate(bytes, object); // the object is its own filter + Assert.assertEquals(o.getClass(), array.getClass(), "Filter not called with the array"); + } + + + /** + * Test repeated limits use the last value. + * Construct a filter with the limit and the limit repeated -1. + * Invoke the filter with the limit to make sure it is rejected. + * Invoke the filter with the limit -1 to make sure it is accepted. + * @param name the name of the limit to test + * @param value a test value + */ + @Test(dataProvider="Limits") + static void testLimits(String name, int value) { + Class<?> arrayClass = new int[0].getClass(); + String pattern = String.format("%s=%d;%s=%d", name, value, name, value - 1); + ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); + Assert.assertEquals( + filter.checkInput(new FilterValues(arrayClass, value, value, value, value)), + ObjectInputFilter.Status.REJECTED, + "last limit value not used: " + filter); + Assert.assertEquals( + filter.checkInput(new FilterValues(arrayClass, value-1, value-1, value-1, value-1)), + ObjectInputFilter.Status.UNDECIDED, + "last limit value not used: " + filter); + } + + /** + * Test that returning null from a filter causes deserialization to fail. + */ + @Test(expectedExceptions=InvalidClassException.class) + static void testNullStatus() throws IOException { + byte[] bytes = writeObjects(0); // an Integer + try { + Object o = validate(bytes, new ObjectInputFilter() { + public ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo f) { + return null; + } + }); + } catch (InvalidClassException ice) { + System.out.printf("Success exception: %s%n", ice); + throw ice; + } + } + + /** + * Verify that malformed patterns throw IAE. + * @param pattern pattern from the data source + */ + @Test(dataProvider="InvalidPatterns", expectedExceptions=IllegalArgumentException.class) + static void testInvalidPatterns(String pattern) { + try { + ObjectInputFilter.Config.createFilter(pattern); + } catch (IllegalArgumentException iae) { + System.out.printf(" success exception: %s%n", iae); + throw iae; + } + } + + /** + * Test that Config.create returns null if the argument does not contain any patterns or limits. + */ + @Test() + static void testEmptyPattern() { + ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(""); + Assert.assertNull(filter, "empty pattern did not return null"); + + filter = ObjectInputFilter.Config.createFilter(";;;;"); + Assert.assertNull(filter, "pattern with only delimiters did not return null"); + } + + /** + * Read objects from the serialized stream, validated with the filter. + * + * @param bytes a byte array to read objects from + * @param filter the ObjectInputFilter + * @return the object deserialized if any + * @throws IOException can be thrown + */ + static Object validate(byte[] bytes, + ObjectInputFilter filter) throws IOException { + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais)) { + ois.setObjectInputFilter(filter); + + Object o = ois.readObject(); + return o; + } catch (EOFException eof) { + // normal completion + } catch (ClassNotFoundException cnf) { + Assert.fail("Deserializing", cnf); + } + return null; + } + + /** + * Write objects and return a byte array with the bytes. + * + * @param objects zero or more objects to serialize + * @return the byte array of the serialized objects + * @throws IOException if an exception occurs + */ + static byte[] writeObjects(Object... objects) throws IOException { + byte[] bytes; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos)) { + for (Object o : objects) { + oos.writeObject(o); + } + bytes = baos.toByteArray(); + } + return bytes; + } + + /** + * A filter that accumulates information about the checkInput callbacks + * that can be checked after readObject completes. + */ + static class Validator implements ObjectInputFilter { + long count; // Count of calls to checkInput + HashSet<Class<?>> classes = new HashSet<>(); + long maxArray = -1; + long maxRefs; + long maxDepth; + long maxBytes; + + Validator() { + } + + @Override + public ObjectInputFilter.Status checkInput(FilterInfo filter) { + count++; + if (filter.serialClass() != null) { + if (filter.serialClass().getName().contains("$$Lambda$")) { + // TBD: proper identification of serialized Lambdas? + // Fold the serialized Lambda into the SerializedLambda type + classes.add(SerializedLambda.class); + } else if (Proxy.isProxyClass(filter.serialClass())) { + classes.add(Proxy.class); + } else { + classes.add(filter.serialClass()); + } + + } + this.maxArray = Math.max(this.maxArray, filter.arrayLength()); + this.maxRefs = Math.max(this.maxRefs, filter.references()); + this.maxDepth = Math.max(this.maxDepth, filter.depth()); + this.maxBytes = Math.max(this.maxBytes, filter.streamBytes()); + return ObjectInputFilter.Status.UNDECIDED; + } + + public String toString(){ + return "count: " + count + + ", classes: " + classes.toString() + + ", maxArray: " + maxArray + + ", maxRefs: " + maxRefs + + ", maxDepth: " + maxDepth +