--- 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
+ + ", maxBytes: " + maxBytes;
+ }
+ }
+
+
+ /**
+ * Create a filter from a pattern and API factory, then serialize and
+ * deserialize an object 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 testPatterns(String pattern, Object object, boolean allowed) {
+ try {
+ byte[] bytes = SerialFilterTest.writeObjects(object);
+ ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern);
+ validate(bytes, filter);
+ 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);
+ }
+ }
+
+ /**
+ * For a filter pattern, generate and apply a test object to the action.
+ * @param pattern a pattern
+ * @param action an action to perform on positive and negative cases
+ */
+ static void evalPattern(String pattern, TriConsumer<String, Object, Boolean> action) {
+ Object o = genTestObject(pattern, true);
+ Assert.assertNotNull(o, "success generation failed");
+ action.accept(pattern, o, true);
+
+ // Test the negative pattern
+ o = genTestObject(pattern, false);
+ Assert.assertNotNull(o, "fail generation failed");
+ String negPattern = pattern.contains("=") ? pattern : "!" + pattern;
+ action.accept(negPattern, o, false);
+ }
+
+ /**
+ * Generate a test object based on the pattern.
+ * Handles each of the forms of the pattern, wildcards,
+ * class name, various limit forms.
+ * @param pattern a pattern
+ * @param allowed a boolean indicating to generate the allowed or disallowed case
+ * @return an object or {@code null} to indicate no suitable object could be generated
+ */
+ static Object genTestObject(String pattern, boolean allowed) {
+ if (pattern.contains("=")) {
+ return genTestLimit(pattern, allowed);
+ } else if (pattern.endsWith("*")) {
+ return genTestObjectWildcard(pattern, allowed);
+ } else {
+ // class
+ // isolate module name, if any
+ int poffset = 0;
+ int soffset = pattern.indexOf('/', poffset);
+ String module = null;
+ if (soffset >= 0) {
+ poffset = soffset + 1;
+ module = pattern.substring(0, soffset);
+ }
+ try {
+ Class<?> clazz = Class.forName(pattern.substring(poffset));
+ Constructor<?> cons = clazz.getConstructor();
+ return cons.newInstance();
+ } catch (ClassNotFoundException ex) {
+ Assert.fail("no such class available: " + pattern);
+ } catch (InvocationTargetException
+ | NoSuchMethodException
+ | InstantiationException
+ | IllegalAccessException ex1) {
+ Assert.fail("newInstance: " + ex1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generate an object to be used with the various wildcard pattern forms.
+ * Explicitly supports only specific package wildcards with specific objects.
+ * @param pattern a wildcard pattern ending in "*"
+ * @param allowed a boolean indicating to generate the allowed or disallowed case
+ * @return an object within or outside the wildcard
+ */
+ static Object genTestObjectWildcard(String pattern, boolean allowed) {
+ if (pattern.endsWith(".**")) {
+ // package hierarchy wildcard
+ if (pattern.startsWith("javax.lang.")) {
+ return SourceVersion.RELEASE_5;
+ }
+ if (pattern.startsWith("java.")) {
+ return 4;
+ }
+ if (pattern.startsWith("javax.")) {
+ return SourceVersion.RELEASE_6;
+ }
+ return otherObject;
+ } else if (pattern.endsWith(".*")) {
+ // package wildcard
+ if (pattern.startsWith("javax.lang.model")) {
+ return SourceVersion.RELEASE_6;
+ }
+ } else {
+ // class wildcard
+ if (pattern.equals("*")) {
+ return otherObject; // any object will do
+ }
+ if (pattern.startsWith("java.util.Hash")) {
+ return new Hashtable<String, String>();
+ }
+ }
+ Assert.fail("Object could not be generated for pattern: "
+ + pattern
+ + ", allowed: " + allowed);
+ return null;
+ }
+
+ /**
+ * Generate a limit test object for the pattern.
+ * For positive cases, the object exactly hits the limit.
+ * For negative cases, the object is 1 greater than the limit
+ * @param pattern the pattern, containing "=" and a maxXXX keyword
+ * @param allowed a boolean indicating to generate the allowed or disallowed case
+ * @return a sitable object
+ */
+ static Object genTestLimit(String pattern, boolean allowed) {
+ int ndx = pattern.indexOf('=');
+ Assert.assertNotEquals(ndx, -1, "missing value in limit");
+ long value = Long.parseUnsignedLong(pattern.substring(ndx+1));
+ if (pattern.startsWith("maxdepth=")) {
+ // Return an object with the requested depth (or 1 greater)
+ long depth = allowed ? value : value + 1;
+ Object[] array = new Object[1];
+ for (int i = 1; i < depth; i++) {
+ Object[] n = new Object[1];
+ n[0] = array;
+ array = n;
+ }
+ return array;
+ } else if (pattern.startsWith("maxbytes=")) {
+ // Return a byte array that when written to OOS creates
+ // a stream of exactly the size requested.
+ return genMaxBytesObject(allowed, value);
+ } else if (pattern.startsWith("maxrefs=")) {
+ Object[] array = new Object[allowed ? (int)value - 1 : (int)value];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = otherObject;
+ }
+ return array;
+ } else if (pattern.startsWith("maxarray=")) {
+ return allowed ? new int[(int)value] : new int[(int)value+1];
+ }
+ Assert.fail("Object could not be generated for pattern: "
+ + pattern
+ + ", allowed: " + allowed);
+ return null;
+ }
+
+ /**
+ * Generate an an object that will be serialized to some number of bytes.
+ * Or 1 greater if allowed is false.
+ * It returns a two element Object array holding a byte array sized
+ * to achieve the desired total size.
+ * @param allowed true if the stream should be allowed at that size,
+ * false if the stream should be larger
+ * @param maxBytes the number of bytes desired in the stream;
+ * should not be less than 72 (due to protocol overhead).
+ * @return a object that will be serialized to the length requested
+ */
+ private static Object genMaxBytesObject(boolean allowed, long maxBytes) {
+ Object[] holder = new Object[2];
+ long desiredSize = allowed ? maxBytes : maxBytes + 1;
+ long actualSize = desiredSize;
+ long byteSize = desiredSize - 72; // estimate needed array size
+ do {
+ byteSize += (desiredSize - actualSize);
+ byte[] a = new byte[(int)byteSize];
+ holder[0] = a;
+ holder[1] = a;
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream os = new ObjectOutputStream(baos)) {
+ os.writeObject(holder);
+ os.flush();
+ actualSize = baos.size();
+ } catch (IOException ie) {
+ Assert.fail("exception generating stream", ie);
+ }
+ } while (actualSize != desiredSize);
+ return holder;
+ }
+
+ /**
+ * Returns a HashSet of a requested depth.
+ * @param depth the depth
+ * @return a HashSet of HashSets...
+ */
+ static HashSet<Object> deepHashSet(int depth) {
+ HashSet<Object> hashSet = new HashSet<>();
+ HashSet<Object> s1 = hashSet;
+ HashSet<Object> s2 = new HashSet<>();
+ for (int i = 0; i < depth; i++ ) {
+ HashSet<Object> t1 = new HashSet<>();
+ HashSet<Object> t2 = new HashSet<>();
+ // make t1 not equal to t2
+ t1.add("by Jimminy");
+ s1.add(t1);
+ s1.add(t2);
+ s2.add(t1);
+ s2.add(t2);
+ s1 = t1;
+ s2 = t2;
+ }
+ return hashSet;
+ }
+
+ /**
+ * Simple method to use with Serialized Lambda.
+ */
+ private static void noop() {}
+
+
+ /**
+ * Class that returns an array from readResolve and also implements
+ * the ObjectInputFilter to check that it has the expected length.
+ */
+ static class ReadResolveToArray implements Serializable, ObjectInputFilter {
+ private static final long serialVersionUID = 123456789L;
+
+ private final Object array;
+ private final int length;
+
+ ReadResolveToArray(Object array, int length) {
+ this.array = array;
+ this.length = length;
+ }
+
+ Object readResolve() {
+ return array;
+ }
+
+ @Override
+ public ObjectInputFilter.Status checkInput(FilterInfo filter) {
+ if (ReadResolveToArray.class.isAssignableFrom(filter.serialClass())) {
+ return ObjectInputFilter.Status.ALLOWED;
+ }
+ if (filter.serialClass() != array.getClass() ||
+ (filter.arrayLength() >= 0 && filter.arrayLength() != length)) {
+ return ObjectInputFilter.Status.REJECTED;
+ }
+ return ObjectInputFilter.Status.UNDECIDED;
+ }
+
+ }
+
+ /**
+ * Hold a snapshot of values to be passed to an ObjectInputFilter.
+ */
+ static class FilterValues implements ObjectInputFilter.FilterInfo {
+ private final Class<?> clazz;
+ private final long arrayLength;
+ private final long depth;
+ private final long references;
+ private final long streamBytes;
+
+ public FilterValues(Class<?> clazz, long arrayLength, long depth, long references, long streamBytes) {
+ this.clazz = clazz;
+ this.arrayLength = arrayLength;
+ this.depth = depth;
+ this.references = references;
+ this.streamBytes = streamBytes;
+ }
+
+ @Override
+ public Class<?> serialClass() {
+ return clazz;
+ }
+
+ public long arrayLength() {
+ return arrayLength;
+ }
+
+ public long depth() {
+ return depth;
+ }
+
+ public long references() {
+ return references;
+ }
+
+ public long streamBytes() {
+ return streamBytes;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/serialFilter/java.security-extra1 Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,4 @@
+# Serialization Input Process-wide Filter
+# See conf/security/java.security for pattern synatx
+#
+jdk.serialFilter=java.**;javax.**;maxarray=34;maxdepth=7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/serialFilter/security.policy Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,12 @@
+// Individual Permissions to for GlobalFilterTest
+grant {
+ // Specific permission under test
+ permission java.security.SerializablePermission "serialFilter";
+ // Permissions needed to run the test
+ permission java.util.PropertyPermission "*", "read";
+ permission java.io.FilePermission "<<ALL FILES>>", "read,write,delete";
+ permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+ permission java.security.SecurityPermission "*";
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/Serializable/serialFilter/security.policy.without.globalFilter Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,9 @@
+// Individual Permissions for FilterWithSecurityManagerTest
+grant {
+ // Permissions needed to run the test
+ permission java.util.PropertyPermission "*", "read";
+ permission java.io.FilePermission "<<ALL FILES>>", "read,write,delete";
+ permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+};
+
--- a/jdk/test/java/lang/invoke/CountedLoopIterationCountsTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/lang/invoke/CountedLoopIterationCountsTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -71,7 +71,7 @@
}
}
- static int step(int counter, int stepCount) {
+ static int step(int stepCount, int counter) {
return stepCount + 1;
}
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -703,6 +703,66 @@
}}
}
+ 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; }
+
+ @Test public void testSimplerLoop() throws Throwable {
+ MethodHandle MH_inc, MH_mult, MH_cmp;
+ Class<?> I = int.class;
+ MH_inc = LOOKUP.findStatic(THIS_CLASS, "inc", methodType(I, I));
+ MH_mult = LOOKUP.findStatic(THIS_CLASS, "mult", methodType(I, I, I));
+ MH_cmp = LOOKUP.findStatic(THIS_CLASS, "cmp", methodType(boolean.class, I, I));
+ {{
+{} /// JAVADOC
+// simplified implementation of the factorial function as a loop handle
+// 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));
+{}
+ }}
+ }
+
+ // for testFacLoop
+{}
+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_inc, MH_mult, and MH_pred are handles to the above methods
+ @Test public void testFacLoop() throws Throwable {
+ MethodHandle MH_FacLoop, MH_inc, MH_mult, MH_pred, MH_fin;
+ Class<?> I = int.class;
+ MH_FacLoop = LOOKUP.findConstructor(FacLoop.class, methodType(void.class, I));
+ MH_inc = LOOKUP.findVirtual(FacLoop.class, "inc", methodType(I, I));
+ MH_mult = LOOKUP.findVirtual(FacLoop.class, "mult", methodType(I, I, I));
+ MH_pred = LOOKUP.findVirtual(FacLoop.class, "pred", methodType(boolean.class, I));
+ MH_fin = LOOKUP.findVirtual(FacLoop.class, "fin", methodType(I, I, I));
+ {{
+{} /// JAVADOC
+// instance-based implementation of the factorial function as a loop handle
+// 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));
+{}
+ }}
+ }
+
static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
@@ -749,36 +809,81 @@
}}
}
- static String start(String arg) { return arg; }
- static String step(int counter, String v, String arg) { return "na " + v; }
+ static String step(String v, int counter, String start_) { return "na " + v; } //#0
+ static String step(String v, int counter ) { return "na " + v; } //#1
+ static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } //#2
+ static String step3(String v, int counter, String pre) { return pre + " " + v; } //#3
@Test public void testCountedLoop() throws Throwable {
- MethodHandle MH_start, MH_step;
- Class<?> S = String.class;
- MH_start = LOOKUP.findStatic(THIS_CLASS, "start", methodType(S, S));
- MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, int.class, S, S));
+ MethodHandle MH_step;
+ Class<?> S = String.class, I = int.class;
+ // Theme:
+ MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, S));
{{
{} /// JAVADOC
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
// => a variation on a well known theme
MethodHandle fit13 = MethodHandles.constant(int.class, 13);
-MethodHandle loop = MethodHandles.countedLoop(fit13, MH_start, MH_step);
+MethodHandle start = MethodHandles.identity(String.class);
+MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step); // (v, i, _) -> "na " + v
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
{}
}}
+ // Variation #1:
+ MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I));
+ {{
+{} /// JAVADOC
+// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+ assertEquals("na na Lambdaman!", loop.invoke(2, "Lambdaman!"));
+ assertEquals("Lambdaman!", loop.invoke(0, "Lambdaman!"));
+ assertEquals("Lambdaman!", loop.invoke(-1, "Lambdaman!"));
+ assertEquals("Lambdaman!", loop.invoke(Integer.MIN_VALUE, "Lambdaman!"));
+ }}
+ // Variation #2:
+ MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, I, S, S));
+ {{
+{} /// JAVADOC
+// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+ }}
+ // Variation #3:
+ MH_step = LOOKUP.findStatic(THIS_CLASS, "step3", methodType(S, S, I, S));
+ {{
+{} /// JAVADOC
+// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
+// => a variation on a well known theme
+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!"));
+{}
+ }}
}
- static List<String> reverseStep(String e, List<String> r, List<String> l) {
+ 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<>(); }
+ static List<String> newArrayList() { return new ArrayList<>(); }
@Test public void testIteratedLoop() throws Throwable {
MethodHandle MH_newArrayList, MH_reverseStep;
- Class<?> L = List.class;
- MH_newArrayList = LOOKUP.findStatic(THIS_CLASS, "newArrayList", methodType(L, L));
- MH_reverseStep = LOOKUP.findStatic(THIS_CLASS, "reverseStep", methodType(L, String.class, L, L));
+ Class<?> L = List.class, S = String.class;
+ MH_newArrayList = LOOKUP.findStatic(THIS_CLASS, "newArrayList", methodType(L));
+ MH_reverseStep = LOOKUP.findStatic(THIS_CLASS, "reverseStep", methodType(L, L, S));
{{
{} /// JAVADOC
// reverse a list
--- a/jdk/test/java/lang/invoke/LoopCombinatorTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/lang/invoke/LoopCombinatorTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -28,6 +28,7 @@
* @bug 8150635
* @bug 8150956
* @bug 8150957
+ * @bug 8151179
* @bug 8152667
* @bug 8153637
* @bug 8154751
@@ -146,6 +147,16 @@
assertEquals(120, loop.invoke(new LoopWithVirtuals(), 5));
}
+ @Test
+ public static void testLoopOmitPred() throws Throwable {
+ // construct a loop to calculate factorial that omits a predicate
+ MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc, null, Fac.MH_fin};
+ MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+ MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+ assertEquals(Fac.MT_fac, loop.type());
+ assertEquals(120, loop.invoke(5));
+ }
+
@DataProvider
static Object[][] negativeTestData() {
MethodHandle i0 = MethodHandles.constant(int.class, 0);
@@ -153,7 +164,8 @@
MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class);
MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class);
List<MethodHandle> inits = Arrays.asList(ii, id, i3);
- List<Class<?>> ints = Arrays.asList(int.class, int.class, int.class);
+ List<Class<?>> ints3 = Arrays.asList(int.class, int.class, int.class);
+ List<Class<?>> ints4 = Arrays.asList(int.class, int.class, int.class, int.class);
List<MethodHandle> finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step);
List<MethodHandle> preds1 = Arrays.asList(null, null, null);
List<MethodHandle> preds2 = Arrays.asList(null, Fac.MH_fin, null);
@@ -174,7 +186,7 @@
"clause 0: init and step return types must match: int != void"},
{new MethodHandle[][]{{ii}, {id}, {i3}},
"found non-effectively identical init parameter type lists: " + inits +
- " (common suffix: " + ints + ")"},
+ " (common suffix: " + ints3 + ")"},
{new MethodHandle[][]{{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc},
{null, Counted.MH_start, null, Counted.MH_step}},
"found non-identical finalizer return types: " + finis + " (return type: int)"},
@@ -185,11 +197,11 @@
{new MethodHandle[][]{{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin},
{null, Fac.MH_dot}},
"found non-effectively identical parameter type lists:\nstep: " + nesteps +
- "\npred: " + nepreds + "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")"},
+ "\npred: " + nepreds + "\nfini: " + nefinis + " (common parameter sequence: " + ints3 + ")"},
{new MethodHandle[][]{{null, LoopWithVirtuals.MH_inc},
{LoopWithVirtuals.MH_one, LoopWithVirtuals.MH_mult, LoopWithVirtuals.MH_pred, LoopWithVirtuals.MH_fin}},
"found non-effectively identical parameter type lists:\nstep: " + lvsteps +
- "\npred: " + lvpreds + "\nfini: " + lvfinis + " (common parameter sequence: " + ints + ")"}
+ "\npred: " + lvpreds + "\nfini: " + lvfinis + " (common parameter sequence: " + ints4 + ")"}
};
}
@@ -207,7 +219,7 @@
public static void testLoopNegative(MethodHandle[][] clauses, String expectedMessage) throws Throwable {
boolean caught = false;
try {
- MH_loop.invokeWithArguments(clauses);
+ MH_loop.invokeWithArguments((Object[]) clauses);
} catch (IllegalArgumentException iae) {
assertEquals(expectedMessage, iae.getMessage());
caught = true;
@@ -215,12 +227,100 @@
assertTrue(caught);
}
- @Test
- public static void testWhileLoop() throws Throwable {
+ @Test(dataProvider = "whileLoopTestData")
+ public static void testWhileLoop(MethodHandle MH_zero,
+ MethodHandle MH_pred,
+ MethodHandle MH_step,
+ String messageOrNull) throws Throwable {
// int i = 0; while (i < limit) { ++i; } return i; => limit
- MethodHandle loop = MethodHandles.whileLoop(While.MH_zero, While.MH_pred, While.MH_step);
- assertEquals(While.MT_while, loop.type());
- assertEquals(23, loop.invoke(23));
+ try {
+ MethodHandle loop = MethodHandles.whileLoop(MH_zero, MH_pred, MH_step);
+ assert messageOrNull == null;
+ if (MH_step.type().equals(While.MH_step.type()))
+ assertEquals(While.MT_while, loop.type());
+ assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type());
+ while (loop.type().parameterCount() > 1) loop = snip(loop);
+ assertEquals(23, loop.invoke(23));
+ } catch (IllegalArgumentException iae) {
+ assert messageOrNull != null;
+ assertEqualsFIXME(messageOrNull, iae.getMessage());
+ }
+ }
+
+ static void assertEqualsFIXME(String expect, String actual) {
+ if (!expect.equals(actual)) {
+ // just issue a warning
+ System.out.println("*** "+actual+"\n != "+expect);
+ }
+ }
+
+ @DataProvider
+ static Object[][] whileLoopTestData() {
+ MethodHandle
+ zeroI = While.MH_zero,
+ zeroX = snip(zeroI),
+ zeroIB = slap(zeroI, byte.class),
+ predII = While.MH_pred,
+ predIX = snip(predII),
+ predIIB = slap(predII, byte.class),
+ stepII = While.MH_step,
+ stepIX = snip(stepII),
+ stepIIB = slap(stepII, byte.class)
+ ;
+ return new Object[][] {
+ // normal while loop clauses, perhaps with effectively-identical reductions
+ {zeroI, predII, stepII, null},
+ {zeroX, predII, stepII, null},
+ {null, predII, stepII, null},
+ // expanded while loop clauses
+ {zeroIB, predIIB, stepIIB, null},
+ {zeroI, predIIB, stepIIB, null},
+ {null, predIIB, stepIIB, null},
+ {zeroIB, predII, stepIIB, null},
+ {zeroX, predII, stepIIB, null},
+ {null, predII, stepIIB, null},
+ // short step clauses cause errors
+ {zeroI, predII, stepIX, "loop predicate must match: (int,int)boolean != (int)boolean"},
+ {zeroIB, predIX, stepIX, "loop initializer must match: (int,byte)int != ()int"},
+ // bad body type
+ {zeroI, predII, tweak(stepII, -1, char.class), "body function must match: (int,int)char != (char,int,int)char"},
+ {zeroI, predII, tweak(stepII, 0, char.class), "body function must match: (char,int)int != (int,char,int)int"},
+ // bad pred type
+ {zeroI, tweak(predII, -1, char.class), stepII, "loop predicate must match: (int,int)char != (int,int)boolean"},
+ {zeroI, tweak(predII, 0, char.class), stepII, "loop predicate must match: (char,int)boolean != (int,int)boolean"},
+ // bad init type
+ {tweak(zeroI, -1, char.class), predII, stepII, "loop initializer must match: (int)char != (int)int"},
+ {tweak(zeroI, 0, char.class), predII, stepII, "loop initializer must match: (char)int != (int)int"},
+ };
+ }
+
+ // tweak the type of an MH
+ static MethodHandle tweak(MethodHandle mh, int argPos, Class<?> type) {
+ MethodType mt = mh.type();
+ if (argPos == -1)
+ mt = mt.changeReturnType(type);
+ else
+ mt = mt.changeParameterType(argPos, type);
+ return MethodHandles.explicitCastArguments(mh, mt);
+ }
+ // snip off an MH argument, hard-wiring to zero
+ static MethodHandle snip(MethodHandle mh, int argPos) {
+ if (argPos < 0) return null; // special case for optional args
+ Class<?> argType = mh.type().parameterType(argPos);
+ Object zero;
+ try {
+ zero = MethodHandles.zero(argType).invoke();
+ } catch (Throwable ex) {
+ throw new AssertionError(ex);
+ }
+ return MethodHandles.insertArguments(mh, argPos, zero);
+ }
+ static MethodHandle snip(MethodHandle mh) {
+ return snip(mh, mh.type().parameterCount()-1);
+ }
+ // slap on an extra type on the end of the MH
+ static MethodHandle slap(MethodHandle mh, Class<?> addType) {
+ return MethodHandles.dropArguments(mh, mh.type().parameterCount(), addType);
}
@Test
@@ -231,22 +331,42 @@
assertEquals("a", loop.invoke());
}
- @Test
- public static void testDoWhileLoop() throws Throwable {
+ @Test(dataProvider = "whileLoopTestData")
+ public static void testDoWhileLoop(MethodHandle MH_zero,
+ MethodHandle MH_pred,
+ MethodHandle MH_step,
+ String messageOrNull) throws Throwable {
// int i = 0; do { ++i; } while (i < limit); return i; => limit
- MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zero, While.MH_step, While.MH_pred);
- assertEquals(While.MT_while, loop.type());
- assertEquals(23, loop.invoke(23));
+ try {
+ MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
+ assert messageOrNull == null;
+ if (MH_step.type().equals(While.MH_step.type()))
+ assertEquals(While.MT_while, loop.type());
+ assertEquals(MH_step.type().dropParameterTypes(0, 1), loop.type());
+ while (loop.type().parameterCount() > 1) loop = snip(loop);
+ assertEquals(23, loop.invoke(23));
+ } catch (IllegalArgumentException iae) {
+ assert messageOrNull != null;
+ if (!messageOrNull.equals(iae.getMessage())) {
+ // just issue a warning
+ System.out.println("*** "+messageOrNull+"\n != "+iae.getMessage());
+ }
+ }
}
@Test
- public static void testDoWhileNullInit() throws Throwable {
- While w = new While();
- int v = 5;
- MethodHandle loop = MethodHandles.doWhileLoop(null, While.MH_voidBody.bindTo(w), While.MH_voidPred.bindTo(w));
- assertEquals(While.MT_void, loop.type());
- loop.invoke(v);
- assertEquals(v, w.i);
+ public static void testDoWhileBadInit() throws Throwable {
+ boolean caught = false;
+ try {
+ While w = new While();
+ MethodHandle loop = MethodHandles.doWhileLoop(MethodHandles.empty(methodType(char.class)),
+ While.MH_voidBody.bindTo(w),
+ While.MH_voidPred.bindTo(w));
+ } catch (IllegalArgumentException iae) {
+ assertEquals("loop initializer must match: ()char != (int)void", iae.getMessage());
+ caught = true;
+ }
+ assertTrue(caught);
}
@Test
@@ -260,13 +380,18 @@
}
@Test
- public static void testWhileNullInit() throws Throwable {
- While w = new While();
- int v = 5;
- MethodHandle loop = MethodHandles.whileLoop(null, While.MH_voidPred.bindTo(w), While.MH_voidBody.bindTo(w));
- assertEquals(While.MT_void, loop.type());
- loop.invoke(v);
- assertEquals(v, w.i);
+ public static void testWhileBadInit() throws Throwable {
+ boolean caught = false;
+ try {
+ While w = new While();
+ MethodHandle loop = MethodHandles.whileLoop(MethodHandles.empty(methodType(void.class, char.class)),
+ While.MH_voidPred.bindTo(w),
+ While.MH_voidBody.bindTo(w));
+ } catch (IllegalArgumentException iae) {
+ assertEquals("loop initializer must match: (char)void != (int)void", iae.getMessage());
+ caught = true;
+ }
+ assertTrue(caught);
}
@Test
@@ -291,10 +416,26 @@
assertEquals(v, w.i);
}
+ @DataProvider
+ static Object[][] nullArgs() {
+ MethodHandle c = MethodHandles.constant(int.class, 1);
+ return new Object[][]{{null, c}, {c, null}};
+ }
+
+ @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class)
+ public static void testWhileNullArgs(MethodHandle pred, MethodHandle body) {
+ MethodHandles.whileLoop(null, pred, body);
+ }
+
+ @Test(dataProvider = "nullArgs", expectedExceptions = NullPointerException.class)
+ public static void testDoWhileNullArgs(MethodHandle body, MethodHandle pred) {
+ MethodHandles.whileLoop(null, body, pred);
+ }
+
@Test
public static void testCountedLoop() throws Throwable {
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme
- MethodHandle fit13 = MethodHandles.constant(int.class, 13);
+ MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, String.class);
MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step);
assertEquals(Counted.MT_counted, loop.type());
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
@@ -303,9 +444,25 @@
@Test
public static void testCountedLoopVoidInit() throws Throwable {
MethodHandle fit5 = MethodHandles.constant(int.class, 5);
- MethodHandle loop = MethodHandles.countedLoop(fit5, MethodHandles.zero(void.class), Counted.MH_printHello);
- assertEquals(Counted.MT_countedPrinting, loop.type());
- loop.invoke();
+ for (int i = 0; i < 8; i++) {
+ MethodHandle zero = MethodHandles.zero(void.class);
+ MethodHandle init = fit5;
+ MethodHandle body = Counted.MH_printHello;
+ boolean useNull = (i & 1) != 0, addInitArg = (i & 2) != 0, addBodyArg = (i & 4) != 0;
+ if (useNull) zero = null;
+ if (addInitArg) init = MethodHandles.dropArguments(init, 0, int.class);
+ if (addBodyArg) body = MethodHandles.dropArguments(body, 1, int.class);
+ System.out.println("testCountedLoopVoidInit i="+i+" : "+Arrays.asList(init, zero, body));
+ MethodHandle loop = MethodHandles.countedLoop(init, zero, body);
+ MethodType expectedType = Counted.MT_countedPrinting;
+ if (addInitArg || addBodyArg)
+ expectedType = expectedType.insertParameterTypes(0, int.class);
+ assertEquals(expectedType, loop.type());
+ if (addInitArg || addBodyArg)
+ loop.invoke(99);
+ else
+ loop.invoke();
+ }
}
@Test
@@ -327,7 +484,7 @@
loop.invoke();
}
- @Test
+ @Test(expectedExceptions = NullPointerException.class)
public static void testCountedLoopNullBody() throws Throwable {
MethodHandle h5 = MethodHandles.constant(int.class, 5);
MethodHandle h13 = MethodHandles.constant(int.class, 13);
@@ -336,14 +493,14 @@
assertEquals(13, loop.invoke());
}
- @Test
+ @Test(expectedExceptions = NullPointerException.class)
public static void testCountedLoopNullIterations() throws Throwable {
MethodHandle loop = MethodHandles.countedLoop(null, null, null);
assertEquals(methodType(void.class), loop.type());
loop.invoke();
}
- @Test
+ @Test(expectedExceptions = NullPointerException.class)
public static void testCountedLoopNullInitAndBody() throws Throwable {
MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, null);
assertEquals(methodType(void.class), loop.type());
@@ -352,45 +509,63 @@
@DataProvider
static Object[][] countedLoopBodyParameters() {
+ Class<?> V = String.class, I = int.class, A = List.class;
+ // return types are of these forms:
+ // {count = int(A...), init = V(A...), body = V(V, I, A...)}
return new Object[][] {
- {methodType(String.class), methodType(String.class, int.class)},
- {methodType(String.class, List.class), methodType(String.class, int.class)},
- {methodType(String.class, List.class), methodType(String.class, int.class, String.class)}
+ // body leads determining A...
+ {methodType(I), methodType(V), methodType(V, V, I)},
+ {methodType(I), methodType(V), methodType(V, V, I, A)},
+ {methodType(I,A), methodType(V), methodType(V, V, I, A)},
+ {methodType(I), methodType(V,A), methodType(V, V, I, A)},
+ // body leads, with void V
+ {methodType(I), methodType(void.class), methodType(void.class, I)},
+ {methodType(I), methodType(void.class), methodType(void.class, I, A)},
+ {methodType(I,A), methodType(void.class), methodType(void.class, I, A)},
+ {methodType(I), methodType(void.class,A), methodType(void.class, I, A)},
+ // count leads determining A..., but only if body drops all A...
+ {methodType(I,A), methodType(V), methodType(V, V, I)},
+ {methodType(I,A), methodType(V,A), methodType(V, V, I)},
+ // count leads, with void V
+ {methodType(I,A), methodType(void.class), methodType(void.class, I)},
+ {methodType(I,A), methodType(void.class,A), methodType(void.class, I)},
};
}
@Test(dataProvider = "countedLoopBodyParameters")
- public static void testCountedLoopBodyParameters(MethodType initType, MethodType bodyType) throws Throwable {
- MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5),
- MethodHandles.empty(initType), MethodHandles.empty(bodyType));
- assertEquals(initType, loop.type());
+ public static void testCountedLoopBodyParameters(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable {
+ MethodHandle loop = MethodHandles.countedLoop(
+ MethodHandles.empty(countType),
+ initType == null ? null : MethodHandles.empty(initType),
+ MethodHandles.empty(bodyType));
+ // The rule: If body takes the minimum number of parameters, then take what countType offers.
+ // The initType has to just roll with whatever the other two agree on.
+ int innerParams = (bodyType.returnType() == void.class ? 1 : 2);
+ MethodType expectType = bodyType.dropParameterTypes(0, innerParams);
+ if (expectType.parameterCount() == 0)
+ expectType = expectType.insertParameterTypes(0, countType.parameterList());
+ assertEquals(expectType, loop.type());
}
- @DataProvider
- static Object[][] countedLoopTypes() {
- return new Object[][]{{void.class}, {int.class}, {Object.class}, {String.class}, {List.class}};
- }
-
- @Test(dataProvider = "countedLoopTypes")
- public static void testCountedLoopBodyParametersNullInit(Class<?> t) throws Throwable {
- MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null,
- MethodHandles.empty(methodType(t, int.class)));
- assertEquals(methodType(t), loop.type());
- loop.invoke();
+ @Test(dataProvider = "countedLoopBodyParameters")
+ public static void testCountedLoopBodyParametersNullInit(MethodType countType, MethodType initType, MethodType bodyType) throws Throwable {
+ testCountedLoopBodyParameters(countType, null, bodyType);
}
@Test
- public static void testCountedLoopStateDefinedByBody() throws Throwable {
- MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, Counted.MH_stateBody);
+ public static void testCountedLoopStateInitializedToNull() throws Throwable {
+ MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5),
+ MethodHandles.empty(methodType(String.class)), Counted.MH_stateBody);
assertEquals(Counted.MT_bodyDeterminesState, loop.type());
assertEquals("sssssnull01234", loop.invoke());
}
@Test
public static void testCountedLoopArgsDefinedByIterations() throws Throwable {
- MethodHandle loop = MethodHandles.countedLoop(
- MethodHandles.dropArguments(MethodHandles.constant(int.class, 3), 0, String.class),
- null, Counted.MH_append);
+ MethodHandle iterations =
+ MethodHandles.dropArguments(MethodHandles.constant(int.class, 3), 0, String.class);
+ MethodHandle loop = MethodHandles.countedLoop(iterations,
+ MethodHandles.empty(iterations.type().changeReturnType(String.class)), Counted.MH_append);
assertEquals(Counted.MT_iterationsDefineArgs, loop.type());
assertEquals("hello012", loop.invoke("hello"));
}
@@ -420,7 +595,8 @@
@Test
public static void testCountedLoopEmpty() throws Throwable {
// for (int i = 0; i < 5; ++i) { /* empty */ }
- MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null, null);
+ MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, 5), null,
+ MethodHandles.empty(methodType(void.class, int.class)));
assertEquals(methodType(void.class), loop.type());
loop.invoke();
}
@@ -429,11 +605,45 @@
public static void testCountedRangeLoopEmpty() throws Throwable {
// for (int i = -5; i < 5; ++i) { /* empty */ }
MethodHandle loop = MethodHandles.countedLoop(MethodHandles.constant(int.class, -5),
- MethodHandles.constant(int.class, 5), null, null);
+ MethodHandles.constant(int.class, 5), null, MethodHandles.empty(methodType(void.class, int.class)));
assertEquals(methodType(void.class), loop.type());
loop.invoke();
}
+ @DataProvider
+ static Object[][] countedLoopNegativeData() {
+ MethodHandle dummy = MethodHandles.zero(void.class);
+ MethodHandle one = MethodHandles.constant(int.class, 1);
+ MethodHandle oneString = MethodHandles.dropArguments(one, 0, String.class);
+ MethodHandle oneDouble = MethodHandles.dropArguments(one, 0, double.class);
+ return new Object[][]{
+ {dummy, one, dummy, dummy, String.format("start/end must return int %s, %s", dummy, one)},
+ {one, dummy, dummy, dummy, String.format("start/end must return int %s, %s", one, dummy)},
+ {oneString, oneDouble, dummy, dummy,
+ String.format("start and end parameter types must match: %s != %s", oneString.type(),
+ oneDouble.type())},
+ {oneString, oneString, dummy, dummy,
+ String.format("start/end and init parameter types must match: %s != %s", oneString.type(),
+ dummy.type())},
+ {one, one, null, dummy, String.format("actual and expected body signatures must match: %s != %s",
+ dummy.type(), dummy.type().appendParameterTypes(int.class))}
+ };
+ }
+
+ @Test(dataProvider = "countedLoopNegativeData")
+ public static void testCountedLoopNegative(MethodHandle start, MethodHandle end, MethodHandle init,
+ MethodHandle body, String msg) {
+ if (true) return; //%%%FIXME%%%%
+ boolean caught = false;
+ try {
+ MethodHandles.countedLoop(start, end, init, body);
+ } catch (IllegalArgumentException iae) {
+ assertEquals(msg, iae.getMessage());
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
@Test
public static void testIterateSum() throws Throwable {
// Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21
@@ -442,50 +652,106 @@
assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6}));
}
- @Test
- public static void testIterateReverse() throws Throwable {
- MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_reverseInit, Iterate.MH_reverseStep);
- assertEquals(Iterate.MT_reverse, loop.type());
- 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));
+ @DataProvider
+ static Object[][] iteratorInits() {
+ return new Object[][]{{Iterate.MH_iteratorFromList}, {Iterate.MH_iteratorFromIterable}, {null}};
+ }
+
+ @Test(dataProvider = "iteratorInits")
+ public static void testIterateReverse(MethodHandle iterator) throws Throwable {
+ // this test uses List as its loop state type; don't try to change that
+ if (iterator != null)
+ iterator = iterator.asType(iterator.type().changeParameterType(0, List.class));
+ for (int i = 0; i < 4; i++) {
+ MethodHandle init = Iterate.MH_reverseInit, body = Iterate.MH_reverseStep;
+ boolean snipInit = (i & 1) != 0, snipBody = (i & 2) != 0;
+ if (snipInit) init = snip(init);
+ if (snipBody) body = snip(body);
+ if (!snipInit && snipBody && iterator == null) {
+ // Body does not determine (A...), so the default guy just picks Iterable.
+ // If body insisted on (List), the default guy would adjust himself.
+ // Init has no authority to change the (A...), so must patch init.
+ // All according to plan!
+ init = slap(snip(init), Iterable.class);
+ }
+ System.out.println("testIterateReverse i="+i+" : "+Arrays.asList(iterator, init, body));
+ MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+ MethodType expectedType = Iterate.MT_reverse;
+ if (iterator == null && i >= 2)
+ expectedType = expectedType.changeParameterType(0, Iterable.class);
+ assertEquals(expectedType, loop.type());
+ 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));
+ }
}
- @Test
- public static void testIterateLength() throws Throwable {
- MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_lengthInit, Iterate.MH_lengthStep);
- assertEquals(Iterate.MT_length, loop.type());
- List<Double> list = Arrays.asList(23.0, 148.0, 42.0);
- assertEquals(list.size(), (int) loop.invoke(list));
+ @Test(dataProvider = "iteratorInits")
+ public static void testIterateLength(MethodHandle iterator) throws Throwable {
+ MethodHandle body = Iterate.MH_lengthStep;
+ MethodHandle init = Iterate.MH_lengthInit;
+ MethodType expectedType = Iterate.MT_length;
+ int barity = body.type().parameterCount();
+ Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+ if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+ // adjust body to accept the other type
+ body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+ init = init.asType(init.type().changeParameterType(0, iteratorSource));
+ expectedType = expectedType.changeParameterType(0, iteratorSource);
+ }
+ for (;; init = snip(init)) {
+ System.out.println("testIterateLength.init = "+init);
+ MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+ assertEquals(expectedType, loop.type());
+ List<Double> list = Arrays.asList(23.0, 148.0, 42.0);
+ assertEquals(list.size(), (int) loop.invoke(list));
+ if (init == null) break;
+ }
}
- @Test
- public static void testIterateMap() throws Throwable {
- MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_mapInit, Iterate.MH_mapStep);
- assertEquals(Iterate.MT_map, loop.type());
- List<String> list = Arrays.asList("Hello", "world", "!");
- List<String> upList = Arrays.asList("HELLO", "WORLD", "!");
- assertEquals(upList, (List<String>) loop.invoke(list));
+ @Test(dataProvider = "iteratorInits")
+ public static void testIterateMap(MethodHandle iterator) throws Throwable {
+ MethodHandle body = Iterate.MH_mapStep;
+ MethodHandle init = Iterate.MH_mapInit;
+ MethodType expectedType = Iterate.MT_map;
+ int barity = body.type().parameterCount();
+ Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+ if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+ // adjust body to accept the other type
+ body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+ init = init.asType(init.type().changeParameterType(0, iteratorSource));
+ expectedType = expectedType.changeParameterType(0, iteratorSource);
+ }
+ for (; init != null; init = snip(init)) {
+ System.out.println("testIterateMap.init = "+init);
+ MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, body);
+ assertEquals(expectedType, loop.type());
+ List<String> list = Arrays.asList("Hello", "world", "!");
+ List<String> upList = Arrays.asList("HELLO", "WORLD", "!");
+ assertEquals(upList, (List<String>) loop.invoke(list));
+ }
}
- @Test
- public static void testIteratePrint() throws Throwable {
- MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep);
- assertEquals(Iterate.MT_print, loop.type());
+ @Test(dataProvider = "iteratorInits")
+ public static void testIteratePrint(MethodHandle iterator) throws Throwable {
+ MethodHandle body = Iterate.MH_printStep;
+ MethodType expectedType = Iterate.MT_print;
+ int barity = body.type().parameterCount();
+ Class<?> iteratorSource = iterator == null ? null : iterator.type().parameterType(0);
+ if (iterator != null && iteratorSource != body.type().parameterType(barity-1)) {
+ // adjust body to accept the other type
+ body = body.asType(body.type().changeParameterType(barity-1, iteratorSource));
+ expectedType = expectedType.changeParameterType(0, iteratorSource);
+ }
+ MethodHandle loop = MethodHandles.iteratedLoop(iterator, null, body);
+ assertEquals(expectedType, loop.type());
loop.invoke(Arrays.asList("hello", "world"));
}
- @Test
+ @Test(expectedExceptions = NullPointerException.class)
public static void testIterateNullBody() {
- boolean caught = false;
- try {
- MethodHandles.iteratedLoop(MethodHandles.empty(methodType(Iterator.class, int.class)),
- MethodHandles.identity(int.class), null);
- } catch (IllegalArgumentException iae) {
- assertEquals("iterated loop body must not be null", iae.getMessage());
- caught = true;
- }
- assertTrue(caught);
+ MethodHandles.iteratedLoop(MethodHandles.empty(methodType(Iterator.class, int.class)),
+ MethodHandles.identity(int.class), null);
}
@DataProvider
@@ -500,15 +766,18 @@
try {
MethodHandles.iteratedLoop(MethodHandles.empty(v), null, MethodHandles.empty(v));
} catch(IllegalArgumentException iae) {
- assertEquals("iteratedLoop first argument must have Iterator return type", iae.getMessage());
+ assertEqualsFIXME("iteratedLoop first argument must have Iterator return type", iae.getMessage());
caught = true;
}
assertTrue(caught);
}
- @Test
- public static void testIterateVoidInit() throws Throwable {
- MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_voidInit, Iterate.MH_printStep);
+ @Test(dataProvider = "iteratorInits")
+ public static void testIterateVoidInit(MethodHandle iterator) throws Throwable {
+ // this test uses List as its loop state type; don't try to change that
+ if (iterator != null)
+ iterator = iterator.asType(iterator.type().changeParameterType(0, List.class));
+ MethodHandle loop = MethodHandles.iteratedLoop(iterator, Iterate.MH_voidInit, Iterate.MH_printStep);
assertEquals(Iterate.MT_print, loop.type());
loop.invoke(Arrays.asList("hello", "world"));
}
@@ -516,60 +785,79 @@
@DataProvider
static Object[][] iterateParameters() {
MethodType i = methodType(int.class);
- MethodType sil_i = methodType(int.class, String.class, int.class, List.class);
+ MethodType sil_v = methodType(void.class, String.class, int.class, List.class);
+ MethodType isl_i = methodType(int.class, int.class, String.class, List.class);
+ MethodType isli_i = methodType(int.class, int.class, String.class, List.class, int.class);
MethodType sl_v = methodType(void.class, String.class, List.class);
+ MethodType sli_v = methodType(void.class, String.class, List.class, int.class);
MethodType l_it = methodType(Iterator.class, List.class);
+ MethodType li_i = methodType(int.class, List.class, int.class);
MethodType li_it = methodType(Iterator.class, List.class, int.class);
+ MethodType il_it = methodType(Iterator.class, int.class, List.class);
MethodType l_i = methodType(int.class, List.class);
- MethodType _it = methodType(Iterator.class);
- MethodType si_i = methodType(int.class, String.class, int.class);
- MethodType s_i = methodType(int.class, String.class);
return new Object[][]{
- {null, null, sl_v},
- {null, i, sil_i},
- {null, l_i, sil_i},
- {l_it, null, sl_v},
- {l_it, i, sil_i},
- {li_it, l_i, sil_i},
- {l_it, null, sil_i},
- {li_it, null, sl_v},
- {_it, l_i, si_i},
- {_it, l_i, s_i}
+ {l_it, null, sl_v, ""},
+ {l_it, l_i, isl_i, ""},
+ {l_it, null, sl_v, ""},
+ {li_it, li_i, isli_i, ""},
+ {il_it, null, sil_v, "inferred first loop argument must inherit from Iterable: int"},
+ {li_it, null, sli_v, ""},
+ {sl_v, null, sl_v, "iteratedLoop first argument must have Iterator return type"},
+ {li_it, l_it, sl_v,
+ String.format("iterator and init parameter lists must match: %s != %s", li_it, l_it)},
+ {li_it, li_i, isl_i,
+ String.format("body types (regard parameter types after index 0, and result type) must match: %s != %s",
+ isl_i, isl_i.dropParameterTypes(0, 1).appendParameterTypes(int.class))}
};
}
@Test(dataProvider = "iterateParameters")
- public static void testIterateParameters(MethodType it, MethodType in, MethodType bo) throws Throwable {
+ public static void testIterateParameters(MethodType it, MethodType in, MethodType bo, String msg) {
+ boolean negative = !msg.isEmpty();
MethodHandle iterator = it == null ? null : MethodHandles.empty(it);
MethodHandle init = in == null ? null : MethodHandles.empty(in);
- MethodHandle loop = MethodHandles.iteratedLoop(iterator, init, MethodHandles.empty(bo));
- MethodType lt = loop.type();
- if (it == null && in == null) {
- assertEquals(bo.dropParameterTypes(0, 1), lt);
- } else if (it == null) {
- if (in.parameterCount() == 0) {
- assertEquals(bo.dropParameterTypes(0, in.returnType() == void.class ? 1 : 2), lt);
- } else {
- assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+ boolean caught = false;
+ MethodHandle loop = null;
+ try {
+ loop = MethodHandles.iteratedLoop(iterator, init, MethodHandles.empty(bo));
+ } catch (Throwable t) {
+ if (!negative) {
+ throw t;
}
- } else if (in == null) {
- assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
- } else if (it.parameterCount() > in.parameterCount()) {
- assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
- } else if (it.parameterCount() < in.parameterCount()) {
- assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+ assertEqualsFIXME(msg, t.getMessage());
+ caught = true;
+ }
+ if (negative) {
+ assertTrue(caught);
} else {
- // both it, in present; with equal parameter list lengths
- assertEquals(it.parameterList(), lt.parameterList());
- assertEquals(in.parameterList(), lt.parameterList());
- assertEquals(bo.returnType(), lt.returnType());
+ MethodType lt = loop.type();
+ if (it == null && in == null) {
+ assertEquals(bo.dropParameterTypes(0, 1), lt);
+ } else if (it == null) {
+ if (in.parameterCount() == 0) {
+ assertEquals(bo.dropParameterTypes(0, in.returnType() == void.class ? 1 : 2), lt);
+ } else {
+ assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+ }
+ } else if (in == null) {
+ assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
+ } else if (it.parameterCount() > in.parameterCount()) {
+ assertEquals(methodType(bo.returnType(), it.parameterArray()), lt);
+ } else if (it.parameterCount() < in.parameterCount()) {
+ assertEquals(methodType(bo.returnType(), in.parameterArray()), lt);
+ } else {
+ // both it, in present; with equal parameter list lengths
+ assertEquals(it.parameterList(), lt.parameterList());
+ assertEquals(in.parameterList(), lt.parameterList());
+ assertEquals(bo.returnType(), lt.returnType());
+ }
}
}
@Test
public static void testIteratorSubclass() throws Throwable {
MethodHandle loop = MethodHandles.iteratedLoop(MethodHandles.empty(methodType(BogusIterator.class, List.class)),
- null, MethodHandles.empty(methodType(void.class, String.class)));
+ null, MethodHandles.empty(methodType(void.class, String.class, List.class)));
assertEquals(methodType(void.class, List.class), loop.type());
}
@@ -892,7 +1180,7 @@
return arg;
}
- static String step(int counter, String v, String arg) {
+ static String step(String v, int counter) {
return "na " + v;
}
@@ -904,15 +1192,15 @@
System.out.print("hello");
}
- static int addCounter(int counter, int x) {
+ static int addCounter(int x, int counter) {
return x + counter;
}
- static String stateBody(int counter, String s) {
+ static String stateBody(String s, int counter) {
return "s" + s + counter;
}
- static String append(int counter, String localState, String loopArg) {
+ static String append(String localState, int counter, String loopArg) {
if (null == localState) {
return loopArg + counter;
}
@@ -922,12 +1210,12 @@
static final Class<Counted> COUNTED = Counted.class;
static final MethodType MT_start = methodType(String.class, String.class);
- static final MethodType MT_step = methodType(String.class, int.class, String.class, String.class);
+ static final MethodType MT_step = methodType(String.class, String.class, int.class);
static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class);
static final MethodType MT_printHello = methodType(void.class, int.class);
static final MethodType MT_addCounter = methodType(int.class, int.class, int.class);
- static final MethodType MT_stateBody = methodType(String.class, int.class, String.class);
- static final MethodType MT_append = methodType(String.class, int.class, String.class, String.class);
+ static final MethodType MT_stateBody = methodType(String.class, String.class, int.class);
+ static final MethodType MT_append = methodType(String.class, String.class, int.class, String.class);
static final MethodHandle MH_13;
static final MethodHandle MH_m5;
@@ -984,7 +1272,7 @@
return new ArrayList<>();
}
- static List<String> reverseStep(String e, List<String> r, List<String> l) {
+ static List<String> reverseStep(List<String> r, String e, List<String> l) {
r.add(0, e);
return r;
}
@@ -993,7 +1281,7 @@
return 0;
}
- static int lengthStep(Object o, int len, List<Double> l) {
+ static int lengthStep(int len, Object o, List<Double> l) {
return len + 1;
}
@@ -1001,7 +1289,7 @@
return new ArrayList<>();
}
- static List<String> mapStep(String e, List<String> r, List<String> l) {
+ static List<String> mapStep(List<String> r, String e, List<String> l) {
r.add(e.toUpperCase());
return r;
}
@@ -1010,10 +1298,18 @@
System.out.print(s);
}
- static void voidInit() {
+ static void voidInit(List<String> l) {
// empty
}
+ static ListIterator<?> iteratorFromList(List<?> l) {
+ return l.listIterator();
+ }
+
+ static Iterator<?> iteratorFromIterable(Iterable<?> l) {
+ return l.iterator();
+ }
+
static final Class<Iterate> ITERATE = Iterate.class;
static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class);
@@ -1024,12 +1320,15 @@
static final MethodType MT_mapInit = methodType(List.class, List.class);
static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class);
- static final MethodType MT_reverseStep = methodType(List.class, String.class, List.class, List.class);
- static final MethodType MT_lengthStep = methodType(int.class, Object.class, int.class, List.class);
- static final MethodType MT_mapStep = methodType(List.class, String.class, List.class, List.class);
+ static final MethodType MT_reverseStep = methodType(List.class, List.class, String.class, List.class);
+ static final MethodType MT_lengthStep = methodType(int.class, int.class, Object.class, List.class);
+ static final MethodType MT_mapStep = methodType(List.class, List.class, String.class, List.class);
static final MethodType MT_printStep = methodType(void.class, String.class, List.class);
- static final MethodType MT_voidInit = methodType(void.class);
+ static final MethodType MT_voidInit = methodType(void.class, List.class);
+
+ static final MethodType MT_iteratorFromList = methodType(ListIterator.class, List.class);
+ static final MethodType MT_iteratorFromIterable = methodType(Iterator.class, Iterable.class);
static final MethodHandle MH_sumIterator;
static final MethodHandle MH_sumInit;
@@ -1047,6 +1346,9 @@
static final MethodHandle MH_voidInit;
+ static final MethodHandle MH_iteratorFromList;
+ static final MethodHandle MH_iteratorFromIterable;
+
static final MethodType MT_sum = methodType(int.class, Integer[].class);
static final MethodType MT_reverse = methodType(List.class, List.class);
static final MethodType MT_length = methodType(int.class, List.class);
@@ -1066,6 +1368,8 @@
MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep);
MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep);
MH_voidInit = LOOKUP.findStatic(ITERATE, "voidInit", MT_voidInit);
+ MH_iteratorFromList = LOOKUP.findStatic(ITERATE, "iteratorFromList", MT_iteratorFromList);
+ MH_iteratorFromIterable = LOOKUP.findStatic(ITERATE, "iteratorFromIterable", MT_iteratorFromIterable);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/DatagramSocket/ReuseAddressTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,466 @@
+/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.SocketException;
+
+/*
+ * @test
+ * @bug 8153674
+ * @summary Expected SocketException not thrown when calling bind() with
+ * setReuseAddress(false)
+ * @run main/othervm ReuseAddressTest
+ */
+
+public class ReuseAddressTest {
+
+ String getInfo(DatagramSocket soc) {
+ if (soc == null) {
+ return null;
+ }
+
+ return "localPort: " + soc.getLocalPort()
+ + "; localAddress: " + soc.getLocalAddress()
+ + "; remotePort: " + soc.getPort()
+ + "; remoteAddress: " + soc.getInetAddress()
+ + "; isClosed: " + soc.isClosed()
+ + "; isBound: " + soc.isBound();
+ }
+
+ static InetSocketAddress createSocketAddress(int testMcastPort) throws Exception {
+ InetAddress localAddress = InetAddress.getLocalHost();
+ InetSocketAddress localSocketAddress = new InetSocketAddress(localAddress, testMcastPort);
+ return localSocketAddress;
+ }
+
+ /* standalone interface */
+ public static void main(String argv[]) throws Exception {
+ ReuseAddressTest test = new ReuseAddressTest();
+ test.DatagramSocket0029();
+ test.DatagramSocket0030();
+ test.DatagramSocket0031();
+ test.DatagramSocket0032();
+ test.DatagramSocket0034();
+ test.DatagramSocket0035();
+ test.DatagramSocket2028();
+ test.DatagramSocket2029();
+ test.DatagramSocket2030();
+
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setReuseAddress(boolean on) throws SocketException,
+ * <br><b>on</b>: false.
+ * <br><b>Expected results</b>: getReuseAddress() will return false
+ */
+ public void DatagramSocket0029() throws Exception {
+ String testCaseID = "DatagramSocket0029";
+ System.out.println(" >> " + testCaseID + ": " + "public void setReuseAddress(boolean on) throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket(null);
+ ds.setReuseAddress(false);
+ if (ds.getReuseAddress() == true) {
+ throw new RuntimeException("SO_REUSEADDR is not set to false");
+ }
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setReuseAddress(boolean on) throws SocketException,
+ * <br><b>on</b>: true.
+ * <br><b>Expected results</b>: Allows completely duplicate bindings (same
+ * address and port) on multicast sockets
+ */
+ public void DatagramSocket0030() throws Exception {
+ String testCaseID = "DatagramSocket0030";
+ System.out.println(" >> " + testCaseID + ": " + "public void setReuseAddress(boolean on) throws SocketException");
+
+ MulticastSocket ms1 = null;
+ MulticastSocket ms2 = null;
+ try {
+ InetSocketAddress addr = createSocketAddress(5050);
+
+ ms1 = new MulticastSocket(null);
+ ms1.setReuseAddress(true);
+ if (!ms1.getReuseAddress()) {
+ System.out.println("Cannot check: "
+ + " safety for SO_REUSEADDR option is not guaranteed");
+ }
+
+ try {
+ ms1.bind(addr);
+ } catch (SocketException e) {
+ throw new RuntimeException("cannot bind first socket to " + addr
+ + " unexpected " + e);
+ }
+
+ ms2 = new MulticastSocket(null);
+ ms2.setReuseAddress(true);
+ if (!ms2.getReuseAddress()) {
+ System.out.println("Cannot check: "
+ + " safety for SO_REUSEADDR option is not guaranteed");
+ }
+
+ try {
+ ms2.bind(addr);
+ } catch (SocketException e) {
+ throw new RuntimeException("cannot bind second socket to " + addr
+ + " unexpected " + e);
+ }
+
+ if (ms1.getLocalPort() != addr.getPort() || !ms1.isBound()
+ || ms2.getLocalPort() != addr.getPort() || !ms2.isBound()) {
+ System.out.println("bind() fails with: " + addr);
+ System.out.println(" ms1 [" + getInfo(ms1) + "]");
+ System.out.println(" ms2 [" + getInfo(ms2) + "]");
+ System.out.println(" getReuseAddress(): " + ms2.getReuseAddress());
+ throw new RuntimeException("bind() fails with: " + addr);
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ms1 != null) {
+ ms1.close();
+ }
+ if (ms2 != null) {
+ ms2.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setReuseAddress(boolean on) throws SocketException,
+ * <br><b>on</b>: false.
+ * <br><b>Expected results</b>: The second bind will throw SocketException,
+ * when SO_REUSEADDR disable
+ */
+ public void DatagramSocket0031() throws Exception {
+ String testCaseID = "DatagramSocket0031";
+ System.out.println(" >> " + testCaseID + ": " + "public void setReuseAddress(boolean on) throws SocketException");
+
+ MulticastSocket ms1 = null;
+ MulticastSocket ms2 = null;
+ try {
+ InetSocketAddress addr = createSocketAddress(6060);
+
+ ms1 = new MulticastSocket(null);
+ try {
+ ms1.bind(addr);
+ } catch (SocketException e) {
+ throw new RuntimeException("cannot bind first socket to " + addr
+ + " unexpected " + e);
+ }
+
+ ms2 = new MulticastSocket(null);
+ ms2.setReuseAddress(false); // method under test
+
+ try {
+ ms2.bind(addr);
+ System.out.println("No exceptions: ");
+ System.out.println(" addr: " + addr);
+ System.out.println(" ms1 [" + getInfo(ms1) + "]");
+ System.out.println(" ms2 [" + getInfo(ms2) + "]");
+ System.out.println(" getReuseAddress(): " + ms2.getReuseAddress());
+ throw new RuntimeException("no exceptions from bind() with " + addr);
+ } catch (SocketException e) {
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ms1 != null) {
+ ms1.close();
+ }
+ if (ms2 != null) {
+ ms2.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setReuseAddress(boolean on) throws SocketException,
+ * <br><b>on</b>: true.
+ * <br><b>Expected results</b>: Allows a single process to bind the same
+ * port to multiple sockets as long as each bind specifies a different local
+ * IP address
+ */
+ public void DatagramSocket0032() throws Exception {
+ String testCaseID = "DatagramSocket0032";
+ System.out.println(" >> " + testCaseID + ": " + "public void setReuseAddress(boolean on) throws SocketException");
+
+ DatagramSocket ds1 = null;
+ DatagramSocket ds2 = null;
+ try {
+
+ InetSocketAddress isa = createSocketAddress(7070);
+ InetAddress addr = isa.getAddress();
+ InetAddress wildcard = InetAddress.getByName("0.0.0.0");
+ if (addr.equals(wildcard) || addr.isLoopbackAddress()) {
+ System.out.println("Cannot check: addresses are equal");
+ }
+
+ InetSocketAddress isa1 = new InetSocketAddress(addr, isa.getPort());
+ InetSocketAddress isa2 = new InetSocketAddress(wildcard, isa.getPort());
+
+ ds1 = new DatagramSocket(null);
+ ds1.setReuseAddress(true); // method under test
+ if (!ds1.getReuseAddress()) {
+ System.out.println("Cannot check: "
+ + " safety for SO_REUSEADDR option is not guaranteed");
+ }
+ ds1.bind(isa1);
+
+ ds2 = new DatagramSocket(null);
+ ds2.setReuseAddress(true); // method under test
+ if (!ds2.getReuseAddress()) {
+ System.out.println("Cannot check: "
+ + " safety for SO_REUSEADDR option is not guaranteed");
+ }
+
+ try {
+ ds2.bind(isa2);
+ } catch (SocketException e) {
+ throw new RuntimeException("cannot bind second socket to " + isa2
+ + " unexpected " + e);
+ }
+
+ if (ds1.getLocalPort() != isa.getPort() || !ds1.isBound()
+ || ds2.getLocalPort() != isa.getPort() || !ds2.isBound()) {
+ System.out.println("bind() fails with: " + addr);
+ System.out.println(" ds1 [" + getInfo(ds1) + "]");
+ System.out.println(" ds2 [" + getInfo(ds2) + "]");
+ System.out.println(" getReuseAddress(): " + ds2.getReuseAddress());
+ throw new RuntimeException("bind() fails with: " + addr);
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ds1 != null) {
+ ds1.close();
+ }
+ if (ds2 != null) {
+ ds2.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Assertion testing for public int getTrafficClass() throws
+ * SocketException, will return a number in range from 0 to 255 or throw
+ * SocketException.
+ */
+ public void DatagramSocket2028() throws Exception {
+ String testCaseID = "DatagramSocket2028";
+ System.out.println(" >> " + testCaseID + ": " + "public int getTrafficClass() throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket();
+ int tc = ds.getTrafficClass();
+ if (tc < 0 || tc > 255) {
+ throw new RuntimeException("getTrafficClass() returns: " + tc);
+ }
+ } catch (SecurityException e) {
+ System.out.println("Security restriction: " + e);
+ } catch (SocketException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("Unexpected exception : " + e);
+ } finally {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Assertion testing for public void setTrafficClass(int tc) throws
+ * SocketException, IAE will be thrown with tc less than 0 or greater than
+ * 255.
+ */
+ public void DatagramSocket2029() throws Exception {
+ String testCaseID = "DatagramSocket2029";
+ System.out.println(" >> " + testCaseID + ": " + "public void setTrafficClass(int tc) throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket();
+ } catch (SecurityException e) {
+ System.out.println("Security restriction: " + e);
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("cannot create socket: " + e);
+ }
+
+ int[] values = {
+ Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -1000, -2, -1,
+ 256, 257, 1000, 50000, Integer.MAX_VALUE - 1, Integer.MAX_VALUE
+ };
+
+ for (int i = 0; i < values.length; i++) {
+ try {
+ ds.setTrafficClass(values[i]);
+ System.out.println("No exception with: " + values[i]);
+ System.out.println("getTrafficClass() returns: " + ds.getTrafficClass());
+ ds.close();
+ throw new RuntimeException("setTrafficClass() fails with : " + values[i]);
+ } catch (SocketException e) {
+ ds.close();
+ e.printStackTrace(System.out);
+ throw new RuntimeException("setTrafficClass() throws : " + e);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Assertion testing for public void setTrafficClass(int tc) throws
+ * SocketException, only SocketException may be thrown with tc in range from
+ * 0 to 255.
+ */
+ public void DatagramSocket2030() throws Exception {
+ String testCaseID = "DatagramSocket2030";
+ System.out.println(" >> " + testCaseID + ": " + "public void setTrafficClass(int tc) throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket();
+ } catch (SecurityException e) {
+ System.out.println("Security restriction: " + e);
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("cannot create socket: " + e);
+ }
+
+ for (int i = 0; i <= 255; i++) {
+ try {
+ ds.setTrafficClass(i);
+ } catch (SocketException e) {
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setBroadcast(boolean on) throws SocketException,
+ * <br><b>on</b>: false.
+ * <br><b>Expected results</b>: getBroadcast() will return false
+ */
+ public void DatagramSocket0034() throws Exception {
+ String testCaseID = "DatagramSocket0034";
+ System.out.println(" >> " + testCaseID + ": " + "public void setBroadcast(boolean on) throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket();
+ ds.setBroadcast(false);
+ if (ds.getBroadcast() == true) {
+ throw new RuntimeException("SO_BROADCAST is not set to false");
+ }
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+
+ /**
+ * Equivalence class partitioning with input values orientation for public
+ * void setBroadcast(boolean on) throws SocketException,
+ * <br><b>on</b>: true.
+ * <br><b>Expected results</b>: getBroadcast() will return true
+ */
+ public void DatagramSocket0035() throws Exception {
+ String testCaseID = "DatagramSocket0035";
+ System.out.println(" >> " + testCaseID + ": " + "public void setBroadcast(boolean on) throws SocketException");
+
+ DatagramSocket ds = null;
+ try {
+ ds = new DatagramSocket();
+ ds.setBroadcast(true);
+ if (ds.getBroadcast() == false) {
+ throw new RuntimeException("SO_BROADCAST is not set to true");
+ }
+ } catch (IOException e) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("unexpected: " + e);
+ } catch (SecurityException e) {
+ System.out.println("Security restriction");
+ } finally {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+
+ System.out.println("OKAY");
+ }
+}
--- a/jdk/test/java/security/AccessController/DoPrivAccompliceTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/security/AccessController/DoPrivAccompliceTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, 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
@@ -31,7 +31,7 @@
import java.util.Arrays;
import java.util.List;
-/**
+/*
* @test
* @bug 8048362
* @compile ../../../lib/testlibrary/JavaToolUtils.java
@@ -41,6 +41,7 @@
* DoPrivAccmplice.jar for reading user.home property from a PrivilagedAction.
* Run DoPrivTest.jar and try to access user.home property using
* DoPrivAccmplice.jar.
+ * @modules jdk.compiler
* @run main/othervm DoPrivAccompliceTest
*/
--- a/jdk/test/java/security/Security/ClassLoader/DeprivilegedModuleLoaderTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/security/Security/ClassLoader/DeprivilegedModuleLoaderTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -21,6 +21,17 @@
* questions.
*/
+/*
+ * @test
+ * @bug 8159964
+ * @summary Classes from deprivileged modules should get loaded through
+ * Platform Classloader.
+ * @modules java.xml.crypto
+ * jdk.security.auth
+ * jdk.security.jgss
+ * @run main DeprivilegedModuleLoaderTest
+ */
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -30,13 +41,6 @@
import com.sun.security.auth.callback.TextCallbackHandler;
import com.sun.security.jgss.AuthorizationDataEntry;
-/*
- * @test
- * @bug 8159964
- * @summary Classes from deprivileged modules should get loaded through
- * Platform Classloader.
- * @run main DeprivilegedModuleLoaderTest
- */
public class DeprivilegedModuleLoaderTest {
public static void main(String[] args) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Signature/ResetAfterException.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8149802
+ * @summary Ensure that Signature objects are reset after verification errored out.
+ */
+import java.util.Arrays;
+import java.security.*;
+
+public class ResetAfterException {
+
+ public static void main(String[] args) throws Exception {
+
+ byte[] data = "data to be signed".getBytes();
+ byte[] shortBuffer = new byte[2];
+
+ Provider[] provs = Security.getProviders();
+ boolean failed = false;
+
+ for (Provider p : provs) {
+ Signature sig;
+ try {
+ sig = Signature.getInstance("SHA256withRSA", p);
+ } catch (NoSuchAlgorithmException nsae) {
+ // no support, skip
+ continue;
+ }
+
+ boolean res = true;
+ System.out.println("Testing Provider: " + p.getName());
+ KeyPairGenerator keyGen = null;
+ try {
+ // It's possible that some provider, e.g. SunMSCAPI,
+ // doesn't work well with keys from other providers
+ // so we use the same provider to generate key first
+ keyGen = KeyPairGenerator.getInstance("RSA", p);
+ } catch (NoSuchAlgorithmException nsae) {
+ keyGen = KeyPairGenerator.getInstance("RSA");
+ }
+ if (keyGen == null) {
+ throw new RuntimeException("Error: No support for RSA KeyPairGenerator");
+ }
+ keyGen.initialize(1024);
+ KeyPair keyPair = keyGen.generateKeyPair();
+
+ sig.initSign(keyPair.getPrivate());
+ sig.update(data);
+ byte[] signature = sig.sign();
+ // First check signing
+ try {
+ sig.update(data);
+ // sign with short output buffer to cause exception
+ int len = sig.sign(shortBuffer, 0, shortBuffer.length);
+ System.out.println("FAIL: Should throw SE with short buffer");
+ res = false;
+ } catch (SignatureException e) {
+ // expected exception; ignore
+ System.out.println("Expected Ex for short output buffer: " + e);
+ }
+ // Signature object should reset after a failed generation
+ sig.update(data);
+ byte[] signature2 = sig.sign();
+ if (!Arrays.equals(signature, signature2)) {
+ System.out.println("FAIL: Generated different signature");
+ res = false;
+ } else {
+ System.out.println("Generated same signature");
+ }
+
+ // Now, check signature verification
+ sig.initVerify(keyPair.getPublic());
+ sig.update(data);
+ try {
+ // first verify with valid signature bytes
+ res = sig.verify(signature);
+ } catch (SignatureException e) {
+ System.out.println("FAIL: Valid signature rejected");
+ e.printStackTrace();
+ res = false;
+ }
+
+ try {
+ sig.update(data);
+ // verify with short signaure to cause exception
+ if (sig.verify(shortBuffer)) {
+ System.out.println("FAIL: Invalid signature verified");
+ res = false;
+ } else {
+ System.out.println("Invalid signature rejected");
+ }
+ } catch (SignatureException e) {
+ // expected exception; ignore
+ System.out.println("Expected Ex for short output buffer: " + e);
+ }
+ // Signature object should reset after an a failed verification
+ sig.update(data);
+ try {
+ // verify with valid signature bytes again
+ res = sig.verify(signature);
+ if (!res) {
+ System.out.println("FAIL: Valid signature is rejected");
+ } else {
+ System.out.println("Valid signature is accepted");
+ }
+ } catch (GeneralSecurityException e) {
+ System.out.println("FAIL: Valid signature is rejected");
+ e.printStackTrace();
+ res = false;
+ }
+ failed |= !res;
+ }
+ if (failed) {
+ throw new RuntimeException("One or more test failed");
+ } else {
+ System.out.println("Test Passed");
+ }
+ }
+}
--- a/jdk/test/java/security/Signature/SignatureLength.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/security/Signature/SignatureLength.java Wed Oct 05 06:28:22 2016 -0700
@@ -21,15 +21,20 @@
* questions.
*/
-import java.security.*;
-
/*
* @test
* @bug 8161571
* @summary Reject signatures presented for verification that contain extra
* bytes.
+ * @modules jdk.crypto.ec
* @run main SignatureLength
*/
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Signature;
+import java.security.SignatureException;
+
public class SignatureLength {
public static void main(String[] args) throws Exception {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Format/DecimalFormat/Bug8165466.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8165466
+ * @summary Checks the subsequent function calls of the DecimalFormat.format()
+ * method in which the minimumFractionDigit is set to 0 and one of
+ * the format() call include formatting of the number with zero
+ * fraction value e.g. 0.00, 9.00
+ */
+
+import java.text.DecimalFormat;
+import java.util.Locale;
+
+public class Bug8165466 {
+
+ public static void main(String[] args) {
+ DecimalFormat nf = (DecimalFormat) DecimalFormat
+ .getPercentInstance(Locale.US);
+ nf.setMaximumFractionDigits(3);
+ nf.setMinimumFractionDigits(0);
+ nf.setMultiplier(1);
+
+ double d = 0.005678;
+ String result = nf.format(d);
+ if (!result.equals("0.006%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 0.006%, Found: " + result
+ + "]");
+ }
+
+ d = 0.00;
+ result = nf.format(d);
+ if (!result.equals("0%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 0%, Found: " + result
+ + "]");
+ }
+
+ d = 0.005678;
+ result = nf.format(d);
+ if (!result.equals("0.006%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 0.006%, Found: " + result
+ + "]");
+ }
+
+ //checking with the non zero value
+ d = 0.005678;
+ result = nf.format(d);
+ if (!result.equals("0.006%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 0.006%, Found: " + result
+ + "]");
+ }
+
+ d = 9.00;
+ result = nf.format(d);
+ if (!result.equals("9%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 9%, Found: " + result
+ + "]");
+ }
+
+ d = 0.005678;
+ result = nf.format(d);
+ if (!result.equals("0.006%")) {
+ throw new RuntimeException("[Failed while formatting the double"
+ + " value: " + d + " Expected: 0.006%, Found: " + result
+ + "]");
+ }
+ }
+
+}
+
--- a/jdk/test/java/text/Format/NumberFormat/DFSSerialization.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/text/Format/NumberFormat/DFSSerialization.java Wed Oct 05 06:28:22 2016 -0700
@@ -27,13 +27,21 @@
* @library /java/text/testlib
* @build DFSSerialization IntlTest HexDumpReader
* @run main DFSSerialization
- * @summary Three different tests are done. 1.read from the object created using jdk1.4.2 2.create a valid DecimalFormatSymbols object with current JDK, then read the object 3.Try to create an valid DecimalFormatSymbols object by passing null to set null for the exponent separator symbol. Expect the NullPointerException.
+ * @summary Three different tests are done.
+ * 1. read from the object created using jdk1.4.2
+ * 2. create a valid DecimalFormatSymbols object with current JDK, then read the object
+ * 3. Try to create an valid DecimalFormatSymbols object by passing null to set null
+ * for the exponent separator symbol. Expect the NullPointerException.
*/
-import java.awt.*;
-import java.text.*;
-import java.util.*;
-import java.io.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
public class DFSSerialization extends IntlTest{
public static void main(String[] args) throws Exception {
--- a/jdk/test/java/text/Format/NumberFormat/SerializationLoadTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/text/Format/NumberFormat/SerializationLoadTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -31,10 +31,13 @@
* @key randomness
*/
-import java.awt.*;
-import java.text.*;
-import java.util.*;
-import java.io.*;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Random;
public class SerializationLoadTest {
--- a/jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java Wed Oct 05 06:28:22 2016 -0700
@@ -40,6 +40,7 @@
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -133,11 +134,14 @@
testCustomMultiReleaseValue(value, Map.of(), expected);
}
+ private static final AtomicInteger JAR_COUNT = new AtomicInteger(0);
+
private void testCustomMultiReleaseValue(String value,
Map<String, String> extraAttributes, boolean expected)
throws Exception {
- creator.buildCustomMultiReleaseJar("custom-mr.jar", value, extraAttributes);
- File custom = new File(userdir, "custom-mr.jar");
+ String fileName = "custom-mr" + JAR_COUNT.incrementAndGet() + ".jar";
+ creator.buildCustomMultiReleaseJar(fileName, value, extraAttributes);
+ File custom = new File(userdir, fileName);
try (JarFile jf = new JarFile(custom, true, ZipFile.OPEN_READ, Runtime.version())) {
Assert.assertEquals(jf.isMultiRelease(), expected);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/crypto/Cipher/EmptyFinalBuffer.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 6946830
+ * @summary Test the Cipher.doFinal() with 0-length buffer
+ * @key randomness
+ */
+
+import java.util.*;
+import java.nio.*;
+
+import java.security.*;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+public class EmptyFinalBuffer {
+
+ private static final String[] ALGOS = {
+ "AES/ECB/PKCS5Padding", "AES/CBC/PKCS5Padding"
+ };
+
+ public static void main(String[] args) throws Exception {
+
+ Provider[] provs = Security.getProviders();
+
+ SecretKey key = new SecretKeySpec(new byte[16], "AES");
+
+ boolean testFailed = false;
+ for (Provider p : provs) {
+ System.out.println("Testing: " + p.getName());
+ for (String algo : ALGOS) {
+ System.out.print("Algo: " + algo);
+ Cipher c;
+ try {
+ c = Cipher.getInstance(algo, p);
+ } catch (NoSuchAlgorithmException nsae) {
+ // skip
+ System.out.println("=> No Support");
+ continue;
+ }
+ c.init(Cipher.ENCRYPT_MODE, key);
+ AlgorithmParameters params = c.getParameters();
+ c.init(Cipher.DECRYPT_MODE, key, params);
+ try {
+ byte[] out = c.doFinal(new byte[0]);
+ System.out.println("=> Accepted w/ " +
+ (out == null? "null" : (out.length + "-byte")) +
+ " output");
+ } catch (Exception e) {
+ testFailed = true;
+ System.out.println("=> Rejected w/ Exception");
+ e.printStackTrace();
+ }
+ }
+ }
+ if (testFailed) {
+ throw new Exception("One or more tests failed");
+ } else {
+ System.out.println("All tests passed");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/templates/SSLTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,493 @@
+/*
+ * 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.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketTimeoutException;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Helper class for JSSE tests.
+ *
+ * Please run in othervm mode. SunJSSE does not support dynamic system
+ * properties, no way to re-use system properties in samevm/agentvm mode.
+ */
+public class SSLTest {
+
+ public static final String TEST_SRC = System.getProperty("test.src", ".");
+
+ /*
+ * Where do we find the keystores?
+ */
+ public static final String PATH_TO_STORES = "../etc";
+ public static final String KEY_STORE_FILE = "keystore";
+ public static final String TRUST_STORE_FILE = "truststore";
+ public static final String PASSWORD = "passphrase";
+
+ public static final int FREE_PORT = 0;
+
+ // in seconds
+ public static final long CLIENT_SIGNAL_TIMEOUT = 30L;
+ public static final long SERVER_SIGNAL_TIMEOUT = 90L;
+
+ // in millis
+ public static final int CLIENT_TIMEOUT = 15000;
+ public static final int SERVER_TIMEOUT = 30000;
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ private boolean separateServerThread = false;
+
+ /*
+ * What's the server port? Use any free port by default
+ */
+ private volatile int serverPort;
+
+ private volatile Exception serverException;
+ private volatile Exception clientException;
+
+ private Thread clientThread;
+ private Thread serverThread;
+
+ private Peer serverPeer;
+ private Peer clientPeer;
+
+ private Application serverApplication;
+ private Application clientApplication;
+
+ private SSLContext context;
+
+ /*
+ * Is the server ready to serve?
+ */
+ private final CountDownLatch serverCondition = new CountDownLatch(1);
+
+ /*
+ * Is the client ready to handshake?
+ */
+ private final CountDownLatch clientCondition = new CountDownLatch(1);
+
+ /*
+ * Public API.
+ */
+
+ public static interface Peer {
+ void run(SSLTest test) throws Exception;
+ }
+
+ public static interface Application {
+ void run(SSLSocket socket, SSLTest test) throws Exception;
+ }
+
+ public static void debug() {
+ debug("ssl");
+ }
+
+ public static void debug(String mode) {
+ System.setProperty("javax.net.debug", mode);
+ }
+
+ public static void setup(String keyFilename, String trustFilename,
+ String password) {
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", password);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", password);
+ }
+
+ public static void setup() throws Exception {
+ String keyFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
+ + KEY_STORE_FILE;
+ String trustFilename = TEST_SRC + "/" + PATH_TO_STORES + "/"
+ + TRUST_STORE_FILE;
+
+ setup(keyFilename, trustFilename, PASSWORD);
+ }
+
+ public static void print(String message, Throwable... errors) {
+ synchronized (System.out) {
+ System.out.println(message);
+ Arrays.stream(errors).forEach(e -> e.printStackTrace(System.out));
+ }
+ }
+
+ public static KeyStore loadJksKeyStore(String filename, String password)
+ throws Exception {
+
+ return loadKeyStore(filename, password, "JKS");
+ }
+
+ public static KeyStore loadKeyStore(String filename, String password,
+ String type) throws Exception {
+
+ KeyStore keystore = KeyStore.getInstance(type);
+ try (FileInputStream fis = new FileInputStream(filename)) {
+ keystore.load(fis, password.toCharArray());
+ }
+ return keystore;
+ }
+
+ public SSLTest setSeparateServerThread(boolean separateServerThread) {
+ this.separateServerThread = separateServerThread;
+ return this;
+ }
+
+ public SSLTest setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ return this;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public SSLTest setSSLContext(SSLContext context) {
+ this.context = context;
+ return this;
+ }
+
+ public SSLContext getSSLContext() {
+ return context;
+ }
+
+ public SSLServerSocketFactory getSSLServerSocketFactory() {
+ if (context != null) {
+ return context.getServerSocketFactory();
+ }
+
+ return (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ }
+
+ public SSLSocketFactory getSSLSocketFactory() {
+ if (context != null) {
+ return context.getSocketFactory();
+ }
+
+ return (SSLSocketFactory) SSLSocketFactory.getDefault();
+ }
+
+ public void signalServerReady() {
+ serverCondition.countDown();
+ }
+
+ public boolean waitForClientSignal(long timeout, TimeUnit unit)
+ throws InterruptedException {
+
+ return clientCondition.await(timeout, unit);
+ }
+
+ public boolean waitForClientSignal() throws InterruptedException {
+ return waitForClientSignal(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
+ }
+
+ public void signalClientReady() {
+ clientCondition.countDown();
+ }
+
+ public boolean waitForServerSignal(long timeout, TimeUnit unit)
+ throws InterruptedException {
+
+ return serverCondition.await(timeout, unit);
+ }
+
+ public boolean waitForServerSignal() throws InterruptedException {
+ return waitForServerSignal(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
+ }
+
+ public SSLTest setServerPeer(Peer serverPeer) {
+ this.serverPeer = serverPeer;
+ return this;
+ }
+
+ public Peer getServerPeer() {
+ return serverPeer;
+ }
+
+ public SSLTest setServerApplication(Application serverApplication) {
+ this.serverApplication = serverApplication;
+ return this;
+ }
+
+ public Application getServerApplication() {
+ return serverApplication;
+ }
+
+ public SSLTest setClientPeer(Peer clientPeer) {
+ this.clientPeer = clientPeer;
+ return this;
+ }
+
+ public Peer getClientPeer() {
+ return clientPeer;
+ }
+
+ public SSLTest setClientApplication(Application clientApplication) {
+ this.clientApplication = clientApplication;
+ return this;
+ }
+
+ public Application getClientApplication() {
+ return clientApplication;
+ }
+
+ public void runTest() throws Exception {
+ if (separateServerThread) {
+ startServer(true, this);
+ startClient(false, this);
+ serverThread.join();
+ } else {
+ startClient(true, this);
+ startServer(false, this);
+ clientThread.join();
+ }
+
+ if (clientException != null || serverException != null) {
+ throw new RuntimeException("Test failed");
+ }
+ }
+
+ public SSLTest() {
+ serverPeer = (test) -> doServerSide(test);
+ clientPeer = (test) -> doClientSide(test);
+ serverApplication = (socket, test) -> runServerApplication(socket);
+ clientApplication = (socket, test) -> runClientApplication(socket);
+ }
+
+ /*
+ * Private part.
+ */
+
+
+ /*
+ * Define the server side of the test.
+ */
+ private static void doServerSide(SSLTest test) throws Exception {
+ SSLServerSocket sslServerSocket;
+
+ // kick start the server side service
+ SSLServerSocketFactory sslssf = test.getSSLServerSocketFactory();
+ sslServerSocket = (SSLServerSocket)sslssf.createServerSocket(FREE_PORT);
+
+ test.setServerPort(sslServerSocket.getLocalPort());
+ print("Server is listening on port " + test.getServerPort());
+
+ // Signal the client, the server is ready to accept connection.
+ test.signalServerReady();
+
+ // Try to accept a connection in 30 seconds.
+ SSLSocket sslSocket;
+ try {
+ sslServerSocket.setSoTimeout(SERVER_TIMEOUT);
+ sslSocket = (SSLSocket) sslServerSocket.accept();
+ print("Server accepted connection");
+ } catch (SocketTimeoutException ste) {
+ sslServerSocket.close();
+
+ // Ignore the test case if no connection within 30 seconds.
+ print("No incoming client connection in 30 seconds. "
+ + "Ignore in server side.", ste);
+ return;
+ }
+
+ // handle the connection
+ try {
+ // Is it the expected client connection?
+ //
+ // Naughty test cases or third party routines may try to
+ // connection to this server port unintentionally. In
+ // order to mitigate the impact of unexpected client
+ // connections and avoid intermittent failure, it should
+ // be checked that the accepted connection is really linked
+ // to the expected client.
+ boolean clientIsReady = test.waitForClientSignal();
+
+ if (clientIsReady) {
+ // Run the application in server side.
+ print("Run server application");
+ test.getServerApplication().run(sslSocket, test);
+ } else { // Otherwise, ignore
+ // We don't actually care about plain socket connections
+ // for TLS communication testing generally. Just ignore
+ // the test if the accepted connection is not linked to
+ // the expected client or the client connection timeout
+ // in 30 seconds.
+ print("The client is not the expected one or timeout. "
+ + "Ignore in server side.");
+ }
+ } finally {
+ sslSocket.close();
+ sslServerSocket.close();
+ }
+ }
+
+ /*
+ * Define the server side application of the test for the specified socket.
+ */
+ private static void runServerApplication(SSLSocket socket)
+ throws Exception {
+
+ // here comes the test logic
+ InputStream sslIS = socket.getInputStream();
+ OutputStream sslOS = socket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+ }
+
+ /*
+ * Define the client side of the test.
+ */
+ private static void doClientSide(SSLTest test) throws Exception {
+
+ // Wait for server to get started.
+ //
+ // The server side takes care of the issue if the server cannot
+ // get started in 90 seconds. The client side would just ignore
+ // the test case if the serer is not ready.
+ boolean serverIsReady = test.waitForServerSignal();
+ if (!serverIsReady) {
+ print("The server is not ready yet in 90 seconds. "
+ + "Ignore in client side.");
+ return;
+ }
+
+ SSLSocketFactory sslsf = test.getSSLSocketFactory();
+ try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
+ try {
+ sslSocket.connect(
+ new InetSocketAddress("localhost",
+ test.getServerPort()), CLIENT_TIMEOUT);
+ print("Client connected to server");
+ } catch (IOException ioe) {
+ // The server side may be impacted by naughty test cases or
+ // third party routines, and cannot accept connections.
+ //
+ // Just ignore the test if the connection cannot be
+ // established.
+ print("Cannot make a connection in 15 seconds. "
+ + "Ignore in client side.", ioe);
+ return;
+ }
+
+ // OK, here the client and server get connected.
+
+ // Signal the server, the client is ready to communicate.
+ test.signalClientReady();
+
+ // There is still a chance in theory that the server thread may
+ // wait client-ready timeout and then quit. The chance should
+ // be really rare so we don't consider it until it becomes a
+ // real problem.
+
+ // Run the application in client side.
+ print("Run client application");
+ test.getClientApplication().run(sslSocket, test);
+ }
+ }
+
+ /*
+ * Define the client side application of the test for the specified socket.
+ */
+ private static void runClientApplication(SSLSocket socket)
+ throws Exception {
+
+ InputStream sslIS = socket.getInputStream();
+ OutputStream sslOS = socket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+ }
+
+ private void startServer(boolean newThread, SSLTest test) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ serverPeer.run(test);
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ print("Server died ...", e);
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ serverPeer.run(test);
+ } catch (Exception e) {
+ print("Server failed ...", e);
+ serverException = e;
+ }
+ }
+ }
+
+ private void startClient(boolean newThread, SSLTest test) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ clientPeer.run(test);
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ print("Client died ...", e);
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ clientPeer.run(test);
+ } catch (Exception e) {
+ print("Client failed ...", e);
+ clientException = e;
+ }
+ }
+ }
+}
--- a/jdk/test/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,9 @@
* @bug 4328195
* @summary Need to include the alternate subject DN for certs,
* https should check for this
- * @run main/othervm ServerIdentityTest
+ * @library /javax/net/ssl/templates
+ * @run main/othervm ServerIdentityTest dnsstore
+ * @run main/othervm ServerIdentityTest ipstore
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
@@ -34,242 +36,71 @@
* @author Yingxian Wang
*/
-import java.io.*;
-import java.net.*;
-import javax.net.ssl.*;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.security.KeyStore;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
public class ServerIdentityTest {
- /*
- * =============================================================
- * Set the various variables needed for the tests, then
- * specify what tests to run on each side.
- */
-
- /*
- * Should we run the client or server in a separate thread?
- * Both sides can throw exceptions, but do you have a preference
- * as to which side should be the main thread.
- */
- static boolean separateServerThread = true;
-
- /*
- * Where do we find the keystores?
- */
- static String pathToStores = "./";
- static String[] keyStoreFiles = {"dnsstore", "ipstore"};
- static String[] trustStoreFiles = {"dnsstore", "ipstore"};
- static String passwd = "changeit";
-
- /*
- * Is the server ready to serve?
- */
- boolean serverReady = false;
-
- /*
- * Turn on SSL debugging?
- */
- static boolean debug = false;
-
- /*
- * If the client or server is doing some kind of object creation
- * that the other side depends on, and that thread prematurely
- * exits, you may experience a hang. The test harness will
- * terminate all hung threads after its timeout has expired,
- * currently 3 minutes by default, but you might try to be
- * smart about it....
- */
-
- /*
- * Define the server side of the test.
- *
- * If the server prematurely exits, serverReady will be set to true
- * to avoid infinite hangs.
- */
- void doServerSide() throws Exception {
- SSLServerSocketFactory sslssf =
- context.getServerSocketFactory();
- SSLServerSocket sslServerSocket =
- (SSLServerSocket) sslssf.createServerSocket(serverPort);
- serverPort = sslServerSocket.getLocalPort();
-
- /*
- * Signal Client, we're ready for his connect.
- */
- serverReady = true;
-
- SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
- OutputStream sslOS = sslSocket.getOutputStream();
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sslOS));
- bw.write("HTTP/1.1 200 OK\r\n\r\n\r\n");
- bw.flush();
- Thread.sleep(2000);
- sslSocket.getSession().invalidate();
- sslSocket.close();
- }
-
- /*
- * Define the client side of the test.
- *
- * If the server prematurely exits, serverReady will be set to true
- * to avoid infinite hangs.
- */
- void doClientSide() throws Exception {
- /*
- * Wait for server to get started.
- */
- while (!serverReady) {
- Thread.sleep(50);
- }
- String host = iphost? "127.0.0.1": "localhost";
- URL url = new URL("https://"+host+":"+serverPort+"/index.html");
-
- HttpURLConnection urlc = (HttpURLConnection)url.openConnection();
- InputStream is = urlc.getInputStream();
- is.close();
- }
-
- /*
- * =============================================================
- * The remainder is just support stuff
- */
-
- volatile int serverPort = 0;
-
- volatile Exception serverException = null;
- volatile Exception clientException = null;
+ private static final String PASSWORD = "changeit";
public static void main(String[] args) throws Exception {
- SSLSocketFactory reservedSFactory =
- HttpsURLConnection.getDefaultSSLSocketFactory();
- try {
- for (int i = 0; i < keyStoreFiles.length; i++) {
- String keyFilename =
- System.getProperty("test.src", ".") + "/" + pathToStores +
- "/" + keyStoreFiles[i];
- String trustFilename =
- System.getProperty("test.src", ".") + "/" + pathToStores +
- "/" + trustStoreFiles[i];
+ final String keystore = args[0];
+ String keystoreFilename = SSLTest.TEST_SRC + "/" + keystore;
- System.setProperty("javax.net.ssl.keyStore", keyFilename);
- System.setProperty("javax.net.ssl.keyStorePassword", passwd);
- System.setProperty("javax.net.ssl.trustStore", trustFilename);
- System.setProperty("javax.net.ssl.trustStorePassword", passwd);
-
- if (debug)
- System.setProperty("javax.net.debug", "all");
- SSLContext context = SSLContext.getInstance("SSL");
+ SSLTest.setup(keystoreFilename, keystoreFilename, PASSWORD);
- KeyManager[] kms = new KeyManager[1];
- KeyStore ks = KeyStore.getInstance("JKS");
- FileInputStream fis = new FileInputStream(keyFilename);
- ks.load(fis, passwd.toCharArray());
- fis.close();
- KeyManager km = new MyKeyManager(ks, passwd.toCharArray());
- kms[0] = km;
- context.init(kms, null, null);
- HttpsURLConnection.setDefaultSSLSocketFactory(
- context.getSocketFactory());
+ SSLContext context = SSLContext.getInstance("SSL");
- /*
- * Start the tests.
- */
- System.out.println("Testing " + keyFilename);
- new ServerIdentityTest(context, keyStoreFiles[i]);
- }
- } finally {
- HttpsURLConnection.setDefaultSSLSocketFactory(reservedSFactory);
- }
- }
-
- Thread clientThread = null;
- Thread serverThread = null;
-
- /*
- * Primary constructor, used to drive remainder of the test.
- *
- * Fork off the other side, then do your work.
- */
- SSLContext context;
- boolean iphost = false;
- ServerIdentityTest(SSLContext context, String keystore)
- throws Exception {
- this.context = context;
- iphost = keystore.equals("ipstore");
- if (separateServerThread) {
- startServer(true);
- startClient(false);
- } else {
- startClient(true);
- startServer(false);
- }
+ KeyManager[] kms = new KeyManager[1];
+ KeyStore ks = SSLTest.loadJksKeyStore(keystoreFilename, PASSWORD);
+ KeyManager km = new MyKeyManager(ks, PASSWORD.toCharArray());
+ kms[0] = km;
+ context.init(kms, null, null);
+ HttpsURLConnection.setDefaultSSLSocketFactory(
+ context.getSocketFactory());
/*
- * Wait for other side to close down.
+ * Start the test.
*/
- if (separateServerThread) {
- serverThread.join();
- } else {
- clientThread.join();
- }
-
- /*
- * When we get here, the test is pretty much over.
- *
- * If the main thread excepted, that propagates back
- * immediately. If the other thread threw an exception, we
- * should report back.
- */
- if (serverException != null)
- throw serverException;
- if (clientException != null)
- throw clientException;
- }
+ System.out.println("Testing " + keystore);
- void startServer(boolean newThread) throws Exception {
- if (newThread) {
- serverThread = new Thread() {
- public void run() {
- try {
- doServerSide();
- } catch (Exception e) {
- e.printStackTrace();
- /*
- * Our server thread just died.
- *
- * Release the client, if not active already...
- */
- System.err.println("Server died...");
- serverReady = true;
- serverException = e;
- }
+ new SSLTest()
+ .setSSLContext(context)
+ .setServerApplication((socket, test) -> {
+ BufferedWriter bw = new BufferedWriter(
+ new OutputStreamWriter(socket.getOutputStream()));
+ bw.write("HTTP/1.1 200 OK\r\n\r\n\r\n");
+ bw.flush();
+ Thread.sleep(2000);
+ socket.getSession().invalidate();
+ SSLTest.print("Server application is done");
+ })
+ .setClientPeer((test) -> {
+ boolean serverIsReady = test.waitForServerSignal();
+ if (!serverIsReady) {
+ SSLTest.print(
+ "The server is not ready, ignore on client side.");
+ return;
}
- };
- serverThread.start();
- } else {
- doServerSide();
- }
- }
+
+ // Signal the server, the client is ready to communicate.
+ test.signalClientReady();
- void startClient(boolean newThread) throws Exception {
- if (newThread) {
- clientThread = new Thread() {
- public void run() {
- try {
- doClientSide();
- } catch (Exception e) {
- /*
- * Our client thread just died.
- */
- System.err.println("Client died...");
- clientException = e;
- }
- }
- };
- clientThread.start();
- } else {
- doClientSide();
- }
+ String host = keystore.equals("ipstore")
+ ? "127.0.0.1" : "localhost";
+ URL url = new URL("https://" + host + ":" + test.getServerPort()
+ + "/index.html");
+
+ ((HttpURLConnection) url.openConnection())
+ .getInputStream().close();
+
+ SSLTest.print("Client is done");
+ }).runTest();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/InputFilesTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8165944
+ * @summary test several jar tool input file scenarios with variations on -C
+ * options with/without a --release option. Some input files are
+ * duplicates that sometimes cause exceptions and other times do not,
+ * demonstrating identical behavior to JDK 8 jar tool.
+ * @library /lib/testlibrary
+ * @modules jdk.jartool/sun.tools.jar
+ * @build jdk.testlibrary.FileUtils
+ * @run testng InputFilesTest
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+import java.util.zip.ZipException;
+
+import jdk.testlibrary.FileUtils;
+
+public class InputFilesTest {
+ private final String nl = System.lineSeparator();
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ private final PrintStream out = new PrintStream(baos);
+ private Runnable onCompletion;
+
+ @BeforeMethod
+ public void reset() {
+ onCompletion = null;
+ }
+
+ @AfterMethod
+ public void run() {
+ if (onCompletion != null) {
+ onCompletion.run();
+ }
+ }
+
+ @Test
+ public void test1() throws IOException {
+ mkdir("test1 test2");
+ touch("test1/testfile1 test2/testfile2");
+ jar("cf test.jar -C test1 . -C test2 .");
+ jar("tf test.jar");
+ println();
+ String output = "META-INF/" + nl +
+ "META-INF/MANIFEST.MF" + nl +
+ "testfile1" + nl +
+ "testfile2" + nl;
+ rm("test.jar test1 test2");
+ Assert.assertEquals(baos.toByteArray(), output.getBytes());
+ }
+
+ @Test
+ public void test2() throws IOException {
+ mkdir("test1 test2 test3 test4");
+ touch("test1/testfile1 test2/testfile2 test3/testfile3 test4/testfile4");
+ jar("cf test.jar -C test1 . -C test2 . --release 9 -C test3 . -C test4 .");
+ jar("tf test.jar");
+ println();
+ String output = "META-INF/" + nl +
+ "META-INF/MANIFEST.MF" + nl +
+ "testfile1" + nl +
+ "testfile2" + nl +
+ "META-INF/versions/9/testfile3" + nl +
+ "META-INF/versions/9/testfile4" + nl;
+ rm("test.jar test1 test2 test3 test4");
+ Assert.assertEquals(baos.toByteArray(), output.getBytes());
+ }
+
+ @Test
+ public void test3() throws IOException {
+ touch("test");
+ jar("cf test.jar test test");
+ jar("tf test.jar");
+ println();
+ String output = "META-INF/" + nl +
+ "META-INF/MANIFEST.MF" + nl +
+ "test" + nl;
+ rm("test.jar test");
+ Assert.assertEquals(baos.toByteArray(), output.getBytes());
+ }
+
+ @Test
+ public void test4() throws IOException {
+ mkdir("a");
+ touch("a/test");
+ jar("cf test.jar -C a test -C a test");
+ jar("tf test.jar");
+ println();
+ String output = "META-INF/" + nl +
+ "META-INF/MANIFEST.MF" + nl +
+ "test" + nl;
+ rm("test.jar a");
+ Assert.assertEquals(baos.toByteArray(), output.getBytes());
+ }
+
+ @Test(expectedExceptions = {ZipException.class})
+ public void test5() throws IOException {
+ mkdir("a");
+ touch("test a/test");
+ onCompletion = () -> rm("test a");
+ jar("cf test.jar -C a test test");
+ }
+
+ @Test(expectedExceptions = {ZipException.class})
+ public void test6() throws IOException {
+ mkdir("test1 test2");
+ touch("test1/a test2/a");
+ onCompletion = () -> rm("test1 test2");
+ jar("cf test.jar --release 9 -C test1 a -C test2 a");
+ }
+
+ private Stream<Path> mkpath(String... args) {
+ return Arrays.stream(args).map(d -> Paths.get(".", d.split("/")));
+ }
+
+ private void mkdir(String cmdline) {
+ System.out.println("mkdir -p " + cmdline);
+ mkpath(cmdline.split(" +")).forEach(p -> {
+ try {
+ Files.createDirectories(p);
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ }
+ });
+ }
+
+ private void touch(String cmdline) {
+ System.out.println("touch " + cmdline);
+ mkpath(cmdline.split(" +")).forEach(p -> {
+ try {
+ Files.createFile(p);
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ }
+ });
+ }
+
+ private void rm(String cmdline) {
+ System.out.println("rm -rf " + cmdline);
+ mkpath(cmdline.split(" +")).forEach(p -> {
+ try {
+ if (Files.isDirectory(p)) {
+ FileUtils.deleteFileTreeWithRetry(p);
+ } else {
+ FileUtils.deleteFileIfExistsWithRetry(p);
+ }
+ } catch (IOException x) {
+ throw new UncheckedIOException(x);
+ }
+ });
+ }
+
+ private void jar(String cmdline) throws IOException {
+ System.out.println("jar " + cmdline);
+ baos.reset();
+
+ // the run method catches IOExceptions, we need to expose them
+ ByteArrayOutputStream baes = new ByteArrayOutputStream();
+ PrintStream err = new PrintStream(baes);
+ PrintStream saveErr = System.err;
+ System.setErr(err);
+ boolean ok = new sun.tools.jar.Main(out, err, "jar").run(cmdline.split(" +"));
+ System.setErr(saveErr);
+ if (!ok) {
+ String s = baes.toString();
+ if (s.startsWith("java.util.zip.ZipException: duplicate entry: ")) {
+ throw new ZipException(s);
+ }
+ throw new IOException(s);
+ }
+ }
+
+ private void println() throws IOException {
+ System.out.println(new String(baos.toByteArray()));
+ }
+}
--- a/jdk/test/tools/jar/multiRelease/Basic.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/tools/jar/multiRelease/Basic.java Wed Oct 05 06:28:22 2016 -0700
@@ -195,6 +195,8 @@
new String[] {"v10", "version", "Version.class"}
);
+ compare(jarfile, names);
+
delete(jarfile);
deleteDir(Paths.get(usr, "classes"));
}
--- a/jdk/test/tools/jlink/JLinkTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/tools/jlink/JLinkTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -150,6 +150,43 @@
}
{
+ String moduleName = "m_8165735"; // JDK-8165735
+ helper.generateDefaultJModule(moduleName+"dependency").assertSuccess();
+ Path jmod = helper.generateDefaultJModule(moduleName, moduleName+"dependency").assertSuccess();
+ JImageGenerator.getJLinkTask()
+ .modulePath(helper.defaultModulePath())
+ .repeatedModulePath(".") // second --module-path overrides the first one
+ .output(helper.createNewImageDir(moduleName))
+ .addMods(moduleName)
+ // second --module-path does not have that module
+ .call().assertFailure("Error: Module m_8165735 not found");
+
+ JImageGenerator.getJLinkTask()
+ .modulePath(".") // first --module-path overridden later
+ .repeatedModulePath(helper.defaultModulePath())
+ .output(helper.createNewImageDir(moduleName))
+ .addMods(moduleName)
+ // second --module-path has that module
+ .call().assertSuccess();
+
+ JImageGenerator.getJLinkTask()
+ .modulePath(helper.defaultModulePath())
+ .output(helper.createNewImageDir(moduleName))
+ .limitMods(moduleName)
+ .repeatedLimitMods("java.base") // second --limit-modules overrides first
+ .addMods(moduleName)
+ .call().assertFailure("Error: Module m_8165735dependency not found, required by m_8165735");
+
+ JImageGenerator.getJLinkTask()
+ .modulePath(helper.defaultModulePath())
+ .output(helper.createNewImageDir(moduleName))
+ .limitMods("java.base")
+ .repeatedLimitMods(moduleName) // second --limit-modules overrides first
+ .addMods(moduleName)
+ .call().assertSuccess();
+ }
+
+ {
// Help
StringWriter writer = new StringWriter();
jdk.tools.jlink.internal.Main.run(new String[]{"--help"}, new PrintWriter(writer));
--- a/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -92,7 +92,7 @@
// Asterisk works exactly the same as above
{
- "*",
+ "--include-locales=*",
"jdk.localedata",
List.of(
"/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",
--- a/jdk/test/tools/lib/tests/JImageGenerator.java Fri Sep 30 02:52:38 2016 -0700
+++ b/jdk/test/tools/lib/tests/JImageGenerator.java Wed Oct 05 06:28:22 2016 -0700
@@ -564,6 +564,10 @@
private final List<String> limitMods = new ArrayList<>();
private final List<String> options = new ArrayList<>();
private String modulePath;
+ // if you want to specifiy repeated --module-path option
+ private String repeatedModulePath;
+ // if you want to specifiy repeated --limit-modules option
+ private String repeatedLimitMods;
private Path output;
private Path existing;
@@ -572,6 +576,11 @@
return this;
}
+ public JLinkTask repeatedModulePath(String modulePath) {
+ this.repeatedModulePath = modulePath;
+ return this;
+ }
+
public JLinkTask addJars(Path jars) {
this.jars.add(jars);
return this;
@@ -597,6 +606,11 @@
return this;
}
+ public JLinkTask repeatedLimitMods(String modules) {
+ this.repeatedLimitMods = modules;
+ return this;
+ }
+
public JLinkTask output(Path output) {
this.output = output;
return this;
@@ -639,6 +653,10 @@
options.add(LIMIT_MODULES_OPTION);
options.add(limitMods.stream().collect(Collectors.joining(",")));
}
+ if (repeatedLimitMods != null) {
+ options.add(LIMIT_MODULES_OPTION);
+ options.add(repeatedLimitMods);
+ }
if (!jars.isEmpty() || !jmods.isEmpty()) {
options.add(MODULE_PATH_OPTION);
options.add(modulePath());
@@ -647,6 +665,10 @@
options.add(MODULE_PATH_OPTION);
options.add(modulePath);
}
+ if (repeatedModulePath != null) {
+ options.add(MODULE_PATH_OPTION);
+ options.add(repeatedModulePath);
+ }
if (!pluginModulePath.isEmpty()) {
options.add(PLUGIN_MODULE_PATH);
options.add(toPath(pluginModulePath));
--- a/langtools/.hgtags Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/.hgtags Wed Oct 05 06:28:22 2016 -0700
@@ -380,3 +380,4 @@
af5eb8f3ffd21288305a54ea177ffad75021a741 jdk-9+135
c8f02f0ecbd7cd6700f47416e4b7e9d5ec20ad77 jdk-9+136
dd56c243c199a540c9f1fbff4855f0934b32a9d0 jdk-9+137
+90dd93e668a521642382561c47abe96ee2e065b7 jdk-9+138
--- a/langtools/make/gendata/Gendata-jdk.compiler.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/make/gendata/Gendata-jdk.compiler.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -52,7 +52,7 @@
$(RM) -r $(@D)
$(MKDIR) -p $(@D)
$(ECHO) Creating ct.sym classes
- $(JAVA) $(INTERIM_LANGTOOLS_ARGS) \
+ $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \
$(COMPILECREATESYMBOLS_ADD_EXPORTS) \
-classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \
build.tools.symbolgenerator.CreateSymbols \
--- a/langtools/make/gensrc/GensrcCommon.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/make/gensrc/GensrcCommon.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -32,12 +32,12 @@
################################################################################
# The compileprops tools compiles a properties file into a resource bundle.
-TOOL_COMPILEPROPS_CMD := $(JAVA) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \
+TOOL_COMPILEPROPS_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \
compileproperties.CompileProperties -quiet
################################################################################
# The compileprops tools compiles a properties file into an enum-like class.
-TOOL_PARSEPROPS_CMD := $(JAVA) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \
+TOOL_PARSEPROPS_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \
propertiesparser.PropertiesParser
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java Wed Oct 05 06:28:22 2016 -0700
@@ -31,6 +31,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.function.Function;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
@@ -38,10 +39,14 @@
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;
+import com.sun.tools.javac.code.Symbol.Completer;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
+import com.sun.tools.javac.jvm.ModuleNameReader;
+import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
+import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.Fragment;
@@ -50,7 +55,6 @@
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
-import com.sun.tools.javac.util.StringUtils;
import static com.sun.tools.javac.code.Kinds.Kind.*;
@@ -84,6 +88,10 @@
private final JCDiagnostic.Factory diags;
+ private ModuleNameReader moduleNameReader;
+
+ public ModuleInfoSourceFileCompleter sourceFileCompleter;
+
/** Get the ModuleFinder instance for this invocation. */
public static ModuleFinder instance(Context context) {
ModuleFinder instance = context.get(moduleFinderKey);
@@ -182,6 +190,8 @@
return list;
}
+ private boolean inFindSingleModule;
+
public ModuleSymbol findSingleModule() {
try {
JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE);
@@ -194,26 +204,41 @@
if (fo == null) {
msym = syms.unnamedModule;
} else {
- // Note: the following may trigger a re-entrant call to Modules.enter
-// msym = new ModuleSymbol();
-// ClassSymbol info = new ClassSymbol(Flags.MODULE, names.module_info, msym);
-// info.modle = msym;
-// info.classfile = fo;
-// info.members_field = WriteableScope.create(info);
-// msym.module_info = info;
- msym = ModuleSymbol.create(null, names.module_info);
- msym.module_info.classfile = fo;
- msym.completer = sym -> classFinder.fillIn(msym.module_info);
-// // TODO: should we do the following here, or as soon as we find the name in
-// // the source or class file?
-// // Consider the case when the class/source path module shadows one on the
-// // module source path
-// if (syms.modules.get(msym.name) != null) {
-// // error: module already defined
-// System.err.println("ERROR: module already defined: " + msym);
-// } else {
-// syms.modules.put(msym.name, msym);
-// }
+ switch (fo.getKind()) {
+ case SOURCE:
+ if (!inFindSingleModule) {
+ try {
+ inFindSingleModule = true;
+ // Note: the following will trigger a re-entrant call to Modules.enter
+ msym = sourceFileCompleter.complete(fo);
+ msym.module_info.classfile = fo;
+ } finally {
+ inFindSingleModule = false;
+ }
+ } else {
+ //the module-info.java does not contain a module declaration,
+ //avoid infinite recursion:
+ msym = syms.unnamedModule;
+ }
+ break;
+ case CLASS:
+ Name name;
+ try {
+ name = names.fromString(readModuleName(fo));
+ } catch (BadClassFile | IOException ex) {
+ //fillIn will report proper errors:
+ name = names.error;
+ }
+ msym = syms.enterModule(name);
+ msym.module_info.classfile = fo;
+ msym.completer = Completer.NULL_COMPLETER;
+ classFinder.fillIn(msym.module_info);
+ break;
+ default:
+ Assert.error();
+ msym = syms.unnamedModule;
+ break;
+ }
}
msym.classLocation = StandardLocation.CLASS_OUTPUT;
@@ -224,6 +249,12 @@
}
}
+ private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile {
+ if (moduleNameReader == null)
+ moduleNameReader = new ModuleNameReader();
+ return moduleNameReader.readModuleName(jfo);
+ }
+
private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException {
if (!fileManager.hasLocation(location))
return null;
@@ -332,4 +363,8 @@
}
}
+ public interface ModuleInfoSourceFileCompleter {
+ public ModuleSymbol complete(JavaFileObject file);
+ }
+
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Wed Oct 05 06:28:22 2016 -0700
@@ -916,7 +916,6 @@
/**
* Create a ModuleSymbol with an associated module-info ClassSymbol.
- * The name of the module may be null, if it is not known yet.
*/
public static ModuleSymbol create(Name name, Name module_info) {
ModuleSymbol msym = new ModuleSymbol(name, null);
@@ -930,6 +929,7 @@
public ModuleSymbol(Name name, Symbol owner) {
super(MDL, 0, name, null, owner);
+ Assert.checkNonNull(name);
this.type = new ModuleType(this);
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Wed Oct 05 06:28:22 2016 -0700
@@ -764,17 +764,6 @@
return msym;
}
- public void enterModule(ModuleSymbol msym, Name name) {
- Assert.checkNull(modules.get(name));
- Assert.checkNull(msym.name);
- msym.name = name;
- addRootPackageFor(msym);
- ClassSymbol info = msym.module_info;
- info.fullname = msym.name.append('.', names.module_info);
- info.flatname = info.fullname;
- modules.put(name, msym);
- }
-
public ModuleSymbol getModule(Name name) {
return modules.get(name);
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java Wed Oct 05 06:28:22 2016 -0700
@@ -292,15 +292,12 @@
Name name = TreeInfo.fullName(decl.qualId);
ModuleSymbol sym;
if (c != null) {
- sym = (ModuleSymbol) c.owner;
- if (sym.name == null) {
- //ModuleFinder.findSingleModule creates a stub of a ModuleSymbol without a name,
- //fill the name here after the module-info.java has been parsed
- //also enter the ModuleSymbol among modules:
- syms.enterModule(sym, name);
- } else {
- // TODO: validate name
- }
+ sym = (ModuleSymbol) c.owner;
+ Assert.checkNonNull(sym.name);
+ Name treeName = TreeInfo.fullName(decl.qualId);
+ if (sym.name != treeName) {
+ log.error(decl.pos(), Errors.ModuleNameMismatch(name, sym.name));
+ }
} else {
sym = syms.enterModule(name);
if (sym.module_info.sourcefile != null && sym.module_info.sourcefile != toplevel.sourcefile) {
@@ -1006,6 +1003,10 @@
return new Symbol.Completer() {
@Override
public void complete(Symbol sym) throws CompletionFailure {
+ if (inInitModules) {
+ sym.completer = this;
+ return ;
+ }
ModuleSymbol msym = (ModuleSymbol) sym;
Set<ModuleSymbol> allModules = allModules();
for (ModuleSymbol m : allModules) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Oct 05 06:28:22 2016 -0700
@@ -415,10 +415,10 @@
if (!isSameMemberWhenErased(dest, impl, impl_erasure))
return true;
- // If the erasure of the return type is different, a
- // bridge is needed.
- return !types.isSameType(impl_erasure.getReturnType(),
- method_erasure.getReturnType());
+ /* Bottom line: A bridge is needed if the erasure of the implementation
+ is different from that of the method that it overrides.
+ */
+ return !types.isSameType(impl_erasure, method_erasure);
} else {
// method and impl are the same...
if ((method.flags() & ABSTRACT) != 0) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Wed Oct 05 06:28:22 2016 -0700
@@ -80,6 +80,7 @@
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.jvm.ModuleNameReader;
import com.sun.tools.javac.util.Pair;
import com.sun.tools.javac.util.StringUtils;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/ModuleNameReader.java Fri Sep 30 02:52:38 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 1999, 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 com.sun.tools.javac.file;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import com.sun.tools.javac.jvm.ClassFile;
-
-import static com.sun.tools.javac.jvm.ClassFile.*;
-
-
-/**
- * Stripped down ClassReader, just sufficient to read module names from module-info.class files
- * while analyzing the module path.
- *
- * <p>
- * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
- * your own risk. This code and its internal interfaces are subject to change or deletion without
- * notice.</b>
- */
-public class ModuleNameReader {
- static class BadClassFile extends Exception {
- private static final long serialVersionUID = 0;
- BadClassFile(String msg) {
- super(msg);
- }
- }
-
- private static final int INITIAL_BUFFER_SIZE = 0x0fff0;
-
- /** The buffer containing the currently read class file.
- */
- private byte[] buf = new byte[INITIAL_BUFFER_SIZE];
-
- /** The current input pointer.
- */
- private int bp;
-
- /** The objects of the constant pool.
- */
- private Object[] poolObj;
-
- /** For every constant pool entry, an index into buf where the
- * defining section of the entry is found.
- */
- private int[] poolIdx;
-
- ModuleNameReader() {
- }
-
- String readModuleName(Path p) throws IOException, BadClassFile {
- try (InputStream in = Files.newInputStream(p)) {
- bp = 0;
- buf = readInputStream(buf, in);
-
- int magic = nextInt();
- if (magic != JAVA_MAGIC)
- throw new BadClassFile("illegal.start.of.class.file");
-
- int minorVersion = nextChar();
- int majorVersion = nextChar();
-
- indexPool();
-
- int accessflags = nextChar();
- return readModuleInfoName(nextChar());
- }
- }
-
- /** Extract a character at position bp from buf.
- */
- char getChar(int bp) {
- return
- (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF));
- }
-
- /** Read a character.
- */
- char nextChar() {
- return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
- }
-
- /** Read an integer.
- */
- int nextInt() {
- return
- ((buf[bp++] & 0xFF) << 24) +
- ((buf[bp++] & 0xFF) << 16) +
- ((buf[bp++] & 0xFF) << 8) +
- (buf[bp++] & 0xFF);
- }
-
- /** Index all constant pool entries, writing their start addresses into
- * poolIdx.
- */
- void indexPool() throws BadClassFile {
- poolIdx = new int[nextChar()];
- poolObj = new Object[poolIdx.length];
- int i = 1;
- while (i < poolIdx.length) {
- poolIdx[i++] = bp;
- byte tag = buf[bp++];
- switch (tag) {
- case CONSTANT_Utf8: case CONSTANT_Unicode: {
- int len = nextChar();
- bp = bp + len;
- break;
- }
- case CONSTANT_Class:
- case CONSTANT_String:
- case CONSTANT_MethodType:
- bp = bp + 2;
- break;
- case CONSTANT_MethodHandle:
- bp = bp + 3;
- break;
- case CONSTANT_Fieldref:
- case CONSTANT_Methodref:
- case CONSTANT_InterfaceMethodref:
- case CONSTANT_NameandType:
- case CONSTANT_Integer:
- case CONSTANT_Float:
- case CONSTANT_InvokeDynamic:
- bp = bp + 4;
- break;
- case CONSTANT_Long:
- case CONSTANT_Double:
- bp = bp + 8;
- i++;
- break;
- default:
- throw new BadClassFile("malformed constant pool");
- }
- }
- }
-
- /** Read the class name of a module-info.class file.
- * The name is stored in a CONSTANT_Class entry, where the
- * class name is of the form module-name/module-info.
- */
- String readModuleInfoName(int i) throws BadClassFile {
- int classIndex = poolIdx[i];
- if (buf[classIndex] == CONSTANT_Class) {
- int utf8Index = poolIdx[getChar(classIndex + 1)];
- if (buf[utf8Index] == CONSTANT_Utf8) {
- int len = getChar(utf8Index + 1);
- int start = utf8Index + 3;
- String suffix = "/module-info";
- if (endsWith(buf, start, len, suffix))
- return new String(ClassFile.internalize(buf, start, len - suffix.length()));
- }
- }
- throw new BadClassFile("bad module-info name");
- }
-
- private boolean endsWith(byte[] buf, int start, int len, String suffix) {
- if (len <= suffix.length())
- return false;
- for (int i = 0; i < suffix.length(); i++) {
- if (buf[start + len - suffix.length() + i] != suffix.charAt(i))
- return false;
- }
- return true;
- }
-
- private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException {
- try {
- buf = ensureCapacity(buf, s.available());
- int r = s.read(buf);
- int bp = 0;
- while (r != -1) {
- bp += r;
- buf = ensureCapacity(buf, bp);
- r = s.read(buf, bp, buf.length - bp);
- }
- return buf;
- } finally {
- try {
- s.close();
- } catch (IOException e) {
- /* Ignore any errors, as this stream may have already
- * thrown a related exception which is the one that
- * should be reported.
- */
- }
- }
- }
-
- /*
- * ensureCapacity will increase the buffer as needed, taking note that
- * the new buffer will always be greater than the needed and never
- * exactly equal to the needed size or bp. If equal then the read (above)
- * will infinitely loop as buf.length - bp == 0.
- */
- private static byte[] ensureCapacity(byte[] buf, int needed) {
- if (buf.length <= needed) {
- byte[] old = buf;
- buf = new byte[Integer.highestOneBit(needed) << 1];
- System.arraycopy(old, 0, buf, 0, old.length);
- }
- return buf;
- }
-}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Oct 05 06:28:22 2016 -0700
@@ -2396,12 +2396,15 @@
} else {
c.flags_field = flags;
Name modInfoName = readModuleInfoName(nextChar());
- if (c.owner.name == null) {
- syms.enterModule((ModuleSymbol) c.owner, Convert.packagePart(modInfoName));
- } else {
- // TODO: validate name
+ currentModule = (ModuleSymbol) c.owner;
+ if (currentModule.name.append('.', names.module_info) != modInfoName) {
+ //strip trailing .module-info, if exists:
+ int modInfoStart = modInfoName.length() - names.module_info.length();
+ modInfoName = modInfoName.subName(modInfoStart, modInfoName.length()) == names.module_info &&
+ modInfoName.charAt(modInfoStart - 1) == '.' ?
+ modInfoName.subName(0, modInfoStart - 1) : modInfoName;
+ throw badClassFile("module.name.mismatch", modInfoName, currentModule.name);
}
- currentModule = (ModuleSymbol) c.owner;
}
// class attributes must be read before class
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ModuleNameReader.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1999, 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 com.sun.tools.javac.jvm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.jvm.ClassFile;
+
+import static com.sun.tools.javac.jvm.ClassFile.*;
+
+
+/**
+ * Stripped down ClassReader, just sufficient to read module names from module-info.class files
+ * while analyzing the module path.
+ *
+ * <p>
+ * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
+ * your own risk. This code and its internal interfaces are subject to change or deletion without
+ * notice.</b>
+ */
+public class ModuleNameReader {
+ public static class BadClassFile extends Exception {
+ private static final long serialVersionUID = 0;
+ BadClassFile(String msg) {
+ super(msg);
+ }
+ }
+
+ private static final int INITIAL_BUFFER_SIZE = 0x0fff0;
+
+ /** The buffer containing the currently read class file.
+ */
+ private byte[] buf = new byte[INITIAL_BUFFER_SIZE];
+
+ /** The current input pointer.
+ */
+ private int bp;
+
+ /** For every constant pool entry, an index into buf where the
+ * defining section of the entry is found.
+ */
+ private int[] poolIdx;
+
+ public ModuleNameReader() {
+ }
+
+ public String readModuleName(Path p) throws IOException, BadClassFile {
+ try (InputStream in = Files.newInputStream(p)) {
+ return readModuleName(in);
+ }
+ }
+
+ public String readModuleName(JavaFileObject jfo) throws IOException, BadClassFile {
+ try (InputStream in = jfo.openInputStream()) {
+ return readModuleName(in);
+ }
+ }
+
+ public String readModuleName(InputStream in) throws IOException, BadClassFile {
+ bp = 0;
+ buf = readInputStream(buf, in);
+
+ int magic = nextInt();
+ if (magic != JAVA_MAGIC)
+ throw new BadClassFile("illegal.start.of.class.file");
+
+ int minorVersion = nextChar();
+ int majorVersion = nextChar();
+
+ indexPool();
+
+ int accessflags = nextChar();
+ return readModuleInfoName(nextChar());
+ }
+
+ /** Extract a character at position bp from buf.
+ */
+ char getChar(int bp) {
+ return
+ (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF));
+ }
+
+ /** Read a character.
+ */
+ char nextChar() {
+ return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
+ }
+
+ /** Read an integer.
+ */
+ int nextInt() {
+ return
+ ((buf[bp++] & 0xFF) << 24) +
+ ((buf[bp++] & 0xFF) << 16) +
+ ((buf[bp++] & 0xFF) << 8) +
+ (buf[bp++] & 0xFF);
+ }
+
+ /** Index all constant pool entries, writing their start addresses into
+ * poolIdx.
+ */
+ void indexPool() throws BadClassFile {
+ poolIdx = new int[nextChar()];
+ int i = 1;
+ while (i < poolIdx.length) {
+ poolIdx[i++] = bp;
+ byte tag = buf[bp++];
+ switch (tag) {
+ case CONSTANT_Utf8: case CONSTANT_Unicode: {
+ int len = nextChar();
+ bp = bp + len;
+ break;
+ }
+ case CONSTANT_Class:
+ case CONSTANT_String:
+ case CONSTANT_MethodType:
+ bp = bp + 2;
+ break;
+ case CONSTANT_MethodHandle:
+ bp = bp + 3;
+ break;
+ case CONSTANT_Fieldref:
+ case CONSTANT_Methodref:
+ case CONSTANT_InterfaceMethodref:
+ case CONSTANT_NameandType:
+ case CONSTANT_Integer:
+ case CONSTANT_Float:
+ case CONSTANT_InvokeDynamic:
+ bp = bp + 4;
+ break;
+ case CONSTANT_Long:
+ case CONSTANT_Double:
+ bp = bp + 8;
+ i++;
+ break;
+ default:
+ throw new BadClassFile("malformed constant pool");
+ }
+ }
+ }
+
+ /** Read the class name of a module-info.class file.
+ * The name is stored in a CONSTANT_Class entry, where the
+ * class name is of the form module-name/module-info.
+ */
+ String readModuleInfoName(int i) throws BadClassFile {
+ int classIndex = poolIdx[i];
+ if (buf[classIndex] == CONSTANT_Class) {
+ int utf8Index = poolIdx[getChar(classIndex + 1)];
+ if (buf[utf8Index] == CONSTANT_Utf8) {
+ int len = getChar(utf8Index + 1);
+ int start = utf8Index + 3;
+ String suffix = "/module-info";
+ if (endsWith(buf, start, len, suffix))
+ return new String(ClassFile.internalize(buf, start, len - suffix.length()));
+ }
+ }
+ throw new BadClassFile("bad module-info name");
+ }
+
+ private boolean endsWith(byte[] buf, int start, int len, String suffix) {
+ if (len <= suffix.length())
+ return false;
+ for (int i = 0; i < suffix.length(); i++) {
+ if (buf[start + len - suffix.length() + i] != suffix.charAt(i))
+ return false;
+ }
+ return true;
+ }
+
+ private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException {
+ try {
+ buf = ensureCapacity(buf, s.available());
+ int r = s.read(buf);
+ int bp = 0;
+ while (r != -1) {
+ bp += r;
+ buf = ensureCapacity(buf, bp);
+ r = s.read(buf, bp, buf.length - bp);
+ }
+ return buf;
+ } finally {
+ try {
+ s.close();
+ } catch (IOException e) {
+ /* Ignore any errors, as this stream may have already
+ * thrown a related exception which is the one that
+ * should be reported.
+ */
+ }
+ }
+ }
+
+ /*
+ * ensureCapacity will increase the buffer as needed, taking note that
+ * the new buffer will always be greater than the needed and never
+ * exactly equal to the needed size or bp. If equal then the read (above)
+ * will infinitely loop as buf.length - bp == 0.
+ */
+ private static byte[] ensureCapacity(byte[] buf, int needed) {
+ if (buf.length <= needed) {
+ byte[] old = buf;
+ buf = new byte[Integer.highestOneBit(needed) << 1];
+ System.arraycopy(old, 0, buf, 0, old.length);
+ }
+ return buf;
+ }
+}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Wed Oct 05 06:28:22 2016 -0700
@@ -37,6 +37,7 @@
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.Set;
+import java.util.function.Function;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
@@ -67,7 +68,9 @@
import com.sun.tools.javac.tree.JCTree.JCLambda;
import com.sun.tools.javac.tree.JCTree.JCMemberReference;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.JCDiagnostic.Factory;
@@ -341,6 +344,13 @@
}
};
+ protected final ModuleFinder.ModuleInfoSourceFileCompleter moduleInfoSourceFileCompleter =
+ fo -> (ModuleSymbol) readSourceFile(parseImplicitFile(fo), null, tl -> {
+ return tl.defs.nonEmpty() && tl.defs.head.hasTag(Tag.MODULEDEF) ?
+ ((JCModuleDecl) tl.defs.head).sym.module_info :
+ syms.defineClass(names.module_info, syms.errModule);
+ }).owner;
+
/**
* Command line options.
*/
@@ -411,6 +421,7 @@
diags = Factory.instance(context);
finder.sourceCompleter = sourceCompleter;
+ moduleFinder.sourceFileCompleter = moduleInfoSourceFileCompleter;
options = Options.instance(context);
@@ -779,6 +790,19 @@
readSourceFile(null, c);
}
+ private JCTree.JCCompilationUnit parseImplicitFile(JavaFileObject filename) {
+ JavaFileObject prev = log.useSource(filename);
+ try {
+ JCTree.JCCompilationUnit t = parse(filename, filename.getCharContent(false));
+ return t;
+ } catch (IOException e) {
+ log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
+ return make.TopLevel(List.<JCTree>nil());
+ } finally {
+ log.useSource(prev);
+ }
+ }
+
/** Compile a ClassSymbol from source, optionally using the given compilation unit as
* the source tree.
* @param tree the compilation unit in which the given ClassSymbol resides,
@@ -789,20 +813,20 @@
if (completionFailureName == c.fullname) {
throw new CompletionFailure(c, "user-selected completion failure by class name");
}
- JavaFileObject filename = c.classfile;
- JavaFileObject prev = log.useSource(filename);
if (tree == null) {
- try {
- tree = parse(filename, filename.getCharContent(false));
- } catch (IOException e) {
- log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
- tree = make.TopLevel(List.<JCTree>nil());
- } finally {
- log.useSource(prev);
- }
+ tree = parseImplicitFile(c.classfile);
}
+ readSourceFile(tree, c, cut -> c);
+ }
+
+ private ClassSymbol readSourceFile(JCCompilationUnit tree,
+ ClassSymbol expectedSymbol,
+ Function<JCCompilationUnit, ClassSymbol> symbolGetter)
+ throws CompletionFailure {
+ Assert.checkNonNull(tree);
+
if (!taskListener.isEmpty()) {
TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
taskListener.started(e);
@@ -814,18 +838,20 @@
// Note that if module resolution failed, we may not even
// have enough modules available to access java.lang, and
// so risk getting FatalError("no.java.lang") from MemberEnter.
- if (!modules.enter(List.of(tree), c)) {
- throw new CompletionFailure(c, diags.fragment("cant.resolve.modules"));
+ if (!modules.enter(List.of(tree), expectedSymbol)) {
+ throw new CompletionFailure(symbolGetter.apply(tree),
+ diags.fragment("cant.resolve.modules"));
}
- enter.complete(List.of(tree), c);
+ enter.complete(List.of(tree), expectedSymbol);
if (!taskListener.isEmpty()) {
TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
taskListener.finished(e);
}
- if (enter.getEnv(c) == null) {
+ ClassSymbol sym = symbolGetter.apply(tree);
+ if (sym == null || enter.getEnv(sym) == null) {
boolean isPkgInfo =
tree.sourcefile.isNameCompatible("package-info",
JavaFileObject.Kind.SOURCE);
@@ -836,24 +862,26 @@
if (enter.getEnv(tree.modle) == null) {
JCDiagnostic diag =
diagFactory.fragment("file.does.not.contain.module");
- throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+ throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
}
} else if (isPkgInfo) {
if (enter.getEnv(tree.packge) == null) {
JCDiagnostic diag =
diagFactory.fragment("file.does.not.contain.package",
- c.location());
- throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+ sym.location());
+ throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
}
} else {
JCDiagnostic diag =
diagFactory.fragment("file.doesnt.contain.class",
- c.getQualifiedName());
- throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+ sym.getQualifiedName());
+ throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
}
}
implicitSourceFilesRead = true;
+
+ return sym;
}
/** Track when the JavaCompiler has been used to compile something. */
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Wed Oct 05 06:28:22 2016 -0700
@@ -425,8 +425,7 @@
J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, ArgKind.ADJACENT) {
@Override
public boolean process(OptionHelper helper, String option) {
- throw new AssertionError
- ("the -J flag should be caught by the launcher.");
+ throw new AssertionError("the -J flag should be caught by the launcher.");
}
},
@@ -691,7 +690,7 @@
* This option takes an argument.
* If the name of option ends with ':' or '=', the argument must be provided directly
* after that separator.
- * Otherwise, if may appear after an '=' or in the following argument position.
+ * Otherwise, it may appear after an '=' or in the following argument position.
*/
REQUIRED,
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Wed Oct 05 06:28:22 2016 -0700
@@ -1336,8 +1336,7 @@
errorStatus = errorStatus || (compiler.errorCount() > 0);
- if (!errorStatus)
- round.finalCompiler();
+ round.finalCompiler();
if (newSourceFiles.size() > 0)
roots = roots.appendList(compiler.parseFiles(newSourceFiles));
@@ -1350,10 +1349,8 @@
if (!taskListener.isEmpty())
taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
- if (errorStatus) {
- if (compiler.errorCount() == 0)
- compiler.log.nerrors++;
- return true;
+ if (errorStatus && compiler.errorCount() == 0) {
+ compiler.log.nerrors++;
}
compiler.enterTreesIfNeeded(roots);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Oct 05 06:28:22 2016 -0700
@@ -2770,6 +2770,10 @@
compiler.err.module.name.mismatch=\
module name {0} does not match expected name {1}
+# 0: name, 1: name
+compiler.misc.module.name.mismatch=\
+ module name {0} does not match expected name {1}
+
compiler.err.module.decl.sb.in.module-info.java=\
module declarations should be in a file named module-info.java
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTool.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTool.java Wed Oct 05 06:28:22 2016 -0700
@@ -165,9 +165,11 @@
public int isSupportedOption(String option) {
if (option == null)
throw new NullPointerException();
- for (ToolOption o: ToolOption.values()) {
- if (o.opt.equals(option))
- return o.hasArg ? 1 : 0;
+ for (ToolOption o : ToolOption.values()) {
+ for (String name : o.names) {
+ if (name.equals(option))
+ return o.hasArg ? 1 : 0;
+ }
}
return -1;
}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java Wed Oct 05 06:28:22 2016 -0700
@@ -595,8 +595,9 @@
@Override
public Set<Doclet.Option> getSupportedOptions() {
+ Resources resources = getResources();
Doclet.Option[] options = {
- new Option(this, "-bottom", 1) {
+ new Option(resources, "-bottom", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -604,7 +605,7 @@
return true;
}
},
- new Option(this, "-charset", 1) {
+ new Option(resources, "-charset", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -612,7 +613,7 @@
return true;
}
},
- new Option(this, "-doctitle", 1) {
+ new Option(resources, "-doctitle", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -620,7 +621,7 @@
return true;
}
},
- new Option(this, "-footer", 1) {
+ new Option(resources, "-footer", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -628,7 +629,7 @@
return true;
}
},
- new Option(this, "-header", 1) {
+ new Option(resources, "-header", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -636,7 +637,7 @@
return true;
}
},
- new Option(this, "-helpfile", 1) {
+ new Option(resources, "-helpfile", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -644,7 +645,7 @@
return true;
}
},
- new Option(this, "-html4") {
+ new Option(resources, "-html4") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -652,7 +653,7 @@
return true;
}
},
- new Option(this, "-html5") {
+ new Option(resources, "-html5") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -660,7 +661,7 @@
return true;
}
},
- new Option(this, "-nohelp") {
+ new Option(resources, "-nohelp") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -668,7 +669,7 @@
return true;
}
},
- new Option(this, "-nodeprecatedlist") {
+ new Option(resources, "-nodeprecatedlist") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -676,7 +677,7 @@
return true;
}
},
- new Option(this, "-noindex") {
+ new Option(resources, "-noindex") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -684,7 +685,7 @@
return true;
}
},
- new Option(this, "-nonavbar") {
+ new Option(resources, "-nonavbar") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -692,7 +693,7 @@
return true;
}
},
- new Hidden(this, "-nooverview") {
+ new Hidden(resources, "-nooverview") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -700,7 +701,7 @@
return true;
}
},
- new Option(this, "-notree") {
+ new Option(resources, "-notree") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -708,7 +709,7 @@
return true;
}
},
- new Option(this, "-overview", 1) {
+ new Option(resources, "-overview", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -716,7 +717,7 @@
return true;
}
},
- new Option(this, "--frames") {
+ new Option(resources, "--frames") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -724,7 +725,7 @@
return true;
}
},
- new Option(this, "--no-frames") {
+ new Option(resources, "--no-frames") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -732,7 +733,7 @@
return true;
}
},
- new Hidden(this, "-packagesheader", 1) {
+ new Hidden(resources, "-packagesheader", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -740,7 +741,7 @@
return true;
}
},
- new Option(this, "-splitindex") {
+ new Option(resources, "-splitindex") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -748,7 +749,7 @@
return true;
}
},
- new Option(this, "-stylesheetfile", 1) {
+ new Option(resources, "-stylesheetfile", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -756,7 +757,7 @@
return true;
}
},
- new Option(this, "-top", 1) {
+ new Option(resources, "-top", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -764,7 +765,7 @@
return true;
}
},
- new Option(this, "-use") {
+ new Option(resources, "-use") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -772,7 +773,7 @@
return true;
}
},
- new Option(this, "-windowtitle", 1) {
+ new Option(resources, "-windowtitle", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -780,7 +781,7 @@
return true;
}
},
- new XOption(this, "-Xdoclint") {
+ new XOption(resources, "-Xdoclint") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -788,7 +789,7 @@
return true;
}
},
- new XOption(this, "-Xdocrootparent", 1) {
+ new XOption(resources, "-Xdocrootparent", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -796,7 +797,7 @@
return true;
}
},
- new XOption(this, "doclet.xusage.xdoclint-extended.", "-Xdoclint:", 0) {
+ new XOption(resources, "doclet.usage.xdoclint-extended", "-Xdoclint:", 0) {
@Override
public boolean matches(String option) {
return option.toLowerCase().startsWith(getName().toLowerCase());
@@ -809,7 +810,7 @@
return true;
}
},
- new XOption(this, "doclet.xusage.xdoclint-package.", "-Xdoclint/package:", 0) {
+ new XOption(resources, "doclet.usage.xdoclint-package", "-Xdoclint/package:", 0) {
@Override
public boolean matches(String option) {
return option.toLowerCase().startsWith(getName().toLowerCase());
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Wed Oct 05 06:28:22 2016 -0700
@@ -191,144 +191,196 @@
doclet.Same_package_name_used=Package name format used twice: {0}
# option specifiers
-doclet.usage.d.parameters=<directory>
-doclet.usage.d.description=Destination directory for output files
+doclet.usage.d.parameters=\
+ <directory>
+doclet.usage.d.description=\
+ Destination directory for output files
-doclet.usage.use.description=Create class and package usage pages
-
-doclet.usage.version.description=Include @version paragraphs
+doclet.usage.use.description=\
+ Create class and package usage pages
-doclet.usage.author.description=Include @author paragraphs
+doclet.usage.version.description=\
+ Include @version paragraphs
-doclet.usage.docfilessubdirs.description=Recursively copy doc-file subdirectories
+doclet.usage.author.description=\
+ Include @author paragraphs
-doclet.usage.splitindex.description=Split index into one file per letter
+doclet.usage.docfilessubdirs.description=\
+ Recursively copy doc-file subdirectories
-doclet.usage.overview.parameters=<file>
-doclet.usage.overview.description=Read overview documentation from HTML file
+doclet.usage.splitindex.description=\
+ Split index into one file per letter
+doclet.usage.overview.parameters=\
+ <file>
+doclet.usage.overview.description=\
+ Read overview documentation from HTML file
-doclet.usage.windowtitle.parameters=<text>
-doclet.usage.windowtitle.description=Browser window title for the documentation
-
-doclet.usage.doctitle.parameters=<html-code>
-doclet.usage.doctitle.description=Include title for the overview page
+doclet.usage.windowtitle.parameters=\
+ <text>
+doclet.usage.windowtitle.description=\
+ Browser window title for the documentation
-doclet.usage.header.parameters=<html-code>
-doclet.usage.header.description=Include header text for each page
-
-doclet.usage.html4.description=Generate HTML 4.01 output
+doclet.usage.doctitle.parameters=\
+ <html-code>
+doclet.usage.doctitle.description=\
+ Include title for the overview page
-doclet.usage.html5.description=Generate HTML 5 output
+doclet.usage.header.parameters=\
+ <html-code>
+doclet.usage.header.description=\
+ Include header text for each page
-doclet.usage.footer.parameters=<html-code>
-doclet.usage.footer.description=Include footer text for each page
+doclet.usage.html4.description=\
+ Generate HTML 4.01 output
+
+doclet.usage.html5.description=\
+ Generate HTML 5 output
-doclet.usage.top.parameters=<html-code>
-doclet.usage.top.description=Include top text for each page
+doclet.usage.footer.parameters=\
+ <html-code>
+doclet.usage.footer.description=\
+ Include footer text for each page
-doclet.usage.bottom.parameters=<html-code>
-doclet.usage.bottom.description=Include bottom text for each page
-
-doclet.usage.link.parameters=<url>
-doclet.usage.link.description=Create links to javadoc output at <url>
+doclet.usage.top.parameters=\
+ <html-code>
+doclet.usage.top.description=\
+ Include top text for each page
-doclet.usage.linkoffline.parameters=<url1> <url2>
-doclet.usage.linkoffline.description=Link to docs at <url1> using package list\n\
-\ at <url2>
+doclet.usage.bottom.parameters=\
+ <html-code>
+doclet.usage.bottom.description=\
+ Include bottom text for each page
-doclet.usage.excludedocfilessubdir.parameters=<name>:..
-doclet.usage.excludedocfilessubdir.description=\n\
-\ Exclude any doc-files subdirectories with\n\
-\ given name
+doclet.usage.link.parameters=\
+ <url>
+doclet.usage.link.description=\
+ Create links to javadoc output at <url>
-doclet.usage.group.parameters=<name> <p1>:<p2>..
-doclet.usage.group.description=Group specified packages together\n\
-\ in overview page
+doclet.usage.linkoffline.parameters=\
+ <url1> <url2>
+doclet.usage.linkoffline.description=\
+ Link to docs at <url1> using package list at <url2>
-doclet.usage.nocomment.description=Suppress description and tags, generate\n\
-\ only declarations
-
-doclet.usage.nodeprecated.description=Do not include @deprecated information
+doclet.usage.excludedocfilessubdir.parameters=\
+ <name>:..
+doclet.usage.excludedocfilessubdir.description=\
+ Exclude any doc-files subdirectories with given name
-doclet.usage.noqualifier.parameters=<name1>:<name2>:..
-doclet.usage.noqualifier.description=Exclude the list of qualifiers from the output
+doclet.usage.group.parameters=\
+ <name> <p1>:<p2>..
+doclet.usage.group.description=\
+ Group specified packages together in overview page
-doclet.usage.nosince.description=Do not include @since information
+doclet.usage.nocomment.description=\
+ Suppress description and tags, generate only declarations
-doclet.usage.notimestamp.description=Do not include hidden time stamp
+doclet.usage.nodeprecated.description=\
+ Do not include @deprecated information
-doclet.usage.nodeprecatedlist.description=Do not generate deprecated list
+doclet.usage.noqualifier.parameters=\
+ <name1>:<name2>:..
+doclet.usage.noqualifier.description=\
+ Exclude the list of qualifiers from the output
-doclet.usage.notree.description=Do not generate class hierarchy
+doclet.usage.nosince.description=\
+ Do not include @since information
-doclet.usage.noindex.description=Do not generate index
+doclet.usage.notimestamp.description=\
+ Do not include hidden time stamp
-doclet.usage.nohelp.description=Do not generate help link
+doclet.usage.nodeprecatedlist.description=\
+ Do not generate deprecated list
-doclet.usage.nonavbar.description=Do not generate navigation bar
+doclet.usage.notree.description=\
+ Do not generate class hierarchy
-doclet.usage.nooverview.description=Do not generate overview pages
+doclet.usage.noindex.description=\
+ Do not generate index
-doclet.usage.serialwarn.description=Generate warning about @serial tag
+doclet.usage.nohelp.description=\
+ Do not generate help link
+
+doclet.usage.nonavbar.description=\
+ Do not generate navigation bar
-doclet.usage.tag.parameters=<name>:<locations>:<header>
-doclet.usage.tag.description=\n\
-\ Specify single argument custom tags
+doclet.usage.nooverview.description=\
+ Do not generate overview pages
-doclet.usage.taglet.description=The fully qualified name of Taglet to register
+doclet.usage.serialwarn.description=\
+ Generate warning about @serial tag
-doclet.usage.tagletpath.description=The path to Taglets
+doclet.usage.tag.parameters=\
+ <name>:<locations>:<header>
+doclet.usage.tag.description=\
+ Specify single argument custom tags
-doclet.usage.charset.parameters=<charset>
-doclet.usage.charset.description=Charset for cross-platform viewing of\n\
-\ generated documentation
+doclet.usage.taglet.description=\
+ The fully qualified name of Taglet to register
-doclet.usage.helpfile.parameters=<file>
-doclet.usage.helpfile.description=Include file that help link links to
+doclet.usage.tagletpath.description=\
+ The path to Taglets
-doclet.usage.linksource.description=Generate source in HTML
+doclet.usage.charset.parameters=\
+ <charset>
+doclet.usage.charset.description=\
+ Charset for cross-platform viewing of generated documentation
-doclet.usage.sourcetab.parameters=<tab length>
-doclet.usage.sourcetab.description=Specify the number of spaces each tab\n\
-\ takes up in the source
+doclet.usage.helpfile.parameters=\
+ <file>
+doclet.usage.helpfile.description=\
+ Include file that help link links to
-doclet.usage.keywords.description=Include HTML meta tags with package,\n\
-\ class and member info
+doclet.usage.linksource.description=\
+ Generate source in HTML
-doclet.usage.stylesheetfile.parameters=<path>
-doclet.usage.stylesheetfile.description=File to change style of the generated\n\
-\ documentation
+doclet.usage.sourcetab.parameters=\
+ <tab length>
+doclet.usage.sourcetab.description=\
+ Specify the number of spaces each tab takes up in the source
-doclet.usage.docencoding.parameters=<name>
-doclet.usage.docencoding.description=Specify the character encoding for the output
-
-doclet.usage.frames.description=Enable the use of frames in the generated output (default)
+doclet.usage.keywords.description=\
+ Include HTML meta tags with package, class and member info
-doclet.usage.no-frames.description=Disable the use of frames in the generated output
+doclet.usage.stylesheetfile.parameters=\
+ <path>
+doclet.usage.stylesheetfile.description=\
+ File to change style of the generated documentation
-doclet.xusage.xdocrootparent.parameters=<url>
-doclet.xusage.xdocrootparent.description=Replaces all @docRoot followed by /..\n\
-\ in doc comments with <url>
+doclet.usage.docencoding.parameters=\
+ <name>
+doclet.usage.docencoding.description=\
+ Specify the character encoding for the output
-doclet.xusage.xdoclint.description=Enable recommended checks for problems in\n\
-\ javadoc comments
+doclet.usage.frames.description=\
+ Enable the use of frames in the generated output (default)
+
+doclet.usage.no-frames.description=\
+ Disable the use of frames in the generated output
-doclet.xusage.xdoclint-extended.parameters=(all|none|[-]<group>)
-# L10N: do not localize these words: all none accessibility html missing reference syntax
-doclet.xusage.xdoclint-extended.description=Enable or disable specific checks\n\
-\ for problems in javadoc comments, where \n\
-\ <group> is one of accessibility, html,\n\
-\ missing, reference, or syntax.\n
+doclet.usage.xdocrootparent.parameters=\
+ <url>
+doclet.usage.xdocrootparent.description=\
+ Replaces all @docRoot followed by /.. in doc comments with\n\
+ <url>
+
+doclet.usage.xdoclint.description=\
+ Enable recommended checks for problems in javadoc comments
-doclet.xusage.xdoclint-package.parameters=([-]<packages>)
-doclet.xusage.xdoclint-package.description=\n\
-\ Enable or disable checks in specific\n\
-\ packages. <packages> is a comma separated\n\
-\ list of package specifiers. Package\n\
-\ specifier is either a qualified name of a\n\
-\ package or a package name prefix followed\n\
-\ by .*, which expands to all sub-packages\n\
-\ of the given package. Prefix the package\n\
-\ specifier with - to disable checks for\n\
-\ the specified packages.\n
+doclet.usage.xdoclint-extended.parameters=\
+ (all|none|[-]<group>)
+# L10N: do not localize these words: all none accessibility html missing reference syntax
+doclet.usage.xdoclint-extended.description=\
+ Enable or disable specific checks for problems in javadoc\n\
+ comments, where <group> is one of accessibility, html,\n\
+ missing, reference, or syntax.
+
+doclet.usage.xdoclint-package.parameters=\
+ ([-]<packages>)
+doclet.usage.xdoclint-package.description=\
+ Enable or disable checks in specific packages. <packages> is a\n\
+ comma separated list of package specifiers. A package\n\
+ specifier is either a qualified name of a package or a package\n\
+ name prefix followed by .*, which expands to all sub-packages\n\
+ of the given package. Prefix the package specifier with - to\n\
+ disable checks for the specified packages.
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java Wed Oct 05 06:28:22 2016 -0700
@@ -404,8 +404,9 @@
}
public Set<Doclet.Option> getSupportedOptions() {
+ Resources resources = getResources();
Doclet.Option[] options = {
- new Option(this, "-author") {
+ new Option(resources, "-author") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -413,7 +414,7 @@
return true;
}
},
- new Option(this, "-d", 1) {
+ new Option(resources, "-d", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -421,7 +422,7 @@
return true;
}
},
- new Option(this, "-docencoding", 1) {
+ new Option(resources, "-docencoding", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -429,7 +430,7 @@
return true;
}
},
- new Option(this, "-docfilessubdirs") {
+ new Option(resources, "-docfilessubdirs") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -437,7 +438,7 @@
return true;
}
},
- new Hidden(this, "-encoding", 1) {
+ new Hidden(resources, "-encoding", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -445,7 +446,7 @@
return true;
}
},
- new Option(this, "-excludedocfilessubdir", 1) {
+ new Option(resources, "-excludedocfilessubdir", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -453,7 +454,7 @@
return true;
}
},
- new Option(this, "-group", 2) {
+ new Option(resources, "-group", 2) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -461,7 +462,7 @@
return true;
}
},
- new Hidden(this, "-javafx") {
+ new Hidden(resources, "-javafx") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -469,7 +470,7 @@
return true;
}
},
- new Option(this, "-keywords") {
+ new Option(resources, "-keywords") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -477,7 +478,7 @@
return true;
}
},
- new Option(this, "-link", 1) {
+ new Option(resources, "-link", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -486,7 +487,7 @@
return true;
}
},
- new Option(this, "-linksource") {
+ new Option(resources, "-linksource") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -494,7 +495,7 @@
return true;
}
},
- new Option(this, "-linkoffline", 2) {
+ new Option(resources, "-linkoffline", 2) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -503,7 +504,7 @@
return true;
}
},
- new Option(this, "-nocomment") {
+ new Option(resources, "-nocomment") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -511,7 +512,7 @@
return true;
}
},
- new Option(this, "-nodeprecated") {
+ new Option(resources, "-nodeprecated") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -519,7 +520,7 @@
return true;
}
},
- new Option(this, "-nosince") {
+ new Option(resources, "-nosince") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -527,7 +528,7 @@
return true;
}
},
- new Option(this, "-notimestamp") {
+ new Option(resources, "-notimestamp") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -535,7 +536,7 @@
return true;
}
},
- new Option(this, "-noqualifier", 1) {
+ new Option(resources, "-noqualifier", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -543,7 +544,7 @@
return true;
}
},
- new Hidden(this, "-quiet") {
+ new Hidden(resources, "-quiet") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -551,7 +552,7 @@
return true;
}
},
- new Option(this, "-serialwarn") {
+ new Option(resources, "-serialwarn") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -559,7 +560,7 @@
return true;
}
},
- new Option(this, "-sourcetab", 1) {
+ new Option(resources, "-sourcetab", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -578,7 +579,7 @@
return true;
}
},
- new Option(this, "-tag", 1) {
+ new Option(resources, "-tag", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -589,7 +590,7 @@
return true;
}
},
- new Option(this, "-taglet", 1) {
+ new Option(resources, "-taglet", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -600,7 +601,7 @@
return true;
}
},
- new Option(this, "-tagletpath", 1) {
+ new Option(resources, "-tagletpath", 1) {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -608,7 +609,7 @@
return true;
}
},
- new Option(this, "-version") {
+ new Option(resources, "-version") {
@Override
public boolean process(String opt, ListIterator<String> args) {
optionsProcessed.add(this);
@@ -1057,37 +1058,30 @@
private final String description;
private final int argCount;
- protected final Configuration c;
+ protected Option(Resources resources, String name, int argCount) {
+ this(resources, "doclet.usage." + name.toLowerCase().replaceAll("^-*", ""), name, argCount);
+ }
- protected Option(Configuration config, String keyName, String name, int argCount) {
- c = config;
+ protected Option(Resources resources, String keyBase, String name, int argCount) {
this.name = name;
- String desc = getOptionsMessage(keyName + "description");
+ String desc = getOptionsMessage(resources, keyBase + ".description");
if (desc.isEmpty()) {
this.description = "<MISSING KEY>";
this.parameters = "<MISSING KEY>";
} else {
this.description = desc;
- this.parameters = getOptionsMessage(keyName + "parameters");
+ this.parameters = getOptionsMessage(resources, keyBase + ".parameters");
}
this.argCount = argCount;
}
- protected Option(String prefix, Configuration config, String name, int argCount) {
- this(config, prefix + name.toLowerCase().replaceAll("^-*", "") + ".", name, argCount);
- }
-
- protected Option(Configuration config, String name, int argCount) {
- this("doclet.usage.", config, name, argCount);
+ protected Option(Resources resources, String name) {
+ this(resources, name, 0);
}
- protected Option(Configuration config, String name) {
- this(config, name, 0);
- }
-
- private String getOptionsMessage(String key) {
+ private String getOptionsMessage(Resources resources, String key) {
try {
- return c.getResources().getText(key);
+ return resources.getText(key);
} catch (MissingResourceException ignore) {
return "";
}
@@ -1113,19 +1107,9 @@
return parameters;
}
- /**
- * Maintains the formatting for javadoc -help. Note the space
- * alignment.
- */
@Override
public String toString() {
- String opt = name + (name.endsWith(":") ? "" : " ") + parameters;
- StringBuffer sb = new StringBuffer(" ").append(opt).append(" ");
- for (int i = opt.length(); i < 32; i++) {
- sb.append(" ");
- }
- sb.append(description);
- return sb.toString();
+ return name;
}
@Override
@@ -1135,7 +1119,14 @@
@Override
public boolean matches(String option) {
- return name.toLowerCase().equals(option.toLowerCase());
+ boolean matchCase = name.startsWith("--");
+ if (option.startsWith("--") && option.contains("=")) {
+ return name.equals(option.substring(option.indexOf("=") + 1));
+ } else if (matchCase) {
+ return name.equals(option);
+ } else {
+ return name.toLowerCase().equals(option.toLowerCase());
+ }
}
@Override
@@ -1146,16 +1137,16 @@
public abstract class XOption extends Option {
- public XOption(Configuration config, String keyname, String name, int argCount) {
- super(config, keyname, name, argCount);
+ public XOption(Resources resources, String prefix, String name, int argCount) {
+ super(resources, prefix, name, argCount);
}
- public XOption(Configuration config, String name, int argCount) {
- super("doclet.xusage.", config, name, argCount);
+ public XOption(Resources resources, String name, int argCount) {
+ super(resources, name, argCount);
}
- public XOption(Configuration config, String name) {
- this(config, name, 0);
+ public XOption(Resources resources, String name) {
+ this(resources, name, 0);
}
@Override
@@ -1166,12 +1157,12 @@
public abstract class Hidden extends Option {
- public Hidden(Configuration config, String name, int argCount) {
- super("doclet.xusage.", config, name, argCount);
+ public Hidden(Resources resources, String name, int argCount) {
+ super(resources, name, argCount);
}
- public Hidden(Configuration config, String name) {
- this(config, name, 0);
+ public Hidden(Resources resources, String name) {
+ this(resources, name, 0);
}
@Override
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java Wed Oct 05 06:28:22 2016 -0700
@@ -255,7 +255,7 @@
private void incrementErrorCount(String prefix, String msg) {
if (nerrors < MaxErrors) {
PrintWriter errWriter = getWriter(WriterKind.ERROR);
- errWriter.println(prefix + ": " + getText("javadoc.error") + " - " + msg);
+ printRawLines(errWriter, prefix + ": " + getText("javadoc.error") + " - " + msg);
errWriter.flush();
prompt();
nerrors++;
@@ -293,7 +293,7 @@
private void incrementWarningCount(String prefix, String msg) {
if (nwarnings < MaxWarnings) {
PrintWriter warnWriter = getWriter(WriterKind.WARNING);
- warnWriter.println(prefix + ": " + getText("javadoc.warning") + " - " + msg);
+ printRawLines(warnWriter, prefix + ": " + getText("javadoc.warning") + " - " + msg);
warnWriter.flush();
nwarnings++;
}
@@ -318,9 +318,9 @@
PrintWriter noticeWriter = getWriter(WriterKind.NOTICE);
if (path == null) {
- noticeWriter.println(msg);
+ printRawLines(noticeWriter, msg);
} else {
- noticeWriter.println(prefix + ": " + msg);
+ printRawLines(noticeWriter, prefix + ": " + msg);
}
noticeWriter.flush();
}
@@ -334,9 +334,9 @@
PrintWriter noticeWriter = getWriter(WriterKind.NOTICE);
if (e == null) {
- noticeWriter.println(msg);
+ printRawLines(noticeWriter, msg);
} else {
- noticeWriter.println(pos + ": " + msg);
+ printRawLines(noticeWriter, pos + ": " + msg);
}
noticeWriter.flush();
}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java Wed Oct 05 06:28:22 2016 -0700
@@ -31,14 +31,18 @@
import java.io.PrintWriter;
import java.nio.file.Path;
import java.text.BreakIterator;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
@@ -61,6 +65,7 @@
import jdk.javadoc.doclet.Doclet;
import jdk.javadoc.doclet.Doclet.Option;
import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.internal.doclets.toolkit.Resources;
import static javax.tools.DocumentationTool.Location.*;
@@ -168,7 +173,10 @@
}
void usage(boolean exit) {
- usage("main.usage", "-help", "main.usage.foot", exit);
+ usage("main.usage", "-help", "main.usage.foot");
+
+ if (exit)
+ throw new Messager.ExitJavadoc();
}
@Override
@@ -177,30 +185,128 @@
}
void Xusage(boolean exit) {
- usage("main.Xusage", "-X", "main.Xusage.foot", exit);
- }
-
- private void usage(String main, String option, String foot, boolean exit) {
- messager.notice(main);
- // let doclet print usage information (does nothing on error)
- if (docletClass != null) {
- String name = doclet.getName();
- Set<Option> supportedOptions = doclet.getSupportedOptions();
- messager.notice("main.doclet.usage.header", name);
- Option.Kind myKind = option.equals("-X")
- ? Option.Kind.EXTENDED
- : Option.Kind.STANDARD;
- supportedOptions.stream()
- .filter(opt -> opt.getKind() == myKind)
- .forEach(opt -> messager.printNotice(opt.toString()));
- }
- if (foot != null)
- messager.notice(foot);
+ usage("main.Xusage", "-X", "main.Xusage.foot");
if (exit)
throw new Messager.ExitJavadoc();
}
+ private void usage(String header, String option, String footer) {
+ messager.notice(header);
+ showToolOptions(option.equals("-X") ? OptionKind.EXTENDED : OptionKind.STANDARD);
+
+ // let doclet print usage information
+ if (docletClass != null) {
+ String name = doclet.getName();
+ messager.notice("main.doclet.usage.header", name);
+ showDocletOptions(option.equals("-X") ? Option.Kind.EXTENDED : Option.Kind.STANDARD);
+ }
+
+ if (footer != null)
+ messager.notice(footer);
+ }
+
+ void showToolOptions(OptionKind kind) {
+ Comparator<ToolOption> comp = new Comparator<ToolOption>() {
+ final Collator collator = Collator.getInstance(Locale.US);
+ { collator.setStrength(Collator.PRIMARY); }
+
+ @Override
+ public int compare(ToolOption o1, ToolOption o2) {
+ return collator.compare(o1.primaryName, o2.primaryName);
+ }
+ };
+
+ Stream.of(ToolOption.values())
+ .filter(opt -> opt.kind == kind)
+ .sorted(comp)
+ .forEach(opt -> showToolOption(opt));
+ }
+
+ void showToolOption(ToolOption option) {
+ List<String> names = option.getNames();
+ String parameters;
+ if (option.hasArg || option.primaryName.endsWith(":")) {
+ String sep = (option == ToolOption.J) || option.primaryName.endsWith(":") ? "" : " ";
+ parameters = sep + option.getParameters(messager);
+ } else {
+ parameters = "";
+ }
+ String description = option.getDescription(messager);
+ showUsage(names, parameters, description);
+ }
+
+ void showDocletOptions(Option.Kind kind) {
+ Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() {
+ final Collator collator = Collator.getInstance(Locale.US);
+ { collator.setStrength(Collator.PRIMARY); }
+
+ @Override
+ public int compare(Doclet.Option o1, Doclet.Option o2) {
+ return collator.compare(o1.getName(), o2.getName());
+ }
+ };
+
+ doclet.getSupportedOptions().stream()
+ .filter(opt -> opt.getKind() == kind)
+ .sorted(comp)
+ .forEach(opt -> showDocletOption(opt));
+ }
+
+ void showDocletOption(Doclet.Option option) {
+ List<String> names = Arrays.asList(option.getName());
+ String parameters;
+ if (option.getArgumentCount() > 0 || option.getName().endsWith(":")) {
+ String sep = option.getName().endsWith(":") ? "" : " ";
+ parameters = sep + option.getParameters();
+ } else {
+ parameters = "";
+ }
+ String description = option.getDescription();
+ showUsage(names, parameters, description);
+ }
+
+ // The following constants are intended to format the output to
+ // be similar to that of the java launcher: i.e. "java -help".
+
+ /** The indent for the option synopsis. */
+ private static final String SMALL_INDENT = " ";
+ /** The automatic indent for the description. */
+ private static final String LARGE_INDENT = " ";
+ /** The space allowed for the synopsis, if the description is to be shown on the same line. */
+ private static final int DEFAULT_SYNOPSIS_WIDTH = 13;
+ /** The nominal maximum line length, when seeing if text will fit on a line. */
+ private static final int DEFAULT_MAX_LINE_LENGTH = 80;
+ /** The format for a single-line help entry. */
+ private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
+
+ void showUsage(List<String> names, String parameters, String description) {
+ String synopses = names.stream()
+ .map(s -> s + parameters)
+ .collect(Collectors.joining(", "));
+ // If option synopses and description fit on a single line of reasonable length,
+ // display using COMPACT_FORMAT
+ if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
+ && !description.contains("\n")
+ && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) {
+ messager.printNotice(String.format(COMPACT_FORMAT, synopses, description));
+ return;
+ }
+
+ // If option synopses fit on a single line of reasonable length, show that;
+ // otherwise, show 1 per line
+ if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
+ messager.printNotice(SMALL_INDENT + synopses);
+ } else {
+ for (String name: names) {
+ messager.printNotice(SMALL_INDENT + name + parameters);
+ }
+ }
+
+ // Finally, show the description
+ messager.printNotice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT));
+ }
+
/**
* Main program - external wrapper. In order to maintain backward
@@ -433,14 +539,37 @@
docletOptions = doclet.getSupportedOptions();
}
String arg = args.get(idx);
+ String argBase, argVal;
+ if (arg.startsWith("--") && arg.contains("=")) {
+ int sep = arg.indexOf("=");
+ argBase = arg.substring(0, sep);
+ argVal = arg.substring(sep + 1);
+ } else {
+ argBase = arg;
+ argVal = null;
+ }
for (Doclet.Option opt : docletOptions) {
- if (opt.matches(arg)) {
- if (args.size() - idx < opt.getArgumentCount()) {
- usageError("main.requires_argument", arg);
+ if (opt.matches(argBase)) {
+ if (argVal != null) {
+ switch (opt.getArgumentCount()) {
+ case 0:
+ usageError("main.unnecessary_arg_provided", argBase);
+ break;
+ case 1:
+ opt.process(arg, Arrays.asList(argVal).listIterator());
+ break;
+ default:
+ usageError("main.only_one_argument_with_equals", argBase);
+ break;
+ }
+ } else {
+ if (args.size() - idx -1 < opt.getArgumentCount()) {
+ usageError("main.requires_argument", arg);
+ }
+ opt.process(arg, args.listIterator(idx + 1));
+ idx += opt.getArgumentCount();
}
- opt.process(arg, args.listIterator(idx + 1));
- idx += opt.getArgumentCount();
return idx;
}
}
@@ -463,11 +592,11 @@
// Step 1: loop through the args, set locale early on, if found.
for (int i = 0 ; i < argv.size() ; i++) {
String arg = argv.get(i);
- if (arg.equals(ToolOption.LOCALE.opt)) {
+ if (arg.equals(ToolOption.LOCALE.primaryName)) {
checkOneArg(argv, i++);
String lname = argv.get(i);
locale = getLocale(lname);
- } else if (arg.equals(ToolOption.DOCLET.opt)) {
+ } else if (arg.equals(ToolOption.DOCLET.primaryName)) {
checkOneArg(argv, i++);
if (userDocletName != null) {
usageError("main.more_than_one_doclet_specified_0_and_1",
@@ -478,7 +607,7 @@
docletName, argv.get(i));
}
userDocletName = argv.get(i);
- } else if (arg.equals(ToolOption.DOCLETPATH.opt)) {
+ } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) {
checkOneArg(argv, i++);
if (userDocletPath == null) {
userDocletPath = argv.get(i);
@@ -599,8 +728,12 @@
handleDocletOptions(i, args, true);
if (o.hasArg) {
- checkOneArg(args, i++);
- o.process(this, args.get(i));
+ if (arg.startsWith("--") && arg.contains("=")) {
+ o.process(this, arg.substring(arg.indexOf('=') + 1));
+ } else {
+ checkOneArg(args, i++);
+ o.process(this, args.get(i));
+ }
} else if (o.hasSuffix) {
o.process(this, arg);
} else {
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java Wed Oct 05 06:28:22 2016 -0700
@@ -35,9 +35,12 @@
import javax.lang.model.element.ElementKind;
import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.main.Option.OptionKind;
import com.sun.tools.javac.main.OptionHelper;
import com.sun.tools.javac.util.Options;
+import static com.sun.tools.javac.main.Option.OptionKind.*;
+
/**
* javadoc tool options.
*
@@ -50,197 +53,169 @@
// ----- options for underlying compiler -----
- BOOTCLASSPATH("-bootclasspath", true) {
+ BOOTCLASSPATH("-bootclasspath", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.BOOT_CLASS_PATH, arg);
}
},
- CLASSPATH("-classpath", true) {
+ CLASS_PATH("--class-path -classpath -cp", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.CLASS_PATH, arg);
}
},
- CP("-cp", true) {
- @Override
- public void process(Helper helper, String arg) {
- helper.setFileManagerOpt(Option.CLASS_PATH, arg);
- }
- },
-
- CLASS_PATH("--class-path", true) {
- @Override
- public void process(Helper helper, String arg) {
- helper.setFileManagerOpt(Option.CLASS_PATH, arg);
- }
- },
-
- EXTDIRS("-extdirs", true) {
+ EXTDIRS("-extdirs", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.EXTDIRS, arg);
}
},
- SOURCEPATH("-sourcepath", true) {
+ SOURCE_PATH("--source-path -sourcepath", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.SOURCE_PATH, arg);
}
},
- SOURCE_PATH("--source-path", true) {
- @Override
- public void process(Helper helper, String arg) {
- helper.setFileManagerOpt(Option.SOURCE_PATH, arg);
- }
- },
-
- SYSCLASSPATH("-sysclasspath", true) {
+ SYSCLASSPATH("-sysclasspath", HIDDEN, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.BOOT_CLASS_PATH, arg);
}
},
- MODULE_SOURCE_PATH("--module-source-path", true) {
+ MODULE_SOURCE_PATH("--module-source-path", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.MODULE_SOURCE_PATH, arg);
}
},
- UPGRADE_MODULE_PATH("--upgrade-module-path", true) {
+ UPGRADE_MODULE_PATH("--upgrade-module-path", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.UPGRADE_MODULE_PATH, arg);
}
},
- SYSTEM("--system", true) {
+ SYSTEM("--system", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.SYSTEM, arg);
}
},
- MODULE_PATH("--module-path", true) {
- @Override
- public void process(Helper helper, String arg) {
- helper.setFileManagerOpt(Option.MODULE_PATH, arg);
- }
- },
-
- P("-p", true) {
+ MODULE_PATH("--module-path -p", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.MODULE_PATH, arg);
}
},
- ADD_MODULES("--add-modules", true) {
+ ADD_MODULES("--add-modules", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- Option.ADD_MODULES.process(helper.getOptionHelper(), opt, arg);
+ Option.ADD_MODULES.process(helper.getOptionHelper(), primaryName, arg);
}
},
- LIMIT_MODULES("--limit-modules", true) {
+ LIMIT_MODULES("--limit-modules", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- Option.LIMIT_MODULES.process(helper.getOptionHelper(), opt, arg);
+ Option.LIMIT_MODULES.process(helper.getOptionHelper(), primaryName, arg);
}
},
- MODULE("--module", true) {
+ MODULE("--module", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.addToList(this, ",", arg);
}
},
- ENCODING("-encoding", true) {
+ ENCODING("-encoding", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFileManagerOpt(Option.ENCODING, arg);
}
},
- RELEASE("--release", true) {
+ RELEASE("--release", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- Option.RELEASE.process(helper.getOptionHelper(), opt, arg);
+ Option.RELEASE.process(helper.getOptionHelper(), primaryName, arg);
}
},
- SOURCE("-source", true) {
+ SOURCE("-source", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- Option.SOURCE.process(helper.getOptionHelper(), opt, arg);
+ Option.SOURCE.process(helper.getOptionHelper(), primaryName, arg);
}
},
- XMAXERRS("-Xmaxerrs", true) {
+ XMAXERRS("-Xmaxerrs", EXTENDED, true) {
@Override
public void process(Helper helper, String arg) {
- Option.XMAXERRS.process(helper.getOptionHelper(), opt, arg);
+ Option.XMAXERRS.process(helper.getOptionHelper(), primaryName, arg);
}
},
- XMAXWARNS("-Xmaxwarns", true) {
+ XMAXWARNS("-Xmaxwarns", EXTENDED, true) {
@Override
public void process(Helper helper, String arg) {
- Option.XMAXWARNS.process(helper.getOptionHelper(), opt, arg);
+ Option.XMAXWARNS.process(helper.getOptionHelper(), primaryName, arg);
}
},
- ADD_READS("--add-reads", true) {
+ ADD_READS("--add-reads", EXTENDED, true) {
@Override
public void process(Helper helper, String arg) {
- Option.ADD_READS.process(helper.getOptionHelper(), opt, arg);
+ Option.ADD_READS.process(helper.getOptionHelper(), primaryName, arg);
}
},
- ADD_EXPORTS("--add-exports", true) {
+ ADD_EXPORTS("--add-exports", EXTENDED, true) {
@Override
public void process(Helper helper, String arg) {
- Option.ADD_EXPORTS.process(helper.getOptionHelper(), opt, arg);
+ Option.ADD_EXPORTS.process(helper.getOptionHelper(), primaryName, arg);
}
},
- XMODULE("-Xmodule:", false) {
+ XMODULE("-Xmodule:", EXTENDED, false) {
@Override
public void process(Helper helper, String arg) {
Option.XMODULE.process(helper.getOptionHelper(), arg);
}
},
- PATCH_MODULE("--patch-module", true) {
+ PATCH_MODULE("--patch-module", EXTENDED, true) {
@Override
public void process(Helper helper, String arg) {
- Option.PATCH_MODULE.process(helper.getOptionHelper(), opt, arg);
+ Option.PATCH_MODULE.process(helper.getOptionHelper(), primaryName, arg);
}
},
// ----- doclet options -----
- DOCLET("-doclet", true), // handled in setDocletInvoker
+ DOCLET("-doclet", STANDARD, true), // handled in setDocletInvoker
- DOCLETPATH("-docletpath", true), // handled in setDocletInvoker
+ DOCLETPATH("-docletpath", STANDARD, true), // handled in setDocletInvoker
// ----- selection options -----
- SUBPACKAGES("-subpackages", true) {
+ SUBPACKAGES("-subpackages", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.addToList(this, ":", arg);
}
},
- EXCLUDE("-exclude", true) {
+ EXCLUDE("-exclude", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.addToList(this, ":", arg);
@@ -249,72 +224,72 @@
// ----- filtering options -----
- PACKAGE("-package") {
+ PACKAGE("-package", STANDARD) {
@Override
public void process(Helper helper) {
helper.setSimpleFilter("package");
}
},
- PRIVATE("-private") {
+ PRIVATE("-private", STANDARD) {
@Override
public void process(Helper helper) {
helper.setSimpleFilter("private");
}
},
- PROTECTED("-protected") {
+ PROTECTED("-protected", STANDARD) {
@Override
public void process(Helper helper) {
helper.setSimpleFilter("protected");
}
},
- PUBLIC("-public") {
+ PUBLIC("-public", STANDARD) {
@Override
public void process(Helper helper) {
helper.setSimpleFilter("public");
}
},
- SHOW_MEMBERS("--show-members:") {
+ SHOW_MEMBERS("--show-members", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFilter(this, arg);
}
},
- SHOW_TYPES("--show-types:") {
+ SHOW_TYPES("--show-types", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.setFilter(this, arg);
}
},
- SHOW_PACKAGES("--show-packages:") {
+ SHOW_PACKAGES("--show-packages", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- helper.setShowPackageAccess(SHOW_PACKAGES, helper.getOptionArgumentValue(arg));
+ helper.setShowPackageAccess(SHOW_PACKAGES, arg);
}
},
- SHOW_MODULE_CONTENTS("--show-module-contents:") {
+ SHOW_MODULE_CONTENTS("--show-module-contents", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- helper.setShowModuleContents(SHOW_MODULE_CONTENTS, helper.getOptionArgumentValue(arg));
+ helper.setShowModuleContents(SHOW_MODULE_CONTENTS, arg);
}
},
- EXPAND_REQUIRES("--expand-requires:") {
+ EXPAND_REQUIRES("--expand-requires", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
- helper.setExpandRequires(EXPAND_REQUIRES, helper.getOptionArgumentValue(arg));
+ helper.setExpandRequires(EXPAND_REQUIRES, arg);
}
},
// ----- output control options -----
- PROMPT("-prompt") {
+ PROMPT("-prompt", HIDDEN) {
@Override
public void process(Helper helper) {
helper.compOpts.put("-prompt", "-prompt");
@@ -322,21 +297,21 @@
}
},
- QUIET("-quiet") {
+ QUIET("-quiet", STANDARD) {
@Override
public void process(Helper helper) {
helper.jdtoolOpts.put(QUIET, true);
}
},
- VERBOSE("-verbose") {
+ VERBOSE("-verbose", STANDARD) {
@Override
public void process(Helper helper) {
helper.compOpts.put("-verbose", "");
}
},
- XWERROR("-Xwerror") {
+ XWERROR("-Xwerror", HIDDEN) {
@Override
public void process(Helper helper) {
helper.rejectWarnings = true;
@@ -346,21 +321,21 @@
// ----- other options -----
- BREAKITERATOR("-breakiterator") {
+ BREAKITERATOR("-breakiterator", STANDARD) {
@Override
public void process(Helper helper) {
helper.breakiterator = true;
}
},
- LOCALE("-locale", true) {
+ LOCALE("-locale", STANDARD, true) {
@Override
public void process(Helper helper, String arg) {
helper.docLocale = arg;
}
},
- XCLASSES("-Xclasses") {
+ XCLASSES("-Xclasses", HIDDEN) {
@Override
public void process(Helper helper) {
helper.jdtoolOpts.put(XCLASSES, true);
@@ -369,32 +344,54 @@
// ----- help options -----
- HELP("-help") {
+ HELP("--help -help", STANDARD) {
@Override
public void process(Helper helper) {
helper.usage();
}
},
- X("-X") {
+ X("-X", STANDARD) {
@Override
public void process(Helper helper) {
helper.Xusage();
}
+ },
+
+ // This option exists only for the purpose of documenting itself.
+ // It's actually implemented by the launcher.
+ J("-J", STANDARD, true) {
+ @Override
+ public void process(Helper helper) {
+ throw new AssertionError("the -J flag should be caught by the launcher.");
+ }
+ },
+
+ // This option exists only for the purpose of documenting itself.
+ // It's actually implemented ahead of the normal option decoding loop.
+ Xold("-Xold", EXTENDED) {
+ @Override
+ public void process(Helper helper) {
+ throw new AssertionError("the -Xold flag should be handled earlier.");
+ }
};
- public final String opt;
+ public final String primaryName;
+ public final List<String> names;
+ public final OptionKind kind;
public final boolean hasArg;
public final boolean hasSuffix; // ex: foo:bar or -foo=bar
- ToolOption(String opt) {
- this(opt, false);
+ ToolOption(String opt, OptionKind kind) {
+ this(opt, kind, false);
}
- ToolOption(String opt, boolean hasArg) {
- this.opt = opt;
+ ToolOption(String names, OptionKind kind, boolean hasArg) {
+ this.names = Arrays.asList(names.split("\\s+"));
+ this.primaryName = this.names.get(0);
+ this.kind = kind;
this.hasArg = hasArg;
- char lastChar = opt.charAt(opt.length() - 1);
+ char lastChar = names.charAt(names.length() - 1);
this.hasSuffix = lastChar == ':' || lastChar == '=';
}
@@ -402,16 +399,42 @@
void process(Helper helper) { }
+ List<String> getNames() {
+ return names;
+ }
+
+ String getParameters(Messager messager) {
+ return (hasArg || primaryName.endsWith(":"))
+ ? messager.getText(getKey(primaryName, ".arg"))
+ : null;
+ }
+
+ String getDescription(Messager messager) {
+ return messager.getText(getKey(primaryName, ".desc"));
+ }
+
+ private String getKey(String optionName, String suffix) {
+ return "main.opt."
+ + optionName
+ .replaceAll("^-*", "") // remove leading '-'
+ .replaceAll("[^A-Za-z0-9]+$", "") // remove trailing non-alphanumeric
+ .replaceAll("[^A-Za-z0-9]", ".") // replace internal non-alphanumeric
+ + suffix;
+ }
+
+
static ToolOption get(String name) {
String oname = name;
if (name.contains(":")) {
oname = name.substring(0, name.indexOf(':') + 1);
} else if (name.contains("=")) {
- oname = name.substring(0, name.indexOf('=') + 1);
+ oname = name.substring(0, name.indexOf('='));
}
for (ToolOption o : values()) {
- if (oname.equals(o.opt)) {
- return o;
+ for (String n : o.names) {
+ if (oname.equals(n)) {
+ return o;
+ }
}
}
return null;
@@ -457,11 +480,6 @@
jdtoolOpts.put(opt, list);
}
- String getOptionArgumentValue(String in) {
- String[] values = in.trim().split(":");
- return values[1];
- }
-
void setExpandRequires(ToolOption opt, String arg) {
switch (arg) {
case "public":
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties Wed Oct 05 06:28:22 2016 -0700
@@ -28,112 +28,234 @@
main.warnings={0} warnings
main.warning={0} warning
-main.usage=Usage: javadoc [options] [packagenames] [sourcefiles] [@files]\n\
-\ -overview <file> Read overview documentation from HTML file\n\
-\ -public Show only public classes and members\n\
-\ -protected Show protected/public classes and \n\
-\ members (default)\n\
-\ -package Show package/protected/public classes\n\
-\ and members\n\
-\ -private Show all classes and members\n\
-\ --show-members:value Specifies which members (fields, methods\n\
-\ etc.) will be documented, where value can\n\
-\ be one of "public", "protected", "package"\n\
-\ or "private".\n\
-\ Default is protected, will show public and\n\
-\ protected members, "public" will show only\n\
-\ public members, "package" will show public,\n\
-\ protected and package members and \n\
-\ "private" will show all members\n\
-\ --show-types:value Specifies which types (classes, interfaces\n\
-\ etc.) will be documented, where value can be\n\
-\ one of "public", "protected", "package" or\n\
-\ "private".\n\
-\ Default is "protected", show public and\n\
-\ protected types, "package" will show public,\n\
-\ protected and package types and "private"\n\
-\ will show all types\n\
-\ --show-packages:value Specifies which module's packages will be\n\
-\ documented. Possible values are "exported"\n\
-\ or "all" packages\n\
-\ --show-module-contents:value Specifies the documentation granularity of\n\
-\ module declarations.\n\
-\ Possible values are "api" or "all".\n\
-\ --expand-requires:value Instructs the tool to expand the "requires"\n\
-\ module dependencies "public" expands all the\n\
-\ "requires public" edges of the module graph.\n\
-\ "all" expands all the "requires" edges of\n\
-\ the module graph by default only the\n\
-\ specified modules will be considered.\n\
-\ -help Display command line options and exit\n\
-\ --module m1, m2.. Document the specified module(s)\n\
-\ -doclet <class> Generate output via alternate doclet\n\
-\ -docletpath <path> Specify where to find doclet class files\n\
-\ --module-source-path <path> Specify where to find input source files\n\
-\ for multiple modules\n\
-\ --upgrade-module-path <path> Override location of upgradeable modules\n\
-\ --module-path <path>, -p <path> Specify where to find application modules\n\
-\ --add-modules <module>(,<module>)*\n\
-\ Root modules to resolve in addition to the\n\
-\ initial modules,\n\
-\ or all modules on the module path if\n\
-\ <module> is ALL-MODULE-PATH.\n\
-\ --limit-modules <module>(,<module>)*\n\
-\ Limit the universe of observable modules\n\
-\ --source-path <path> Specify where to find source files\n\
-\ -sourcepath <path> Specify where to find source files\n\
-\ --class-path <path> Specify where to find user class files\n\
-\ -classpath <path> Specify where to find user class files\n\
-\ -cp <path> Specify where to find user class files\n\
-\ -exclude <pkglist> Specify a list of packages to exclude\n\
-\ -subpackages <subpkglist> Specify subpackages to recursively load\n\
-\ -breakiterator Compute first sentence with BreakIterator\n\
-\ -bootclasspath <path> Override location of platform class files\n\
-\ used for non-modular releases\n\
-\ --system <jdk> Override location of system modules used\n\
-\ for modular releases.\n\
-\ --release <release> Provide source compatibility with\n\
-\ specified release\n\
-\ -source <release> Provide source compatibility with\n\
-\ specified release\n\
-\ -extdirs <dirlist> Override location of installed extensions\n\
-\ -verbose Output messages about what Javadoc is doing\n\
-\ -locale <name> Locale to be used, e.g. en_US or en_US_WIN\n\
-\ -encoding <name> Source file encoding name\n\
-\ -quiet Do not display status messages\n\
-\ -J<flag> Pass <flag> directly to the runtime system\n\
-\ -X Print a synopsis of nonstandard\n\
-\ options and exit\n
+main.usage=Usage:\n\
+\ javadoc [options] [packagenames] [sourcefiles] [@files]\n\
+where options include:
+
+main.opt.public.desc=\
+ Show only public classes and members
+
+main.opt.protected.desc=\
+ Show protected/public classes and members (default)
+
+main.opt.package.desc=\
+ Show package/protected/public classes and members
+
+main.opt.private.desc=\
+ Show all classes and members
+
+main.opt.show.members.arg=\
+ <value>
+main.opt.show.members.desc=\
+ Specifies which members (fields, methods, etc.) will be\n\
+ documented, where value can be one of "public", "protected",\n\
+ "package" or "private". The default is "protected", which will\n\
+ show public and protected members, "public" will show only\n\
+ public members, "package" will show public, protected and\n\
+ package members and "private" will show all members.
+
+main.opt.show.types.arg=\
+ <value>
+main.opt.show.types.desc=\
+ Specifies which types (classes, interfaces, etc.) will be\n\
+ documented, where value can be one of "public", "protected",\n\
+ "package" or "private". The default is "protected", which will\n\
+ show public and protected types, "public" will show only\n\
+ public types, "package" will show public, protected and\n\
+ package types and "private" will show all types.
+
+main.opt.show.packages.arg=\
+ <value>
+main.opt.show.packages.desc=\
+ Specifies which module's packages will be documented. Possible\n\
+ values are "exported" or "all" packages.
+
+main.opt.show.module.contents.arg=\
+ <value>
+main.opt.show.module.contents.desc=\
+ Specifies the documentation granularity of module\n\
+ declarations. Possible values are "api" or "all".
+
+main.opt.expand.requires.arg=\
+ <value>
+main.opt.expand.requires.desc=\
+ Instructs the tool to expand the set of modules to be\n\
+ documented. By default, only the modules given explicitly on\n\
+ the command line will be documented. A value of "public" will\n\
+ additionally include all "requires public" dependencies of\n\
+ those modules. A value of "all" will include all dependencies\n\
+ of those modules.
+
+main.opt.help.desc=\
+ Display command line options and exit
+
+main.opt.module.arg=\
+ <module>(,<module>)*
+main.opt.module.desc=\
+ Document the specified module(s)
+
+main.opt.doclet.arg=\
+ <class>
+main.opt.doclet.desc=\
+ Generate output via alternate doclet
+
+main.opt.docletpath.arg=\
+ <path>
+main.opt.docletpath.desc=\
+ Specify where to find doclet class files
+
+main.opt.module.source.path.arg=\
+ <path>
+main.opt.module.source.path.desc=\
+ Specify where to find input source files for multiple modules
+
+main.opt.upgrade.module.path.arg=\
+ <path>
+main.opt.upgrade.module.path.desc=\
+ Override location of upgradeable modules
+
+main.opt.module.path.arg=\
+ <path>
+main.opt.module.path.desc=\
+ Specify where to find application modules
+
+main.opt.add.modules.arg=\
+ <module>(,<module>)*
+main.opt.add.modules.desc=\
+ Root modules to resolve in addition to the initial modules,\n\
+ or all modules on the module path if <module> is\n\
+ ALL-MODULE-PATH.
+
+main.opt.limit.modules.arg=\
+ <module>(,<module>)*
+main.opt.limit.modules.desc=\
+ Limit the universe of observable modules
+
+main.opt.source.path.arg=\
+ <path>
+main.opt.source.path.desc=\
+ Specify where to find source files
+
+main.opt.class.path.arg=\
+ <path>
+main.opt.class.path.desc=\
+ Specify where to find user class files
+
+main.opt.exclude.arg=\
+ <pkglist>
+main.opt.exclude.desc=\
+ Specify a list of packages to exclude
+
+main.opt.subpackages.arg=\
+ <subpkglist>
+main.opt.subpackages.desc=\
+ Specify subpackages to recursively load
+
+main.opt.breakiterator.desc=\
+ Compute first sentence with BreakIterator
+
+main.opt.bootclasspath.arg=\
+ <path>
+main.opt.bootclasspath.desc=\
+ Override location of platform class files used for non-modular\n\
+ releases
+
+main.opt.system.arg=\
+ <jdk>
+main.opt.system.desc=\
+ Override location of system modules used for modular releases
+
+main.opt.release.arg=\
+ <release>
+main.opt.release.desc=\
+ Provide source compatibility with specified release
+
+main.opt.source.arg=\
+ <release>
+main.opt.source.desc=\
+ Provide source compatibility with specified release
+
+main.opt.extdirs.arg=\
+ <dirlist>
+main.opt.extdirs.desc=\
+ Override location of installed extensions
+
+main.opt.verbose.desc=\
+ Output messages about what Javadoc is doing
+
+main.opt.locale.arg=\
+ <name>
+main.opt.locale.desc=\
+ Locale to be used, e.g. en_US or en_US_WIN
+
+main.opt.encoding.arg=\
+ <name>
+main.opt.encoding.desc=\
+ Source file encoding name
+
+main.opt.quiet.desc=\
+ Do not display status messages
+
+main.opt.J.arg=\
+ <flag>
+main.opt.J.desc=\
+ Pass <flag> directly to the runtime system
+
+main.opt.X.desc=\
+ Print a synopsis of nonstandard options and exit
main.usage.foot=\n\
GNU-style options may use '=' instead of whitespace to separate the name of an\n\
option from its value.\n
-main.Xusage=\
-\ -Xmaxerrs <number> Set the maximum number of errors to print\n\
-\ -Xmaxwarns <number> Set the maximum number of warnings to print\n\
-\ --add-exports <module>/<package>=<other-module>(,<other-module>)*\n\
-\ Specify a package to be considered as exported\n\
-\ from its defining module to additional modules,\n\
-\ or to all unnamed modules if <other-module> is\n\
-\ ALL-UNNAMED.\n\
-\ --add-reads <module>=<other-module>(,<other-module>)*\n\
-\ Specify additional modules to be considered as\n\
-\ required by a given module. <other-module> may be\n\
-\ ALL-UNNAMED to require the unnamed module.\n\
-\ -Xmodule:<module-name> Specify a module to which the classes being\n\
-\ compiled belong.\n\
-\ --patch-module <module>=<file>(:<file>)*\n\
-\ Override or augment a module with classes\n\
-\ and resources in JAR files or directories\n\
-\ -Xold Invoke the legacy javadoc tool\n
+main.Xusage=
+
+main.opt.Xmaxerrs.arg=\
+ <number>
+main.opt.Xmaxerrs.desc=\
+ Set the maximum number of errors to print
+
+main.opt.Xmaxwarns.arg=\
+ <number>
+main.opt.Xmaxwarns.desc=\
+ Set the maximum number of warnings to print
+
+main.opt.add.exports.arg=\
+ <module>/<package>=<other-module>(,<other-module>)*
+main.opt.add.exports.desc=\
+ Specify a package to be considered as exported from its\n\
+ defining module to additional modules, or to all unnamed\n\
+ modules if <other-module> is ALL-UNNAMED
-main.Xusage.foot=\
+main.opt.add.reads.arg=\
+ <module>=<other-module>(,<other-module>)*
+main.opt.add.reads.desc=\
+ Specify additional modules to be considered as required by a\n\
+ given module. <other-module> may be ALL-UNNAMED to require\n\
+ the unnamed module.
+
+main.opt.Xmodule.arg=\
+ <module-name>
+main.opt.Xmodule.desc=\
+ Specify a module to which the classes being compiled belong
+
+main.opt.patch.module.arg=\
+ <module>=<file>(:<file>)*
+main.opt.patch.module.desc=\
+ Override or augment a module with classes and resources in\n\
+ JAR files or directories
+
+main.opt.Xold.desc=\
+ Invoke the legacy javadoc tool
+
+main.Xusage.foot=\n\
These options are non-standard and subject to change without notice.
-main.doclet.usage.header=Provided by the {0} doclet:
+main.doclet.usage.header=\nProvided by the {0} doclet:
main.requires_argument=option {0} requires an argument.
+main.unnecessary_arg_provided=option {0} does not require an argument
+main.only_one_argument_with_equals=cannot use ''='' syntax for options that require multiple arguments
main.invalid_flag=invalid flag: {0}
main.No_modules_packages_or_classes_specified=No modules, packages or classes specified.
main.module_not_found=module {0} not found.\n
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java Wed Oct 05 06:28:22 2016 -0700
@@ -150,7 +150,7 @@
task.options.help = true;
}
},
- new Option(true, "-dotoutput") {
+ new Option(true, "-dotoutput", "--dot-output") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
Path p = Paths.get(arg);
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
@@ -191,7 +191,7 @@
}
}
},
- new Option(false, "-apionly") {
+ new Option(false, "-apionly", "--api-only") {
void process(JdepsTask task, String opt, String arg) {
task.options.apiOnly = true;
}
@@ -203,7 +203,7 @@
task.options.addmods.addAll(mods);
}
},
- new Option(true, "--gen-module-info") {
+ new Option(true, "--generate-module-info") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
Path p = Paths.get(arg);
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
@@ -212,7 +212,7 @@
task.options.genModuleInfo = Paths.get(arg);
}
},
- new Option(false, "-jdkinternals") {
+ new Option(false, "-jdkinternals", "--jdk-internals") {
void process(JdepsTask task, String opt, String arg) {
task.options.findJDKInternals = true;
task.options.verbose = CLASS;
@@ -263,19 +263,36 @@
task.options.addmods.add(arg);
}
},
+ new Option(true, "--multi-release") {
+ void process(JdepsTask task, String opt, String arg) throws BadArgs {
+ if (arg.equalsIgnoreCase("base")) {
+ task.options.multiRelease = JarFile.baseVersion();
+ } else {
+ try {
+ int v = Integer.parseInt(arg);
+ if (v < 9) {
+ throw new BadArgs("err.invalid.arg.for.option", arg);
+ }
+ } catch (NumberFormatException x) {
+ throw new BadArgs("err.invalid.arg.for.option", arg);
+ }
+ task.options.multiRelease = Runtime.Version.parse(arg);
+ }
+ }
+ },
// ---- Target filtering options ----
- new Option(true, "-p", "-package") {
+ new Option(true, "-p", "-package", "--package") {
void process(JdepsTask task, String opt, String arg) {
task.options.packageNames.add(arg);
}
},
- new Option(true, "-e", "-regex") {
+ new Option(true, "-e", "-regex", "--regex") {
void process(JdepsTask task, String opt, String arg) {
task.options.regex = Pattern.compile(arg);
}
},
- new Option(true, "-requires") {
+ new Option(true, "--require") {
void process(JdepsTask task, String opt, String arg) {
task.options.requires.add(arg);
}
@@ -336,7 +353,7 @@
}
},
- new Option(false, "-I", "-inverse") {
+ new Option(false, "-I", "--inverse") {
void process(JdepsTask task, String opt, String arg) {
task.options.inverse = true;
// equivalent to the inverse of compile-time view analysis
@@ -361,7 +378,7 @@
}
},
- new Option(false, "-version") {
+ new Option(false, "-version", "--version") {
void process(JdepsTask task, String opt, String arg) {
task.options.version = true;
}
@@ -390,23 +407,6 @@
}
}
},
- new Option(true, "--multi-release") {
- void process(JdepsTask task, String opt, String arg) throws BadArgs {
- if (arg.equalsIgnoreCase("base")) {
- task.options.multiRelease = JarFile.baseVersion();
- } else {
- try {
- int v = Integer.parseInt(arg);
- if (v < 9) {
- throw new BadArgs("err.invalid.arg.for.option", arg);
- }
- } catch (NumberFormatException x) {
- throw new BadArgs("err.invalid.arg.for.option", arg);
- }
- task.options.multiRelease = Runtime.Version.parse(arg);
- }
- }
- },
};
private static final String PROGNAME = "jdeps";
--- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Wed Oct 05 06:28:22 2016 -0700
@@ -13,162 +13,168 @@
main.opt.h=\
\ -h -? -help\n\
-\ --help Print this usage message
+\ --help Print this usage message
main.opt.version=\
-\ -version Version information
+\ -version --version Version information
main.opt.v=\
-\ -v -verbose Print all class level dependencies\n\
-\ Equivalent to -verbose:class -filter:none.\n\
-\ -verbose:package Print package-level dependencies excluding\n\
-\ dependencies within the same package by default\n\
-\ -verbose:class Print class-level dependencies excluding\n\
-\ dependencies within the same package by default
+\ -v -verbose Print all class level dependences\n\
+\ Equivalent to -verbose:class -filter:none.\n\
+\ -verbose:package Print package-level dependences excluding\n\
+\ dependences within the same package by default\n\
+\ -verbose:class Print class-level dependences excluding\n\
+\ dependences within the same package by default
main.opt.s=\
-\ -s -summary Print dependency summary only.
+\ -s -summary Print dependency summary only.
main.opt.f=\
-\ -f <regex> -filter <regex> Filter dependences matching the given\n\
-\ pattern. If given multiple times, the last\n\
-\ one will be used.\n\
-\ -filter:package Filter dependences within the same package.\n\
-\ This is the default.\n\
-\ -filter:archive Filter dependences within the same archive.\n\
-\ -filter:module Filter dependences within the same module.\n\
-\ -filter:none No -filter:package and -filter:archive\n\
-\ filtering. Filtering specified via the\n\
-\ -filter option still applies.\n\
+\ -f <regex> -filter <regex> Filter dependences matching the given\n\
+\ pattern. If given multiple times, the last\n\
+\ one will be used.\n\
+\ -filter:package Filter dependences within the same package.\n\
+\ This is the default.\n\
+\ -filter:archive Filter dependences within the same archive.\n\
+\ -filter:module Filter dependences within the same module.\n\
+\ -filter:none No -filter:package and -filter:archive\n\
+\ filtering. Filtering specified via the\n\
+\ -filter option still applies.\n\
main.opt.p=\n\
-\Options to filter dependencies:\n\
-\ -p <pkgname> -package <pkgname> Finds dependences matching the given package\n\
-\ name (may be given multiple times).
+\Options to filter dependences:\n\
+\ -p <pkg>\n\
+\ -package <pkg>\n\
+\ --package <pkg> Finds dependences matching the given package\n\
+\ name (may be given multiple times).
main.opt.e=\
-\ -e <regex> -regex <regex> Finds dependences matching the given pattern.
+\ -e <regex>\n\
+\ -regex <regex>\n\
+\ --regex <regex> Finds dependences matching the given pattern.
-main.opt.requires=\
-\ -requires <module-name> Finds dependences matching the given module\n\
-\ name (may be given multiple times).\n\
-\ -package, -regex, -requires are mutual exclusive.
+main.opt.require=\
+\ --require <module-name> Finds dependences matching the given module\n\
+\ name (may be given multiple times). --package,\n\
+\ --regex, --require are mutual exclusive.
main.opt.include=\n\
\Options to filter classes to be analyzed:\n\
-\ -include <regex> Restrict analysis to classes matching pattern\n\
-\ This option filters the list of classes to\n\
-\ be analyzed. It can be used together with\n\
-\ -p and -e which apply pattern to the dependences
+\ -include <regex> Restrict analysis to classes matching pattern\n\
+\ This option filters the list of classes to\n\
+\ be analyzed. It can be used together with\n\
+\ -p and -e which apply pattern to the dependences
main.opt.P=\
-\ -P -profile Show profile containing a package
+\ -P -profile Show profile containing a package
main.opt.cp=\
\ -cp <path>\n\
\ -classpath <path>\n\
-\ --class-path <path> Specify where to find class files
+\ --class-path <path> Specify where to find class files
main.opt.module-path=\
-\ --module-path <module path>... Specify module path
+\ --module-path <module path> Specify module path
main.opt.upgrade-module-path=\
-\ --upgrade-module-path <module path>... Specify upgrade module path
+\ --upgrade-module-path <module path> Specify upgrade module path
main.opt.system=\
-\ --system <java-home> Specify an alternate system module path
+\ --system <java-home> Specify an alternate system module path
main.opt.add-modules=\
\ --add-modules <module-name>[,<module-name>...]\n\
-\ Adds modules to the root set for analysis
+\ Adds modules to the root set for analysis
main.opt.m=\
\ -m <module-name>\n\
-\ --module <module-name> Specify the root module for analysis
+\ --module <module-name> Specify the root module for analysis
main.opt.R=\
-\ -R -recursive Recursively traverse all run-time dependencies.\n\
-\ The -R option implies -filter:none. If -p,\n\
-\ -e, -foption is specified, only the matching\n\
-\ dependences are analyzed.
+\ -R -recursive Recursively traverse all run-time dependences.\n\
+\ The -R option implies -filter:none. If -p,\n\
+\ -e, -foption is specified, only the matching\n\
+\ dependences are analyzed.
main.opt.I=\
-\ -I -inverse Analyzes the dependences per other given options\n\
-\ and then find all artifacts that directly\n\
-\ and indirectly depend on the matching nodes.\n\
-\ This is equivalent to the inverse of\n\
-\ compile-time view analysis and print\n\
-\ dependency summary. This option must use\n\
-\ with -requires, -package or -regex option.
+\ -I --inverse Analyzes the dependences per other given options\n\
+\ and then find all artifacts that directly\n\
+\ and indirectly depend on the matching nodes.\n\
+\ This is equivalent to the inverse of\n\
+\ compile-time view analysis and print\n\
+\ dependency summary. This option must use\n\
+\ with --require, --package or --regex option.
main.opt.compile-time=\
-\ --compile-time Compile-time view of transitive dependencies\n\
-\ i.e. compile-time view of -R option.\n\
-\ Analyzes the dependences per other given options\n\
-\ If a dependence is found from a directory,\n\
-\ a JAR file or a module, all classes in that \n\
-\ containing archive are analyzed.
+\ --compile-time Compile-time view of transitive dependences\n\
+\ i.e. compile-time view of -R option.\n\
+\ Analyzes the dependences per other given options\n\
+\ If a dependence is found from a directory,\n\
+\ a JAR file or a module, all classes in that \n\
+\ containing archive are analyzed.
main.opt.apionly=\
-\ -apionly Restrict analysis to APIs i.e. dependences\n\
-\ from the signature of public and protected\n\
-\ members of public classes including field\n\
-\ type, method parameter types, returned type,\n\
-\ checked exception types etc.
+\ -apionly\n\
+\ --api-only Restrict analysis to APIs i.e. dependences\n\
+\ from the signature of public and protected\n\
+\ members of public classes including field\n\
+\ type, method parameter types, returned type,\n\
+\ checked exception types etc.
-main.opt.gen-module-info=\
-\ --gen-module-info <dir> Generate module-info.java under the specified\n\
-\ directory. The specified JAR files will be\n\
-\ analyzed. This option cannot be used with\n\
-\ -dotoutput or -cp.
+main.opt.generate-module-info=\
+\ --generate-module-info <dir> Generate module-info.java under the specified\n\
+\ directory. The specified JAR files will be\n\
+\ analyzed. This option cannot be used with\n\
+\ --dot-output or --class-path.
main.opt.check=\
\ --check <module-name>[,<module-name>...\n\
-\ Analyze the dependence of the specified modules\n\
-\ It prints the module descriptor, the resulting\n\
-\ module dependences after analysis and the\n\
-\ graph after transition reduction. It also\n\
-\ identifies any unused qualified exports.
+\ Analyze the dependence of the specified modules\n\
+\ It prints the module descriptor, the resulting\n\
+\ module dependences after analysis and the\n\
+\ graph after transition reduction. It also\n\
+\ identifies any unused qualified exports.
main.opt.dotoutput=\
-\ -dotoutput <dir> Destination directory for DOT file output
+\ -dotoutput <dir>\n\
+\ --dot-output <dir> Destination directory for DOT file output
main.opt.jdkinternals=\
-\ -jdkinternals Finds class-level dependences on JDK internal\n\
-\ APIs. By default, it analyzes all classes\n\
-\ on -classpath and input files unless -include\n\
-\ option is specified. This option cannot be\n\
-\ used with -p, -e and -s options.\n\
-\ WARNING: JDK internal APIs are inaccessible.
+\ -jdkinternals\n\
+\ --jdk-internals Finds class-level dependences on JDK internal\n\
+\ APIs. By default, it analyzes all classes\n\
+\ on --class-path and input files unless -include\n\
+\ option is specified. This option cannot be\n\
+\ used with -p, -e and -s options.\n\
+\ WARNING: JDK internal APIs are inaccessible.
main.opt.depth=\
-\ -depth=<depth> Specify the depth of the transitive\n\
-\ dependency analysis
+\ -depth=<depth> Specify the depth of the transitive\n\
+\ dependency analysis
main.opt.q=\
-\ -q -quiet Do not show missing dependencies from \n\
-\ -genmoduleinfo output.
+\ -q -quiet Do not show missing dependences from \n\
+\ --generate-module-info output.
main.opt.multi-release=\
-\ --multi-release <version> Specifies the version when processing\n\
-\ multi-release jar files. <version> should\n\
-\ be integer >= 9 or base.
+\ --multi-release <version> Specifies the version when processing\n\
+\ multi-release jar files. <version> should\n\
+\ be integer >= 9 or base.
err.unknown.option=unknown option: {0}
err.missing.arg=no value given for {0}
err.invalid.arg.for.option=invalid argument for option: {0}
err.option.after.class=option must be specified before classes: {0}
-err.genmoduleinfo.not.jarfile={0} not valid for --gen-module-info option (must be non-modular JAR file)
+err.genmoduleinfo.not.jarfile={0} not valid for --generate-module-info option (must be non-modular JAR file)
err.profiles.msg=No profile information
err.exception.message={0}
err.invalid.path=invalid path: {0}
err.invalid.module.option=Cannot set {0} with {1} option.
-err.invalid.filters=Only one of -package (-p), -regex (-e), -requires option can be set
+err.invalid.filters=Only one of --package (-p), --regex (-e), --require option can be set
err.module.not.found=module not found: {0}
err.root.module.not.set=root module set empty
-err.invalid.inverse.option={0} cannot be used with -inverse option
-err.inverse.filter.not.set={0} cannot be used with -inverse option
+err.invalid.inverse.option={0} cannot be used with --inverse option
err.multirelease.option.exists={0} is not a multi-release jar file, but the --multi-release option is set
err.multirelease.option.notfound={0} is a multi-release jar file, but the --multi-release option is not set
err.multirelease.version.associated=class {0} already associated with version {1}, trying to add version {2}
@@ -178,7 +184,7 @@
warn.replace.useJDKInternals=\
JDK internal APIs are unsupported and private to JDK implementation that are\n\
subject to be removed or changed incompatibly and could break your application.\n\
-Please modify your code to eliminate dependency on any JDK internal APIs.\n\
+Please modify your code to eliminate dependence on any JDK internal APIs.\n\
For the most recent update on JDK internal API replacements, please check:\n\
{0}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Wed Oct 05 06:28:22 2016 -0700
@@ -199,6 +199,7 @@
private boolean feedbackInitialized = false;
private String commandLineFeedbackMode = null;
private List<String> remoteVMOptions = new ArrayList<>();
+ private List<String> compilerOptions = new ArrayList<>();
SourceCodeAnalysis analysis;
JShell state = null;
@@ -558,9 +559,14 @@
parser.accepts("s");
parser.accepts("v");
OptionSpec<String> r = parser.accepts("R").withRequiredArg();
+ OptionSpec<String> c = parser.accepts("C").withRequiredArg();
parser.acceptsAll(asList("h", "help"));
parser.accepts("version");
parser.accepts("full-version");
+
+ parser.accepts("X");
+ OptionSpec<String> addExports = parser.accepts("add-exports").withRequiredArg();
+
NonOptionArgumentSpec<String> loadFileSpec = parser.nonOptions();
OptionSet options;
@@ -585,6 +591,10 @@
printUsage();
return null;
}
+ if (options.has("X")) {
+ printUsageX();
+ return null;
+ }
if (options.has("version")) {
cmdout.printf("jshell %s\n", version());
return null;
@@ -630,7 +640,19 @@
commandLineFeedbackMode = "verbose";
}
if (options.has(r)) {
- remoteVMOptions = options.valuesOf(r);
+ remoteVMOptions.addAll(options.valuesOf(r));
+ }
+ if (options.has(c)) {
+ compilerOptions.addAll(options.valuesOf(c));
+ }
+
+ if (options.has(addExports)) {
+ List<String> exports = options.valuesOf(addExports).stream()
+ .map(mp -> mp + "=ALL-UNNAMED")
+ .flatMap(mp -> Stream.of("--add-exports", mp))
+ .collect(toList());
+ remoteVMOptions.addAll(exports);
+ compilerOptions.addAll(exports);
}
return options.valuesOf(loadFileSpec);
@@ -640,6 +662,10 @@
cmdout.print(getResourceString("help.usage"));
}
+ private void printUsageX() {
+ cmdout.print(getResourceString("help.usage.x"));
+ }
+
/**
* Message handler to use during initial start-up.
*/
@@ -683,7 +709,8 @@
.idGenerator((sn, i) -> (currentNameSpace == startNamespace || state.status(sn).isActive())
? currentNameSpace.tid(sn)
: errorNamespace.tid(sn))
- .remoteVMOptions(remoteVMOptions.toArray(new String[remoteVMOptions.size()]))
+ .remoteVMOptions(remoteVMOptions.stream().toArray(String[]::new))
+ .compilerOptions(compilerOptions.stream().toArray(String[]::new))
.build();
shutdownSubscription = state.onShutdown((JShell deadState) -> {
if (deadState == state) {
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties Wed Oct 05 06:28:22 2016 -0700
@@ -164,8 +164,15 @@
\ Use one -J for each runtime flag or flag argument\n\
\ -R<flag> Pass <flag> to the remote runtime system.\n\
\ Use one -R for each remote flag or flag argument\n\
+\ -C<flag> Pass <flag> to the compiler.\n\
+\ Use one -C for each compiler flag or flag argument\n\
\ --help Print this synopsis of standard options\n\
-\ --version Version information\n
+\ --version Version information\n\
+\ -X Print help on non-standard options\n
+help.usage.x = \
+\ --add-exports <module>/<package> Export specified module-private package to snippets\n\
+\ \n\
+\These options are non-standard and subject to change without notice.\n
help.list.summary = list the source you have typed
help.list.args = [<name or id>|-all|-start]
--- a/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java Wed Oct 05 06:28:22 2016 -0700
@@ -41,6 +41,7 @@
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
@@ -410,6 +411,23 @@
}
/**
+ * Get the content of the one of the output streams written by
+ * javadoc.
+ */
+ public String getOutput(Output output) {
+ return outputMap.get(output);
+ }
+
+ /**
+ * Get the content of the one of the output streams written by
+ * javadoc.
+ */
+ public List<String> getOutputLines(Output output) {
+ String text = outputMap.get(output);
+ return (text == null) ? Collections.emptyList() : Arrays.asList(text.split(NL));
+ }
+
+ /**
* Check for files in (or not in) the generated output.
* @param expectedFound true if all of the files are expected
* to be found, or false if all of the files are expected to be
--- a/langtools/test/jdk/javadoc/doclet/testHelpOption/TestHelpOption.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/doclet/testHelpOption/TestHelpOption.java Wed Oct 05 06:28:22 2016 -0700
@@ -32,6 +32,9 @@
* @run main TestHelpOption
*/
+import java.util.*;
+import java.util.stream.*;
+
public class TestHelpOption extends JavadocTester {
public static void main(String... args) throws Exception {
@@ -40,6 +43,26 @@
}
@Test
+ void testLineLengths() {
+ javadoc("-d", "out1",
+ "-sourcepath", testSrc,
+ "-X",
+ testSrc("TestXOption.java"));
+ checkExit(Exit.OK);
+ List<String> longLines = getOutputLines(Output.OUT).stream()
+ .filter(s -> s.length() > 80)
+ .collect(Collectors.toList());
+ checking("line lengths");
+ if (longLines.isEmpty()) {
+ passed("all lines OK");
+ } else {
+ out.println("long lines:");
+ longLines.stream().forEach(s -> out.println(">>>" + s + "<<<"));
+ failed(longLines.size() + " long lines");
+ }
+ }
+
+ @Test
void testWithOption() {
javadoc("-d", "out1",
"-sourcepath", testSrc,
@@ -107,7 +130,7 @@
"-use ",
"-version ",
"-author ",
- "-docfilessubdirs ",
+ "-docfilessubdirs\n",
"-splitindex ",
"-windowtitle ",
"-doctitle ",
@@ -119,11 +142,11 @@
"-excludedocfilessubdir ",
"-group ",
"-nocomment ",
- "-nodeprecated ",
+ "-nodeprecated\n",
"-noqualifier ",
"-nosince ",
"-notimestamp ",
- "-nodeprecatedlist ",
+ "-nodeprecatedlist\n",
"-notree ",
"-noindex ",
"-nohelp ",
--- a/langtools/test/jdk/javadoc/doclet/testXOption/TestXOption.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/doclet/testXOption/TestXOption.java Wed Oct 05 06:28:22 2016 -0700
@@ -31,6 +31,9 @@
* @run main TestXOption
*/
+import java.util.*;
+import java.util.stream.*;
+
public class TestXOption extends JavadocTester {
public static void main(String... args) throws Exception {
@@ -39,6 +42,26 @@
}
@Test
+ void testLineLengths() {
+ javadoc("-d", "out1",
+ "-sourcepath", testSrc,
+ "-X",
+ testSrc("TestXOption.java"));
+ checkExit(Exit.OK);
+ List<String> longLines = getOutputLines(Output.OUT).stream()
+ .filter(s -> s.length() > 80)
+ .collect(Collectors.toList());
+ checking("line lengths");
+ if (longLines.isEmpty()) {
+ passed("all lines OK");
+ } else {
+ out.println("long lines:");
+ longLines.stream().forEach(s -> out.println(">>>" + s + "<<<"));
+ failed(longLines.size() + " long lines");
+ }
+ }
+
+ @Test
void testWithOption() {
javadoc("-d", "out1",
"-sourcepath", testSrc,
@@ -58,14 +81,9 @@
}
private void checkOutput(boolean expectFound) {
- // TODO: It's an ugly hidden side-effect of the current doclet API
- // that the -X output from the tool and the -X output from the doclet
- // come out on different streams!
- // When we clean up the doclet API, this should be rationalized.
checkOutput(Output.OUT, expectFound,
"-Xmaxerrs ",
- "-Xmaxwarns ");
- checkOutput(Output.OUT, expectFound,
+ "-Xmaxwarns ",
"-Xdocrootparent ",
"-Xdoclint ",
"-Xdoclint:");
--- a/langtools/test/jdk/javadoc/tool/CheckResourceKeys.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/tool/CheckResourceKeys.java Wed Oct 05 06:28:22 2016 -0700
@@ -145,6 +145,9 @@
// ignore these synthesized keys, tested by usageTests
if (rk.startsWith("doclet.usage.") || rk.startsWith("doclet.xusage"))
continue;
+ // ignore these synthesized keys, tested by usageTests
+ if (rk.matches("main\\.opt\\..*\\.(arg|desc)"))
+ continue;
if (codeKeys.contains(rk))
continue;
@@ -161,6 +164,9 @@
// ignore these synthesized keys, tested by usageTests
if (ck.startsWith("doclet.usage.") || ck.startsWith("doclet.xusage."))
continue;
+ // ignore this partial key, tested by usageTests
+ if (ck.equals("main.opt."))
+ continue;
if (resourceKeys.contains(ck))
continue;
error("No resource for \"" + ck + "\"");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/javadoc/tool/OptionSyntaxTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8166144
+ * @summary support new-style options
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * @modules jdk.compiler/com.sun.tools.javac.main
+ * @modules jdk.javadoc/jdk.javadoc.internal.api
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @library /tools/lib
+ * @build toolbox.JavacTask toolbox.JavadocTask toolbox.ModuleBuilder toolbox.TestRunner toolbox.ToolBox
+ * @run main OptionSyntaxTest
+ */
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.lang.model.SourceVersion;
+
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+
+import toolbox.JavadocTask;
+import toolbox.ModuleBuilder;
+import toolbox.Task;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+
+public class OptionSyntaxTest extends TestRunner {
+ public static class TestDoclet implements Doclet {
+ @Override
+ public boolean run(DocletEnvironment root) {
+ System.out.println("TestDoclet.run");
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "Test";
+ }
+
+ @Override
+ public Set<Option> getSupportedOptions() {
+ return options;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public void init(Locale locale, Reporter reporter) {
+ }
+
+ private final Set<Doclet.Option> options = new HashSet<>(Arrays.asList(
+ new DOption("-old", 0),
+ new DOption("-oldWithArg", 1),
+ new DOption("-oldWithArgs", 2),
+ new DOption("--new", 0),
+ new DOption("--newWithArg", 1),
+ new DOption("--newWithArgs", 2)
+ ));
+
+ }
+
+ static class DOption implements Doclet.Option {
+ private final String name;
+ private final int argCount;
+
+ DOption(String name, int argCount) {
+ this.name = name;
+ this.argCount = argCount;
+ }
+
+ @Override
+ public int getArgumentCount() {
+ return argCount;
+ }
+
+ @Override
+ public String getDescription() {
+ return "description[" + name + "]";
+ }
+
+ @Override
+ public Kind getKind() {
+ return Doclet.Option.Kind.STANDARD;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getParameters() {
+ return argCount > 0 ? "parameters[" + name + "," + argCount + "]" : null;
+ }
+
+ @Override
+ public boolean matches(String option) {
+ return option.equals(name);
+ }
+
+ @Override
+ public boolean process(String option, ListIterator<String> arguments) {
+ List<String> args = new ArrayList<>();
+ for (int i = 0; i < argCount && arguments.hasNext(); i++) {
+ args.add(arguments.next());
+ }
+ System.out.println("process " + option + " " + args);
+ return args.stream().filter(s -> s.startsWith("arg")).count() == argCount;
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ OptionSyntaxTest t = new OptionSyntaxTest();
+ t.runTests();
+ }
+
+ private final ToolBox tb = new ToolBox();
+ private final Path src = Paths.get("src");
+ private final Path modules = Paths.get("modules");
+
+ OptionSyntaxTest() throws IOException {
+ super(System.err);
+ initModules();
+ }
+
+ void initModules() throws IOException {
+ new ModuleBuilder(tb, "m1")
+ .exports("p1")
+ .classes("package p1; public class C1 { }")
+ .write(src);
+
+ new ModuleBuilder(tb, "m2")
+ .exports("p2")
+ .classes("package p2; public class C2 { }")
+ .build(modules);
+
+ }
+
+ @Test
+ public void testBasic() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testNewSourcePath() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "--source-path", "src/m1",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testNewSourcePathEquals() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "--source-path=src/m1",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testOldDocletArgs() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "-old",
+ "-oldWithArg", "arg",
+ "-oldWithArgs", "arg1", "arg2",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testNewDocletArgs() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "--new",
+ "--newWithArg", "arg",
+ "--newWithArgs", "arg1", "arg2",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testNewDocletArgsEquals() {
+ new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "--new", "--newWithArg=arg",
+ "p1")
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testNewDocletArgsMissingArgs() throws Exception {
+ String log = new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "--newWithArg")
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutput(Task.OutputKind.DIRECT);
+ if (!log.contains("option --newWithArg requires an argument"))
+ throw new Exception("expected output not found");
+ }
+
+ @Test
+ public void testNewDocletArgsExtraArgs() throws Exception {
+ String log = new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "--new=arg",
+ "p1")
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutput(Task.OutputKind.DIRECT);
+ if (!log.contains("option --new does not require an argument"))
+ throw new Exception("expected output not found");
+ }
+
+ @Test
+ public void testNewDocletArgsExtraArgs2() throws Exception {
+ String log = new JavadocTask(tb, Task.Mode.CMDLINE)
+ .options("-docletpath", System.getProperty("test.classes"),
+ "-doclet", TestDoclet.class.getName(),
+ "-sourcepath", "src/m1",
+ "--newWithArgs=arg1 arg2",
+ "p1")
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutput(Task.OutputKind.DIRECT);
+ if (!log.contains("cannot use '=' syntax for options that require multiple arguments"))
+ throw new Exception("expected output not found");
+ }
+
+}
--- a/langtools/test/jdk/javadoc/tool/api/basic/IsSupportedOptionTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/tool/api/basic/IsSupportedOptionTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -35,7 +35,7 @@
import javax.tools.ToolProvider;
/**
- * Tests for DocumentationTool.usSupportedOption method.
+ * Tests for DocumentationTool.isSupportedOption method.
*/
public class IsSupportedOptionTest extends APITest {
public static void main(String... args) throws Exception {
--- a/langtools/test/jdk/javadoc/tool/modules/FilterOptions.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/tool/modules/FilterOptions.java Wed Oct 05 06:28:22 2016 -0700
@@ -70,7 +70,7 @@
@Test
public void testModuleModeApi(Path base) throws Exception {
execTask("--module-source-path", src,
- "--module", "m1", "--show-module-contents:api");
+ "--module", "m1", "--show-module-contents", "api");
checkModuleMode("API");
}
@@ -78,7 +78,7 @@
@Test
public void testModuleModeAll(Path base) throws Exception {
execTask("--module-source-path", src,
- "--module", "m1", "--show-module-contents:all");
+ "--module", "m1", "--show-module-contents", "all");
checkModuleMode("ALL");
}
@@ -87,7 +87,7 @@
public void testShowPackagesExported(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-packages:exported"); // default
+ "--show-packages", "exported"); // default
checkModulesSpecified("m1");
checkModulesIncluded("m1");
@@ -99,7 +99,7 @@
public void testShowPackagesAll(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-packages:all");
+ "--show-packages", "all");
checkModulesSpecified("m1");
checkModulesIncluded("m1");
checkPackagesIncluded("pub", "pro");
@@ -112,7 +112,7 @@
public void testShowTypesPrivate(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-types:private");
+ "--show-types", "private");
checkModulesSpecified("m1");
checkModulesIncluded("m1");
@@ -129,7 +129,7 @@
public void testShowTypesPackage(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-types:package");
+ "--show-types", "package");
checkModulesSpecified("m1");
checkModulesIncluded("m1");
@@ -145,7 +145,7 @@
public void testShowTypesProtected(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-types:protected");
+ "--show-types", "protected");
checkModulesSpecified("m1");
checkModulesIncluded("m1");
@@ -162,7 +162,7 @@
public void testShowTypesPublic(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-types:public");
+ "--show-types", "public");
checkModulesSpecified("m1");
checkModulesIncluded("m1");
@@ -179,7 +179,7 @@
public void testShowMembersPrivate(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-members:private");
+ "--show-members", "private");
checkMembers(Visibility.PRIVATE);
}
@@ -188,7 +188,7 @@
public void testShowMembersPackage(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-members:package");
+ "--show-members", "package");
checkMembers(Visibility.PACKAGE);
}
@@ -197,7 +197,7 @@
public void testShowMembersProtected(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-members:protected");
+ "--show-members", "protected");
checkMembers(Visibility.PROTECTED);
}
@@ -206,7 +206,7 @@
public void testShowMembersPublic(Path base) throws Exception {
execTask("--module-source-path", src,
"--module", "m1",
- "--show-members:public");
+ "--show-members", "public");
checkMembers(Visibility.PUBLIC);
}
--- a/langtools/test/jdk/javadoc/tool/modules/Modules.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/javadoc/tool/modules/Modules.java Wed Oct 05 06:28:22 2016 -0700
@@ -440,7 +440,7 @@
execTask("--module-source-path", src.toString(),
"--module", "M",
- "--expand-requires:public");
+ "--expand-requires", "public");
checkModulesSpecified("M", "N", "O");
checkModulesIncluded("M", "N", "O");
@@ -465,7 +465,7 @@
execTask("--module-source-path", src.toString(),
"--module", "M",
- "--expand-requires:all");
+ "--expand-requires", "all");
checkModulesSpecified("M", "java.base", "N", "L", "O");
checkModulesIncluded("M", "java.base", "N", "L", "O");
@@ -493,7 +493,7 @@
execNegativeTask("--module-source-path", src.toString(),
"--module", "MIA",
- "--expand-requires:all");
+ "--expand-requires", "all");
assertErrorPresent("javadoc: error - module MIA not found.");
}
@@ -515,7 +515,7 @@
execNegativeTask("--module-source-path", src.toString(),
"--module", "M,N,L,MIA,O,P",
- "--expand-requires:all");
+ "--expand-requires", "all");
assertErrorPresent("javadoc: error - module MIA not found");
}
--- a/langtools/test/jdk/jshell/HistoryTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/jshell/HistoryTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -23,6 +23,7 @@
/*
* @test
+ * @bug 8166744
* @summary Test Completion
* @modules jdk.internal.le/jdk.internal.jline.extra
* jdk.jshell/jdk.internal.jshell.tool
@@ -75,6 +76,44 @@
});
}
+ public void test8166744() {
+ test(
+ a -> {if (!a) setCommandInput("class C {\n");},
+ a -> {if (!a) setCommandInput("void f() {\n");},
+ a -> {if (!a) setCommandInput("}\n");},
+ a -> {assertCommand(a, "}", "| created class C");},
+ a -> {
+ if (!a) {
+ try {
+ previousAndAssert(getHistory(), "}");
+ previousAndAssert(getHistory(), "}");
+ previousAndAssert(getHistory(), "void f() {");
+ previousAndAssert(getHistory(), "class C {");
+ getHistory().add("class C{");
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ assertCommand(a, "int dummy;", "dummy ==> 0");
+ });
+ test(
+ a -> {if (!a) setCommandInput("class C {\n");},
+ a -> {if (!a) setCommandInput("void f() {\n");},
+ a -> {if (!a) setCommandInput("}\n");},
+ a -> {assertCommand(a, "}", "| created class C");},
+ a -> {
+ if (!a) {
+ try {
+ previousSnippetAndAssert(getHistory(), "class C {");
+ getHistory().add("class C{");
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ assertCommand(a, "int dummy;", "dummy ==> 0");
+ });
+ }
+
private EditingHistory getHistory() throws Exception {
Field input = repl.getClass().getDeclaredField("input");
input.setAccessible(true);
--- a/langtools/test/jdk/jshell/ToolBasicTest.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/jdk/jshell/ToolBasicTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347
+ * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317 8143955 8157953 8080347 8154714
* @summary Tests for Basic tests for REPL tool
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@@ -574,4 +574,20 @@
}
}
+ public void testAddExports() {
+ test(false, new String[]{"--no-startup"},
+ a -> assertCommandOutputStartsWith(a, "import jdk.internal.misc.VM;", "| Error:")
+ );
+ test(false, new String[]{"--no-startup",
+ "-R--add-exports", "-Rjava.base/jdk.internal.misc=ALL-UNNAMED",
+ "-C--add-exports", "-Cjava.base/jdk.internal.misc=ALL-UNNAMED"},
+ a -> assertImport(a, "import jdk.internal.misc.VM;", "", "jdk.internal.misc.VM"),
+ a -> assertCommand(a, "System.err.println(VM.isBooted())", "", "", null, "", "true\n")
+ );
+ test(false, new String[]{"--no-startup", "--add-exports", "java.base/jdk.internal.misc"},
+ a -> assertImport(a, "import jdk.internal.misc.VM;", "", "jdk.internal.misc.VM"),
+ a -> assertCommand(a, "System.err.println(VM.isBooted())", "", "", null, "", "true\n")
+ );
+ }
+
}
--- a/langtools/test/tools/javac/diags/examples.not-yet.txt Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/javac/diags/examples.not-yet.txt Wed Oct 05 06:28:22 2016 -0700
@@ -66,6 +66,7 @@
compiler.misc.kindname.type.variable.bound
compiler.misc.kindname.value
compiler.misc.incompatible.eq.lower.bounds # cannot happen?
+compiler.misc.module.name.mismatch
compiler.misc.no.unique.minimal.instance.exists
compiler.misc.no.unique.maximal.instance.exists # cannot happen?
compiler.misc.resume.abort # prompt for a response
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/bridges/ReorderedBoundsTest.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8166363
+ * @summary Method with reordered type parameter bounds compiles with Override annotation but does not actually override superclass method.
+ * @run main ReorderedBoundsTest
+ */
+
+import java.io.Serializable;
+
+public class ReorderedBoundsTest {
+ public static class Parent {
+ public <T extends Appendable & Serializable> String printClassName(T t) {
+ return "Parent";
+ }
+ }
+
+ public static class OrderedChild extends Parent {
+ @Override
+ public <T extends Appendable & Serializable> String printClassName(T t) {
+ return "OrderedChild";
+ }
+ }
+
+ public static class ReorderedChild extends Parent {
+ @Override
+ public <T extends Serializable & Appendable> String printClassName(T t) {
+ return "ReorderedChild";
+ }
+ }
+
+ public static void main(String[] args) {
+
+ String s;
+ Parent p = new OrderedChild();
+ if (!(s = p.printClassName(new StringBuilder())).equals("OrderedChild"))
+ throw new AssertionError("Bad output: " + s);
+
+ p = new ReorderedChild();
+ if (!(s = p.printClassName(new StringBuilder())).equals("ReorderedChild"))
+ throw new AssertionError("Bad output: " + s);
+ }
+}
\ No newline at end of file
--- a/langtools/test/tools/javac/modules/EdgeCases.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/javac/modules/EdgeCases.java Wed Oct 05 06:28:22 2016 -0700
@@ -58,6 +58,8 @@
import toolbox.JarTask;
import toolbox.JavacTask;
import toolbox.Task;
+import toolbox.Task.Expect;
+import toolbox.Task.OutputKind;
public class EdgeCases extends ModuleTestBase {
@@ -304,4 +306,147 @@
}
}
+ @Test
+ public void testImplicitJavaBase(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Path src_java_base = src.resolve("java.base");
+ Files.createDirectories(src_java_base);
+ tb.writeJavaFiles(src_java_base, "module java.base { exports java.lang; }");
+ tb.writeJavaFiles(src_java_base,
+ "package java.lang; public class Object {}");
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+
+ //module-info from source:
+ new JavacTask(tb)
+ .options("-sourcepath", src_java_base.toString())
+ .outdir(classes)
+ .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+ .run()
+ .writeAll();
+
+ //module-info from class:
+ if (!Files.exists(classes.resolve("module-info.class"))) {
+ throw new AssertionError("module-info.class not created!");
+ }
+
+ new JavacTask(tb)
+ .outdir(classes)
+ .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+ .run()
+ .writeAll();
+
+ //broken module-info.class:
+ Files.newOutputStream(classes.resolve("module-info.class")).close();
+
+ List<String> log = new JavacTask(tb)
+ .options("-XDrawDiagnostics")
+ .outdir(classes)
+ .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+ .run(Expect.FAIL)
+ .writeAll()
+ .getOutputLines(OutputKind.DIRECT);
+
+ List<String> expected = Arrays.asList(
+ "- compiler.err.cant.access: <error>.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.illegal.start.of.class.file))",
+ "1 error");
+
+ if (!expected.equals(log)) {
+ throw new AssertionError("Unexpected output: " + log);
+ }
+
+ //broken module-info.java:
+ Files.delete(classes.resolve("module-info.class"));
+
+ try (Writer out = Files.newBufferedWriter(src_java_base.resolve("module-info.java"))) {
+ out.write("class Broken {}");
+ }
+
+ log = new JavacTask(tb)
+ .options("-sourcepath", src_java_base.toString(),
+ "-XDrawDiagnostics")
+ .outdir(classes)
+ .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+ .run(Expect.FAIL)
+ .writeAll()
+ .getOutputLines(OutputKind.DIRECT);
+
+ expected = Arrays.asList("X");
+
+ if (expected.equals(log)) {
+ throw new AssertionError("Unexpected output: " + log);
+ }
+ }
+
+ @Test
+ public void testModuleInfoNameMismatchSource(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Path m1 = src.resolve("m1");
+ Files.createDirectories(m1);
+ tb.writeJavaFiles(m1, "module other { }",
+ "package test; public class Test {}");
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+
+ List<String> log = new JavacTask(tb)
+ .options("--module-source-path", src.toString(),
+ "-XDrawDiagnostics")
+ .outdir(classes)
+ .files(findJavaFiles(m1.resolve("test").resolve("Test.java")))
+ .run(Expect.FAIL)
+ .writeAll()
+ .getOutputLines(OutputKind.DIRECT);
+
+ List<String> expected = Arrays.asList(
+ "module-info.java:1:1: compiler.err.module.name.mismatch: other, m1",
+ "- compiler.err.cant.access: m1.module-info, (compiler.misc.cant.resolve.modules)",
+ "2 errors");
+
+ if (!expected.equals(log)) {
+ throw new AssertionError("Unexpected output: " + log);
+ }
+ }
+
+ @Test
+ public void testModuleInfoNameMismatchClass(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Files.createDirectories(src);
+ tb.writeJavaFiles(src, "module other { }",
+ "package test; public class Test {}");
+ Path classes = base.resolve("classes");
+ Path m1Classes = classes.resolve("m1");
+ tb.createDirectories(m1Classes);
+
+ new JavacTask(tb)
+ .outdir(m1Classes)
+ .files(findJavaFiles(src))
+ .run()
+ .writeAll()
+ .getOutputLines(OutputKind.DIRECT);
+
+ Path src2 = base.resolve("src2");
+ Files.createDirectories(src2);
+ tb.writeJavaFiles(src2, "module use { requires m1; }");
+
+ Path classes2 = base.resolve("classes2");
+ tb.createDirectories(classes2);
+
+ List<String> log = new JavacTask(tb)
+ .options("--module-path", classes.toString(),
+ "-XDrawDiagnostics")
+ .outdir(classes2)
+ .files(findJavaFiles(src2))
+ .run(Expect.FAIL)
+ .writeAll()
+ .getOutputLines(OutputKind.DIRECT);
+
+ List<String> expected = Arrays.asList(
+ "- compiler.err.cant.access: m1.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.name.mismatch: other, m1))",
+ "1 error");
+
+ if (!expected.equals(log)) {
+ throw new AssertionError("Unexpected output: " + log);
+ }
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/StopAfterError/StopAfterError.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8073844
+ * @summary If an error is produced by an annotation processor, the code should not be Attred, \
+ * unless requested
+ * @modules jdk.compiler
+ * @compile StopAfterError.java
+ * @compile/fail/ref=StopAfterError.out -XDrawDiagnostics -processor StopAfterError StopAfterErrorAux.java
+ * @compile/fail/ref=StopAfterError.out -XDshould-stop.ifError=PROCESS -XDrawDiagnostics -processor StopAfterError StopAfterErrorAux.java
+ * @compile/fail/ref=StopAfterErrorContinue.out -XDshould-stop.ifError=ATTR -XDrawDiagnostics -processor StopAfterError StopAfterErrorAux.java
+ */
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+@SupportedAnnotationTypes("*")
+public class StopAfterError extends AbstractProcessor {
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Stop!");
+ }
+ return false;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/StopAfterError/StopAfterError.out Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,2 @@
+- compiler.err.proc.messager: Stop!
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/StopAfterError/StopAfterErrorAux.java Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,6 @@
+/* /nodynamiccopyright/ */
+class StopAfterErrorAux {
+ public void test() {
+ should not; get here;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/StopAfterError/StopAfterErrorContinue.out Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,4 @@
+- compiler.err.proc.messager: Stop!
+StopAfterErrorAux.java:4:9: compiler.err.cant.resolve.location: kindname.class, should, , , (compiler.misc.location: kindname.class, StopAfterErrorAux, null)
+StopAfterErrorAux.java:4:21: compiler.err.cant.resolve.location: kindname.class, get, , , (compiler.misc.location: kindname.class, StopAfterErrorAux, null)
+3 errors
--- a/langtools/test/tools/jdeps/APIDeps.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/jdeps/APIDeps.java Wed Oct 05 06:28:22 2016 -0700
@@ -135,7 +135,7 @@
"java.util.Set",
"c.C", "d.D", "c.I", "e.E", "m.Bar"},
new String[] {"compact1", testDirBasename, mDir.getName()},
- new String[] {"-classpath", testDir.getPath(), "-verbose", "-P", "-apionly"});
+ new String[] {"-classpath", testDir.getPath(), "-verbose", "-P", "--api-only"});
return errors;
}
--- a/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java Wed Oct 05 06:28:22 2016 -0700
@@ -116,7 +116,7 @@
@Test
public void removedPackage() {
Path file = Paths.get("q", "RemovedPackage.class");
- String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString());
+ String[] output = JdepsUtil.jdeps("--jdk-internals", CLASSES_DIR.resolve(file).toString());
int i = 0;
// expect no replacement
while (i < output.length && !output[i].contains("Suggested Replacement")) {
--- a/langtools/test/tools/jdeps/modules/GenModuleInfo.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/GenModuleInfo.java Wed Oct 05 06:28:22 2016 -0700
@@ -23,7 +23,7 @@
/*
* @test
- * @summary Tests jdeps --gen-module-info option
+ * @summary Tests jdeps --generate-module-info option
* @library ../lib
* @build CompilerUtils JdepsUtil
* @modules jdk.jdeps/com.sun.tools.jdeps
@@ -106,7 +106,7 @@
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.map(Path::toString);
- JdepsUtil.jdeps(Stream.concat(Stream.of("--gen-module-info", DEST_DIR.toString()),
+ JdepsUtil.jdeps(Stream.concat(Stream.of("--generate-module-info", DEST_DIR.toString()),
files).toArray(String[]::new));
// check file exists
--- a/langtools/test/tools/jdeps/modules/InverseDeps.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/InverseDeps.java Wed Oct 05 06:28:22 2016 -0700
@@ -91,7 +91,7 @@
@DataProvider(name = "testrequires")
public Object[][] expected1() {
return new Object[][] {
- // -requires and result
+ // --require and result
{ "java.sql", new String[][] {
new String[] { "java.sql", "m5" },
}
@@ -117,7 +117,7 @@
@Test(dataProvider = "testrequires")
public void testrequires(String name, String[][] expected) throws Exception {
- String cmd1 = String.format("jdeps -inverse --module-path %s -requires %s --add-modules %s%n",
+ String cmd1 = String.format("jdeps --inverse --module-path %s --require %s --add-modules %s%n",
MODS_DIR, name, modules.stream().collect(Collectors.joining(",")));
try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd1)) {
@@ -128,7 +128,7 @@
runJdeps(jdeps, expected);
}
- String cmd2 = String.format("jdeps -inverse --module-path %s -requires %s" +
+ String cmd2 = String.format("jdeps --inverse --module-path %s --require %s" +
" --add-modules ALL-MODULE-PATH%n", LIBS_DIR, name);
// automatic module
@@ -164,7 +164,7 @@
@Test(dataProvider = "testpackage")
public void testpackage(String name, String[][] expected) throws Exception {
- String cmd = String.format("jdeps -inverse --module-path %s -package %s --add-modules %s%n",
+ String cmd = String.format("jdeps --inverse --module-path %s -package %s --add-modules %s%n",
MODS_DIR, name, modules.stream().collect(Collectors.joining(",")));
try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
jdeps.appModulePath(MODS_DIR.toString())
@@ -195,7 +195,7 @@
@Test(dataProvider = "testregex")
public void testregex(String name, String[][] expected) throws Exception {
- String cmd = String.format("jdeps -inverse --module-path %s -regex %s --add-modules %s%n",
+ String cmd = String.format("jdeps --inverse --module-path %s -regex %s --add-modules %s%n",
MODS_DIR, name, modules.stream().collect(Collectors.joining(",")));
try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
@@ -240,7 +240,7 @@
Path jarfile = LIBS_DIR.resolve("m7.jar");
- String cmd1 = String.format("jdeps -inverse -classpath %s -regex %s %s%n",
+ String cmd1 = String.format("jdeps --inverse -classpath %s -regex %s %s%n",
cpath, name, jarfile);
try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd1)) {
jdeps.verbose("-verbose:class")
@@ -253,7 +253,7 @@
Set<Path> paths = modules.stream()
.map(mn -> LIBS_DIR.resolve(mn + ".jar"))
.collect(Collectors.toSet());
- String cmd2 = String.format("jdeps -inverse -regex %s %s%n", name, paths);
+ String cmd2 = String.format("jdeps --inverse -regex %s %s%n", name, paths);
try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd2)) {
jdeps.verbose("-verbose:class").regex(name);
paths.forEach(jdeps::addRoot);
--- a/langtools/test/tools/jdeps/modules/src/m3/module-info.java Fri Sep 30 02:52:38 2016 -0700
+++ b/langtools/test/tools/jdeps/modules/src/m3/module-info.java Wed Oct 05 06:28:22 2016 -0700
@@ -24,7 +24,7 @@
module m3 {
requires public java.sql;
requires public m2;
- requires java.logging; // TODO: --gen-module-info to do transitive reduction
+ requires java.logging; // TODO: --generate-module-info to do transitive reduction
requires public m1;
exports p3;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/ExplodedImageOptimize.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+# Runs a tool on the exploded image to improve performance
+
+default: all
+
+include $(SPEC)
+include MakeBase.gmk
+include $(JDK_TOPDIR)/make/ModuleTools.gmk
+
+################################################################################
+
+PACKAGES_ATTRIBUTE_TARGET := $(JDK_OUTPUTDIR)/_packages_attribute.done
+ALL_MODULEINFO_CLASSES := $(wildcard $(JDK_OUTPUTDIR)/modules/*/module_info.class)
+
+$(PACKAGES_ATTRIBUTE_TARGET): $(ALL_MODULEINFO_CLASSES) $(BUILD_JIGSAW_CLASSES)
+ $(call LogInfo, Optimizing the exploded image)
+ $(TOOL_ADD_PACKAGES_ATTRIBUTE) $(JDK_OUTPUTDIR)
+
+TARGETS := $(PACKAGES_ATTRIBUTE_TARGET)
+
+################################################################################
+
+all: $(TARGETS)
+
+.PHONY: all default
--- a/make/Javadoc.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/make/Javadoc.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -1575,7 +1575,7 @@
$(JACCESSAPI_INDEX_HTML): $(JACCESSAPI_OPTIONS_FILE) $(JACCESSAPI_PACKAGES_FILE) $(COREAPI_INDEX_FILE)
$(prep-javadoc)
$(call JavadocSummary,$(JACCESSAPI_OPTIONS_FILE),$(JACCESSAPI_PACKAGES_FILE))
- $(JAVADOC_CMD) -d $(@D) \
+ $(JAVADOC_CMD_SMALL) -d $(@D) \
@$(JACCESSAPI_OPTIONS_FILE) @$(JACCESSAPI_PACKAGES_FILE)
# Create file with javadoc options in it
--- a/make/Main.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/make/Main.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -82,10 +82,13 @@
buildtools-jdk:
+($(CD) $(JDK_TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f CompileTools.gmk)
+
+ buildtools-modules:
+ +($(CD) $(JDK_TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f CompileModuleTools.gmk)
endif
ALL_TARGETS += buildtools-langtools interim-langtools \
- interim-rmic interim-cldrconverter buildtools-jdk
+ interim-rmic interim-cldrconverter buildtools-jdk buildtools-modules
################################################################################
# Special targets for certain modules
@@ -340,8 +343,12 @@
mac-bundles-jdk:
+($(CD) $(SRC_ROOT)/make && $(MAKE) $(MAKE_ARGS) -f MacBundles.gmk)
+exploded-image-optimize:
+ +($(CD) $(SRC_ROOT)/make && $(MAKE) $(MAKE_ARGS) -f ExplodedImageOptimize.gmk)
+
ALL_TARGETS += source-tips create-hgtip-files bootcycle-images zip-security \
- zip-source jrtfs-jar jimages profiles mac-bundles-jdk
+ zip-source jrtfs-jar jimages profiles mac-bundles-jdk \
+ exploded-image-optimize
################################################################################
# Docs targets
@@ -521,6 +528,8 @@
buildtools-jdk: interim-langtools interim-cldrconverter
+ buildtools-modules: exploded-image-base
+
$(CORBA_GENSRC_TARGETS): interim-langtools
$(HOTSPOT_GENSRC_TARGETS): interim-langtools
@@ -646,6 +655,7 @@
# Avoid calling create-buildjdk from within a create-buildjdk call
ifneq ($(CREATING_BUILDJDK), true)
$(JMOD_TARGETS): create-buildjdk
+ buildtools-modules: create-buildjdk
endif
endif
@@ -677,6 +687,8 @@
mac-bundles-jdk: jimages
+ exploded-image-optimize: exploded-image-base buildtools-modules
+
bootcycle-images: jimages
docs-javadoc: $(GENSRC_TARGETS) rmic
@@ -728,7 +740,7 @@
docs-bundles: docs-image
- generate-summary: jmods
+ generate-summary: jmods buildtools-modules
endif
@@ -774,7 +786,8 @@
samples: samples-jdk
# The "exploded image" is a locally runnable JDK in $(BUILD_OUTPUT)/jdk.
-exploded-image: $(ALL_MODULES)
+exploded-image-base: $(ALL_MODULES)
+exploded-image: exploded-image-base exploded-image-optimize
create-buildjdk: create-buildjdk-copy create-buildjdk-interim-image
@@ -815,7 +828,8 @@
all-bundles: product-bundles test-bundles docs-bundles
ALL_TARGETS += buildtools gensrc gendata copy java rmic libs launchers jmods \
- jdk.jdwp.agent-gensrc $(ALL_MODULES) demos samples exploded-image \
+ jdk.jdwp.agent-gensrc $(ALL_MODULES) demos samples \
+ exploded-image-base exploded-image \
create-buildjdk mac-bundles product-images docs-image test-image all-images \
all-bundles
--- a/make/MainSupport.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/make/MainSupport.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -137,7 +137,6 @@
define DeclareRecipeForModuleMakefile
ifeq ($$($1_MULTIPLE_MAKEFILES), true)
$2-$$($1_TARGET_SUFFIX): $2-$$($1_TARGET_SUFFIX)-$$(notdir $3)
- $1 += $2-$$($1_TARGET_SUFFIX)-$$(notdir $3)
$2-$$($1_TARGET_SUFFIX)-$$(notdir $3):
else
@@ -173,6 +172,12 @@
# Only declare recipes if there are makefiles to call
ifneq ($$($1_$2_TOPDIRS), )
+ # Add the top dir specific target to target list regardless of if recipe
+ # generation is disabled.
+ ifeq ($$($1_MULTIPLE_MAKEFILES), true)
+ $$(foreach d, $$($1_$2_TOPDIRS), \
+ $$(eval $1 += $2-$$($1_TARGET_SUFFIX)-$$(notdir $$d)))
+ endif
ifeq ($(NO_RECIPES),)
$$(foreach d, $$($1_$2_TOPDIRS), \
$$(eval $$(call DeclareRecipeForModuleMakefile,$1,$2,$$d)))
--- a/make/common/SetupJavaCompilers.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/make/common/SetupJavaCompilers.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -38,7 +38,10 @@
# and the interim javac, to be run by the boot jdk.
$(eval $(call SetupJavaCompiler,BOOT_JAVAC, \
JAVAC := $(JAVAC), \
- FLAGS := $(BOOT_JDK_SOURCETARGET) -XDignore.symbol.file=true -g \
+ FLAGS := \
+ $(JAVA_TOOL_FLAGS_SMALL) \
+ $(BOOT_JDK_SOURCETARGET) \
+ -XDignore.symbol.file=true -g \
-Xlint:all$(COMMA)-deprecation$(COMMA)-options -Werror, \
DISABLE_SJAVAC := true, \
))
--- a/nashorn/.hgtags Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/.hgtags Wed Oct 05 06:28:22 2016 -0700
@@ -371,3 +371,4 @@
cb00d5ef023a18a66fcb4311ed4474d4145c66e9 jdk-9+135
f11b8f5c4ccbf9c87d283815abac6c0117fba3c0 jdk-9+136
17ed43add2f9e3528686cd786ae2ed49c8ed36e9 jdk-9+137
+4a6ee1185fc821df063e4d1537fa7ad2ebe9eb02 jdk-9+138
--- a/nashorn/make/BuildNashorn.gmk Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/make/BuildNashorn.gmk Wed Oct 05 06:28:22 2016 -0700
@@ -26,7 +26,7 @@
# This must be the first rule
default: all
--include $(SPEC)
+include $(SPEC)
include MakeBase.gmk
include JarArchive.gmk
include JavaCompilation.gmk
@@ -42,7 +42,7 @@
# Need to use source and target 8 for nasgen to work.
$(eval $(call SetupJavaCompiler,GENERATE_NEWBYTECODE_DEBUG, \
- JVM := $(JAVA), \
+ JVM := $(JAVA_JAVAC), \
JAVAC := $(NEW_JAVAC), \
FLAGS := -g -source 9 -target 9 --upgrade-module-path "$(JDK_OUTPUTDIR)/modules/" \
--system none --module-source-path "$(MODULESOURCEPATH)", \
@@ -91,7 +91,7 @@
$(MKDIR) -p $(@D)
$(RM) -rf $(@D)/jdk $(@D)/netscape
$(CP) -R -p $(SUPPORT_OUTPUTDIR)/special_classes/jdk.scripting.nashorn/* $(@D)/
- $(JAVA) $(NASGEN_OPTIONS) \
+ $(JAVA_SMALL) $(NASGEN_OPTIONS) \
jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D)
$(TOUCH) $@
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 05 06:28:22 2016 -0700
@@ -2486,11 +2486,6 @@
}
@Override
- public boolean enterObjectNode(final ObjectNode objectNode) {
- return false;
- }
-
- @Override
public boolean enterDefault(final Node node) {
if (contains) {
return false;
@@ -2562,7 +2557,8 @@
oc = new FieldObjectCreator<Expression>(this, tuples) {
@Override
protected void loadValue(final Expression node, final Type type) {
- loadExpressionAsType(node, type);
+ // Use generic type in order to avoid conversion between object types
+ loadExpressionAsType(node, Type.generic(type));
}};
}
@@ -2578,10 +2574,7 @@
//handler
if (restOfProperty) {
final ContinuationInfo ci = getContinuationInfo();
- // Can be set at most once for a single rest-of method
- assert ci.getObjectLiteralMap() == null;
- ci.setObjectLiteralMap(oc.getMap());
- ci.setObjectLiteralStackDepth(method.getStackSize());
+ ci.setObjectLiteralMap(method.getStackSize(), oc.getMap());
}
method.dup();
@@ -5309,10 +5302,8 @@
private Type[] stackTypes;
// If non-null, this node should perform the requisite type conversion
private Type returnValueType;
- // If we are in the middle of an object literal initialization, we need to update the map
- private PropertyMap objectLiteralMap;
- // Object literal stack depth for object literal - not necessarily top if property is a tree
- private int objectLiteralStackDepth = -1;
+ // If we are in the middle of an object literal initialization, we need to update the property maps
+ private Map<Integer, PropertyMap> objectLiteralMaps;
// The line number at the continuation point
private int lineNumber;
// The active catch label, in case the continuation point is in a try/catch block
@@ -5364,20 +5355,15 @@
this.returnValueType = returnValueType;
}
- int getObjectLiteralStackDepth() {
- return objectLiteralStackDepth;
- }
-
- void setObjectLiteralStackDepth(final int objectLiteralStackDepth) {
- this.objectLiteralStackDepth = objectLiteralStackDepth;
- }
-
- PropertyMap getObjectLiteralMap() {
- return objectLiteralMap;
- }
-
- void setObjectLiteralMap(final PropertyMap objectLiteralMap) {
- this.objectLiteralMap = objectLiteralMap;
+ void setObjectLiteralMap(final int objectLiteralStackDepth, final PropertyMap objectLiteralMap) {
+ if (objectLiteralMaps == null) {
+ objectLiteralMaps = new HashMap<>();
+ }
+ objectLiteralMaps.put(objectLiteralStackDepth, objectLiteralMap);
+ }
+
+ PropertyMap getObjectLiteralMap(final int stackDepth) {
+ return objectLiteralMaps == null ? null : objectLiteralMaps.get(stackDepth);
}
@Override
@@ -5467,10 +5453,9 @@
final int[] stackStoreSpec = ci.getStackStoreSpec();
final Type[] stackTypes = ci.getStackTypes();
final boolean isStackEmpty = stackStoreSpec.length == 0;
- boolean replacedObjectLiteralMap = false;
+ int replacedObjectLiteralMaps = 0;
if(!isStackEmpty) {
// Load arguments on the stack
- final int objectLiteralStackDepth = ci.getObjectLiteralStackDepth();
for(int i = 0; i < stackStoreSpec.length; ++i) {
final int slot = stackStoreSpec[i];
method.load(lvarTypes.get(slot), slot);
@@ -5478,18 +5463,18 @@
// stack: s0=object literal being initialized
// change map of s0 so that the property we are initializing when we failed
// is now ci.returnValueType
- if (i == objectLiteralStackDepth) {
+ final PropertyMap map = ci.getObjectLiteralMap(i);
+ if (map != null) {
method.dup();
- assert ci.getObjectLiteralMap() != null;
assert ScriptObject.class.isAssignableFrom(method.peekType().getTypeClass()) : method.peekType().getTypeClass() + " is not a script object";
- loadConstant(ci.getObjectLiteralMap());
+ loadConstant(map);
method.invoke(ScriptObject.SET_MAP);
- replacedObjectLiteralMap = true;
+ replacedObjectLiteralMaps++;
}
}
}
- // Must have emitted the code for replacing the map of an object literal if we have a set object literal stack depth
- assert ci.getObjectLiteralStackDepth() == -1 || replacedObjectLiteralMap;
+ // Must have emitted the code for replacing all object literal maps
+ assert ci.objectLiteralMaps == null || ci.objectLiteralMaps.size() == replacedObjectLiteralMaps;
// Load RewriteException back.
method.load(rewriteExceptionType, lvarCount);
// Get rid of the stored reference
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SpillObjectCreator.java Wed Oct 05 06:28:22 2016 -0700
@@ -189,7 +189,8 @@
@Override
protected void loadValue(final Expression expr, final Type type) {
- codegen.loadExpressionAsType(expr, type);
+ // Use generic type in order to avoid conversion between object types
+ codegen.loadExpressionAsType(expr, Type.generic(type));
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed Oct 05 06:28:22 2016 -0700
@@ -473,22 +473,6 @@
}
@Override
- protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
- if (overrides && super.hasOwnProperty(NashornCallSiteDescriptor.getOperand(desc))) {
- try {
- final GuardedInvocation inv = super.findCallMethodMethod(desc, request);
- if (inv != null) {
- return inv;
- }
- } catch (final Exception e) {
- //ignored
- }
- }
-
- return findHook(desc, __call__);
- }
-
- @Override
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) {
final String name = NashornCallSiteDescriptor.getOperand(desc);
if (overrides && super.hasOwnProperty(name)) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Wed Oct 05 06:28:22 2016 -0700
@@ -80,7 +80,6 @@
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
-import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -1883,8 +1882,6 @@
return findCallMethod(desc, request);
case NEW:
return findNewMethod(desc, request);
- case CALL_METHOD:
- return findCallMethodMethod(desc, request);
default:
}
return null;
@@ -1920,32 +1917,6 @@
}
/**
- * Find an implementation for a CALL_METHOD operation. Note that Nashorn internally never uses
- * CALL_METHOD, but instead always emits two call sites in bytecode, one for GET_METHOD, and then another
- * one for CALL. Explicit support for CALL_METHOD is provided for the benefit of potential external
- * callers. The implementation itself actually folds a GET_METHOD method handle into a CALL method handle.
- *
- * @param desc the call site descriptor.
- * @param request the link request
- *
- * @return GuardedInvocation to be invoked at call site.
- */
- protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
- // R(P0, P1, ...)
- final MethodType callType = desc.getMethodType();
- // use type Object(P0) for the getter
- final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
- final GuardedInvocation getter = findGetMethod(getterType, request, StandardOperation.GET_METHOD);
-
- // Object(P0) => Object(P0, P1, ...)
- final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
- // R(Object, P0, P1, ...)
- final MethodHandle invoker = Bootstrap.createDynamicInvoker("", NashornCallSiteDescriptor.CALL, callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
- // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
- return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
- }
-
- /**
* Test whether this object contains in its prototype chain or is itself a with-object.
* @return true if a with-object was found
*/
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Wed Oct 05 06:28:22 2016 -0700
@@ -103,8 +103,6 @@
final String name = NashornCallSiteDescriptor.getOperand(desc);
final String msg = name != null? "not.a.function" : "cant.call.undefined";
throw typeError(msg, name);
- case CALL_METHOD:
- throw lookupTypeError("cant.read.property.of.undefined", desc);
case GET_PROPERTY:
case GET_ELEMENT:
case GET_METHOD:
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Fri Sep 30 02:52:38 2016 -0700
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Wed Oct 05 06:28:22 2016 -0700
@@ -86,13 +86,11 @@
private static final MethodHandle EMPTY_ELEM_SETTER =
MH.dropArguments(EMPTY_PROP_SETTER, 0, Object.class);
- private static final MethodHandle THROW_NO_SUCH_FUNCTION;
private static final MethodHandle THROW_STRICT_PROPERTY_SETTER;
private static final MethodHandle THROW_OPTIMISTIC_UNDEFINED;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
- THROW_NO_SUCH_FUNCTION = lookup.findOwnStatic("throwNoSuchFunction", Object.class, Object.class, Object.class);
THROW_STRICT_PROPERTY_SETTER = lookup.findOwnStatic("throwStrictPropertySetter", void.class, Object.class, Object.class);
THROW_OPTIMISTIC_UNDEFINED = lookup.findOwnStatic("throwOptimisticUndefined", Object.class, int.class);
}
@@ -130,8 +128,6 @@
if (op != null) {
final String operand = NashornCallSiteDescriptor.getOperand(desc);
switch (op) {
- case CALL_METHOD:
- return adaptThrower(bindOperand(THROW_NO_SUCH_FUNCTION, operand), desc);
case GET_METHOD:
case GET_PROPERTY:
case GET_ELEMENT: {
@@ -172,11 +168,6 @@
}
@SuppressWarnings("unused")
- private static Object throwNoSuchFunction(final Object self, final Object name) {
- throw createTypeError(self, name, "no.such.function");
- }
-
- @SuppressWarnings("unused")
private static void throwStrictPropertySetter(final Object self, final Object name) {
throw createTypeError(self, name, "cant.set.property");
}
@@ -230,7 +221,6 @@
case NEW:
case CALL:
throw typeError("not.a.function", "null");
- case CALL_METHOD:
case GET_METHOD:
throw typeError("no.such.function", getArgument(linkRequest), "null");
case GET_PROPERTY:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8166902.js Wed Oct 05 06:28:22 2016 -0700
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8166902: Nested object literal property maps not reset in optimistic recompilation
+ *
+ * @test
+ * @run
+ */
+
+var o = {
+ a: "A",
+ b: "B"
+};
+
+var m = {
+ x: { z: o.a },
+ y: o.b
+};
+
+Assert.assertEquals(m.x.z, "A");
+Assert.assertEquals(m.y, "B");
+