# HG changeset patch # User duke # Date 1499278648 -7200 # Node ID 4746a63313f638054e5b965f70fdb86accdf7967 # Parent ac59fe6cd98f75e049a9156af53a3be1b42f9cfc# Parent 4adaea63f46577e25a657314c1b025d1d66967a6 Merge diff -r ac59fe6cd98f -r 4746a63313f6 .hgtags-top-repo --- a/.hgtags-top-repo Thu Jan 29 15:36:21 2015 -0800 +++ b/.hgtags-top-repo Wed Jul 05 20:17:28 2017 +0200 @@ -290,3 +290,4 @@ 3dd628fde2086218d548841022ee8436b6b88185 jdk9-b45 12f1e276447bcc81516e85367d53e4f08897049d jdk9-b46 b6cca3e6175a69f39e5799b7349ddb0176630291 jdk9-b47 +0064e246d83f6f9fc245c19b6d05041ecaf4b6d4 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 common/autoconf/basics.m4 --- a/common/autoconf/basics.m4 Thu Jan 29 15:36:21 2015 -0800 +++ b/common/autoconf/basics.m4 Wed Jul 05 20:17:28 2017 +0200 @@ -987,3 +987,26 @@ IS_RECONFIGURE=no fi ]) + +# Check for support for specific options in bash +AC_DEFUN_ONCE([BASIC_CHECK_BASH_OPTIONS], +[ + # Test if bash supports pipefail. + AC_MSG_CHECKING([if bash supports pipefail]) + if ${BASH} -c 'set -o pipefail'; then + BASH_ARGS="$BASH_ARGS -o pipefail" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + + AC_MSG_CHECKING([if bash supports errexit (-e)]) + if ${BASH} -e -c 'true'; then + BASH_ARGS="$BASH_ARGS -e" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + + AC_SUBST(BASH_ARGS) +]) diff -r ac59fe6cd98f -r 4746a63313f6 common/autoconf/bootcycle-spec.gmk.in --- a/common/autoconf/bootcycle-spec.gmk.in Thu Jan 29 15:36:21 2015 -0800 +++ b/common/autoconf/bootcycle-spec.gmk.in Wed Jul 05 20:17:28 2017 +0200 @@ -46,8 +46,12 @@ BOOT_JDK := $(JDK_IMAGE_DIR) # The bootcycle build has a different output directory -BUILD_OUTPUT:=@BUILD_OUTPUT@/bootcycle-build -SJAVAC_SERVER_DIR:=$(subst @BUILD_OUTPUT@,$(BUILD_OUTPUT),$(SJAVAC_SERVER_DIR)) +OLD_BUILD_OUTPUT:=@BUILD_OUTPUT@ +BUILD_OUTPUT:=$(OLD_BUILD_OUTPUT)/bootcycle-build +# The HOTSPOT_DIST dir is not defined relative to BUILD_OUTPUT in spec.gmk. Must not +# use space in this patsubst to avoid leading space in HOTSPOT_DIST. +HOTSPOT_DIST:=$(patsubst $(OLD_BUILD_OUTPUT)%,$(BUILD_OUTPUT)%,$(HOTSPOT_DIST)) +SJAVAC_SERVER_DIR:=$(patsubst $(OLD_BUILD_OUTPUT)%, $(BUILD_OUTPUT)%, $(SJAVAC_SERVER_DIR)) JAVA_CMD:=$(BOOT_JDK)/bin/java JAVAC_CMD:=$(BOOT_JDK)/bin/javac diff -r ac59fe6cd98f -r 4746a63313f6 common/autoconf/configure.ac --- a/common/autoconf/configure.ac Thu Jan 29 15:36:21 2015 -0800 +++ b/common/autoconf/configure.ac Wed Jul 05 20:17:28 2017 +0200 @@ -113,6 +113,7 @@ # Setup tools that requires more complex handling, or that is not needed by the configure script. BASIC_SETUP_COMPLEX_TOOLS +BASIC_CHECK_BASH_OPTIONS # Check if pkg-config is available. PKG_PROG_PKG_CONFIG diff -r ac59fe6cd98f -r 4746a63313f6 common/autoconf/generated-configure.sh --- a/common/autoconf/generated-configure.sh Thu Jan 29 15:36:21 2015 -0800 +++ b/common/autoconf/generated-configure.sh Wed Jul 05 20:17:28 2017 +0200 @@ -853,6 +853,7 @@ OS_VERSION_MINOR OS_VERSION_MAJOR PKG_CONFIG +BASH_ARGS CODESIGN XATTR DSYMUTIL @@ -3522,6 +3523,9 @@ +# Check for support for specific options in bash + + # # Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -4329,7 +4333,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1420811523 +DATE_WHEN_GENERATED=1421247827 ############################################################################### # @@ -19609,6 +19613,32 @@ fi + # Test if bash supports pipefail. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if bash supports pipefail" >&5 +$as_echo_n "checking if bash supports pipefail... " >&6; } + if ${BASH} -c 'set -o pipefail'; then + BASH_ARGS="$BASH_ARGS -o pipefail" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if bash supports errexit (-e)" >&5 +$as_echo_n "checking if bash supports errexit (-e)... " >&6; } + if ${BASH} -e -c 'true'; then + BASH_ARGS="$BASH_ARGS -e" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + + + # Check if pkg-config is available. @@ -27408,8 +27438,8 @@ # The trailing space for everyone except PATH is no typo, but is needed due # to trailing \ in the Windows paths. These will be stripped later. $ECHO "$WINPATH_BASH -c 'echo VS_PATH="'\"$PATH\" > set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE - $ECHO "$WINPATH_BASH -c 'echo VS_INCLUDE="'\"$INCLUDE\;$include \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE - $ECHO "$WINPATH_BASH -c 'echo VS_LIB="'\"$LIB\;$lib \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE + $ECHO "$WINPATH_BASH -c 'echo VS_INCLUDE="'\"$INCLUDE \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE + $ECHO "$WINPATH_BASH -c 'echo VS_LIB="'\"$LIB \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE $ECHO "$WINPATH_BASH -c 'echo VCINSTALLDIR="'\"$VCINSTALLDIR \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE $ECHO "$WINPATH_BASH -c 'echo WindowsSdkDir="'\"$WindowsSdkDir \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE $ECHO "$WINPATH_BASH -c 'echo WINDOWSSDKDIR="'\"$WINDOWSSDKDIR \" >> set-vs-env.sh' >> $EXTRACT_VC_ENV_BAT_FILE diff -r ac59fe6cd98f -r 4746a63313f6 common/autoconf/spec.gmk.in --- a/common/autoconf/spec.gmk.in Thu Jan 29 15:36:21 2015 -0800 +++ b/common/autoconf/spec.gmk.in Wed Jul 05 20:17:28 2017 +0200 @@ -78,6 +78,11 @@ OUTPUT_SYNC_SUPPORTED:=@OUTPUT_SYNC_SUPPORTED@ OUTPUT_SYNC:=@OUTPUT_SYNC@ +# Override the shell with bash +BASH:=@BASH@ +BASH_ARGS:=@BASH_ARGS@ +SHELL:=$(BASH) $(BASH_ARGS) + # The "human readable" name of this configuration CONF_NAME:=@CONF_NAME@ @@ -243,7 +248,7 @@ HOTSPOT_OUTPUTDIR=$(BUILD_OUTPUT)/hotspot JDK_OUTPUTDIR=$(BUILD_OUTPUT)/jdk IMAGES_OUTPUTDIR=$(BUILD_OUTPUT)/images -TESTMAKE_OUTPUTDIR=$(BUILD_OUTPUT)/testmake +TESTMAKE_OUTPUTDIR=$(BUILD_OUTPUT)/test-make MAKESUPPORT_OUTPUTDIR=$(BUILD_OUTPUT)/make-support HOTSPOT_DIST=@HOTSPOT_DIST@ @@ -495,7 +500,6 @@ # Tools adhering to a minimal and common standard of posix compliance. AWK:=@AWK@ BASENAME:=@BASENAME@ -BASH:=@BASH@ CAT:=@CAT@ CCACHE:=@CCACHE@ # CD is going away, but remains to cater for legacy makefiles. diff -r ac59fe6cd98f -r 4746a63313f6 corba/.hgtags --- a/corba/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/corba/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -290,3 +290,4 @@ 9e3f2bed80c0e5a84a256ce41f1d10c5ade48466 jdk9-b45 326f2068b4a4c05e2fa27d6acf93eba7b54b090d jdk9-b46 ee8447ca632e1d39180b4767c749db101bff7314 jdk9-b47 +a13c49c5f2899b702652a460ed7aa73123e671e6 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/.hgtags --- a/hotspot/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -450,3 +450,4 @@ 5dc8184af1e2bb30b0103113d1f1a58a21a80c37 jdk9-b45 a184ee1d717297bd35b7c3e35393e137921a3ed2 jdk9-b46 3b241fb72b8925b75941d612db762a6d5da66d02 jdk9-b47 +cc775a4a24c7f5d9e624b4205e9fbd48a17331f6 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/aix/Makefile --- a/hotspot/make/aix/Makefile Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/aix/Makefile Wed Jul 05 20:17:28 2017 +0200 @@ -246,8 +246,7 @@ XSLT_CHECK = $(REMOTE) $(RUN.JAVAP) javax.xml.transform.TransformerFactory # If not found then fail fast. check_j2se_version: - $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ - if [ $$? -ne 0 ]; then \ + $(QUIETLY) if ! $(XSLT_CHECK) > /dev/null 2>&1; then \ $(REMOTE) $(RUN.JAVA) -version; \ echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ "to bootstrap this build" 1>&2; \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/aix/makefiles/xlc.make --- a/hotspot/make/aix/makefiles/xlc.make Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/aix/makefiles/xlc.make Wed Jul 05 20:17:28 2017 +0200 @@ -74,6 +74,12 @@ # no xlc counterpart for -fcheck-new # CFLAGS += -fcheck-new +# We need to define this on the command line if we want to use the the +# predefined format specifiers from "inttypes.h". Otherwise system headrs +# can indirectly include inttypes.h before we define __STDC_FORMAT_MACROS +# in globalDefinitions.hpp +CFLAGS += -D__STDC_FORMAT_MACROS + ARCHFLAG = -q64 CFLAGS += $(ARCHFLAG) diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/bsd/Makefile --- a/hotspot/make/bsd/Makefile Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/bsd/Makefile Wed Jul 05 20:17:28 2017 +0200 @@ -240,8 +240,7 @@ XSLT_CHECK = $(REMOTE) $(RUN.JAVAP) javax.xml.transform.TransformerFactory # If not found then fail fast. check_j2se_version: - $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ - if [ $$? -ne 0 ]; then \ + $(QUIETLY) if ! $(XSLT_CHECK) > /dev/null 2>&1; then \ $(REMOTE) $(RUN.JAVA) -version; \ echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ "to bootstrap this build" 1>&2; \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/bsd/makefiles/dtrace.make --- a/hotspot/make/bsd/makefiles/dtrace.make Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/bsd/makefiles/dtrace.make Wed Jul 05 20:17:28 2017 +0200 @@ -179,23 +179,23 @@ # $@.tmp is created first to avoid an empty $(JVMOFFS).h if an error occurs. $(JVMOFFS).h: $(GENOFFS) $(QUIETLY) DYLD_LIBRARY_PATH=.:$(DYLD_LIBRARY_PATH) ./$(GENOFFS) -header > $@.tmp; touch $@; \ - if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ - then rm -f $@; mv $@.tmp $@; \ - else rm -f $@.tmp; \ + if diff $@.tmp $@ > /dev/null 2>&1 ; \ + then rm -f $@.tmp; \ + else rm -f $@; mv $@.tmp $@; \ fi $(JVMOFFS)Index.h: $(GENOFFS) $(QUIETLY) DYLD_LIBRARY_PATH=.:$(DYLD_LIBRARY_PATH) ./$(GENOFFS) -index > $@.tmp; touch $@; \ - if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ - then rm -f $@; mv $@.tmp $@; \ - else rm -f $@.tmp; \ + if diff $@.tmp $@ > /dev/null 2>&1 ; \ + then rm -f $@.tmp; \ + else rm -f $@; mv $@.tmp $@; \ fi $(JVMOFFS).cpp: $(GENOFFS) $(JVMOFFS).h $(JVMOFFS)Index.h $(QUIETLY) DYLD_LIBRARY_PATH=.:$(DYLD_LIBRARY_PATH) ./$(GENOFFS) -table > $@.tmp; touch $@; \ - if [ `diff $@.tmp $@ > /dev/null 2>&1; echo $$?` -ne 0 ] ; \ - then rm -f $@; mv $@.tmp $@; \ - else rm -f $@.tmp; \ + if diff $@.tmp $@ > /dev/null 2>&1; \ + then rm -f $@.tmp; \ + else rm -f $@; mv $@.tmp $@; \ fi $(JVMOFFS.o): $(JVMOFFS).h $(JVMOFFS).cpp diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/bsd/makefiles/universal.gmk --- a/hotspot/make/bsd/makefiles/universal.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/bsd/makefiles/universal.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -59,7 +59,7 @@ # Package built libraries in a universal binary $(UNIVERSAL_LIPO_LIST): - BUILT_LIPO_FILES="`find $(EXPORT_JRE_LIB_DIR)/{i386,amd64}/$(subst $(EXPORT_JRE_LIB_DIR)/,,$@) 2>/dev/null`"; \ + BUILT_LIPO_FILES="`find $(EXPORT_JRE_LIB_DIR)/{i386,amd64}/$(subst $(EXPORT_JRE_LIB_DIR)/,,$@) 2>/dev/null`" || test $$? = "1"; \ if [ -n "$${BUILT_LIPO_FILES}" ]; then \ $(MKDIR) -p $(shell dirname $@); \ lipo -create -output $@ $${BUILT_LIPO_FILES}; \ @@ -70,7 +70,7 @@ # - copies directories; including empty dirs # - copies files, symlinks, other non-directory files $(UNIVERSAL_COPY_LIST): - BUILT_COPY_FILES="`find $(EXPORT_JRE_LIB_DIR)/{i386,amd64}/$(subst $(EXPORT_JRE_LIB_DIR)/,,$@) -prune 2>/dev/null`"; \ + BUILT_COPY_FILES="`find $(EXPORT_JRE_LIB_DIR)/{i386,amd64}/$(subst $(EXPORT_JRE_LIB_DIR)/,,$@) -prune 2>/dev/null`" || test $$? = "1"; \ if [ -n "$${BUILT_COPY_FILES}" ]; then \ for i in $${BUILT_COPY_FILES}; do \ $(MKDIR) -p $(shell dirname $@); \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/linux/Makefile --- a/hotspot/make/linux/Makefile Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/linux/Makefile Wed Jul 05 20:17:28 2017 +0200 @@ -246,8 +246,7 @@ XSLT_CHECK = $(REMOTE) $(RUN.JAVAP) javax.xml.transform.TransformerFactory # If not found then fail fast. check_j2se_version: - $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ - if [ $$? -ne 0 ]; then \ + $(QUIETLY) if ! $(XSLT_CHECK) > /dev/null 2>&1; then \ $(REMOTE) $(RUN.JAVA) -version; \ echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ "to bootstrap this build" 1>&2; \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/linux/makefiles/vm.make --- a/hotspot/make/linux/makefiles/vm.make Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/linux/makefiles/vm.make Wed Jul 05 20:17:28 2017 +0200 @@ -334,10 +334,8 @@ rm -f $@.1; ln -s $@ $@.1; \ if [ \"$(CROSS_COMPILE_ARCH)\" = \"\" ] ; then \ if [ -x /usr/sbin/selinuxenabled ] ; then \ - /usr/sbin/selinuxenabled; \ - if [ $$? = 0 ] ; then \ - /usr/bin/chcon -t textrel_shlib_t $@; \ - if [ $$? != 0 ]; then \ + if /usr/sbin/selinuxenabled; then \ + if ! /usr/bin/chcon -t textrel_shlib_t $@; then \ echo "ERROR: Cannot chcon $@"; \ fi \ fi \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/sa.files --- a/hotspot/make/sa.files Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/sa.files Wed Jul 05 20:17:28 2017 +0200 @@ -39,6 +39,7 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/c1/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/ci/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/classfile/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/code/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/compiler/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/*.java \ @@ -49,8 +50,10 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dummy/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/ia64/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ia64/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ppc64/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/x86/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/sparc/*.java \ @@ -71,6 +74,7 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/win32/coff/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/ia64/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/x86/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windows/x86/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windows/amd64/*.java \ @@ -101,6 +105,8 @@ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_x86/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/sparc/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/x86/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_amd64/*.java \ +$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_x86/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/ppc64/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/*.java \ $(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/jcore/*.java \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/solaris/Makefile --- a/hotspot/make/solaris/Makefile Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/solaris/Makefile Wed Jul 05 20:17:28 2017 +0200 @@ -190,8 +190,7 @@ XSLT_CHECK = $(RUN.JAVAP) javax.xml.transform.TransformerFactory # If not found then fail fast. check_j2se_version: - $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ - if [ $$? -ne 0 ]; then \ + $(QUIETLY) if ! $(XSLT_CHECK) > /dev/null 2>&1; then \ $(RUN.JAVA) -version; \ echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ "to bootstrap this build" 1>&2; \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/make/solaris/makefiles/dtrace.make --- a/hotspot/make/solaris/makefiles/dtrace.make Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/make/solaris/makefiles/dtrace.make Wed Jul 05 20:17:28 2017 +0200 @@ -171,11 +171,11 @@ ./lib$(GENOFFS).so CONDITIONALLY_UPDATE_JVMOFFS_TARGET = \ - cmp -s $@ $@.tmp; \ - case $$? in \ - 0) rm -f $@.tmp;; \ - *) rm -f $@ && mv $@.tmp $@ && echo Updated $@;; \ - esac + if cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + rm -f $@ && mv $@.tmp $@ && echo Updated $@; \ + fi # $@.tmp is created first to avoid an empty $(JVMOFFS).h if an error occurs. $(JVMOFFS).h: $(GENOFFS) diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -567,16 +567,21 @@ inline void load_with_trap_null_check(Register d, int si16, Register s1); // Load heap oop and decompress. Loaded oop may not be null. - inline void load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1 = noreg); + // Specify tmp to save one cycle. + inline void load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1 = noreg, + Register tmp = noreg); + // Store heap oop and decompress. Decompressed oop may not be null. + // Specify tmp register if d should not be changed. inline void store_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1, - /*specify if d must stay uncompressed*/ Register tmp = noreg); + Register tmp = noreg); // Null allowed. inline void load_heap_oop(Register d, RegisterOrConstant offs, Register s1 = noreg); // Encode/decode heap oop. Oop may not be null, else en/decoding goes wrong. + // src == d allowed. inline Register encode_heap_oop_not_null(Register d, Register src = noreg); - inline void decode_heap_oop_not_null(Register d); + inline Register decode_heap_oop_not_null(Register d, Register src = noreg); // Null allowed. inline void decode_heap_oop(Register d); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -311,11 +311,14 @@ ld(d, si16, s1); } -inline void MacroAssembler::load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1) { +inline void MacroAssembler::load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1, Register tmp) { if (UseCompressedOops) { - lwz(d, offs, s1); + // In disjoint mode decoding can save a cycle if src != dst. + Register narrowOop = (tmp != noreg && Universe::narrow_oop_base_disjoint()) ? tmp : d; + lwz(narrowOop, offs, s1); // Attention: no null check here! - decode_heap_oop_not_null(d); + Register res = decode_heap_oop_not_null(d, narrowOop); + assert(res == d, "caller will not consume loaded value"); } else { ld(d, offs, s1); } @@ -340,26 +343,36 @@ } inline Register MacroAssembler::encode_heap_oop_not_null(Register d, Register src) { - Register current = (src!=noreg) ? src : d; // Compressed oop is in d if no src provided. - if (Universe::narrow_oop_base() != NULL) { + Register current = (src != noreg) ? src : d; // Oop to be compressed is in d if no src provided. + if (Universe::narrow_oop_base_overlaps()) { sub(d, current, R30); current = d; } if (Universe::narrow_oop_shift() != 0) { - srdi(d, current, LogMinObjAlignmentInBytes); + rldicl(d, current, 64-Universe::narrow_oop_shift(), 32); // Clears the upper bits. current = d; } return current; // Encoded oop is in this register. } -inline void MacroAssembler::decode_heap_oop_not_null(Register d) { +inline Register MacroAssembler::decode_heap_oop_not_null(Register d, Register src) { + if (Universe::narrow_oop_base_disjoint() && src != noreg && src != d && + Universe::narrow_oop_shift() != 0) { + mr(d, R30); + rldimi(d, src, Universe::narrow_oop_shift(), 32-Universe::narrow_oop_shift()); + return d; + } + + Register current = (src != noreg) ? src : d; // Compressed oop is in d if no src provided. if (Universe::narrow_oop_shift() != 0) { - assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); - sldi(d, d, LogMinObjAlignmentInBytes); + sldi(d, current, Universe::narrow_oop_shift()); + current = d; } if (Universe::narrow_oop_base() != NULL) { - add(d, d, R30); + add(d, current, R30); + current = d; } + return current; // Decoded oop is in this register. } inline void MacroAssembler::decode_heap_oop(Register d) { @@ -368,13 +381,7 @@ cmpwi(CCR0, d, 0); beq(CCR0, isNull); } - if (Universe::narrow_oop_shift() != 0) { - assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); - sldi(d, d, LogMinObjAlignmentInBytes); - } - if (Universe::narrow_oop_base() != NULL) { - add(d, d, R30); - } + decode_heap_oop_not_null(d); bind(isNull); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp --- a/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -172,15 +172,15 @@ // Load the invoker, as MH -> MH.form -> LF.vmentry __ verify_oop(recv); - __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes()), recv); + __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes()), recv, temp2); __ verify_oop(method_temp); - __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes()), method_temp); + __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes()), method_temp, temp2); __ verify_oop(method_temp); - // the following assumes that a Method* is normally compressed in the vmtarget field: + // The following assumes that a Method* is normally compressed in the vmtarget field: __ ld(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()), method_temp); if (VerifyMethodHandles && !for_compiler_entry) { - // make sure recv is already on stack + // Make sure recv is already on stack. __ ld(temp2, in_bytes(Method::const_offset()), method_temp); __ load_sized_value(temp2, in_bytes(ConstMethod::size_of_parameters_offset()), temp2, sizeof(u2), /*is_signed*/ false); @@ -259,8 +259,9 @@ } if (TraceMethodHandles) { - if (tmp_mh != noreg) + if (tmp_mh != noreg) { __ mr(R23_method_handle, tmp_mh); // make stub happy + } trace_method_handle_interpreter_entry(_masm, iid); } @@ -332,7 +333,7 @@ if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { Label L_ok; Register temp2_defc = temp2; - __ load_heap_oop_not_null(temp2_defc, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg); + __ load_heap_oop_not_null(temp2_defc, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg, temp3); load_klass_from_Class(_masm, temp2_defc, temp3, temp4); __ verify_klass_ptr(temp2_defc); __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, temp4, L_ok); @@ -407,7 +408,7 @@ } Register temp2_intf = temp2; - __ load_heap_oop_not_null(temp2_intf, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg); + __ load_heap_oop_not_null(temp2_intf, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg, temp3); load_klass_from_Class(_masm, temp2_intf, temp3, temp4); __ verify_klass_ptr(temp2_intf); @@ -464,7 +465,7 @@ strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH const char* mh_reg_name = has_mh ? "R23_method_handle" : "G23"; tty->print_cr("MH %s %s="INTPTR_FORMAT " sp=" INTPTR_FORMAT, - adaptername, mh_reg_name, (intptr_t) mh, (intptr_t) entry_sp); + adaptername, mh_reg_name, (intptr_t) mh, entry_sp); if (Verbose) { tty->print_cr("Registers:"); @@ -535,23 +536,22 @@ BLOCK_COMMENT("trace_method_handle {"); - int nbytes_save = 10 * 8; // 10 volatile gprs - __ save_LR_CR(R0); - __ mr(R0, R1_SP); // saved_sp - assert(Assembler::is_simm(-nbytes_save, 16), "Overwriting R0"); - // Push_frame_reg_args only uses R0 if nbytes_save is wider than 16 bit. - __ push_frame_reg_args(nbytes_save, R0); - __ save_volatile_gprs(R1_SP, frame::abi_reg_args_size); // Except R0. + const Register tmp = R11; // Will be preserved. + const int nbytes_save = 11*8; // volatile gprs except R0 + __ save_volatile_gprs(R1_SP, -nbytes_save); // except R0 + __ save_LR_CR(tmp); // save in old frame - __ load_const(R3_ARG1, (address)adaptername); + __ mr(R5_ARG3, R1_SP); // saved_sp + __ push_frame_reg_args(nbytes_save, tmp); + + __ load_const_optimized(R3_ARG1, (address)adaptername, tmp); __ mr(R4_ARG2, R23_method_handle); - __ mr(R5_ARG3, R0); // saved_sp __ mr(R6_ARG4, R1_SP); __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub)); - __ restore_volatile_gprs(R1_SP, 112); // Except R0. __ pop_frame(); - __ restore_LR_CR(R0); + __ restore_LR_CR(tmp); + __ restore_volatile_gprs(R1_SP, -nbytes_save); // except R0 BLOCK_COMMENT("} trace_method_handle"); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/ppc/vm/ppc.ad --- a/hotspot/src/cpu/ppc/vm/ppc.ad Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/ppc/vm/ppc.ad Wed Jul 05 20:17:28 2017 +0200 @@ -1,6 +1,6 @@ // -// Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. -// Copyright 2012, 2014 SAP AG. All rights reserved. +// Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +// Copyright 2012, 2015 SAP AG. 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 @@ -2698,7 +2698,7 @@ const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); __ relocate(a.rspec()); } else if (constant_reloc == relocInfo::metadata_type) { - AddressLiteral a = __ allocate_metadata_address((Metadata *)val); + AddressLiteral a = __ constant_metadata_address((Metadata *)val); const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); __ relocate(a.rspec()); } else { @@ -2727,7 +2727,7 @@ const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); __ relocate(a.rspec()); } else if (constant_reloc == relocInfo::metadata_type) { - AddressLiteral a = __ allocate_metadata_address((Metadata *)val); + AddressLiteral a = __ constant_metadata_address((Metadata *)val); const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); __ relocate(a.rspec()); } else { // non-oop pointers, e.g. card mark base, heap top @@ -6029,6 +6029,20 @@ ins_pipe(pipe_class_default); %} +// Optimize DecodeN for disjoint base. +// Load base of compressed oops into a register +instruct loadBase(iRegLdst dst) %{ + effect(DEF dst); + + format %{ "MR $dst, r30_heapbase" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr($dst$$Register, R30); + %} + ins_pipe(pipe_class_default); +%} + // Loading ConN must be postalloc expanded so that edges between // the nodes are safe. They may not interfere with a safepoint. // GL TODO: This needs three instructions: better put this into the constant pool. @@ -6724,13 +6738,12 @@ ins_pipe(pipe_class_default); %} -// base != 0 -// 32G aligned narrow oop base. -instruct encodeP_32GAligned(iRegNdst dst, iRegPsrc src) %{ +// Disjoint narrow oop base. +instruct encodeP_Disjoint(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodeP src)); - predicate(false /* TODO: PPC port Universe::narrow_oop_base_disjoint()*/); - - format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with 32G aligned base" %} + predicate(Universe::narrow_oop_base_disjoint()); + + format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with disjoint base" %} size(4); ins_encode %{ // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); @@ -6745,7 +6758,7 @@ effect(TEMP crx); predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull && Universe::narrow_oop_shift() != 0 && - true /* TODO: PPC port Universe::narrow_oop_base_overlaps()*/); + Universe::narrow_oop_base_overlaps()); format %{ "EncodeP $dst, $crx, $src \t// postalloc expanded" %} postalloc_expand( postalloc_expand_encode_oop(dst, src, crx)); @@ -6756,7 +6769,7 @@ match(Set dst (EncodeP src)); predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull && Universe::narrow_oop_shift() != 0 && - true /* TODO: PPC port Universe::narrow_oop_base_overlaps()*/); + Universe::narrow_oop_base_overlaps()); format %{ "EncodeP $dst, $src\t// $src != Null, postalloc expanded" %} postalloc_expand( postalloc_expand_encode_oop_not_null(dst, src) ); @@ -6876,6 +6889,7 @@ n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && Universe::narrow_oop_shift() != 0 && Universe::narrow_oop_base() != 0); + ins_cost(4 * DEFAULT_COST); // Should be more expensive than decodeN_Disjoint_isel_Ex. effect(TEMP crx); format %{ "DecodeN $dst, $src \t// Kills $crx, postalloc expanded" %} @@ -6897,6 +6911,106 @@ ins_pipe(pipe_class_default); %} +// Optimize DecodeN for disjoint base. +// Shift narrow oop and or it into register that already contains the heap base. +// Base == dst must hold, and is assured by construction in postaloc_expand. +instruct decodeN_mergeDisjoint(iRegPdst dst, iRegNsrc src, iRegLsrc base) %{ + match(Set dst (DecodeN src)); + effect(TEMP base); + predicate(false); + + format %{ "RLDIMI $dst, $src, shift, 32-shift \t// DecodeN (disjoint base)" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldimi); + __ rldimi($dst$$Register, $src$$Register, Universe::narrow_oop_shift(), 32-Universe::narrow_oop_shift()); + %} + ins_pipe(pipe_class_default); +%} + +// Optimize DecodeN for disjoint base. +// This node requires only one cycle on the critical path. +// We must postalloc_expand as we can not express use_def effects where +// the used register is L and the def'ed register P. +instruct decodeN_Disjoint_notNull_Ex(iRegPdst dst, iRegNsrc src) %{ + match(Set dst (DecodeN src)); + effect(TEMP_DEF dst); + predicate((n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull || + n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant) && + Universe::narrow_oop_base_disjoint()); + ins_cost(DEFAULT_COST); + + format %{ "MOV $dst, R30 \t\n" + "RLDIMI $dst, $src, shift, 32-shift \t// decode with disjoint base" %} + postalloc_expand %{ + loadBaseNode *n1 = new loadBaseNode(); + n1->add_req(NULL); + n1->_opnds[0] = op_dst; + + decodeN_mergeDisjointNode *n2 = new decodeN_mergeDisjointNode(); + n2->add_req(n_region, n_src, n1); + n2->_opnds[0] = op_dst; + n2->_opnds[1] = op_src; + n2->_opnds[2] = op_dst; + n2->_bottom_type = _bottom_type; + + ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n1); + nodes->push(n2); + %} +%} + +instruct decodeN_Disjoint_isel_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ + match(Set dst (DecodeN src)); + effect(TEMP_DEF dst, TEMP crx); + predicate((n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull && + n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && + Universe::narrow_oop_base_disjoint() && VM_Version::has_isel()); + ins_cost(3 * DEFAULT_COST); + + format %{ "DecodeN $dst, $src \t// decode with disjoint base using isel" %} + postalloc_expand %{ + loadBaseNode *n1 = new loadBaseNode(); + n1->add_req(NULL); + n1->_opnds[0] = op_dst; + + cmpN_reg_imm0Node *n_compare = new cmpN_reg_imm0Node(); + n_compare->add_req(n_region, n_src); + n_compare->_opnds[0] = op_crx; + n_compare->_opnds[1] = op_src; + n_compare->_opnds[2] = new immN_0Oper(TypeNarrowOop::NULL_PTR); + + decodeN_mergeDisjointNode *n2 = new decodeN_mergeDisjointNode(); + n2->add_req(n_region, n_src, n1); + n2->_opnds[0] = op_dst; + n2->_opnds[1] = op_src; + n2->_opnds[2] = op_dst; + n2->_bottom_type = _bottom_type; + + cond_set_0_ptrNode *n_cond_set = new cond_set_0_ptrNode(); + n_cond_set->add_req(n_region, n_compare, n2); + n_cond_set->_opnds[0] = op_dst; + n_cond_set->_opnds[1] = op_crx; + n_cond_set->_opnds[2] = op_dst; + n_cond_set->_bottom_type = _bottom_type; + + assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); + ra_->set_oop(n_cond_set, true); + + ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); + ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n1); + nodes->push(n_compare); + nodes->push(n2); + nodes->push(n_cond_set); + %} +%} + // src != 0, shift != 0, base != 0 instruct decodeN_notNull_addBase_Ex(iRegPdst dst, iRegNsrc src) %{ match(Set dst (DecodeN src)); @@ -6904,6 +7018,7 @@ n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant) && Universe::narrow_oop_shift() != 0 && Universe::narrow_oop_base() != 0); + ins_cost(2 * DEFAULT_COST); format %{ "DecodeN $dst, $src \t// $src != NULL, postalloc expanded" %} postalloc_expand( postalloc_expand_decode_oop_not_null(dst, src)); @@ -6973,13 +7088,12 @@ ins_pipe(pipe_class_default); %} -// base != 0 -// 32G aligned narrow oop base. -instruct encodePKlass_32GAligned(iRegNdst dst, iRegPsrc src) %{ +// Disjoint narrow oop base. +instruct encodePKlass_Disjoint(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodePKlass src)); predicate(false /* TODO: PPC port Universe::narrow_klass_base_disjoint()*/); - format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with 32G aligned base" %} + format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with disjoint base" %} size(4); ins_encode %{ // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); @@ -7486,7 +7600,7 @@ ins_encode %{ // TODO: PPC port $archOpcode(ppc64Opcode_compound); __ cmpxchgd($crx$$CondRegister, R0, $oldVal$$Register, $newVal$$Register, $mem_ptr$$Register, - MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + MacroAssembler::MemBarAcq, MacroAssembler::cmpxchgx_hint_atomic_update(), noreg, NULL, true); %} ins_pipe(pipe_class_default); @@ -10476,7 +10590,7 @@ match(Set crx (CmpN src1 src2)); size(4); - ins_cost(DEFAULT_COST); + ins_cost(2); format %{ "CMPLW $crx, $src1, $src2 \t// compressed ptr" %} ins_encode %{ // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); @@ -10488,7 +10602,7 @@ instruct cmpN_reg_imm0(flagsReg crx, iRegNsrc src1, immN_0 src2) %{ match(Set crx (CmpN src1 src2)); // Make this more expensive than zeroCheckN_iReg_imm0. - ins_cost(DEFAULT_COST); + ins_cost(2); format %{ "CMPLWI $crx, $src1, $src2 \t// compressed ptr" %} size(4); @@ -10508,6 +10622,7 @@ _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne && _leaf->as_If()->_prob >= PROB_LIKELY_MAG(4) && Matcher::branches_to_uncommon_trap(_leaf)); + ins_cost(1); // Should not be cheaper than zeroCheckN. ins_is_TrapBasedCheckNode(true); @@ -10889,7 +11004,7 @@ instruct partialSubtypeCheck(iRegPdst result, iRegP_N2P subklass, iRegP_N2P superklass, iRegPdst tmp_klass, iRegPdst tmp_arrayptr) %{ match(Set result (PartialSubtypeCheck subklass superklass)); - effect(TEMP result, TEMP tmp_klass, TEMP tmp_arrayptr); + effect(TEMP_DEF result, TEMP tmp_klass, TEMP tmp_arrayptr); ins_cost(DEFAULT_COST*10); format %{ "PartialSubtypeCheck $result = ($subklass instanceOf $superklass) tmp: $tmp_klass, $tmp_arrayptr" %} @@ -11000,7 +11115,7 @@ predicate(SpecialStringIndexOf); // type check implicit by parameter type, See Matcher::match_rule_supported match(Set result (StrIndexOf (Binary haystack haycnt) (Binary (AddP needleImm offsetImm) needlecntImm))); - effect(TEMP result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1); + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1); ins_cost(150); format %{ "String IndexOf CSCL1 $haystack[0..$haycnt], $needleImm+$offsetImm[0..$needlecntImm]" @@ -11037,7 +11152,7 @@ iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); - effect(USE_KILL needle, /* TDEF needle, */ TEMP result, + effect(USE_KILL needle, /* TDEF needle, */ TEMP_DEF result, TEMP tmp1, TEMP tmp2); // Required for EA: check if it is still a type_array. predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && @@ -11084,7 +11199,7 @@ iRegIdst tmp1, iRegIdst tmp2, iRegIdst tmp3, iRegIdst tmp4, iRegIdst tmp5, flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); - effect(USE_KILL haycnt, /* better: TDEF haycnt, */ TEMP result, + effect(USE_KILL haycnt, /* better: TDEF haycnt, */ TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6); // Required for EA: check if it is still a type_array. predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && @@ -11118,7 +11233,7 @@ flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecnt))); effect(USE_KILL haycnt, USE_KILL needlecnt, /*better: TDEF haycnt, TDEF needlecnt,*/ - TEMP result, + TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr0, KILL cr1, KILL cr6); predicate(SpecialStringIndexOf); // See Matcher::match_rule_supported. ins_cost(300); @@ -11142,7 +11257,7 @@ iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0, flagsRegCR6 cr6, regCTR ctr) %{ match(Set result (StrEquals (Binary str1 str2) cntImm)); - effect(TEMP result, TEMP tmp1, TEMP tmp2, + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr6, KILL ctr); predicate(SpecialStringEquals); // See Matcher::match_rule_supported. ins_cost(250); @@ -11165,7 +11280,7 @@ iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, iRegPdst tmp4, iRegPdst tmp5, flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6, regCTR ctr) %{ match(Set result (StrEquals (Binary str1 str2) cnt)); - effect(TEMP result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6, KILL ctr); predicate(SpecialStringEquals); // See Matcher::match_rule_supported. ins_cost(300); @@ -11188,7 +11303,7 @@ instruct string_compare(rarg1RegP str1, rarg2RegP str2, rarg3RegI cnt1, rarg4RegI cnt2, iRegIdst result, iRegPdst tmp, flagsRegCR0 cr0, regCTR ctr) %{ match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); - effect(USE_KILL cnt1, USE_KILL cnt2, USE_KILL str1, USE_KILL str2, TEMP result, TEMP tmp, KILL cr0, KILL ctr); + effect(USE_KILL cnt1, USE_KILL cnt2, USE_KILL str1, USE_KILL str2, TEMP_DEF result, TEMP tmp, KILL cr0, KILL ctr); ins_cost(300); ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -483,15 +483,6 @@ } -jbyte* G1PostBarrierStub::_byte_map_base = NULL; - -jbyte* G1PostBarrierStub::byte_map_base_slow() { - BarrierSet* bs = Universe::heap()->barrier_set(); - assert(bs->is_a(BarrierSet::G1SATBCTLogging), - "Must be if we're using this."); - return ((G1SATBCardTableModRefBS*)bs)->byte_map_base; -} - void G1PostBarrierStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1374,6 +1374,7 @@ } void InterpreterMacroAssembler::test_invocation_counter_for_mdp(Register invocation_count, + Register method_counters, Register Rtmp, Label &profile_continue) { assert(ProfileInterpreter, "must be profiling interpreter"); @@ -1386,9 +1387,8 @@ br_notnull_short(ImethodDataPtr, Assembler::pn, done); // Test to see if we should create a method data oop - AddressLiteral profile_limit((address) &InvocationCounter::InterpreterProfileLimit); - sethi(profile_limit, Rtmp); - ld(Rtmp, profile_limit.low10(), Rtmp); + Address profile_limit(method_counters, MethodCounters::interpreter_profile_limit_offset()); + ld(profile_limit, Rtmp); cmp(invocation_count, Rtmp); // Use long branches because call_VM() code and following code generated by // test_backedge_count_for_osr() is large in debug VM. @@ -2375,6 +2375,7 @@ #ifndef CC_INTERP void InterpreterMacroAssembler::test_backedge_count_for_osr( Register backedge_count, + Register method_counters, Register branch_bcp, Register Rtmp ) { Label did_not_overflow; @@ -2382,8 +2383,8 @@ assert_different_registers(backedge_count, Rtmp, branch_bcp); assert(UseOnStackReplacement,"Must UseOnStackReplacement to test_backedge_count_for_osr"); - AddressLiteral limit(&InvocationCounter::InterpreterBackwardBranchLimit); - load_contents(limit, Rtmp); + Address limit(method_counters, in_bytes(MethodCounters::interpreter_backward_branch_limit_offset())); + ld(limit, Rtmp); cmp_and_br_short(backedge_count, Rtmp, Assembler::lessUnsigned, Assembler::pt, did_not_overflow); // When ProfileInterpreter is on, the backedge_count comes from the @@ -2500,17 +2501,13 @@ // Jump if ((*counter_addr += increment) & mask) satisfies the condition. void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask_addr, Register scratch1, Register scratch2, Condition cond, Label *where) { ld(counter_addr, scratch1); add(scratch1, increment, scratch1); - if (is_simm13(mask)) { - andcc(scratch1, mask, G0); - } else { - set(mask, scratch2); - andcc(scratch1, scratch2, G0); - } + ld(mask_addr, scratch2); + andcc(scratch1, scratch2, G0); br(cond, false, Assembler::pn, *where); delayed()->st(scratch1, counter_addr); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp --- a/hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/sparc/vm/interp_masm_sparc.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -267,7 +267,7 @@ void increment_invocation_counter( Register Rcounters, Register Rtmp, Register Rtmp2 ); void increment_backedge_counter( Register Rcounters, Register Rtmp, Register Rtmp2 ); #ifndef CC_INTERP - void test_backedge_count_for_osr( Register backedge_count, Register branch_bcp, Register Rtmp ); + void test_backedge_count_for_osr(Register backedge_count, Register method_counters, Register branch_bcp, Register Rtmp ); #endif /* CC_INTERP */ // Object locking @@ -280,7 +280,7 @@ void set_method_data_pointer_for_bcp(); void test_method_data_pointer(Label& zero_continue); void verify_method_data_pointer(); - void test_invocation_counter_for_mdp(Register invocation_count, Register Rtmp, Label &profile_continue); + void test_invocation_counter_for_mdp(Register invocation_count, Register method_counters, Register Rtmp, Label &profile_continue); void set_mdp_data_at(int constant, Register value); void increment_mdp_data_at(Address counter, Register bumped_count, @@ -291,7 +291,7 @@ Register bumped_count, Register scratch2, bool decrement = false); void increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask_addr, Register scratch1, Register scratch2, Condition cond, Label *where); void set_mdp_flag_at(int flag_constant, Register scratch); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -282,12 +282,11 @@ void InterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { // Note: In tiered we increment either counters in MethodCounters* or in // MDO depending if we're profiling or not. - const Register Rcounters = G3_scratch; + const Register G3_method_counters = G3_scratch; Label done; if (TieredCompilation) { const int increment = InvocationCounter::count_increment; - const int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; Label no_mdo; if (ProfileInterpreter) { // If no method data exists, go to profile_continue. @@ -297,6 +296,7 @@ Address mdo_invocation_counter(G4_scratch, in_bytes(MethodData::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + Address mask(G4_scratch, in_bytes(MethodData::invoke_mask_offset())); __ increment_mask_and_jump(mdo_invocation_counter, increment, mask, G3_scratch, Lscratch, Assembler::zero, overflow); @@ -305,20 +305,21 @@ // Increment counter in MethodCounters* __ bind(no_mdo); - Address invocation_counter(Rcounters, + Address invocation_counter(G3_method_counters, in_bytes(MethodCounters::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); - __ get_method_counters(Lmethod, Rcounters, done); + __ get_method_counters(Lmethod, G3_method_counters, done); + Address mask(G3_method_counters, in_bytes(MethodCounters::invoke_mask_offset())); __ increment_mask_and_jump(invocation_counter, increment, mask, G4_scratch, Lscratch, Assembler::zero, overflow); __ bind(done); - } else { + } else { // not TieredCompilation // Update standard invocation counters - __ get_method_counters(Lmethod, Rcounters, done); - __ increment_invocation_counter(Rcounters, O0, G4_scratch); + __ get_method_counters(Lmethod, G3_method_counters, done); + __ increment_invocation_counter(G3_method_counters, O0, G4_scratch); if (ProfileInterpreter) { - Address interpreter_invocation_counter(Rcounters, + Address interpreter_invocation_counter(G3_method_counters, in_bytes(MethodCounters::interpreter_invocation_counter_offset())); __ ld(interpreter_invocation_counter, G4_scratch); __ inc(G4_scratch); @@ -327,16 +328,16 @@ if (ProfileInterpreter && profile_method != NULL) { // Test to see if we should create a method data oop - AddressLiteral profile_limit((address)&InvocationCounter::InterpreterProfileLimit); - __ load_contents(profile_limit, G3_scratch); - __ cmp_and_br_short(O0, G3_scratch, Assembler::lessUnsigned, Assembler::pn, *profile_method_continue); + Address profile_limit(G3_method_counters, in_bytes(MethodCounters::interpreter_profile_limit_offset())); + __ ld(profile_limit, G1_scratch); + __ cmp_and_br_short(O0, G1_scratch, Assembler::lessUnsigned, Assembler::pn, *profile_method_continue); // if no method data exists, go to profile_method __ test_method_data_pointer(*profile_method); } - AddressLiteral invocation_limit((address)&InvocationCounter::InterpreterInvocationLimit); - __ load_contents(invocation_limit, G3_scratch); + Address invocation_limit(G3_method_counters, in_bytes(MethodCounters::interpreter_invocation_limit_offset())); + __ ld(invocation_limit, G3_scratch); __ cmp(O0, G3_scratch); __ br(Assembler::greaterEqualUnsigned, false, Assembler::pn, *overflow); // Far distance __ delayed()->nop(); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1599,13 +1599,12 @@ // Bump bytecode pointer by displacement (take the branch) __ delayed()->add( O1_disp, Lbcp, Lbcp ); // add to bc addr - const Register Rcounters = G3_scratch; - __ get_method_counters(Lmethod, Rcounters, Lforward); + const Register G3_method_counters = G3_scratch; + __ get_method_counters(Lmethod, G3_method_counters, Lforward); if (TieredCompilation) { Label Lno_mdo, Loverflow; int increment = InvocationCounter::count_increment; - int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; if (ProfileInterpreter) { // If no method data exists, go to profile_continue. __ ld_ptr(Lmethod, Method::method_data_offset(), G4_scratch); @@ -1614,6 +1613,7 @@ // Increment backedge counter in the MDO Address mdo_backedge_counter(G4_scratch, in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + Address mask(G4_scratch, in_bytes(MethodData::backedge_mask_offset())); __ increment_mask_and_jump(mdo_backedge_counter, increment, mask, G3_scratch, O0, Assembler::notZero, &Lforward); __ ba_short(Loverflow); @@ -1621,9 +1621,10 @@ // If there's no MDO, increment counter in MethodCounters* __ bind(Lno_mdo); - Address backedge_counter(Rcounters, + Address backedge_counter(G3_method_counters, in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + Address mask(G3_method_counters, in_bytes(MethodCounters::backedge_mask_offset())); __ increment_mask_and_jump(backedge_counter, increment, mask, G4_scratch, O0, Assembler::notZero, &Lforward); __ bind(Loverflow); @@ -1663,18 +1664,19 @@ __ jmp(O2, G0); __ delayed()->nop(); - } else { + } else { // not TieredCompilation // Update Backedge branch separately from invocations const Register G4_invoke_ctr = G4; - __ increment_backedge_counter(Rcounters, G4_invoke_ctr, G1_scratch); + __ increment_backedge_counter(G3_method_counters, G4_invoke_ctr, G1_scratch); if (ProfileInterpreter) { - __ test_invocation_counter_for_mdp(G4_invoke_ctr, G3_scratch, Lforward); + __ test_invocation_counter_for_mdp(G4_invoke_ctr, G3_method_counters, G1_scratch, Lforward); if (UseOnStackReplacement) { - __ test_backedge_count_for_osr(O2_bumped_count, l_cur_bcp, G3_scratch); + + __ test_backedge_count_for_osr(O2_bumped_count, G3_method_counters, l_cur_bcp, G1_scratch); } } else { if (UseOnStackReplacement) { - __ test_backedge_count_for_osr(G4_invoke_ctr, l_cur_bcp, G3_scratch); + __ test_backedge_count_for_osr(G4_invoke_ctr, G3_method_counters, l_cur_bcp, G1_scratch); } } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp --- a/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -541,15 +541,6 @@ } -jbyte* G1PostBarrierStub::_byte_map_base = NULL; - -jbyte* G1PostBarrierStub::byte_map_base_slow() { - BarrierSet* bs = Universe::heap()->barrier_set(); - assert(bs->is_a(BarrierSet::G1SATBCTLogging), - "Must be if we're using this."); - return ((G1SATBCardTableModRefBS*)bs)->byte_map_base; -} - void G1PostBarrierStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); assert(addr()->is_register(), "Precondition."); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp --- a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1360,7 +1360,7 @@ // Jump if ((*counter_addr += increment) & mask) satisfies the condition. void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask, Register scratch, bool preloaded, Condition cond, Label* where) { if (!preloaded) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp --- a/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -182,7 +182,7 @@ void increment_mdp_data_at(Register mdp_in, Register reg, int constant, bool decrement = false); void increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask, Register scratch, bool preloaded, Condition cond, Label* where); void set_mdp_flag_at(Register mdp_in, int flag_constant); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp --- a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1426,7 +1426,7 @@ // Jump if ((*counter_addr += increment) & mask) satisfies the condition. void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask, Register scratch, bool preloaded, Condition cond, Label* where) { if (!preloaded) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp --- a/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -191,7 +191,7 @@ void increment_mdp_data_at(Register mdp_in, Register reg, int constant, bool decrement = false); void increment_mask_and_jump(Address counter_addr, - int increment, int mask, + int increment, Address mask, Register scratch, bool preloaded, Condition cond, Label* where); void set_mdp_flag_at(Register mdp_in, int flag_constant); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -346,7 +346,6 @@ // depending if we're profiling or not. if (TieredCompilation) { int increment = InvocationCounter::count_increment; - int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; Label no_mdo; if (ProfileInterpreter) { // Are we profiling? @@ -356,6 +355,7 @@ // Increment counter in the MDO const Address mdo_invocation_counter(rax, in_bytes(MethodData::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + const Address mask(rax, in_bytes(MethodData::invoke_mask_offset())); __ increment_mask_and_jump(mdo_invocation_counter, increment, mask, rcx, false, Assembler::zero, overflow); __ jmp(done); } @@ -366,11 +366,12 @@ InvocationCounter::counter_offset()); __ get_method_counters(rbx, rax, done); + const Address mask(rax, in_bytes(MethodCounters::invoke_mask_offset())); __ increment_mask_and_jump(invocation_counter, increment, mask, rcx, false, Assembler::zero, overflow); __ bind(done); - } else { - const Address backedge_counter (rax, + } else { // not TieredCompilation + const Address backedge_counter(rax, MethodCounters::backedge_counter_offset() + InvocationCounter::counter_offset()); const Address invocation_counter(rax, @@ -400,16 +401,16 @@ if (ProfileInterpreter && profile_method != NULL) { // Test to see if we should create a method data oop - __ cmp32(rcx, - ExternalAddress((address)&InvocationCounter::InterpreterProfileLimit)); + __ movptr(rax, Address(rbx, Method::method_counters_offset())); + __ cmp32(rcx, Address(rax, in_bytes(MethodCounters::interpreter_profile_limit_offset()))); __ jcc(Assembler::less, *profile_method_continue); // if no method data exists, go to profile_method __ test_method_data_pointer(rax, *profile_method); } - __ cmp32(rcx, - ExternalAddress((address)&InvocationCounter::InterpreterInvocationLimit)); + __ movptr(rax, Address(rbx, Method::method_counters_offset())); + __ cmp32(rcx, Address(rax, in_bytes(MethodCounters::interpreter_invocation_limit_offset()))); __ jcc(Assembler::aboveEqual, *overflow); __ bind(done); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -299,7 +299,6 @@ // Note: In tiered we increment either counters in Method* or in MDO depending if we're profiling or not. if (TieredCompilation) { int increment = InvocationCounter::count_increment; - int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; Label no_mdo; if (ProfileInterpreter) { // Are we profiling? @@ -309,6 +308,7 @@ // Increment counter in the MDO const Address mdo_invocation_counter(rax, in_bytes(MethodData::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + const Address mask(rax, in_bytes(MethodData::invoke_mask_offset())); __ increment_mask_and_jump(mdo_invocation_counter, increment, mask, rcx, false, Assembler::zero, overflow); __ jmp(done); } @@ -318,10 +318,11 @@ MethodCounters::invocation_counter_offset() + InvocationCounter::counter_offset()); __ get_method_counters(rbx, rax, done); + const Address mask(rax, in_bytes(MethodCounters::invoke_mask_offset())); __ increment_mask_and_jump(invocation_counter, increment, mask, rcx, false, Assembler::zero, overflow); __ bind(done); - } else { + } else { // not TieredCompilation const Address backedge_counter(rax, MethodCounters::backedge_counter_offset() + InvocationCounter::counter_offset()); @@ -350,14 +351,16 @@ if (ProfileInterpreter && profile_method != NULL) { // Test to see if we should create a method data oop - __ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreterProfileLimit)); + __ movptr(rax, Address(rbx, Method::method_counters_offset())); + __ cmp32(rcx, Address(rax, in_bytes(MethodCounters::interpreter_profile_limit_offset()))); __ jcc(Assembler::less, *profile_method_continue); // if no method data exists, go to profile_method __ test_method_data_pointer(rax, *profile_method); } - __ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreterInvocationLimit)); + __ movptr(rax, Address(rbx, Method::method_counters_offset())); + __ cmp32(rcx, Address(rax, in_bytes(MethodCounters::interpreter_invocation_limit_offset()))); __ jcc(Assembler::aboveEqual, *overflow); __ bind(done); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp --- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1621,7 +1621,6 @@ if (TieredCompilation) { Label no_mdo; int increment = InvocationCounter::count_increment; - int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; if (ProfileInterpreter) { // Are we profiling? __ movptr(rbx, Address(rcx, in_bytes(Method::method_data_offset()))); @@ -1630,6 +1629,7 @@ // Increment the MDO backedge counter const Address mdo_backedge_counter(rbx, in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + const Address mask(rbx, in_bytes(MethodData::backedge_mask_offset())); __ increment_mask_and_jump(mdo_backedge_counter, increment, mask, rax, false, Assembler::zero, &backedge_counter_overflow); __ jmp(dispatch); @@ -1637,9 +1637,10 @@ __ bind(no_mdo); // Increment backedge counter in MethodCounters* __ movptr(rcx, Address(rcx, Method::method_counters_offset())); + const Address mask(rcx, in_bytes(MethodCounters::backedge_mask_offset())); __ increment_mask_and_jump(Address(rcx, be_offset), increment, mask, rax, false, Assembler::zero, &backedge_counter_overflow); - } else { + } else { // not TieredCompilation // increment counter __ movptr(rcx, Address(rcx, Method::method_counters_offset())); __ movl(rax, Address(rcx, be_offset)); // load backedge counter @@ -1653,8 +1654,7 @@ if (ProfileInterpreter) { // Test to see if we should create a method data oop - __ cmp32(rax, - ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit)); + __ cmp32(rax, Address(rcx, in_bytes(MethodCounters::interpreter_profile_limit_offset()))); __ jcc(Assembler::less, dispatch); // if no method data exists, go to profile method @@ -1662,8 +1662,7 @@ if (UseOnStackReplacement) { // check for overflow against rbx, which is the MDO taken count - __ cmp32(rbx, - ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ cmp32(rbx, Address(rcx, in_bytes(MethodCounters::interpreter_backward_branch_limit_offset()))); __ jcc(Assembler::below, dispatch); // When ProfileInterpreter is on, the backedge_count comes from the @@ -1678,8 +1677,7 @@ } else { if (UseOnStackReplacement) { // check for overflow against rax, which is the sum of the counters - __ cmp32(rax, - ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ cmp32(rax, Address(rcx, in_bytes(MethodCounters::interpreter_backward_branch_limit_offset()))); __ jcc(Assembler::aboveEqual, backedge_counter_overflow); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp --- a/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1642,7 +1642,6 @@ if (TieredCompilation) { Label no_mdo; int increment = InvocationCounter::count_increment; - int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; if (ProfileInterpreter) { // Are we profiling? __ movptr(rbx, Address(rcx, in_bytes(Method::method_data_offset()))); @@ -1651,6 +1650,7 @@ // Increment the MDO backedge counter const Address mdo_backedge_counter(rbx, in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset())); + const Address mask(rbx, in_bytes(MethodData::backedge_mask_offset())); __ increment_mask_and_jump(mdo_backedge_counter, increment, mask, rax, false, Assembler::zero, &backedge_counter_overflow); __ jmp(dispatch); @@ -1658,9 +1658,10 @@ __ bind(no_mdo); // Increment backedge counter in MethodCounters* __ movptr(rcx, Address(rcx, Method::method_counters_offset())); + const Address mask(rcx, in_bytes(MethodCounters::backedge_mask_offset())); __ increment_mask_and_jump(Address(rcx, be_offset), increment, mask, rax, false, Assembler::zero, &backedge_counter_overflow); - } else { + } else { // not TieredCompilation // increment counter __ movptr(rcx, Address(rcx, Method::method_counters_offset())); __ movl(rax, Address(rcx, be_offset)); // load backedge counter @@ -1674,8 +1675,7 @@ if (ProfileInterpreter) { // Test to see if we should create a method data oop - __ cmp32(rax, - ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit)); + __ cmp32(rax, Address(rcx, in_bytes(MethodCounters::interpreter_profile_limit_offset()))); __ jcc(Assembler::less, dispatch); // if no method data exists, go to profile method @@ -1683,8 +1683,7 @@ if (UseOnStackReplacement) { // check for overflow against ebx which is the MDO taken count - __ cmp32(rbx, - ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ cmp32(rbx, Address(rcx, in_bytes(MethodCounters::interpreter_backward_branch_limit_offset()))); __ jcc(Assembler::below, dispatch); // When ProfileInterpreter is on, the backedge_count comes @@ -1702,8 +1701,7 @@ if (UseOnStackReplacement) { // check for overflow against eax, which is the sum of the // counters - __ cmp32(rax, - ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit)); + __ cmp32(rax, Address(rcx, in_bytes(MethodCounters::interpreter_backward_branch_limit_offset()))); __ jcc(Assembler::aboveEqual, backedge_counter_overflow); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/os/aix/vm/perfMemory_aix.cpp --- a/hotspot/src/os/aix/vm/perfMemory_aix.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/os/aix/vm/perfMemory_aix.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -31,6 +31,7 @@ #include "os_aix.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/perfMemory.hpp" +#include "services/memTracker.hpp" #include "utilities/exceptions.hpp" // put OS-includes here @@ -196,12 +197,37 @@ return pid; } +// Check if the given statbuf is considered a secure directory for +// the backing store files. Returns true if the directory is considered +// a secure location. Returns false if the statbuf is a symbolic link or +// if an error occurred. +static bool is_statbuf_secure(struct stat *statp) { + if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) { + // The path represents a link or some non-directory file type, + // which is not what we expected. Declare it insecure. + // + return false; + } + // We have an existing directory, check if the permissions are safe. + if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) { + // The directory is open for writing and could be subjected + // to a symlink or a hard link attack. Declare it insecure. + return false; + } + // See if the uid of the directory matches the effective uid of the process. + // + if (statp->st_uid != geteuid()) { + // The directory was not created by this user, declare it insecure. + return false; + } + return true; +} -// check if the given path is considered a secure directory for + +// Check if the given path is considered a secure directory for // the backing store files. Returns true if the directory exists // and is considered a secure location. Returns false if the path // is a symbolic link or if an error occurred. -// static bool is_directory_secure(const char* path) { struct stat statbuf; int result = 0; @@ -211,38 +237,276 @@ return false; } - // the path exists, now check it's mode - if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) { - // the path represents a link or some non-directory file type, - // which is not what we expected. declare it insecure. - // + // The path exists, see if it is secure. + return is_statbuf_secure(&statbuf); +} + +// (Taken over from Solaris to support the O_NOFOLLOW case on AIX.) +// Check if the given directory file descriptor is considered a secure +// directory for the backing store files. Returns true if the directory +// exists and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occurred. +static bool is_dirfd_secure(int dir_fd) { + struct stat statbuf; + int result = 0; + + RESTARTABLE(::fstat(dir_fd, &statbuf), result); + if (result == OS_ERR) { + return false; + } + + // The path exists, now check its mode. + return is_statbuf_secure(&statbuf); +} + + +// Check to make sure fd1 and fd2 are referencing the same file system object. +static bool is_same_fsobject(int fd1, int fd2) { + struct stat statbuf1; + struct stat statbuf2; + int result = 0; + + RESTARTABLE(::fstat(fd1, &statbuf1), result); + if (result == OS_ERR) { + return false; + } + RESTARTABLE(::fstat(fd2, &statbuf2), result); + if (result == OS_ERR) { + return false; + } + + if ((statbuf1.st_ino == statbuf2.st_ino) && + (statbuf1.st_dev == statbuf2.st_dev)) { + return true; + } else { return false; } - else { - // we have an existing directory, check if the permissions are safe. - // - if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) { - // the directory is open for writing and could be subjected - // to a symlnk attack. declare it insecure. - // - return false; +} + +// Helper functions for open without O_NOFOLLOW which is not present on AIX 5.3/6.1. +// We use the jdk6 implementation here. +#ifndef O_NOFOLLOW +// The O_NOFOLLOW oflag doesn't exist before solaris 5.10, this is to simulate that behaviour +// was done in jdk 5/6 hotspot by Oracle this way +static int open_o_nofollow_impl(const char* path, int oflag, mode_t mode, bool use_mode) { + struct stat orig_st; + struct stat new_st; + bool create; + int error; + int fd; + + create = false; + + if (lstat(path, &orig_st) != 0) { + if (errno == ENOENT && (oflag & O_CREAT) != 0) { + // File doesn't exist, but_we want to create it, add O_EXCL flag + // to make sure no-one creates it (or a symlink) before us + // This works as we expect with symlinks, from posix man page: + // 'If O_EXCL and O_CREAT are set, and path names a symbolic + // link, open() shall fail and set errno to [EEXIST]'. + oflag |= O_EXCL; + create = true; + } else { + // File doesn't exist, and we are not creating it. + return OS_ERR; + } + } else { + // Lstat success, check if existing file is a link. + if ((orig_st.st_mode & S_IFMT) == S_IFLNK) { + // File is a symlink. + errno = ELOOP; + return OS_ERR; + } + } + + if (use_mode == true) { + fd = open(path, oflag, mode); + } else { + fd = open(path, oflag); + } + + if (fd == OS_ERR) { + return fd; + } + + // Can't do inode checks on before/after if we created the file. + if (create == false) { + if (fstat(fd, &new_st) != 0) { + // Keep errno from fstat, in case close also fails. + error = errno; + ::close(fd); + errno = error; + return OS_ERR; + } + + if (orig_st.st_dev != new_st.st_dev || orig_st.st_ino != new_st.st_ino) { + // File was tampered with during race window. + ::close(fd); + errno = EEXIST; + if (PrintMiscellaneous && Verbose) { + warning("possible file tampering attempt detected when opening %s", path); + } + return OS_ERR; } } + + return fd; +} + +static int open_o_nofollow(const char* path, int oflag, mode_t mode) { + return open_o_nofollow_impl(path, oflag, mode, true); +} + +static int open_o_nofollow(const char* path, int oflag) { + return open_o_nofollow_impl(path, oflag, 0, false); +} +#endif + +// Open the directory of the given path and validate it. +// Return a DIR * of the open directory. +static DIR *open_directory_secure(const char* dirname) { + // Open the directory using open() so that it can be verified + // to be secure by calling is_dirfd_secure(), opendir() and then check + // to see if they are the same file system object. This method does not + // introduce a window of opportunity for the directory to be attacked that + // calling opendir() and is_directory_secure() does. + int result; + DIR *dirp = NULL; + + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case. +#ifdef O_NOFOLLOW + RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result); +#else + // workaround (jdk6 coding) + RESTARTABLE(::open_o_nofollow(dirname, O_RDONLY), result); +#endif + + if (result == OS_ERR) { + // Directory doesn't exist or is a symlink, so there is nothing to cleanup. + if (PrintMiscellaneous && Verbose) { + if (errno == ELOOP) { + warning("directory %s is a symlink and is not secure\n", dirname); + } else { + warning("could not open directory %s: %s\n", dirname, strerror(errno)); + } + } + return dirp; + } + int fd = result; + + // Determine if the open directory is secure. + if (!is_dirfd_secure(fd)) { + // The directory is not a secure directory. + os::close(fd); + return dirp; + } + + // Open the directory. + dirp = ::opendir(dirname); + if (dirp == NULL) { + // The directory doesn't exist, close fd and return. + os::close(fd); + return dirp; + } + + // Check to make sure fd and dirp are referencing the same file system object. + if (!is_same_fsobject(fd, dirp->dd_fd)) { + // The directory is not secure. + os::close(fd); + os::closedir(dirp); + dirp = NULL; + return dirp; + } + + // Close initial open now that we know directory is secure + os::close(fd); + + return dirp; +} + +// NOTE: The code below uses fchdir(), open() and unlink() because +// fdopendir(), openat() and unlinkat() are not supported on all +// versions. Once the support for fdopendir(), openat() and unlinkat() +// is available on all supported versions the code can be changed +// to use these functions. + +// Open the directory of the given path, validate it and set the +// current working directory to it. +// Return a DIR * of the open directory and the saved cwd fd. +// +static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) { + + // Open the directory. + DIR* dirp = open_directory_secure(dirname); + if (dirp == NULL) { + // Directory doesn't exist or is insecure, so there is nothing to cleanup. + return dirp; + } + int fd = dirp->dd_fd; + + // Open a fd to the cwd and save it off. + int result; + RESTARTABLE(::open(".", O_RDONLY), result); + if (result == OS_ERR) { + *saved_cwd_fd = -1; + } else { + *saved_cwd_fd = result; + } + + // Set the current directory to dirname by using the fd of the directory. + result = fchdir(fd); + + return dirp; +} + +// Close the directory and restore the current working directory. +static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) { + + int result; + // If we have a saved cwd change back to it and close the fd. + if (saved_cwd_fd != -1) { + result = fchdir(saved_cwd_fd); + ::close(saved_cwd_fd); + } + + // Close the directory. + os::closedir(dirp); +} + +// Check if the given file descriptor is considered a secure. +static bool is_file_secure(int fd, const char *filename) { + + int result; + struct stat statbuf; + + // Determine if the file is secure. + RESTARTABLE(::fstat(fd, &statbuf), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("fstat failed on %s: %s\n", filename, strerror(errno)); + } + return false; + } + if (statbuf.st_nlink > 1) { + // A file with multiple links is not expected. + if (PrintMiscellaneous && Verbose) { + warning("file %s has multiple links\n", filename); + } + return false; + } return true; } - -// return the user name for the given user id +// Return the user name for the given user id. // -// the caller is expected to free the allocated memory. -// +// The caller is expected to free the allocated memory. static char* get_user_name(uid_t uid) { struct passwd pwent; - // determine the max pwbuf size from sysconf, and hardcode + // Determine the max pwbuf size from sysconf, and hardcode // a default if this not available through sysconf. - // long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; @@ -344,7 +608,8 @@ strcat(usrdir_name, "/"); strcat(usrdir_name, dentry->d_name); - DIR* subdirp = os::opendir(usrdir_name); + // Open the user directory. + DIR* subdirp = open_directory_secure(usrdir_name); if (subdirp == NULL) { FREE_C_HEAP_ARRAY(char, usrdir_name); @@ -464,28 +729,7 @@ } } - -// remove file -// -// this method removes the file with the given file name in the -// named directory. -// -static void remove_file(const char* dirname, const char* filename) { - - size_t nbytes = strlen(dirname) + strlen(filename) + 2; - char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); - - strcpy(path, dirname); - strcat(path, "/"); - strcat(path, filename); - - remove_file(path); - - FREE_C_HEAP_ARRAY(char, path); -} - - -// cleanup stale shared memory resources +// Cleanup stale shared memory resources // // This method attempts to remove all stale shared memory files in // the named user temporary directory. It scans the named directory @@ -493,33 +737,26 @@ // process id is extracted from the file name and a test is run to // determine if the process is alive. If the process is not alive, // any stale file resources are removed. -// static void cleanup_sharedmem_resources(const char* dirname) { - // open the user temp directory - DIR* dirp = os::opendir(dirname); - + int saved_cwd_fd; + // Open the directory. + DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd); if (dirp == NULL) { - // directory doesn't exist, so there is nothing to cleanup + // Directory doesn't exist or is insecure, so there is nothing to cleanup. return; } - if (!is_directory_secure(dirname)) { - // the directory is not a secure directory - os::closedir(dirp); - return; - } - - // for each entry in the directory that matches the expected file + // For each entry in the directory that matches the expected file // name pattern, determine if the file resources are stale and if // so, remove the file resources. Note, instrumented HotSpot processes // for this user may start and/or terminate during this search and // remove or create new files in this directory. The behavior of this // loop under these conditions is dependent upon the implementation of // opendir/readdir. - // struct dirent* entry; char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal); + errno = 0; while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) { @@ -529,56 +766,55 @@ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { - // attempt to remove all unexpected files, except "." and ".." - remove_file(dirname, entry->d_name); + // Attempt to remove all unexpected files, except "." and "..". + unlink(entry->d_name); } errno = 0; continue; } - // we now have a file name that converts to a valid integer + // We now have a file name that converts to a valid integer // that could represent a process id . if this process id // matches the current process id or the process is not running, // then remove the stale file resources. // - // process liveness is detected by sending signal number 0 to + // Process liveness is detected by sending signal number 0 to // the process id (see kill(2)). if kill determines that the // process does not exist, then the file resources are removed. // if kill determines that that we don't have permission to // signal the process, then the file resources are assumed to // be stale and are removed because the resources for such a // process should be in a different user specific directory. - // if ((pid == os::current_process_id()) || (kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) { - remove_file(dirname, entry->d_name); + unlink(entry->d_name); } errno = 0; } - os::closedir(dirp); - FREE_C_HEAP_ARRAY(char, dbuf); + + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); + + FREE_C_HEAP_ARRAY(char, dbuf, mtInternal); } -// make the user specific temporary directory. Returns true if +// Make the user specific temporary directory. Returns true if // the directory exists and is secure upon return. Returns false // if the directory exists but is either a symlink, is otherwise // insecure, or if an error occurred. -// static bool make_user_tmp_dir(const char* dirname) { - // create the directory with 0755 permissions. note that the directory + // Create the directory with 0755 permissions. note that the directory // will be owned by euid::egid, which may not be the same as uid::gid. - // if (mkdir(dirname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == OS_ERR) { if (errno == EEXIST) { // The directory already exists and was probably created by another // JVM instance. However, this could also be the result of a // deliberate symlink. Verify that the existing directory is safe. - // if (!is_directory_secure(dirname)) { - // directory is not secure + // Directory is not secure. if (PrintMiscellaneous && Verbose) { warning("%s directory is insecure\n", dirname); } @@ -614,19 +850,63 @@ return -1; } + int saved_cwd_fd; + // Open the directory and set the current working directory to it. + DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd); + if (dirp == NULL) { + // Directory doesn't exist or is insecure, so cannot create shared + // memory file. + return -1; + } + + // Open the filename in the current directory. + // Cannot use O_TRUNC here; truncation of an existing file has to happen + // after the is_file_secure() check below. int result; - RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result); + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case. +#ifdef O_NOFOLLOW + RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result); +#else + // workaround function (jdk6 code) + RESTARTABLE(::open_o_nofollow(filename, O_RDWR|O_CREAT, S_IREAD|S_IWRITE), result); +#endif + if (result == OS_ERR) { if (PrintMiscellaneous && Verbose) { - warning("could not create file %s: %s\n", filename, strerror(errno)); + if (errno == ELOOP) { + warning("file %s is a symlink and is not secure\n", filename); + } else { + warning("could not create file %s: %s\n", filename, strerror(errno)); + } } + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); + return -1; } + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); // save the file descriptor int fd = result; + // Check to see if the file is secure. + if (!is_file_secure(fd, filename)) { + ::close(fd); + return -1; + } + + // Truncate the file to get rid of any existing data. + RESTARTABLE(::ftruncate(fd, (off_t)0), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not truncate shared memory file: %s\n", strerror(errno)); + } + ::close(fd); + return -1; + } // set the file size RESTARTABLE(::ftruncate(fd, (off_t)size), result); if (result == OS_ERR) { @@ -648,7 +928,14 @@ // open the file int result; + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case +#ifdef O_NOFOLLOW RESTARTABLE(::open(filename, oflags), result); +#else + RESTARTABLE(::open_o_nofollow(filename, oflags), result); +#endif + if (result == OS_ERR) { if (errno == ENOENT) { THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), @@ -662,8 +949,15 @@ THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); } } + int fd = result; - return result; + // Check to see if the file is secure. + if (!is_file_secure(fd, filename)) { + ::close(fd); + return -1; + } + + return fd; } // create a named shared memory region. returns the address of the @@ -695,13 +989,21 @@ char* dirname = get_user_tmp_dir(user_name); char* filename = get_sharedmem_filename(dirname, vmid); + // Get the short filename. + char* short_filename = strrchr(filename, '/'); + if (short_filename == NULL) { + short_filename = filename; + } else { + short_filename++; + } + // cleanup any stale shared memory files cleanup_sharedmem_resources(dirname); assert(((size > 0) && (size % os::vm_page_size() == 0)), "unexpected PerfMemory region size"); - fd = create_sharedmem_resources(dirname, filename, size); + fd = create_sharedmem_resources(dirname, short_filename, size); FREE_C_HEAP_ARRAY(char, user_name); FREE_C_HEAP_ARRAY(char, dirname); @@ -733,6 +1035,9 @@ // clear the shared memory region (void)::memset((void*) mapAddress, 0, size); + // It does not go through os api, the operation has to record from here. + MemTracker::record_virtual_memory_reserve((address)mapAddress, size, CURRENT_PC, mtInternal); + return mapAddress; } @@ -807,7 +1112,7 @@ char* mapAddress; int result; int fd; - size_t size; + size_t size = 0; const char* luser = NULL; int mmap_prot; @@ -819,12 +1124,18 @@ // constructs for the file and the shared memory mapping. if (mode == PerfMemory::PERF_MODE_RO) { mmap_prot = PROT_READ; + + // No O_NOFOLLOW defined at buildtime, and it is not documented for open. +#ifdef O_NOFOLLOW + file_flags = O_RDONLY | O_NOFOLLOW; +#else file_flags = O_RDONLY; +#endif } else if (mode == PerfMemory::PERF_MODE_RW) { #ifdef LATER mmap_prot = PROT_READ | PROT_WRITE; - file_flags = O_RDWR; + file_flags = O_RDWR | O_NOFOLLOW; #else THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Unsupported access mode"); @@ -853,9 +1164,9 @@ // store file, we don't follow them when attaching either. // if (!is_directory_secure(dirname)) { - FREE_C_HEAP_ARRAY(char, dirname); + FREE_C_HEAP_ARRAY(char, dirname, mtInternal); if (luser != user) { - FREE_C_HEAP_ARRAY(char, luser); + FREE_C_HEAP_ARRAY(char, luser, mtInternal); } THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Process not found"); @@ -901,6 +1212,9 @@ "Could not map PerfMemory"); } + // It does not go through os api, the operation has to record from here. + MemTracker::record_virtual_memory_reserve((address)mapAddress, size, CURRENT_PC, mtInternal); + *addr = mapAddress; *sizep = size; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/os/windows/vm/os_windows.cpp --- a/hotspot/src/os/windows/vm/os_windows.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/os/windows/vm/os_windows.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -428,9 +428,9 @@ } // Diagnostic code to investigate JDK-6573254 - int res = 50115; // non-java thread + int res = 30115; // non-java thread if (thread->is_Java_thread()) { - res = 40115; // java thread + res = 20115; // java thread } // Install a win32 structured exception handler around every thread created @@ -3791,6 +3791,7 @@ static INIT_ONCE init_once_crit_sect = INIT_ONCE_STATIC_INIT; static CRITICAL_SECTION crit_sect; + static volatile jint process_exiting = 0; int i, j; DWORD res; HANDLE hproc, hthr; @@ -3798,10 +3799,10 @@ // The first thread that reached this point, initializes the critical section. if (!InitOnceExecuteOnce(&init_once_crit_sect, init_crit_sect_call, &crit_sect, NULL)) { warning("crit_sect initialization failed in %s: %d\n", __FILE__, __LINE__); - } else { + } else if (OrderAccess::load_acquire(&process_exiting) == 0) { EnterCriticalSection(&crit_sect); - if (what == EPT_THREAD) { + if (what == EPT_THREAD && OrderAccess::load_acquire(&process_exiting) == 0) { // Remove from the array those handles of the threads that have completed exiting. for (i = 0, j = 0; i < handle_count; ++i) { res = WaitForSingleObject(handles[i], 0 /* don't wait */); @@ -3856,7 +3857,7 @@ // The current exiting thread has stored its handle in the array, and now // should leave the critical section before calling _endthreadex(). - } else { // what != EPT_THREAD + } else if (what != EPT_THREAD) { if (handle_count > 0) { // Before ending the process, make sure all the threads that had called // _endthreadex() completed. @@ -3882,24 +3883,28 @@ handle_count = 0; } - // End the process, not leaving critical section. - // This makes sure no other thread executes exit-related code at the same - // time, thus a race is avoided. - if (what == EPT_PROCESS) { - ::exit(exit_code); - } else { - _exit(exit_code); - } + OrderAccess::release_store(&process_exiting, 1); } LeaveCriticalSection(&crit_sect); } + + if (what == EPT_THREAD) { + while (OrderAccess::load_acquire(&process_exiting) != 0) { + // Some other thread is about to call exit(), so we + // don't let the current thread proceed to _endthreadex() + SuspendThread(GetCurrentThread()); + // Avoid busy-wait loop, if SuspendThread() failed. + Sleep(EXIT_TIMEOUT); + } + } } // We are here if either // - there's no 'race at exit' bug on this OS release; // - initialization of the critical section failed (unlikely); - // - the current thread has stored its handle and left the critical section. + // - the current thread has stored its handle and left the critical section; + // - the process-exiting thread has raised the flag and left the critical section. if (what == EPT_THREAD) { _endthreadex((unsigned)exit_code); } else if (what == EPT_PROCESS) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/c1/c1_CodeStubs.hpp --- a/hotspot/src/share/vm/c1/c1_CodeStubs.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/c1/c1_CodeStubs.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -601,15 +601,6 @@ LIR_Opr _addr; LIR_Opr _new_val; - static jbyte* _byte_map_base; - static jbyte* byte_map_base_slow(); - static jbyte* byte_map_base() { - if (_byte_map_base == NULL) { - _byte_map_base = byte_map_base_slow(); - } - return _byte_map_base; - } - public: // addr (the address of the object head) and new_val must be registers. G1PostBarrierStub(LIR_Opr addr, LIR_Opr new_val): _addr(addr), _new_val(new_val) { } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/c1/c1_LIRGenerator.cpp --- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -32,6 +32,7 @@ #include "ci/ciArrayKlass.hpp" #include "ci/ciInstance.hpp" #include "ci/ciObjArray.hpp" +#include "runtime/arguments.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "runtime/vm_version.hpp" @@ -3351,7 +3352,12 @@ if (!x->inlinee()->is_accessor()) { CodeEmitInfo* info = state_for(x, x->state(), true); // Notify the runtime very infrequently only to take care of counter overflows - increment_event_counter_impl(info, x->inlinee(), (1 << Tier23InlineeNotifyFreqLog) - 1, InvocationEntryBci, false, true); + int freq_log = Tier23InlineeNotifyFreqLog; + double scale; + if (_method->has_option_value("CompileThresholdScaling", scale)) { + freq_log = Arguments::scaled_freq_log(freq_log, scale); + } + increment_event_counter_impl(info, x->inlinee(), right_n_bits(freq_log), InvocationEntryBci, false, true); } } @@ -3366,7 +3372,11 @@ ShouldNotReachHere(); } // Increment the appropriate invocation/backedge counter and notify the runtime. - increment_event_counter_impl(info, info->scope()->method(), (1 << freq_log) - 1, bci, backedge, true); + double scale; + if (_method->has_option_value("CompileThresholdScaling", scale)) { + freq_log = Arguments::scaled_freq_log(freq_log, scale); + } + increment_event_counter_impl(info, info->scope()->method(), right_n_bits(freq_log), bci, backedge, true); } void LIRGenerator::decrement_age(CodeEmitInfo* info) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/classfile/compactHashtable.cpp --- a/hotspot/src/share/vm/classfile/compactHashtable.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -221,6 +221,30 @@ return (const char*)end; } +template void CompactHashtable::symbols_do(SymbolClosure *cl) { + assert(!DumpSharedSpaces, "run-time only"); + for (juint i = 0; i < _bucket_count; i ++) { + juint bucket_info = _buckets[i]; + juint bucket_offset = BUCKET_OFFSET(bucket_info); + int bucket_type = BUCKET_TYPE(bucket_info); + juint* bucket = _buckets + bucket_offset; + juint* bucket_end = _buckets; + + Symbol* sym; + if (bucket_type == COMPACT_BUCKET_TYPE) { + sym = (Symbol*)((void*)(_base_address + bucket[0])); + cl->do_symbol(&sym); + } else { + bucket_end += BUCKET_OFFSET(_buckets[i + 1]); + while (bucket < bucket_end) { + sym = (Symbol*)((void*)(_base_address + bucket[1])); + cl->do_symbol(&sym); + bucket += 2; + } + } + } +} + // Explicitly instantiate these types template class CompactHashtable; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/classfile/compactHashtable.hpp --- a/hotspot/src/share/vm/classfile/compactHashtable.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -249,6 +249,9 @@ } return NULL; } + + // iterate over symbols + void symbols_do(SymbolClosure *cl); }; //////////////////////////////////////////////////////////////////////// diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/classfile/symbolTable.cpp --- a/hotspot/src/share/vm/classfile/symbolTable.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -82,6 +82,10 @@ // Call function for all symbols in the symbol table. void SymbolTable::symbols_do(SymbolClosure *cl) { + // all symbols from shared table + _shared_table.symbols_do(cl); + + // all symbols from the dynamic table const int n = the_table()->table_size(); for (int i = 0; i < n; i++) { for (HashtableEntry* p = the_table()->bucket(i); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/classfile/verifier.cpp --- a/hotspot/src/share/vm/classfile/verifier.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/classfile/verifier.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -1546,10 +1546,15 @@ no_control_flow = true; break; case Bytecodes::_getstatic : case Bytecodes::_putstatic : + // pass TRUE, operand can be an array type for getstatic/putstatic. + verify_field_instructions( + &bcs, ¤t_frame, cp, true, CHECK_VERIFY(this)); + no_control_flow = false; break; case Bytecodes::_getfield : case Bytecodes::_putfield : + // pass FALSE, operand can't be an array type for getfield/putfield. verify_field_instructions( - &bcs, ¤t_frame, cp, CHECK_VERIFY(this)); + &bcs, ¤t_frame, cp, false, CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_invokevirtual : case Bytecodes::_invokespecial : @@ -2107,6 +2112,7 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, StackMapFrame* current_frame, constantPoolHandle cp, + bool allow_arrays, TRAPS) { u2 index = bcs->get_index_u2(); verify_cp_type(bcs->bci(), index, cp, @@ -2126,8 +2132,8 @@ // Get referenced class type VerificationType ref_class_type = cp_ref_index_to_type( index, cp, CHECK_VERIFY(this)); - if (!ref_class_type.is_object()) { - /* Unreachable? Class file parser verifies Fieldref contents */ + if (!ref_class_type.is_object() && + (!allow_arrays || !ref_class_type.is_array())) { verify_error(ErrorContext::bad_type(bcs->bci(), TypeOrigin::cp(index, ref_class_type)), "Expecting reference to class in class %s at constant pool index %d", diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/classfile/verifier.hpp --- a/hotspot/src/share/vm/classfile/verifier.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/classfile/verifier.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -297,7 +297,7 @@ void verify_field_instructions( RawBytecodeStream* bcs, StackMapFrame* current_frame, - constantPoolHandle cp, TRAPS); + constantPoolHandle cp, bool allow_arrays, TRAPS); void verify_invoke_init( RawBytecodeStream* bcs, u2 ref_index, VerificationType ref_class_type, diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/code/codeCache.cpp --- a/hotspot/src/share/vm/code/codeCache.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/code/codeCache.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -233,8 +233,8 @@ ReservedCodeSpace CodeCache::reserve_heap_memory(size_t size) { // Determine alignment const size_t page_size = os::can_execute_large_page_memory() ? - MIN2(os::page_size_for_region(InitialCodeCacheSize, 8), - os::page_size_for_region(size, 8)) : + MIN2(os::page_size_for_region_aligned(InitialCodeCacheSize, 8), + os::page_size_for_region_aligned(size, 8)) : os::vm_page_size(); const size_t granularity = os::vm_allocation_granularity(); const size_t r_align = MAX2(page_size, granularity); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/compiler/compileBroker.cpp --- a/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1470,7 +1470,9 @@ // The method may be explicitly excluded by the user. bool quietly; - if (CompilerOracle::should_exclude(method, quietly)) { + double scale; + if (CompilerOracle::should_exclude(method, quietly) + || (CompilerOracle::has_option_value(method, "CompileThresholdScaling", scale) && scale == 0)) { if (!quietly) { // This does not happen quietly... ResourceMark rm; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -162,8 +162,8 @@ "we should have already filtered out humongous regions"); assert(_end == orig_end(), "we should have already filtered out humongous regions"); - - _in_collection_set = false; + assert(!_in_collection_set, + err_msg("Should not clear heap region %u in the collection set", hrm_index())); set_allocation_context(AllocationContext::system()); set_young_index_in_cset(-1); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1194,8 +1194,10 @@ return real_forwardee(old); } - new_obj = _next_gen->par_promote(par_scan_state->thread_num(), - old, m, sz); + if (!_promotion_failed) { + new_obj = _next_gen->par_promote(par_scan_state->thread_num(), + old, m, sz); + } if (new_obj == NULL) { // promotion failed, forward to self diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/generationSizer.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -61,9 +61,9 @@ void GenerationSizer::initialize_size_info() { trace_gen_sizes("ps heap raw"); - const size_t max_page_sz = os::page_size_for_region(_max_heap_byte_size, 8); + const size_t max_page_sz = os::page_size_for_region_aligned(_max_heap_byte_size, 8); const size_t min_pages = 4; // 1 for eden + 1 for each survivor + 1 for old - const size_t min_page_sz = os::page_size_for_region(_min_heap_byte_size, min_pages); + const size_t min_page_sz = os::page_size_for_region_aligned(_min_heap_byte_size, min_pages); const size_t page_sz = MIN2(max_page_sz, min_page_sz); // Can a page size be something else than a power of two? diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -41,7 +41,7 @@ const size_t words = bits / BitsPerWord; const size_t raw_bytes = words * sizeof(idx_t); - const size_t page_sz = os::page_size_for_region(raw_bytes, 10); + const size_t page_sz = os::page_size_for_region_aligned(raw_bytes, 10); const size_t granularity = os::vm_allocation_granularity(); _reserved_byte_size = align_size_up(raw_bytes, MAX2(page_sz, granularity)); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -403,7 +403,7 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) { const size_t raw_bytes = count * element_size; - const size_t page_sz = os::page_size_for_region(raw_bytes, 10); + const size_t page_sz = os::page_size_for_region_aligned(raw_bytes, 10); const size_t granularity = os::vm_allocation_granularity(); _reserved_byte_size = align_size_up(raw_bytes, MAX2(page_sz, granularity)); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp --- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -53,7 +53,7 @@ /* * USELABELS - If using GCC, then use labels for the opcode dispatching * rather -then a switch statement. This improves performance because it - * gives us the oportunity to have the instructions that calculate the + * gives us the opportunity to have the instructions that calculate the * next opcode to jump to be intermixed with the rest of the instructions * that implement the opcode (see UPDATE_PC_AND_TOS_AND_CONTINUE macro). */ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/interpreter/invocationCounter.hpp --- a/hotspot/src/share/vm/interpreter/invocationCounter.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/interpreter/invocationCounter.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -36,7 +36,7 @@ // Implementation notes: For space reasons, state & counter are both encoded in one word, // The state is encoded using some of the least significant bits, the counter is using the // more significant bits. The counter is incremented before a method is activated and an -// action is triggered when when count() > limit(). +// action is triggered when count() > limit(). class InvocationCounter VALUE_OBJ_CLASS_SPEC { friend class VMStructs; @@ -48,7 +48,6 @@ number_of_state_bits = 2, number_of_carry_bits = 1, number_of_noncount_bits = number_of_state_bits + number_of_carry_bits, - number_of_count_bits = BitsPerInt - number_of_noncount_bits, state_limit = nth_bit(number_of_state_bits), count_grain = nth_bit(number_of_state_bits + number_of_carry_bits), carry_mask = right_n_bits(number_of_carry_bits) << number_of_state_bits, @@ -68,6 +67,7 @@ count_increment = count_grain, // use this value to increment the 32bit _counter word count_mask_value = count_mask, // use this value to mask the backedge counter count_shift = number_of_noncount_bits, + number_of_count_bits = BitsPerInt - number_of_noncount_bits, count_limit = nth_bit(number_of_count_bits - 1) }; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/memory/heap.cpp --- a/hotspot/src/share/vm/memory/heap.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/memory/heap.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -104,8 +104,8 @@ size_t page_size = os::vm_page_size(); if (os::can_execute_large_page_memory()) { const size_t min_pages = 8; - page_size = MIN2(os::page_size_for_region(committed_size, min_pages), - os::page_size_for_region(rs.size(), min_pages)); + page_size = MIN2(os::page_size_for_region_aligned(committed_size, min_pages), + os::page_size_for_region_aligned(rs.size(), min_pages)); } const size_t granularity = os::vm_allocation_granularity(); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/oops/method.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -412,15 +412,14 @@ } methodHandle mh(m); - ClassLoaderData* loader_data = mh->method_holder()->class_loader_data(); - MethodCounters* counters = MethodCounters::allocate(loader_data, THREAD); + MethodCounters* counters = MethodCounters::allocate(mh, THREAD); if (HAS_PENDING_EXCEPTION) { CompileBroker::log_metaspace_failure(); ClassLoaderDataGraph::set_metaspace_oom(true); return NULL; // return the exception (which is cleared) } if (!mh->init_method_counters(counters)) { - MetadataFactory::free_metadata(loader_data, counters); + MetadataFactory::free_metadata(mh->method_holder()->class_loader_data(), counters); } return mh->method_counters(); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/oops/methodCounters.cpp --- a/hotspot/src/share/vm/oops/methodCounters.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/oops/methodCounters.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -23,10 +23,11 @@ */ #include "precompiled.hpp" #include "oops/methodCounters.hpp" -#include "runtime/thread.inline.hpp" +#include "runtime/handles.inline.hpp" -MethodCounters* MethodCounters::allocate(ClassLoaderData* loader_data, TRAPS) { - return new(loader_data, size(), false, MetaspaceObj::MethodCountersType, THREAD) MethodCounters(); +MethodCounters* MethodCounters::allocate(methodHandle mh, TRAPS) { + ClassLoaderData* loader_data = mh->method_holder()->class_loader_data(); + return new(loader_data, size(), false, MetaspaceObj::MethodCountersType, THREAD) MethodCounters(mh); } void MethodCounters::clear_counters() { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/oops/methodCounters.hpp --- a/hotspot/src/share/vm/oops/methodCounters.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/oops/methodCounters.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -26,7 +26,9 @@ #define SHARE_VM_OOPS_METHODCOUNTERS_HPP #include "oops/metadata.hpp" +#include "compiler/compilerOracle.hpp" #include "interpreter/invocationCounter.hpp" +#include "runtime/arguments.hpp" class MethodCounters: public MetaspaceObj { friend class VMStructs; @@ -45,7 +47,11 @@ // 3. (INT_MIN..0] - method is hot and will deopt and get // recompiled without the counters int _nmethod_age; - + int _interpreter_invocation_limit; // per-method InterpreterInvocationLimit + int _interpreter_backward_branch_limit; // per-method InterpreterBackwardBranchLimit + int _interpreter_profile_limit; // per-method InterpreterProfileLimit + int _invoke_mask; // per-method Tier0InvokeNotifyFreqLog + int _backedge_mask; // per-method Tier0BackedgeNotifyFreqLog #ifdef TIERED float _rate; // Events (invocation and backedge counter increments) per millisecond jlong _prev_time; // Previous time the rate was acquired @@ -53,15 +59,15 @@ u1 _highest_osr_comp_level; // Same for OSR level #endif - MethodCounters() : _interpreter_invocation_count(0), - _interpreter_throwout_count(0), - _number_of_breakpoints(0), - _nmethod_age(INT_MAX) + MethodCounters(methodHandle mh) : _interpreter_invocation_count(0), + _interpreter_throwout_count(0), + _number_of_breakpoints(0), + _nmethod_age(INT_MAX) #ifdef TIERED - , _rate(0), - _prev_time(0), - _highest_comp_level(0), - _highest_osr_comp_level(0) + , _rate(0), + _prev_time(0), + _highest_comp_level(0), + _highest_osr_comp_level(0) #endif { invocation_counter()->init(); @@ -70,10 +76,28 @@ if (StressCodeAging) { set_nmethod_age(HotMethodDetectionLimit); } + + // Set per-method thresholds. + double scale = 1.0; + CompilerOracle::has_option_value(mh, "CompileThresholdScaling", scale); + + int compile_threshold = Arguments::scaled_compile_threshold(CompileThreshold, scale); + _interpreter_invocation_limit = compile_threshold << InvocationCounter::count_shift; + if (ProfileInterpreter) { + // If interpreter profiling is enabled, the backward branch limit + // is compared against the method data counter rather than an invocation + // counter, therefore no shifting of bits is required. + _interpreter_backward_branch_limit = (compile_threshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100; + } else { + _interpreter_backward_branch_limit = ((compile_threshold * OnStackReplacePercentage) / 100) << InvocationCounter::count_shift; + } + _interpreter_profile_limit = ((compile_threshold * InterpreterProfilePercentage) / 100) << InvocationCounter::count_shift; + _invoke_mask = right_n_bits(Arguments::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _backedge_mask = right_n_bits(Arguments::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; } public: - static MethodCounters* allocate(ClassLoaderData* loader_data, TRAPS); + static MethodCounters* allocate(methodHandle mh, TRAPS); void deallocate_contents(ClassLoaderData* loader_data) {} DEBUG_ONLY(bool on_stack() { return false; }) // for template @@ -161,5 +185,24 @@ return offset_of(MethodCounters, _interpreter_invocation_count); } + static ByteSize interpreter_invocation_limit_offset() { + return byte_offset_of(MethodCounters, _interpreter_invocation_limit); + } + + static ByteSize interpreter_backward_branch_limit_offset() { + return byte_offset_of(MethodCounters, _interpreter_backward_branch_limit); + } + + static ByteSize interpreter_profile_limit_offset() { + return byte_offset_of(MethodCounters, _interpreter_profile_limit); + } + + static ByteSize invoke_mask_offset() { + return byte_offset_of(MethodCounters, _invoke_mask); + } + + static ByteSize backedge_mask_offset() { + return byte_offset_of(MethodCounters, _backedge_mask); + } }; #endif //SHARE_VM_OOPS_METHODCOUNTERS_HPP diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/oops/methodData.cpp --- a/hotspot/src/share/vm/oops/methodData.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/oops/methodData.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -31,6 +31,7 @@ #include "memory/heapInspection.hpp" #include "oops/methodData.hpp" #include "prims/jvmtiRedefineClasses.hpp" +#include "runtime/arguments.hpp" #include "runtime/compilationPolicy.hpp" #include "runtime/deoptimization.hpp" #include "runtime/handles.inline.hpp" @@ -1131,6 +1132,13 @@ _backedge_counter.init(); _invocation_counter_start = 0; _backedge_counter_start = 0; + + // Set per-method invoke- and backedge mask. + double scale = 1.0; + CompilerOracle::has_option_value(_method, "CompileThresholdScaling", scale); + _invoke_mask = right_n_bits(Arguments::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _backedge_mask = right_n_bits(Arguments::scaled_freq_log(Tier0BackedgeNotifyFreqLog, scale)) << InvocationCounter::count_shift; + _tenure_traps = 0; _num_loops = 0; _num_blocks = 0; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/oops/methodData.hpp --- a/hotspot/src/share/vm/oops/methodData.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/oops/methodData.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -2088,6 +2088,8 @@ int _invocation_counter_start; int _backedge_counter_start; uint _tenure_traps; + int _invoke_mask; // per-method Tier0InvokeNotifyFreqLog + int _backedge_mask; // per-method Tier0BackedgeNotifyFreqLog #if INCLUDE_RTM_OPT // State of RTM code generation during compilation of the method @@ -2447,10 +2449,19 @@ static ByteSize invocation_counter_offset() { return byte_offset_of(MethodData, _invocation_counter); } + static ByteSize backedge_counter_offset() { return byte_offset_of(MethodData, _backedge_counter); } + static ByteSize invoke_mask_offset() { + return byte_offset_of(MethodData, _invoke_mask); + } + + static ByteSize backedge_mask_offset() { + return byte_offset_of(MethodData, _backedge_mask); + } + static ByteSize parameters_type_data_di_offset() { return byte_offset_of(MethodData, _parameters_type_data_di); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/chaitin.cpp --- a/hotspot/src/share/vm/opto/chaitin.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/chaitin.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -582,6 +582,9 @@ // Peephole remove copies post_allocate_copy_removal(); + // Merge multidefs if multiple defs representing the same value are used in a single block. + merge_multidefs(); + #ifdef ASSERT // Veify the graph after RA. verify(&live_arena); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/chaitin.hpp --- a/hotspot/src/share/vm/opto/chaitin.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/chaitin.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -681,6 +681,32 @@ // Extend the node to LRG mapping void add_reference( const Node *node, const Node *old_node); + // Record the first use of a def in the block for a register. + class RegDefUse { + Node* _def; + Node* _first_use; + public: + RegDefUse() : _def(NULL), _first_use(NULL) { } + Node* def() const { return _def; } + Node* first_use() const { return _first_use; } + + void update(Node* def, Node* use) { + if (_def != def) { + _def = def; + _first_use = use; + } + } + void clear() { + _def = NULL; + _first_use = NULL; + } + }; + typedef GrowableArray RegToDefUseMap; + int possibly_merge_multidef(Node *n, uint k, Block *block, RegToDefUseMap& reg2defuse); + + // Merge nodes that are a part of a multidef lrg and produce the same value within a block. + void merge_multidefs(); + private: static int _final_loads, _final_stores, _final_copies, _final_memoves; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/doCall.cpp --- a/hotspot/src/share/vm/opto/doCall.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/doCall.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -94,7 +94,7 @@ if (log != NULL) { int rid = (receiver_count >= 0)? log->identify(profile.receiver(0)): -1; int r2id = (rid != -1 && profile.has_receiver(1))? log->identify(profile.receiver(1)):-1; - log->begin_elem("call method='%d' count='%d' prof_factor='%g'", + log->begin_elem("call method='%d' count='%d' prof_factor='%f'", log->identify(callee), site_count, prof_factor); if (call_does_dispatch) log->print(" virtual='1'"); if (allow_inline) log->print(" inline='1'"); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/escape.cpp --- a/hotspot/src/share/vm/opto/escape.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/escape.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -2010,14 +2010,9 @@ bt = field->layout_type(); } else { // Check for unsafe oop field access - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - int opcode = n->fast_out(i)->Opcode(); - if (opcode == Op_StoreP || opcode == Op_LoadP || - opcode == Op_StoreN || opcode == Op_LoadN) { - bt = T_OBJECT; - (*unsafe) = true; - break; - } + if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN)) { + bt = T_OBJECT; + (*unsafe) = true; } } } else if (adr_type->isa_aryptr()) { @@ -2031,13 +2026,8 @@ } } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) { // Allocation initialization, ThreadLocal field access, unsafe access - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - int opcode = n->fast_out(i)->Opcode(); - if (opcode == Op_StoreP || opcode == Op_LoadP || - opcode == Op_StoreN || opcode == Op_LoadN) { - bt = T_OBJECT; - break; - } + if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN)) { + bt = T_OBJECT; } } } @@ -3092,13 +3082,7 @@ continue; } else if (n->Opcode() == Op_EncodeISOArray) { // get the memory projection - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node *use = n->fast_out(i); - if (use->Opcode() == Op_SCMemProj) { - n = use; - break; - } - } + n = n->find_out_with(Op_SCMemProj); assert(n->Opcode() == Op_SCMemProj, "memory projection required"); } else { assert(n->is_Mem(), "memory node required."); @@ -3122,13 +3106,7 @@ continue; // don't push users } else if (n->is_LoadStore()) { // get the memory projection - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node *use = n->fast_out(i); - if (use->Opcode() == Op_SCMemProj) { - n = use; - break; - } - } + n = n->find_out_with(Op_SCMemProj); assert(n->Opcode() == Op_SCMemProj, "memory projection required"); } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/ifg.cpp --- a/hotspot/src/share/vm/opto/ifg.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/ifg.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -535,12 +535,8 @@ // The method add_input_to_liveout() keeps such nodes alive (put them on liveout list) // when it sees SCMemProj node in a block. Unfortunately SCMemProj node could be placed // in block in such order that KILL MachProj nodes are processed first. - uint cnt = def->outcnt(); - for (uint i = 0; i < cnt; i++) { - Node* proj = def->raw_out(i); - if (proj->Opcode() == Op_SCMemProj) { - return false; - } + if (def->has_out_with(Op_SCMemProj)) { + return false; } } b->remove_node(location); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/loopTransform.cpp --- a/hotspot/src/share/vm/opto/loopTransform.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/loopTransform.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -2057,10 +2057,9 @@ } Node *main_cmp = main_bol->in(1); if( main_cmp->outcnt() > 1 ) { // CmpNode shared? - _igvn.hash_delete(main_bol); main_cmp = main_cmp->clone();// Clone a private CmpNode register_new_node( main_cmp, main_cle->in(0) ); - main_bol->set_req(1,main_cmp); + _igvn.replace_input_of(main_bol, 1, main_cmp); } // Hack the now-private loop bounds _igvn.replace_input_of(main_cmp, 2, main_limit); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/machnode.hpp --- a/hotspot/src/share/vm/opto/machnode.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/machnode.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -616,6 +616,29 @@ #endif }; +// MachMergeNode is similar to a PhiNode in a sense it merges multiple values, +// however it doesn't have a control input and is more like a MergeMem. +// It is inserted after the register allocation is done to ensure that nodes use single +// definition of a multidef lrg in a block. +class MachMergeNode : public MachIdealNode { +public: + MachMergeNode(Node *n1) { + init_class_id(Class_MachMerge); + add_req(NULL); + add_req(n1); + } + virtual const RegMask &out_RegMask() const { return in(1)->out_RegMask(); } + virtual const RegMask &in_RegMask(uint idx) const { return in(1)->in_RegMask(idx); } + virtual const class Type *bottom_type() const { return in(1)->bottom_type(); } + virtual uint ideal_reg() const { return bottom_type()->ideal_reg(); } + virtual uint oper_input_base() const { return 1; } + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } + virtual uint size(PhaseRegAlloc *ra_) const { return 0; } +#ifndef PRODUCT + virtual const char *Name() const { return "MachMerge"; } +#endif +}; + //------------------------------MachBranchNode-------------------------------- // Abstract machine branch Node class MachBranchNode : public MachIdealNode { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/macro.cpp --- a/hotspot/src/share/vm/opto/macro.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/macro.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -258,14 +258,7 @@ // Search for CastP2X->Xor->URShift->Cmp path which // checks if the store done to a different from the value's region. // And replace Cmp with #0 (false) to collapse G1 post barrier. - Node* xorx = NULL; - for (DUIterator_Fast imax, i = p2x->fast_outs(imax); i < imax; i++) { - Node* u = p2x->fast_out(i); - if (u->Opcode() == Op_XorX) { - xorx = u; - break; - } - } + Node* xorx = p2x->find_out_with(Op_XorX); assert(xorx != NULL, "missing G1 post barrier"); Node* shift = xorx->unique_out(); Node* cmpx = shift->unique_out(); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/memnode.cpp --- a/hotspot/src/share/vm/opto/memnode.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/memnode.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -2609,7 +2609,6 @@ return false; // if not a distinct instance, there may be aliases of the address for (DUIterator_Fast imax, i = adr->fast_outs(imax); i < imax; i++) { Node *use = adr->fast_out(i); - int opc = use->Opcode(); if (use->is_Load() || use->is_LoadStore()) { return false; } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/node.cpp --- a/hotspot/src/share/vm/opto/node.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/node.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -881,6 +881,34 @@ return (Node*) this; } +// Find out of current node that matches opcode. +Node* Node::find_out_with(int opcode) { + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* use = fast_out(i); + if (use->Opcode() == opcode) { + return use; + } + } + return NULL; +} + +// Return true if the current node has an out that matches opcode. +bool Node::has_out_with(int opcode) { + return (find_out_with(opcode) != NULL); +} + +// Return true if the current node has an out that matches any of the opcodes. +bool Node::has_out_with(int opcode1, int opcode2, int opcode3, int opcode4) { + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + int opcode = fast_out(i)->Opcode(); + if (opcode == opcode1 || opcode == opcode2 || opcode == opcode3 || opcode == opcode4) { + return true; + } + } + return false; +} + + //---------------------------uncast_helper------------------------------------- Node* Node::uncast_helper(const Node* p) { #ifdef ASSERT diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/node.hpp --- a/hotspot/src/share/vm/opto/node.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/node.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -98,6 +98,7 @@ class MachSafePointNode; class MachSpillCopyNode; class MachTempNode; +class MachMergeNode; class Matcher; class MemBarNode; class MemBarStoreStoreNode; @@ -436,6 +437,13 @@ return (this->uncast() == n->uncast()); } + // Find out of current node that matches opcode. + Node* find_out_with(int opcode); + // Return true if the current node has an out that matches opcode. + bool has_out_with(int opcode); + // Return true if the current node has an out that matches any of the opcodes. + bool has_out_with(int opcode1, int opcode2, int opcode3, int opcode4); + private: static Node* uncast_helper(const Node* n); @@ -507,18 +515,25 @@ //----------------- Other Node Properties - // Generate class id for some ideal nodes to avoid virtual query - // methods is_(). - // Class id is the set of bits corresponded to the node class and all its - // super classes so that queries for super classes are also valid. - // Subclasses of the same super class have different assigned bit - // (the third parameter in the macro DEFINE_CLASS_ID). - // Classes with deeper hierarchy are declared first. - // Classes with the same hierarchy depth are sorted by usage frequency. + // Generate class IDs for (some) ideal nodes so that it is possible to determine + // the type of a node using a non-virtual method call (the method is_() below). + // + // A class ID of an ideal node is a set of bits. In a class ID, a single bit determines + // the type of the node the ID represents; another subset of an ID's bits are reserved + // for the superclasses of the node represented by the ID. + // + // By design, if A is a supertype of B, A.is_B() returns true and B.is_A() + // returns false. A.is_A() returns true. // - // The query method masks the bits to cut off bits of subclasses - // and then compare the result with the class id - // (see the macro DEFINE_CLASS_QUERY below). + // If two classes, A and B, have the same superclass, a different bit of A's class id + // is reserved for A's type than for B's type. That bit is specified by the third + // parameter in the macro DEFINE_CLASS_ID. + // + // By convention, classes with deeper hierarchy are declared first. Moreover, + // classes with the same hierarchy depth are sorted by usage frequency. + // + // The query method masks the bits to cut off bits of subclasses and then compares + // the result with the class id (see the macro DEFINE_CLASS_QUERY below). // // Class_MachCall=30, ClassMask_MachCall=31 // 12 8 4 0 @@ -592,6 +607,7 @@ DEFINE_CLASS_ID(MachTemp, Mach, 3) DEFINE_CLASS_ID(MachConstantBase, Mach, 4) DEFINE_CLASS_ID(MachConstant, Mach, 5) + DEFINE_CLASS_ID(MachMerge, Mach, 6) DEFINE_CLASS_ID(Type, Node, 2) DEFINE_CLASS_ID(Phi, Type, 0) @@ -763,6 +779,7 @@ DEFINE_CLASS_QUERY(MachSafePoint) DEFINE_CLASS_QUERY(MachSpillCopy) DEFINE_CLASS_QUERY(MachTemp) + DEFINE_CLASS_QUERY(MachMerge) DEFINE_CLASS_QUERY(Mem) DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/parse1.cpp --- a/hotspot/src/share/vm/opto/parse1.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/parse1.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -441,7 +441,7 @@ CompileLog* log = C->log(); if (log != NULL) { - log->begin_head("parse method='%d' uses='%g'", + log->begin_head("parse method='%d' uses='%f'", log->identify(parse_method), expected_uses); if (depth() == 1 && C->is_osr_compilation()) { log->print(" osr_bci='%d'", C->entry_bci()); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/parse2.cpp --- a/hotspot/src/share/vm/opto/parse2.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/parse2.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -832,7 +832,7 @@ sprintf(prob_str_buf, "%g", prob); prob_str = prob_str_buf; } - C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d' cnt='%g' prob='%s'", + C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d' cnt='%f' prob='%s'", iter().get_dest(), taken, not_taken, cnt, prob_str); } return prob; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/phase.cpp --- a/hotspot/src/share/vm/opto/phase.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/phase.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -110,6 +110,7 @@ tty->print_cr (" Compute Liveness: %7.3f s", timers[_t_computeLive].seconds()); tty->print_cr (" Regalloc Split: %7.3f s", timers[_t_regAllocSplit].seconds()); tty->print_cr (" Postalloc Copy Rem: %7.3f s", timers[_t_postAllocCopyRemoval].seconds()); + tty->print_cr (" Merge multidefs: %7.3f s", timers[_t_mergeMultidefs].seconds()); tty->print_cr (" Fixup Spills: %7.3f s", timers[_t_fixupSpills].seconds()); tty->print_cr (" Compact: %7.3f s", timers[_t_chaitinCompact].seconds()); tty->print_cr (" Coalesce 1: %7.3f s", timers[_t_chaitinCoalesce1].seconds()); @@ -126,6 +127,7 @@ timers[_t_computeLive].seconds() + timers[_t_regAllocSplit].seconds() + timers[_t_postAllocCopyRemoval].seconds() + + timers[_t_mergeMultidefs].seconds() + timers[_t_fixupSpills].seconds() + timers[_t_chaitinCompact].seconds() + timers[_t_chaitinCoalesce1].seconds() + diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/phase.hpp --- a/hotspot/src/share/vm/opto/phase.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/phase.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -88,6 +88,7 @@ _t_computeLive, _t_regAllocSplit, _t_postAllocCopyRemoval, + _t_mergeMultidefs, _t_fixupSpills, _t_chaitinCompact, _t_chaitinCoalesce1, diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/postaloc.cpp --- a/hotspot/src/share/vm/opto/postaloc.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/postaloc.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -263,20 +263,6 @@ // intermediate copies might be illegal, i.e., value is stored down to stack // then reloaded BUT survives in a register the whole way. Node *val = skip_copies(n->in(k)); - - if (val == x && nk_idx != 0 && - regnd[nk_reg] != NULL && regnd[nk_reg] != x && - _lrg_map.live_range_id(x) == _lrg_map.live_range_id(regnd[nk_reg])) { - // When rematerialzing nodes and stretching lifetimes, the - // allocator will reuse the original def for multidef LRG instead - // of the current reaching def because it can't know it's safe to - // do so. After allocation completes if they are in the same LRG - // then it should use the current reaching def instead. - n->set_req(k, regnd[nk_reg]); - blk_adjust += yank_if_dead(val, current_block, &value, ®nd); - val = skip_copies(n->in(k)); - } - if (val == x) return blk_adjust; // No progress? int n_regs = RegMask::num_registers(val->ideal_reg()); @@ -382,6 +368,94 @@ return false; } +// The algorithms works as follows: +// We traverse the block top to bottom. possibly_merge_multidef() is invoked for every input edge k +// of the instruction n. We check to see if the input is a multidef lrg. If it is, we record the fact that we've +// seen a definition (coming as an input) and add that fact to the reg2defuse array. The array maps registers to their +// current reaching definitions (we track only multidefs though). With each definition we also associate the first +// instruction we saw use it. If we encounter the situation when we observe an def (an input) that is a part of the +// same lrg but is different from the previous seen def we merge the two with a MachMerge node and substitute +// all the uses that we've seen so far to use the merge. After that we keep replacing the new defs in the same lrg +// as they get encountered with the merge node and keep adding these defs to the merge inputs. +void PhaseChaitin::merge_multidefs() { + Compile::TracePhase tp("mergeMultidefs", &timers[_t_mergeMultidefs]); + ResourceMark rm; + // Keep track of the defs seen in registers and collect their uses in the block. + RegToDefUseMap reg2defuse(_max_reg, _max_reg, RegDefUse()); + for (uint i = 0; i < _cfg.number_of_blocks(); i++) { + Block* block = _cfg.get_block(i); + for (uint j = 1; j < block->number_of_nodes(); j++) { + Node* n = block->get_node(j); + if (n->is_Phi()) continue; + for (uint k = 1; k < n->req(); k++) { + j += possibly_merge_multidef(n, k, block, reg2defuse); + } + // Null out the value produced by the instruction itself, since we're only interested in defs + // implicitly defined by the uses. We are actually interested in tracking only redefinitions + // of the multidef lrgs in the same register. For that matter it's enough to track changes in + // the base register only and ignore other effects of multi-register lrgs and fat projections. + // It is also ok to ignore defs coming from singledefs. After an implicit overwrite by one of + // those our register is guaranteed to be used by another lrg and we won't attempt to merge it. + uint lrg = _lrg_map.live_range_id(n); + if (lrg > 0 && lrgs(lrg).is_multidef()) { + OptoReg::Name reg = lrgs(lrg).reg(); + reg2defuse.at(reg).clear(); + } + } + // Clear reg->def->use tracking for the next block + for (int j = 0; j < reg2defuse.length(); j++) { + reg2defuse.at(j).clear(); + } + } +} + +int PhaseChaitin::possibly_merge_multidef(Node *n, uint k, Block *block, RegToDefUseMap& reg2defuse) { + int blk_adjust = 0; + + uint lrg = _lrg_map.live_range_id(n->in(k)); + if (lrg > 0 && lrgs(lrg).is_multidef()) { + OptoReg::Name reg = lrgs(lrg).reg(); + + Node* def = reg2defuse.at(reg).def(); + if (def != NULL && lrg == _lrg_map.live_range_id(def) && def != n->in(k)) { + // Same lrg but different node, we have to merge. + MachMergeNode* merge; + if (def->is_MachMerge()) { // is it already a merge? + merge = def->as_MachMerge(); + } else { + merge = new MachMergeNode(def); + + // Insert the merge node into the block before the first use. + uint use_index = block->find_node(reg2defuse.at(reg).first_use()); + block->insert_node(merge, use_index++); + + // Let the allocator know about the new node, use the same lrg + _lrg_map.extend(merge->_idx, lrg); + blk_adjust++; + + // Fixup all the uses (there is at least one) that happened between the first + // use and before the current one. + for (; use_index < block->number_of_nodes(); use_index++) { + Node* use = block->get_node(use_index); + if (use == n) { + break; + } + use->replace_edge(def, merge); + } + } + if (merge->find_edge(n->in(k)) == -1) { + merge->add_req(n->in(k)); + } + n->set_req(k, merge); + } + + // update the uses + reg2defuse.at(reg).update(n->in(k), n); + } + + return blk_adjust; +} + //------------------------------post_allocate_copy_removal--------------------- // Post-Allocation peephole copy removal. We do this in 1 pass over the diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/opto/stringopts.cpp --- a/hotspot/src/share/vm/opto/stringopts.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/opto/stringopts.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1507,10 +1507,12 @@ } case StringConcat::StringMode: { const Type* type = kit.gvn().type(arg); + Node* count = NULL; if (type == TypePtr::NULL_PTR) { // replace the argument with the null checked version arg = null_string; sc->set_argument(argi, arg); + count = kit.load_String_length(kit.control(), arg); } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { // s = s != null ? s : "null"; // length = length + (s.count - s.offset); @@ -1533,10 +1535,13 @@ // replace the argument with the null checked version arg = phi; sc->set_argument(argi, arg); + count = kit.load_String_length(kit.control(), arg); + } else { + // A corresponding nullcheck will be connected during IGVN MemNode::Ideal_common_DU_postCCP + // kit.control might be a different test, that can be hoisted above the actual nullcheck + // in case, that the control input is not null, Ideal_common_DU_postCCP will not look for a nullcheck. + count = kit.load_String_length(NULL, arg); } - - Node* count = kit.load_String_length(kit.control(), arg); - length = __ AddI(length, count); string_sizes->init_req(argi, NULL); break; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp --- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -68,11 +68,11 @@ ~JvmtiConstantPoolReconstituter() { if (_symmap != NULL) { - os::free(_symmap); + delete _symmap; _symmap = NULL; } if (_classmap != NULL) { - os::free(_classmap); + delete _classmap; _classmap = NULL; } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/prims/jvmtiTagMap.cpp --- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1046,10 +1046,16 @@ { assert(str->klass() == SystemDictionary::String_klass(), "not a string"); + typeArrayOop s_value = java_lang_String::value(str); + + // JDK-6584008: the value field may be null if a String instance is + // partially constructed. + if (s_value == NULL) { + return 0; + } // get the string value and length // (string value may be offset from the base) int s_len = java_lang_String::length(str); - typeArrayOop s_value = java_lang_String::value(str); int s_offset = java_lang_String::offset(str); jchar* value; if (s_len > 0) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/prims/perf.cpp --- a/hotspot/src/share/vm/prims/perf.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/prims/perf.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -100,6 +100,11 @@ PerfWrapper("Perf_Detach"); + if (!UsePerfData) { + // With -XX:-UsePerfData, detach is just a NOP + return; + } + void* address = 0; jlong capacity = 0; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/prims/unsafe.cpp --- a/hotspot/src/share/vm/prims/unsafe.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/prims/unsafe.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, 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 @@ -1104,43 +1104,6 @@ -UNSAFE_ENTRY(void, Unsafe_MonitorEnter(JNIEnv *env, jobject unsafe, jobject jobj)) - UnsafeWrapper("Unsafe_MonitorEnter"); - { - if (jobj == NULL) { - THROW(vmSymbols::java_lang_NullPointerException()); - } - Handle obj(thread, JNIHandles::resolve_non_null(jobj)); - ObjectSynchronizer::jni_enter(obj, CHECK); - } -UNSAFE_END - - -UNSAFE_ENTRY(jboolean, Unsafe_TryMonitorEnter(JNIEnv *env, jobject unsafe, jobject jobj)) - UnsafeWrapper("Unsafe_TryMonitorEnter"); - { - if (jobj == NULL) { - THROW_(vmSymbols::java_lang_NullPointerException(), JNI_FALSE); - } - Handle obj(thread, JNIHandles::resolve_non_null(jobj)); - bool res = ObjectSynchronizer::jni_try_enter(obj, CHECK_0); - return (res ? JNI_TRUE : JNI_FALSE); - } -UNSAFE_END - - -UNSAFE_ENTRY(void, Unsafe_MonitorExit(JNIEnv *env, jobject unsafe, jobject jobj)) - UnsafeWrapper("Unsafe_MonitorExit"); - { - if (jobj == NULL) { - THROW(vmSymbols::java_lang_NullPointerException()); - } - Handle obj(THREAD, JNIHandles::resolve_non_null(jobj)); - ObjectSynchronizer::jni_exit(obj(), CHECK); - } -UNSAFE_END - - UNSAFE_ENTRY(void, Unsafe_ThrowException(JNIEnv *env, jobject unsafe, jthrowable thr)) UnsafeWrapper("Unsafe_ThrowException"); { @@ -1365,8 +1328,6 @@ {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, {CC"defineClass", CC"("DC_Args")"CLS, FN_PTR(Unsafe_DefineClass)}, {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, - {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, - {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)} }; @@ -1411,8 +1372,6 @@ {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, {CC"defineClass", CC"("DC_Args")"CLS, FN_PTR(Unsafe_DefineClass)}, {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, - {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, - {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)} }; @@ -1461,8 +1420,6 @@ {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, {CC"defineClass", CC"("DC_Args")"CLS, FN_PTR(Unsafe_DefineClass)}, {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, - {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, - {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)}, {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, @@ -1515,9 +1472,6 @@ {CC"defineClass", CC"("DC0_Args")"CLS, FN_PTR(Unsafe_DefineClass0)}, {CC"defineClass", CC"("DC_Args")"CLS, FN_PTR(Unsafe_DefineClass)}, {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, - {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, - {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, - {CC"tryMonitorEnter", CC"("OBJ")Z", FN_PTR(Unsafe_TryMonitorEnter)}, {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)}, {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, @@ -1571,9 +1525,6 @@ {CC"defineClass", CC"("DC_Args")"CLS, FN_PTR(Unsafe_DefineClass)}, {CC"allocateInstance", CC"("CLS")"OBJ, FN_PTR(Unsafe_AllocateInstance)}, - {CC"monitorEnter", CC"("OBJ")V", FN_PTR(Unsafe_MonitorEnter)}, - {CC"monitorExit", CC"("OBJ")V", FN_PTR(Unsafe_MonitorExit)}, - {CC"tryMonitorEnter", CC"("OBJ")Z", FN_PTR(Unsafe_TryMonitorEnter)}, {CC"throwException", CC"("THR")V", FN_PTR(Unsafe_ThrowException)}, {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -155,7 +155,7 @@ if (mdo != NULL) { int i = mdo->invocation_count_delta(); int b = mdo->backedge_count_delta(); - return call_predicate_helper(i, b, 1); + return call_predicate_helper(i, b, 1, method); } return false; } @@ -229,32 +229,32 @@ // Tier?LoadFeedback is basically a coefficient that determines of // how many methods per compiler thread can be in the queue before // the threshold values double. -bool AdvancedThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level) { +bool AdvancedThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level, Method* method) { switch(cur_level) { case CompLevel_none: case CompLevel_limited_profile: { double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback); - return loop_predicate_helper(i, b, k); + return loop_predicate_helper(i, b, k, method); } case CompLevel_full_profile: { double k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback); - return loop_predicate_helper(i, b, k); + return loop_predicate_helper(i, b, k, method); } default: return true; } } -bool AdvancedThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level) { +bool AdvancedThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level, Method* method) { switch(cur_level) { case CompLevel_none: case CompLevel_limited_profile: { double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback); - return call_predicate_helper(i, b, k); + return call_predicate_helper(i, b, k, method); } case CompLevel_full_profile: { double k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback); - return call_predicate_helper(i, b, k); + return call_predicate_helper(i, b, k, method); } default: return true; @@ -271,7 +271,7 @@ int i = method->invocation_count(); int b = method->backedge_count(); double k = Tier0ProfilingStartPercentage / 100.0; - return call_predicate_helper(i, b, k) || loop_predicate_helper(i, b, k); + return call_predicate_helper(i, b, k, method) || loop_predicate_helper(i, b, k, method); } return false; } @@ -348,7 +348,7 @@ // If we were at full profile level, would we switch to full opt? if (common(p, method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) { next_level = CompLevel_full_optimization; - } else if ((this->*p)(i, b, cur_level)) { + } else if ((this->*p)(i, b, cur_level, method)) { // C1-generated fully profiled code is about 30% slower than the limited profile // code that has only invocation and backedge counters. The observation is that // if C2 queue is large enough we can spend too much time in the fully profiled code @@ -374,7 +374,7 @@ if (mdo->would_profile()) { if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <= Tier3DelayOff * compiler_count(CompLevel_full_optimization) && - (this->*p)(i, b, cur_level))) { + (this->*p)(i, b, cur_level, method))) { next_level = CompLevel_full_profile; } } else { @@ -390,7 +390,7 @@ if (mdo->would_profile()) { int mdo_i = mdo->invocation_count_delta(); int mdo_b = mdo->backedge_count_delta(); - if ((this->*p)(mdo_i, mdo_b, cur_level)) { + if ((this->*p)(mdo_i, mdo_b, cur_level, method)) { next_level = CompLevel_full_optimization; } } else { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -84,7 +84,7 @@ * invocation and backedge notifications. Basically every n-th invocation or backedge a mutator thread * makes a call into the runtime. * - * - Tier?CompileThreshold, Tier?BackEdgeThreshold, Tier?MinInvocationThreshold control + * - Tier?InvocationThreshold, Tier?CompileThreshold, Tier?BackEdgeThreshold, Tier?MinInvocationThreshold control * compilation thresholds. * Level 2 thresholds are not used and are provided for option-compatibility and potential future use. * Other thresholds work as follows: @@ -100,7 +100,9 @@ * The same predicate is used to control the transition from level 3 to level 4 (C2). It should be * noted though that the thresholds are relative. Moreover i and b for the 0->3 transition come * from Method* and for 3->4 transition they come from MDO (since profiled invocations are - * counted separately). + * counted separately). Finally, if a method does not contain anything worth profiling, a transition + * from level 3 to level 4 occurs without considering thresholds (e.g., with fewer invocations than + * what is specified by Tier4InvocationThreshold). * * OSR transitions are controlled simply with b > TierXBackEdgeThreshold * s predicates. * @@ -164,9 +166,9 @@ // Call and loop predicates determine whether a transition to a higher compilation // level should be performed (pointers to predicate functions are passed to common(). // Predicates also take compiler load into account. - typedef bool (AdvancedThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level); - bool call_predicate(int i, int b, CompLevel cur_level); - bool loop_predicate(int i, int b, CompLevel cur_level); + typedef bool (AdvancedThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level, Method* method); + bool call_predicate(int i, int b, CompLevel cur_level, Method* method); + bool loop_predicate(int i, int b, CompLevel cur_level, Method* method); // Common transition function. Given a predicate determines if a method should transition to another level. CompLevel common(Predicate p, Method* method, CompLevel cur_level, bool disable_feedback = false); // Transition functions. diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1126,16 +1126,35 @@ } #endif -// Returns threshold scaled with CompileThresholdScaling -intx Arguments::get_scaled_compile_threshold(intx threshold) { - return (intx)(threshold * CompileThresholdScaling); +intx Arguments::scaled_compile_threshold(intx threshold, double scale) { + if (scale == 1.0 || scale < 0.0) { + return threshold; + } else { + return (intx)(threshold * scale); + } } // Returns freq_log scaled with CompileThresholdScaling -intx Arguments::get_scaled_freq_log(intx freq_log) { - intx scaled_freq = get_scaled_compile_threshold((intx)1 << freq_log); - if (scaled_freq == 0) { - return 0; +intx Arguments::scaled_freq_log(intx freq_log, double scale) { + // Check if scaling is necessary or negative value was specified. + if (scale == 1.0 || scale < 0.0) { + return freq_log; + } + + // Check value to avoid calculating log2 of 0. + if (scale == 0.0) { + return 1; + } + + intx scaled_freq = scaled_compile_threshold((intx)1 << freq_log, scale); + // Determine the maximum notification frequency value currently supported. + // The largest mask value that the interpreter/C1 can handle is + // of length InvocationCounter::number_of_count_bits. Mask values are always + // one bit shorter then the value of the notification frequency. Set + // max_freq_bits accordingly. + intx max_freq_bits = InvocationCounter::number_of_count_bits + 1; + if (scaled_freq > nth_bit(max_freq_bits)) { + return max_freq_bits; } else { return log2_intptr(scaled_freq); } @@ -1180,31 +1199,36 @@ Tier3InvokeNotifyFreqLog = 0; Tier4InvocationThreshold = 0; } + + if (CompileThresholdScaling < 0) { + vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", NULL); + } + // Scale tiered compilation thresholds if (!FLAG_IS_DEFAULT(CompileThresholdScaling)) { - FLAG_SET_ERGO(intx, Tier0InvokeNotifyFreqLog, get_scaled_freq_log(Tier0InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier0BackedgeNotifyFreqLog, get_scaled_freq_log(Tier0BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier3InvocationThreshold, get_scaled_compile_threshold(Tier3InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3MinInvocationThreshold, get_scaled_compile_threshold(Tier3MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3CompileThreshold, get_scaled_compile_threshold(Tier3CompileThreshold)); - FLAG_SET_ERGO(intx, Tier3BackEdgeThreshold, get_scaled_compile_threshold(Tier3BackEdgeThreshold)); + FLAG_SET_ERGO(intx, Tier0InvokeNotifyFreqLog, scaled_freq_log(Tier0InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier0BackedgeNotifyFreqLog, scaled_freq_log(Tier0BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier3InvocationThreshold, scaled_compile_threshold(Tier3InvocationThreshold)); + FLAG_SET_ERGO(intx, Tier3MinInvocationThreshold, scaled_compile_threshold(Tier3MinInvocationThreshold)); + FLAG_SET_ERGO(intx, Tier3CompileThreshold, scaled_compile_threshold(Tier3CompileThreshold)); + FLAG_SET_ERGO(intx, Tier3BackEdgeThreshold, scaled_compile_threshold(Tier3BackEdgeThreshold)); // Tier2{Invocation,MinInvocation,Compile,Backedge}Threshold should be scaled here // once these thresholds become supported. - FLAG_SET_ERGO(intx, Tier2InvokeNotifyFreqLog, get_scaled_freq_log(Tier2InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier2BackedgeNotifyFreqLog, get_scaled_freq_log(Tier2BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier3InvokeNotifyFreqLog, get_scaled_freq_log(Tier3InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier3BackedgeNotifyFreqLog, get_scaled_freq_log(Tier3BackedgeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier23InlineeNotifyFreqLog, get_scaled_freq_log(Tier23InlineeNotifyFreqLog)); - - FLAG_SET_ERGO(intx, Tier4InvocationThreshold, get_scaled_compile_threshold(Tier4InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4MinInvocationThreshold, get_scaled_compile_threshold(Tier4MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4CompileThreshold, get_scaled_compile_threshold(Tier4CompileThreshold)); - FLAG_SET_ERGO(intx, Tier4BackEdgeThreshold, get_scaled_compile_threshold(Tier4BackEdgeThreshold)); + FLAG_SET_ERGO(intx, Tier2InvokeNotifyFreqLog, scaled_freq_log(Tier2InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier2BackedgeNotifyFreqLog, scaled_freq_log(Tier2BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier3InvokeNotifyFreqLog, scaled_freq_log(Tier3InvokeNotifyFreqLog)); + FLAG_SET_ERGO(intx, Tier3BackedgeNotifyFreqLog, scaled_freq_log(Tier3BackedgeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier23InlineeNotifyFreqLog, scaled_freq_log(Tier23InlineeNotifyFreqLog)); + + FLAG_SET_ERGO(intx, Tier4InvocationThreshold, scaled_compile_threshold(Tier4InvocationThreshold)); + FLAG_SET_ERGO(intx, Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold)); + FLAG_SET_ERGO(intx, Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold)); + FLAG_SET_ERGO(intx, Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold)); } } @@ -3456,7 +3480,7 @@ } if ((TieredCompilation && CompileThresholdScaling == 0) - || (!TieredCompilation && get_scaled_compile_threshold(CompileThreshold) == 0)) { + || (!TieredCompilation && scaled_compile_threshold(CompileThreshold) == 0)) { set_mode_flags(_int); } @@ -3896,7 +3920,7 @@ } // Scale CompileThreshold if (!FLAG_IS_DEFAULT(CompileThresholdScaling)) { - FLAG_SET_ERGO(intx, CompileThreshold, get_scaled_compile_threshold(CompileThreshold)); + FLAG_SET_ERGO(intx, CompileThreshold, scaled_compile_threshold(CompileThreshold)); } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/arguments.hpp --- a/hotspot/src/share/vm/runtime/arguments.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/arguments.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -328,9 +328,6 @@ static bool _ClipInlining; static bool _CIDynamicCompilePriority; - // Scale compile thresholds - static intx get_scaled_compile_threshold(intx threshold); - static intx get_scaled_freq_log(intx freq_log); // Tiered static void set_tiered_flags(); static int get_min_number_of_compiler_threads(); @@ -452,6 +449,18 @@ static char* SharedArchivePath; public: + // Scale compile thresholds + // Returns threshold scaled with CompileThresholdScaling + static intx scaled_compile_threshold(intx threshold, double scale); + static intx scaled_compile_threshold(intx threshold) { + return scaled_compile_threshold(threshold, CompileThresholdScaling); + } + // Returns freq_log scaled with CompileThresholdScaling + static intx scaled_freq_log(intx freq_log, double scale); + static intx scaled_freq_log(intx freq_log) { + return scaled_freq_log(freq_log, CompileThresholdScaling); + } + // Parses the arguments, first phase static jint parse(const JavaVMInitArgs* args); // Apply ergonomics diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/globals.hpp --- a/hotspot/src/share/vm/runtime/globals.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -2477,7 +2477,7 @@ "Number of compiler threads to run") \ \ product(intx, CompilationPolicyChoice, 0, \ - "which compilation policy (0/1)") \ + "which compilation policy (0-3)") \ \ develop(bool, UseStackBanging, true, \ "use stack banging for stack overflow checks (required for " \ @@ -3528,7 +3528,16 @@ \ product(double, CompileThresholdScaling, 1.0, \ "Factor to control when first compilation happens " \ - "(both with and without tiered compilation)") \ + "(both with and without tiered compilation): " \ + "values greater than 1.0 delay counter overflow, " \ + "values between 0 and 1.0 rush counter overflow, " \ + "value of 1.0 leave compilation thresholds unchanged " \ + "value of 0.0 is equivalent to -Xint. " \ + "" \ + "Flag can be set as per-method option. " \ + "If a value is specified for a method, compilation thresholds " \ + "for that method are scaled by both the value of the global flag "\ + "and the value of the per-method flag.") \ \ product(intx, Tier0InvokeNotifyFreqLog, 7, \ "Interpreter (tier 0) invocation notification frequency") \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/os.cpp --- a/hotspot/src/share/vm/runtime/os.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/os.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1401,15 +1401,17 @@ return (sp > (stack_limit + reserved_area)); } -size_t os::page_size_for_region(size_t region_size, size_t min_pages) { +size_t os::page_size_for_region(size_t region_size, size_t min_pages, bool must_be_aligned) { assert(min_pages > 0, "sanity"); if (UseLargePages) { const size_t max_page_size = region_size / min_pages; for (size_t i = 0; _page_sizes[i] != 0; ++i) { const size_t page_size = _page_sizes[i]; - if (page_size <= max_page_size && is_size_aligned(region_size, page_size)) { - return page_size; + if (page_size <= max_page_size) { + if (!must_be_aligned || is_size_aligned(region_size, page_size)) { + return page_size; + } } } } @@ -1417,6 +1419,14 @@ return vm_page_size(); } +size_t os::page_size_for_region_aligned(size_t region_size, size_t min_pages) { + return page_size_for_region(region_size, min_pages, true); +} + +size_t os::page_size_for_region_unaligned(size_t region_size, size_t min_pages) { + return page_size_for_region(region_size, min_pages, false); +} + #ifndef PRODUCT void os::trace_page_sizes(const char* str, const size_t* page_sizes, int count) { @@ -1665,17 +1675,17 @@ static size_t large_page_size() { const size_t large_page_size_example = 4 * M; - return os::page_size_for_region(large_page_size_example, 1); + return os::page_size_for_region_aligned(large_page_size_example, 1); } - static void test_page_size_for_region() { + static void test_page_size_for_region_aligned() { if (UseLargePages) { const size_t small_page = small_page_size(); const size_t large_page = large_page_size(); if (large_page > small_page) { size_t num_small_pages_in_large = large_page / small_page; - size_t page = os::page_size_for_region(large_page, num_small_pages_in_large); + size_t page = os::page_size_for_region_aligned(large_page, num_small_pages_in_large); assert_eq(page, small_page); } @@ -1688,21 +1698,53 @@ const size_t large_page = large_page_size(); if (large_page > small_page) { const size_t unaligned_region = large_page + 17; - size_t page = os::page_size_for_region(unaligned_region, 1); + size_t page = os::page_size_for_region_aligned(unaligned_region, 1); assert_eq(page, small_page); const size_t num_pages = 5; const size_t aligned_region = large_page * num_pages; - page = os::page_size_for_region(aligned_region, num_pages); + page = os::page_size_for_region_aligned(aligned_region, num_pages); assert_eq(page, large_page); } } } + static void test_page_size_for_region_unaligned() { + if (UseLargePages) { + // Given exact page size, should return that page size. + for (size_t i = 0; os::_page_sizes[i] != 0; i++) { + size_t expected = os::_page_sizes[i]; + size_t actual = os::page_size_for_region_unaligned(expected, 1); + assert_eq(expected, actual); + } + + // Given slightly larger size than a page size, return the page size. + for (size_t i = 0; os::_page_sizes[i] != 0; i++) { + size_t expected = os::_page_sizes[i]; + size_t actual = os::page_size_for_region_unaligned(expected + 17, 1); + assert_eq(expected, actual); + } + + // Given a slightly smaller size than a page size, + // return the next smaller page size. + if (os::_page_sizes[1] > os::_page_sizes[0]) { + size_t expected = os::_page_sizes[0]; + size_t actual = os::page_size_for_region_unaligned(os::_page_sizes[1] - 17, 1); + assert_eq(actual, expected); + } + + // Return small page size for values less than a small page. + size_t small_page = small_page_size(); + size_t actual = os::page_size_for_region_unaligned(small_page - 17, 1); + assert_eq(small_page, actual); + } + } + public: static void run_tests() { - test_page_size_for_region(); + test_page_size_for_region_aligned(); test_page_size_for_region_alignment(); + test_page_size_for_region_unaligned(); } }; diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/os.hpp --- a/hotspot/src/share/vm/runtime/os.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/os.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -148,6 +148,7 @@ static void pd_free_memory(char *addr, size_t bytes, size_t alignment_hint); static void pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint); + static size_t page_size_for_region(size_t region_size, size_t min_pages, bool must_be_aligned); public: static void init(void); // Called before command line parsing @@ -267,8 +268,13 @@ // Returns the page size to use for a region of memory. // region_size / min_pages will always be greater than or equal to the - // returned value. - static size_t page_size_for_region(size_t region_size, size_t min_pages); + // returned value. The returned value will divide region_size. + static size_t page_size_for_region_aligned(size_t region_size, size_t min_pages); + + // Returns the page size to use for a region of memory. + // region_size / min_pages will always be greater than or equal to the + // returned value. The returned value might not divide region_size. + static size_t page_size_for_region_unaligned(size_t region_size, size_t min_pages); // Return the largest page size that can be used static size_t max_page_size() { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -257,28 +257,28 @@ // Call and loop predicates determine whether a transition to a higher // compilation level should be performed (pointers to predicate functions // are passed to common() transition function). -bool SimpleThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level) { +bool SimpleThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level, Method* method) { switch(cur_level) { case CompLevel_none: case CompLevel_limited_profile: { - return loop_predicate_helper(i, b, 1.0); + return loop_predicate_helper(i, b, 1.0, method); } case CompLevel_full_profile: { - return loop_predicate_helper(i, b, 1.0); + return loop_predicate_helper(i, b, 1.0, method); } default: return true; } } -bool SimpleThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level) { +bool SimpleThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level, Method* method) { switch(cur_level) { case CompLevel_none: case CompLevel_limited_profile: { - return call_predicate_helper(i, b, 1.0); + return call_predicate_helper(i, b, 1.0, method); } case CompLevel_full_profile: { - return call_predicate_helper(i, b, 1.0); + return call_predicate_helper(i, b, 1.0, method); } default: return true; @@ -293,8 +293,8 @@ int i = mdo->invocation_count(); int b = mdo->backedge_count(); double k = ProfileMaturityPercentage / 100.0; - return call_predicate_helper(i, b, k) || - loop_predicate_helper(i, b, k); + return call_predicate_helper(i, b, k, method) || + loop_predicate_helper(i, b, k, method); } return false; } @@ -313,7 +313,7 @@ // If we were at full profile level, would we switch to full opt? if (common(p, method, CompLevel_full_profile) == CompLevel_full_optimization) { next_level = CompLevel_full_optimization; - } else if ((this->*p)(i, b, cur_level)) { + } else if ((this->*p)(i, b, cur_level, method)) { next_level = CompLevel_full_profile; } break; @@ -325,7 +325,7 @@ if (mdo->would_profile()) { int mdo_i = mdo->invocation_count_delta(); int mdo_b = mdo->backedge_count_delta(); - if ((this->*p)(mdo_i, mdo_b, cur_level)) { + if ((this->*p)(mdo_i, mdo_b, cur_level, method)) { next_level = CompLevel_full_optimization; } } else { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -43,9 +43,9 @@ // Call and loop predicates determine whether a transition to a higher compilation // level should be performed (pointers to predicate functions are passed to common_TF(). // Predicates also take compiler load into account. - typedef bool (SimpleThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level); - bool call_predicate(int i, int b, CompLevel cur_level); - bool loop_predicate(int i, int b, CompLevel cur_level); + typedef bool (SimpleThresholdPolicy::*Predicate)(int i, int b, CompLevel cur_level, Method* method); + bool call_predicate(int i, int b, CompLevel cur_level, Method* method); + bool loop_predicate(int i, int b, CompLevel cur_level, Method* method); // Common transition function. Given a predicate determines if a method should transition to another level. CompLevel common(Predicate p, Method* method, CompLevel cur_level); // Transition functions. @@ -76,8 +76,8 @@ // Predicate helpers are used by .*_predicate() methods as well as others. // They check the given counter values, multiplied by the scale against the thresholds. - template static inline bool call_predicate_helper(int i, int b, double scale); - template static inline bool loop_predicate_helper(int i, int b, double scale); + template static inline bool call_predicate_helper(int i, int b, double scale, Method* method); + template static inline bool loop_predicate_helper(int i, int b, double scale, Method* method); // Get a compilation level for a given method. static CompLevel comp_level(Method* method) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/simpleThresholdPolicy.inline.hpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.inline.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.inline.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -25,8 +25,14 @@ #ifndef SHARE_VM_RUNTIME_SIMPLETHRESHOLDPOLICY_INLINE_HPP #define SHARE_VM_RUNTIME_SIMPLETHRESHOLDPOLICY_INLINE_HPP +#include "compiler/compilerOracle.hpp" + template -bool SimpleThresholdPolicy::call_predicate_helper(int i, int b, double scale) { +bool SimpleThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) { + double threshold_scaling; + if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) { + scale *= threshold_scaling; + } switch(level) { case CompLevel_none: case CompLevel_limited_profile: @@ -40,7 +46,11 @@ } template -bool SimpleThresholdPolicy::loop_predicate_helper(int i, int b, double scale) { +bool SimpleThresholdPolicy::loop_predicate_helper(int i, int b, double scale, Method* method) { + double threshold_scaling; + if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) { + scale *= threshold_scaling; + } switch(level) { case CompLevel_none: case CompLevel_limited_profile: diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/synchronizer.cpp --- a/hotspot/src/share/vm/runtime/synchronizer.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/synchronizer.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -276,18 +276,6 @@ THREAD->set_current_pending_monitor_is_from_java(true); } -// NOTE: must use heavy weight monitor to handle jni monitor enter -bool ObjectSynchronizer::jni_try_enter(Handle obj, Thread* THREAD) { - if (UseBiasedLocking) { - BiasedLocking::revoke_and_rebias(obj, false, THREAD); - assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); - } - - ObjectMonitor* monitor = ObjectSynchronizer::inflate_helper(obj()); - return monitor->try_enter(THREAD); -} - - // NOTE: must use heavy weight monitor to handle jni monitor exit void ObjectSynchronizer::jni_exit(oop obj, Thread* THREAD) { TEVENT(jni_exit); diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/synchronizer.hpp --- a/hotspot/src/share/vm/runtime/synchronizer.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/synchronizer.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -65,7 +65,6 @@ // Used only to handle jni locks or other unmatched monitor enter/exit // Internally they will use heavy weight monitor. static void jni_enter(Handle obj, TRAPS); - static bool jni_try_enter(Handle obj, Thread* THREAD); // Implements Unsafe.tryMonitorEnter static void jni_exit(oop obj, Thread* THREAD); // Handle all interpreter, compiler and jni cases diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/virtualspace.cpp --- a/hotspot/src/share/vm/runtime/virtualspace.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/virtualspace.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -38,7 +38,8 @@ } ReservedSpace::ReservedSpace(size_t size) { - size_t page_size = os::page_size_for_region(size, 1); + // Want to use large pages where possible and pad with small pages. + size_t page_size = os::page_size_for_region_unaligned(size, 1); bool large_pages = page_size != (size_t)os::vm_page_size(); // Don't force the alignment to be large page aligned, // since that will waste memory. @@ -617,7 +618,7 @@ bool VirtualSpace::initialize(ReservedSpace rs, size_t committed_size) { - const size_t max_commit_granularity = os::page_size_for_region(rs.size(), 1); + const size_t max_commit_granularity = os::page_size_for_region_unaligned(rs.size(), 1); return initialize_with_granularity(rs, committed_size, max_commit_granularity); } @@ -1239,7 +1240,7 @@ case Disable: return vs.initialize_with_granularity(rs, 0, os::vm_page_size()); case Commit: - return vs.initialize_with_granularity(rs, 0, os::page_size_for_region(rs.size(), 1)); + return vs.initialize_with_granularity(rs, 0, os::page_size_for_region_unaligned(rs.size(), 1)); } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/runtime/vmStructs.cpp --- a/hotspot/src/share/vm/runtime/vmStructs.cpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Wed Jul 05 20:17:28 2017 +0200 @@ -351,11 +351,18 @@ nonstatic_field(MethodData, _arg_stack, intx) \ nonstatic_field(MethodData, _arg_returned, intx) \ nonstatic_field(MethodData, _tenure_traps, uint) \ + nonstatic_field(MethodData, _invoke_mask, int) \ + nonstatic_field(MethodData, _backedge_mask, int) \ nonstatic_field(DataLayout, _header._struct._tag, u1) \ nonstatic_field(DataLayout, _header._struct._flags, u1) \ nonstatic_field(DataLayout, _header._struct._bci, u2) \ nonstatic_field(DataLayout, _cells[0], intptr_t) \ nonstatic_field(MethodCounters, _nmethod_age, int) \ + nonstatic_field(MethodCounters, _interpreter_invocation_limit, int) \ + nonstatic_field(MethodCounters, _interpreter_backward_branch_limit, int) \ + nonstatic_field(MethodCounters, _interpreter_profile_limit, int) \ + nonstatic_field(MethodCounters, _invoke_mask, int) \ + nonstatic_field(MethodCounters, _backedge_mask, int) \ nonstatic_field(MethodCounters, _interpreter_invocation_count, int) \ nonstatic_field(MethodCounters, _interpreter_throwout_count, u2) \ nonstatic_field(MethodCounters, _number_of_breakpoints, u2) \ diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/src/share/vm/utilities/globalDefinitions.hpp --- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp Wed Jul 05 20:17:28 2017 +0200 @@ -1142,17 +1142,18 @@ return ((x != NoLongBits) && (mask_long_bits(x, x - 1) == NoLongBits)); } -//* largest i such that 2^i <= x -// A negative value of 'x' will return '31' +// Returns largest i such that 2^i <= x. +// If x < 0, the function returns 31 on a 32-bit machine and 63 on a 64-bit machine. +// If x == 0, the function returns -1. inline int log2_intptr(intptr_t x) { int i = -1; - uintptr_t p = 1; + uintptr_t p = 1; while (p != 0 && p <= (uintptr_t)x) { // p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x) i++; p *= 2; } // p = 2^(i+1) && x < p (i.e., 2^i <= x < 2^(i+1)) - // (if p = 0 then overflow occurred and i = 31) + // If p = 0, overflow has occurred and i = 31 or i = 63 (depending on the machine word size). return i; } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/arguments/CheckCompileThresholdScaling.java --- a/hotspot/test/compiler/arguments/CheckCompileThresholdScaling.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/compiler/arguments/CheckCompileThresholdScaling.java Wed Jul 05 20:17:28 2017 +0200 @@ -54,7 +54,7 @@ // // Tier0InvokeNotifyFreqLog, Tier0BackedgeNotifyFreqLog, // Tier3InvocationThreshold, Tier3MinInvocationThreshold, - // Tier3CompileThreshold, and Tier3BackEdgeThreshold, + // Tier3CompileThreshold, Tier3BackEdgeThreshold, // Tier2InvokeNotifyFreqLog, Tier2BackedgeNotifyFreqLog, // Tier3InvokeNotifyFreqLog, Tier3BackedgeNotifyFreqLog, // Tier23InlineeNotifyFreqLog, Tier4InvocationThreshold, diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/codecache/jmx/PoolsIndependenceTest.java --- a/hotspot/test/compiler/codecache/jmx/PoolsIndependenceTest.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/compiler/codecache/jmx/PoolsIndependenceTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -98,11 +98,13 @@ return false; }); for (BlobType bt : BlobType.getAvailable()) { - int expectedNotificationsAmount = bt.equals(btype) ? 1 : 0; - Asserts.assertEQ(counters.get(bt.getMemoryPool().getName()).get(), - expectedNotificationsAmount, String.format("Unexpected " - + "amount of notifications for pool: %s", - bt.getMemoryPool().getName())); + if (CodeCacheUtils.isCodeHeapPredictable(bt)) { + int expectedNotificationsAmount = bt.equals(btype) ? 1 : 0; + Asserts.assertEQ(counters.get(bt.getMemoryPool().getName()).get(), + expectedNotificationsAmount, String.format("Unexpected " + + "amount of notifications for pool: %s", + bt.getMemoryPool().getName())); + } } try { ((NotificationEmitter) ManagementFactory.getMemoryMXBean()). diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/codecache/jmx/ThresholdNotificationsTest.java --- a/hotspot/test/compiler/codecache/jmx/ThresholdNotificationsTest.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/compiler/codecache/jmx/ThresholdNotificationsTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -52,7 +52,9 @@ public static void main(String[] args) { for (BlobType bt : BlobType.getAvailable()) { - new ThresholdNotificationsTest(bt).runTest(); + if (CodeCacheUtils.isCodeHeapPredictable(bt)) { + new ThresholdNotificationsTest(bt).runTest(); + } } } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/loopopts/7052494/Test7052494.java --- a/hotspot/test/compiler/loopopts/7052494/Test7052494.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/compiler/loopopts/7052494/Test7052494.java Wed Jul 05 20:17:28 2017 +0200 @@ -25,7 +25,6 @@ /** * @test * @bug 7052494 - * @ignore 7154567 * @summary Eclipse test fails on JDK 7 b142 * * @run main/othervm -Xbatch Test7052494 diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright 2015 SAP AG. 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 8068909 + * @key regression + * @summary test that string optimizations produce code, that doesn't lead to a crash. + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestOptimizeStringConcat + * @author axel.siebenborn@sap.com + */ +public class TestOptimizeStringConcat { + + static boolean checkArgumentSyntax(String value, String allowedchars, String notallowedchars, String logmsg) { + String rc = null; + + int maxchar = 99999; + int minchar = 1; + if ((allowedchars != null && notallowedchars != null) || minchar > maxchar) { + rc = "internal error"; + } else { + if (value == null) { + rc = "the value null is not allowed, it is missing"; + } else if (value != null && minchar > 0 && value.trim().equals("")) { + rc = "the value must not be empty"; + } else if (value != null) { + if (value.length() < minchar || value.length() > maxchar) { + if (rc == null) { + rc = "the value length must be between +minchar+ and +maxchar"; + } + } + char[] _value = value.toCharArray(); + boolean dotfound = false; + int i = 1; + if (_value[i] == '.' && !dotfound) { + dotfound = true; + } else if (allowedchars != null && allowedchars.indexOf(_value[i]) == -1) { + if (rc == null) { + rc = "the value contains an illegal character: '" + _value[i] + "', only following characters are allowed: '+allowedchars+'"; + } else { + rc += " / the value contains an illegal character: '" + _value[i] + "', only following characters are allowed: '+allowedchars+'"; + } + } else if (notallowedchars != null && notallowedchars.indexOf(_value[i]) != -1) { + if (rc == null) { + rc = "the value contains an illegal character: '" + _value[i] + "', following characters are not allowed '+notallowedchars+'"; + } else { + rc += " / the value contains an illegal character: '" + _value[i] + "', following characters are not allowed '+notallowedchars+'"; + } + } + } + } + + if (rc != null) { + System.out.println(logmsg + " ==> " + rc); + return false; + } + return true; + } + + public static void main(String[] args) { + boolean failed = false; + for (int i = 0; i < 10000; i++) { + failed |= !checkArgumentSyntax("theName", null, "\"<&", "Error consistencyCheck: name in component definition"); + failed |= !checkArgumentSyntax(null, null, "\"<&", "Error consistencyCheck: name in component definition"); + failed |= !checkArgumentSyntax("42", "0123456789.", null, "Error consistencyCheck: counter in component definition"); + } + System.out.println(failed); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/compiler/testlibrary/rtm/BusyLock.java --- a/hotspot/test/compiler/testlibrary/rtm/BusyLock.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/compiler/testlibrary/rtm/BusyLock.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -24,9 +24,6 @@ package rtm; -import com.oracle.java.testlibrary.Utils; -import sun.misc.Unsafe; - import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; @@ -42,7 +39,6 @@ // Following field have to be static in order to avoid escape analysis. @SuppressWarnings("UnsuedDeclaration") private static int field = 0; - private static final Unsafe UNSAFE = Utils.getUnsafe(); protected final Object monitor; protected final int timeout; @@ -59,18 +55,9 @@ @Override public void run() { try { - // wait until forceAbort leave monitor - barrier.await(); - if (UNSAFE.tryMonitorEnter(monitor)) { - try { - barrier.await(); - Thread.sleep(timeout); - } finally { - UNSAFE.monitorExit(monitor); - } - } else { - throw new RuntimeException("Monitor should be entered by " + - "::run() first."); + synchronized (monitor) { + barrier.await(); + Thread.sleep(timeout); } } catch (InterruptedException | BrokenBarrierException e) { throw new RuntimeException("Synchronization error happened.", e); @@ -79,7 +66,6 @@ public void syncAndTest() { try { - barrier.await(); // wait until monitor is locked by a ::run method barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/gc/TestNUMAPageSize.java --- a/hotspot/test/gc/TestNUMAPageSize.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/gc/TestNUMAPageSize.java Wed Jul 05 20:17:28 2017 +0200 @@ -25,6 +25,7 @@ * @test TestNUMAPageSize * @summary Make sure that start up with NUMA support does not cause problems. * @bug 8061467 + * @requires (vm.opt.AggressiveOpts == null) | (vm.opt.AggressiveOpts == false) * @key gc * @key regression * @run main/othervm -Xmx8M -XX:+UseNUMA TestNUMAPageSize diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/gc/TestSmallHeap.java --- a/hotspot/test/gc/TestSmallHeap.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/gc/TestSmallHeap.java Wed Jul 05 20:17:28 2017 +0200 @@ -25,6 +25,7 @@ * @test TestSmallHeap * @bug 8067438 * @requires vm.gc=="null" + * @requires (vm.opt.AggressiveOpts=="null") | (vm.opt.AggressiveOpts=="false") * @summary Verify that starting the VM with a small heap works * @library /testlibrary /../../test/lib * @build TestSmallHeap @@ -33,8 +34,9 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx2m -XX:+UseSerialGC TestSmallHeap * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx2m -XX:+UseG1GC TestSmallHeap * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx2m -XX:+UseConcMarkSweepGC TestSmallHeap - * - * Note: It would be nice to verify the minimal supported heap size (2m) here, + */ + +/* Note: It would be nice to verify the minimal supported heap size (2m) here, * but we align the heap size based on the card table size. And the card table * size is aligned based on the minimal pages size provided by the os. This * means that on most platforms, where the minimal page size is 4k, we get a diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java --- a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java Wed Jul 05 20:17:28 2017 +0200 @@ -116,7 +116,14 @@ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0])); OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); + try { + output.shouldHaveExitValue(0); + } catch (RuntimeException e) { + // It's ok if there is no client vm in the jdk. + if (output.firstMatch("Unrecognized option: -client") == null) { + throw e; + } + } return output; } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/runtime/ErrorHandling/ProblematicFrameTest.java --- a/hotspot/test/runtime/ErrorHandling/ProblematicFrameTest.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/runtime/ErrorHandling/ProblematicFrameTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -44,7 +44,7 @@ public static void main(String[] args) throws Exception { ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( - "-Xmx64m", "-XX:-TransmitErrorReport", Crasher.class.getName()); + "-Xmx64m", "-XX:-TransmitErrorReport", "-XX:-CreateMinidumpOnCrash", Crasher.class.getName()); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotMatch("error occurred during error reporting \\(printing problematic frame\\)"); } diff -r ac59fe6cd98f -r 4746a63313f6 hotspot/test/serviceability/dcmd/compiler/CompilerQueueTest.java --- a/hotspot/test/serviceability/dcmd/compiler/CompilerQueueTest.java Thu Jan 29 15:36:21 2015 -0800 +++ b/hotspot/test/serviceability/dcmd/compiler/CompilerQueueTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -25,6 +25,7 @@ * @test CompilerQueueTest * @bug 8054889 * @library .. + * @ignore 8069160 * @build DcmdUtil CompilerQueueTest * @run main CompilerQueueTest * @run main/othervm -XX:-TieredCompilation CompilerQueueTest diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/.hgtags --- a/jaxp/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -290,3 +290,4 @@ 0dab3e848229127c7aca4c58b98e2d90ba70372f jdk9-b45 74eaf7ad986576c792df4dbff05eed63e5727695 jdk9-b46 e391de88e69b59d7c618387e3cf91032f6991ce9 jdk9-b47 +833051855168a973780fafeb6fc59e7370bcf400 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/BasisLibrary.java --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/BasisLibrary.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/BasisLibrary.java Wed Jul 05 20:17:28 2017 +0200 @@ -270,8 +270,8 @@ if (Double.isNaN(start)) return(EMPTYSTRING); - final int strlen = value.length(); - int istart = (int)Math.round(start) - 1; + final int strlen = value.length(); + int istart = (int)Math.round(start) - 1; if (istart > strlen) return(EMPTYSTRING); @@ -292,10 +292,11 @@ public static String substringF(String value, double start, double length) { if (Double.isInfinite(start) || Double.isNaN(start) || - Double.isNaN(length)) + Double.isNaN(length) || + length < 0) return(EMPTYSTRING); - int istart = (int)Math.round(start) - 1; + int istart = (int)Math.round(start) - 1; final int isum; if (Double.isInfinite(length)) isum = Integer.MAX_VALUE; diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathExpressionImpl.java --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathExpressionImpl.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathExpressionImpl.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,15 +1,15 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,36 +17,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// $Id: XPathExpressionImpl.java,v 1.3 2005/09/27 09:40:43 sunithareddy Exp $ package com.sun.org.apache.xpath.internal.jaxp; -import com.sun.org.apache.xpath.internal.*; -import javax.xml.transform.TransformerException; - +import com.sun.org.apache.xalan.internal.utils.FeatureManager; import com.sun.org.apache.xpath.internal.objects.XObject; -import com.sun.org.apache.xml.internal.dtm.DTM; -import com.sun.org.apache.xml.internal.utils.PrefixResolver; -import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; -import com.sun.org.apache.xalan.internal.res.XSLMessages; -import com.sun.org.apache.xalan.internal.utils.FactoryImpl; -import com.sun.org.apache.xalan.internal.utils.FeatureManager; - -import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; +import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathEvaluationResult; +import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; -import javax.xml.xpath.XPathConstants; - +import org.w3c.dom.Document; import org.w3c.dom.Node; -import org.w3c.dom.Document; -import org.w3c.dom.DOMImplementation; -import org.w3c.dom.traversal.NodeIterator; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.DocumentBuilder; - import org.xml.sax.InputSource; /** @@ -54,21 +39,9 @@ * * @author Ramesh Mandava */ -public class XPathExpressionImpl implements javax.xml.xpath.XPathExpression{ - - private XPathFunctionResolver functionResolver; - private XPathVariableResolver variableResolver; - private JAXPPrefixResolver prefixResolver; - private com.sun.org.apache.xpath.internal.XPath xpath; +public class XPathExpressionImpl extends XPathImplUtil implements XPathExpression { - // By default Extension Functions are allowed in XPath Expressions. If - // Secure Processing Feature is set on XPathFactory then the invocation of - // extensions function need to throw XPathFunctionException - private boolean featureSecureProcessing = false; - - private boolean useServicesMechanism = true; - - private final FeatureManager featureManager; + private com.sun.org.apache.xpath.internal.XPath xpath; /** Protected constructor to prevent direct instantiation; use compile() * from the context. @@ -81,7 +54,7 @@ protected XPathExpressionImpl(com.sun.org.apache.xpath.internal.XPath xpath, JAXPPrefixResolver prefixResolver, XPathFunctionResolver functionResolver, - XPathVariableResolver variableResolver ) { + XPathVariableResolver variableResolver) { this(xpath, prefixResolver, functionResolver, variableResolver, false, true, new FeatureManager()); }; @@ -89,291 +62,108 @@ protected XPathExpressionImpl(com.sun.org.apache.xpath.internal.XPath xpath, JAXPPrefixResolver prefixResolver,XPathFunctionResolver functionResolver, XPathVariableResolver variableResolver, boolean featureSecureProcessing, - boolean useServicesMechanism, FeatureManager featureManager ) { + boolean useServiceMechanism, FeatureManager featureManager) { this.xpath = xpath; this.prefixResolver = prefixResolver; this.functionResolver = functionResolver; this.variableResolver = variableResolver; this.featureSecureProcessing = featureSecureProcessing; - this.useServicesMechanism = useServicesMechanism; + this.useServiceMechanism = useServiceMechanism; this.featureManager = featureManager; }; - public void setXPath (com.sun.org.apache.xpath.internal.XPath xpath ) { + public void setXPath (com.sun.org.apache.xpath.internal.XPath xpath) { this.xpath = xpath; } public Object eval(Object item, QName returnType) throws javax.xml.transform.TransformerException { - XObject resultObject = eval ( item ); - return getResultAsType( resultObject, returnType ); - } - - private XObject eval ( Object contextItem ) - throws javax.xml.transform.TransformerException { - com.sun.org.apache.xpath.internal.XPathContext xpathSupport = null; - if ( functionResolver != null ) { - JAXPExtensionsProvider jep = new JAXPExtensionsProvider( - functionResolver, featureSecureProcessing, featureManager ); - xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext( jep ); - } else { - xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(); - } - - xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); - XObject xobj = null; - - Node contextNode = (Node)contextItem; - // We always need to have a ContextNode with Xalan XPath implementation - // To allow simple expression evaluation like 1+1 we are setting - // dummy Document as Context Node - - if ( contextNode == null ) - xobj = xpath.execute(xpathSupport, DTM.NULL, prefixResolver); - else - xobj = xpath.execute(xpathSupport, contextNode, prefixResolver); - - return xobj; + XObject resultObject = eval(item, xpath); + return getResultAsType(resultObject, returnType); } - - /** - *

Evaluate the compiled XPath expression in the specified context and - * return the result as the specified type.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If returnType is not one of the types defined - * in {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

- * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If returnType is null, then a - * NullPointerException is thrown.

- * - * @param item The starting context (node or node list, for example). - * @param returnType The desired return type. - * - * @return The Object that is the result of evaluating the - * expression and converting the result to - * returnType. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one - * of the types defined in {@link XPathConstants}. - * @throws NullPointerException If returnType is - * null. - */ + @Override public Object evaluate(Object item, QName returnType) throws XPathExpressionException { - //Validating parameters to enforce constraints defined by JAXP spec - if ( returnType == null ) { - //Throwing NullPointerException as defined in spec - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"returnType"} ); - throw new NullPointerException( fmsg ); - } - // Checking if requested returnType is supported. returnType need to be - // defined in XPathConstants - if ( !isSupported ( returnType ) ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString() } ); - throw new IllegalArgumentException ( fmsg ); - } + isSupported(returnType); try { - return eval( item, returnType); - } catch ( java.lang.NullPointerException npe ) { + return eval(item, returnType); + } catch (java.lang.NullPointerException npe) { // If VariableResolver returns null Or if we get // NullPointerException at this stage for some other reason // then we have to reurn XPathException - throw new XPathExpressionException ( npe ); - } catch ( javax.xml.transform.TransformerException te ) { + throw new XPathExpressionException (npe); + } catch (javax.xml.transform.TransformerException te) { Throwable nestedException = te.getException(); - if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { + if (nestedException instanceof javax.xml.xpath.XPathFunctionException) { throw (javax.xml.xpath.XPathFunctionException)nestedException; } else { // For any other exceptions we need to throw - // XPathExpressionException ( as per spec ) - throw new XPathExpressionException( te); + // XPathExpressionException (as per spec) + throw new XPathExpressionException(te); } } - - } - - /** - *

Evaluate the compiled XPath expression in the specified context and - * return the result as a String.

- * - *

This method calls {@link #evaluate(Object item, QName returnType)} - * with a returnType of - * {@link XPathConstants#STRING}.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * - * @param item The starting context (node or node list, for example). - * - * @return The String that is the result of evaluating the - * expression and converting the result to a - * String. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - */ - public String evaluate(Object item) - throws XPathExpressionException { - return (String)this.evaluate( item, XPathConstants.STRING ); } - - static DocumentBuilderFactory dbf = null; - static DocumentBuilder db = null; - static Document d = null; + @Override + public String evaluate(Object item) + throws XPathExpressionException { + return (String)this.evaluate(item, XPathConstants.STRING); + } - /** - *

Evaluate the compiled XPath expression in the context of the - * specified InputSource and return the result as the - * specified type.

- * - *

This method builds a data model for the {@link InputSource} and calls - * {@link #evaluate(Object item, QName returnType)} on the resulting - * document object.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If returnType is not one of the types defined in - * {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

- * - *

If source or returnType is null, - * then a NullPointerException is thrown.

- * - * @param source The InputSource of the document to evaluate - * over. - * @param returnType The desired return type. - * - * @return The Object that is the result of evaluating the - * expression and converting the result to - * returnType. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one - * of the types defined in {@link XPathConstants}. - * @throws NullPointerException If source or - * returnType is null. - */ + @Override public Object evaluate(InputSource source, QName returnType) throws XPathExpressionException { - if ( ( source == null ) || ( returnType == null ) ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL, - null ); - throw new NullPointerException ( fmsg ); - } - // Checking if requested returnType is supported. returnType need to be - // defined in XPathConstants - if ( !isSupported ( returnType ) ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString() } ); - throw new IllegalArgumentException ( fmsg ); - } + isSupported (returnType); try { - if ( dbf == null ) { - dbf = FactoryImpl.getDOMFactory(useServicesMechanism); - dbf.setNamespaceAware( true ); - dbf.setValidating( false ); - } - db = dbf.newDocumentBuilder(); - Document document = db.parse( source ); - return eval( document, returnType ); - } catch ( Exception e ) { - throw new XPathExpressionException ( e ); + Document document = getDocument(source); + return eval(document, returnType); + } catch (TransformerException e) { + throw new XPathExpressionException(e); } } - /** - *

Evaluate the compiled XPath expression in the context of the specified InputSource and return the result as a - * String.

- * - *

This method calls {@link #evaluate(InputSource source, QName returnType)} with a returnType of - * {@link XPathConstants#STRING}.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If source is null, then a NullPointerException is thrown.

- * - * @param source The InputSource of the document to evaluate over. - * - * @return The String that is the result of evaluating the expression and converting the result to a - * String. - * - * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws NullPointerException If source is null. - */ + @Override public String evaluate(InputSource source) throws XPathExpressionException { - return (String)this.evaluate( source, XPathConstants.STRING ); + return (String)this.evaluate(source, XPathConstants.STRING); } - private boolean isSupported( QName returnType ) { - // XPathConstants.STRING - if ( ( returnType.equals( XPathConstants.STRING ) ) || - ( returnType.equals( XPathConstants.NUMBER ) ) || - ( returnType.equals( XPathConstants.BOOLEAN ) ) || - ( returnType.equals( XPathConstants.NODE ) ) || - ( returnType.equals( XPathConstants.NODESET ) ) ) { + @Override + public T evaluateExpression(Object item, Class type) + throws XPathExpressionException { + isSupportedClassType(type); - return true; - } - return false; - } + try { + XObject resultObject = eval(item, xpath); + if (type.isAssignableFrom(XPathEvaluationResult.class)) { + return getXPathResult(resultObject, type); + } else { + return XPathResultImpl.getValue(resultObject, type); + } - private Object getResultAsType( XObject resultObject, QName returnType ) - throws javax.xml.transform.TransformerException { - // XPathConstants.STRING - if ( returnType.equals( XPathConstants.STRING ) ) { - return resultObject.str(); + } catch (javax.xml.transform.TransformerException te) { + throw new XPathExpressionException(te); } - // XPathConstants.NUMBER - if ( returnType.equals( XPathConstants.NUMBER ) ) { - return new Double ( resultObject.num()); - } - // XPathConstants.BOOLEAN - if ( returnType.equals( XPathConstants.BOOLEAN ) ) { - return new Boolean( resultObject.bool()); - } - // XPathConstants.NODESET ---ORdered, UNOrdered??? - if ( returnType.equals( XPathConstants.NODESET ) ) { - return resultObject.nodelist(); - } - // XPathConstants.NODE - if ( returnType.equals( XPathConstants.NODE ) ) { - NodeIterator ni = resultObject.nodeset(); - //Return the first node, or null - return ni.nextNode(); - } - // If isSupported check is already done then the execution path - // shouldn't come here. Being defensive - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString()}); - throw new IllegalArgumentException ( fmsg ); } + @Override + public XPathEvaluationResult evaluateExpression(Object item) + throws XPathExpressionException { + return evaluateExpression(item, XPathEvaluationResult.class); + } + + @Override + public T evaluateExpression(InputSource source, Class type) + throws XPathExpressionException { + Document document = getDocument(source); + return evaluateExpression(document, type); + } + + @Override + public XPathEvaluationResult evaluateExpression(InputSource source) + throws XPathExpressionException { + return evaluateExpression(source, XPathEvaluationResult.class); + } } diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,6 +1,5 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 1999-2004 The Apache Software Foundation. @@ -28,55 +27,37 @@ import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; import javax.xml.xpath.XPathExpression; - -import com.sun.org.apache.xml.internal.dtm.DTM; import com.sun.org.apache.xpath.internal.*; import com.sun.org.apache.xpath.internal.objects.XObject; -import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; -import com.sun.org.apache.xalan.internal.res.XSLMessages; -import com.sun.org.apache.xalan.internal.utils.FactoryImpl; import com.sun.org.apache.xalan.internal.utils.FeatureManager; - -import org.w3c.dom.Node; import org.w3c.dom.Document; -import org.w3c.dom.traversal.NodeIterator; - import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import javax.xml.parsers.*; - -import java.io.IOException; +import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPathEvaluationResult; /** * The XPathImpl class provides implementation for the methods defined in - * javax.xml.xpath.XPath interface. This provide simple access to the results + * javax.xml.xpath.XPath interface. This provides simple access to the results * of an XPath expression. * - * * @author Ramesh Mandava + * + * Updated 12/04/2014: + * New methods: evaluateExpression + * Refactored to share code with XPathExpressionImpl. */ -public class XPathImpl implements javax.xml.xpath.XPath { +public class XPathImpl extends XPathImplUtil implements javax.xml.xpath.XPath { // Private variables - private XPathVariableResolver variableResolver; - private XPathFunctionResolver functionResolver; private XPathVariableResolver origVariableResolver; private XPathFunctionResolver origFunctionResolver; private NamespaceContext namespaceContext=null; - private JAXPPrefixResolver prefixResolver; - // By default Extension Functions are allowed in XPath Expressions. If - // Secure Processing Feature is set on XPathFactory then the invocation of - // extensions function need to throw XPathFunctionException - private boolean featureSecureProcessing = false; - private boolean useServiceMechanism = true; - private final FeatureManager featureManager; - XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr ) { + XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr) { this(vr, fr, false, true, new FeatureManager()); } - XPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr, + XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr, boolean featureSecureProcessing, boolean useServiceMechanism, FeatureManager featureManager) { this.origVariableResolver = this.variableResolver = vr; @@ -86,451 +67,173 @@ this.featureManager = featureManager; } - /** - *

Establishes a variable resolver.

- * - * @param resolver Variable Resolver - */ + + //-Override- public void setXPathVariableResolver(XPathVariableResolver resolver) { - if ( resolver == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"XPathVariableResolver"} ); - throw new NullPointerException( fmsg ); - } + requireNonNull(resolver, "XPathVariableResolver"); this.variableResolver = resolver; } - /** - *

Returns the current variable resolver.

- * - * @return Current variable resolver - */ + //-Override- public XPathVariableResolver getXPathVariableResolver() { return variableResolver; } - /** - *

Establishes a function resolver.

- * - * @param resolver XPath function resolver - */ + //-Override- public void setXPathFunctionResolver(XPathFunctionResolver resolver) { - if ( resolver == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"XPathFunctionResolver"} ); - throw new NullPointerException( fmsg ); - } + requireNonNull(resolver, "XPathFunctionResolver"); this.functionResolver = resolver; } - /** - *

Returns the current function resolver.

- * - * @return Current function resolver - */ + //-Override- public XPathFunctionResolver getXPathFunctionResolver() { return functionResolver; } - /** - *

Establishes a namespace context.

- * - * @param nsContext Namespace context to use - */ + //-Override- public void setNamespaceContext(NamespaceContext nsContext) { - if ( nsContext == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"NamespaceContext"} ); - throw new NullPointerException( fmsg ); - } + requireNonNull(nsContext, "NamespaceContext"); this.namespaceContext = nsContext; - this.prefixResolver = new JAXPPrefixResolver ( nsContext ); + this.prefixResolver = new JAXPPrefixResolver (nsContext); } - /** - *

Returns the current namespace context.

- * - * @return Current Namespace context - */ + //-Override- public NamespaceContext getNamespaceContext() { return namespaceContext; } - private static Document d = null; - - private DocumentBuilder getParser() { - try { - // we'd really like to cache those DocumentBuilders, but we can't because: - // 1. thread safety. parsers are not thread-safe, so at least - // we need one instance per a thread. - // 2. parsers are non-reentrant, so now we are looking at having a - // pool of parsers. - // 3. then the class loading issue. The look-up procedure of - // DocumentBuilderFactory.newInstance() depends on context class loader - // and system properties, which may change during the execution of JVM. - // - // so we really have to create a fresh DocumentBuilder every time we need one - // - KK - DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(useServiceMechanism); - dbf.setNamespaceAware( true ); - dbf.setValidating( false ); - return dbf.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - // this should never happen with a well-behaving JAXP implementation. - throw new Error(e); - } - } - + /** + * Evaluate an {@code XPath} expression in the specified context. + * @param expression The XPath expression. + * @param contextItem The starting context. + * @return an XObject as the result of evaluating the expression + * @throws TransformerException if evaluating fails + */ + private XObject eval(String expression, Object contextItem) + throws TransformerException { + requireNonNull(expression, "XPath expression"); + com.sun.org.apache.xpath.internal.XPath xpath = new com.sun.org.apache.xpath.internal.XPath(expression, + null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT); - private XObject eval(String expression, Object contextItem) - throws javax.xml.transform.TransformerException { - com.sun.org.apache.xpath.internal.XPath xpath = new com.sun.org.apache.xpath.internal.XPath( expression, - null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT ); - com.sun.org.apache.xpath.internal.XPathContext xpathSupport = null; - if ( functionResolver != null ) { - JAXPExtensionsProvider jep = new JAXPExtensionsProvider( - functionResolver, featureSecureProcessing, featureManager ); - xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext( jep ); - } else { - xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(); - } - - XObject xobj = null; - - xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); - - // If item is null, then we will create a a Dummy contextNode - if ( contextItem instanceof Node ) { - xobj = xpath.execute (xpathSupport, (Node)contextItem, - prefixResolver ); - } else { - xobj = xpath.execute ( xpathSupport, DTM.NULL, prefixResolver ); - } - - return xobj; + return eval(contextItem, xpath); } - /** - *

Evaluate an XPath expression in the specified context and return the result as the specified type.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If returnType is not one of the types defined in {@link XPathConstants} ( - * {@link XPathConstants#NUMBER NUMBER}, - * {@link XPathConstants#STRING STRING}, - * {@link XPathConstants#BOOLEAN BOOLEAN}, - * {@link XPathConstants#NODE NODE} or - * {@link XPathConstants#NODESET NODESET}) - * then an IllegalArgumentException is thrown.

- * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If expression or returnType is null, then a - * NullPointerException is thrown.

- * - * @param expression The XPath expression. - * @param item The starting context (node or node list, for example). - * @param returnType The desired return type. - * - * @return Result of evaluating an XPath expression as an Object of returnType. - * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If expression or returnType is null. - */ + //-Override- public Object evaluate(String expression, Object item, QName returnType) throws XPathExpressionException { - if ( expression == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"XPath expression"} ); - throw new NullPointerException ( fmsg ); - } - if ( returnType == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"returnType"} ); - throw new NullPointerException ( fmsg ); - } - // Checking if requested returnType is supported. returnType need to - // be defined in XPathConstants - if ( !isSupported ( returnType ) ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString() } ); - throw new IllegalArgumentException ( fmsg ); - } + //this check is necessary before calling eval to maintain binary compatibility + requireNonNull(expression, "XPath expression"); + isSupported(returnType); try { - XObject resultObject = eval( expression, item ); - return getResultAsType( resultObject, returnType ); - } catch ( java.lang.NullPointerException npe ) { + XObject resultObject = eval(expression, item); + return getResultAsType(resultObject, returnType); + } catch (java.lang.NullPointerException npe) { // If VariableResolver returns null Or if we get // NullPointerException at this stage for some other reason // then we have to reurn XPathException - throw new XPathExpressionException ( npe ); - } catch ( javax.xml.transform.TransformerException te ) { + throw new XPathExpressionException (npe); + } catch (TransformerException te) { Throwable nestedException = te.getException(); - if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { + if (nestedException instanceof javax.xml.xpath.XPathFunctionException) { throw (javax.xml.xpath.XPathFunctionException)nestedException; } else { // For any other exceptions we need to throw - // XPathExpressionException ( as per spec ) - throw new XPathExpressionException ( te ); + // XPathExpressionException (as per spec) + throw new XPathExpressionException (te); } } } - private boolean isSupported( QName returnType ) { - if ( ( returnType.equals( XPathConstants.STRING ) ) || - ( returnType.equals( XPathConstants.NUMBER ) ) || - ( returnType.equals( XPathConstants.BOOLEAN ) ) || - ( returnType.equals( XPathConstants.NODE ) ) || - ( returnType.equals( XPathConstants.NODESET ) ) ) { - - return true; - } - return false; - } - - private Object getResultAsType( XObject resultObject, QName returnType ) - throws javax.xml.transform.TransformerException { - // XPathConstants.STRING - if ( returnType.equals( XPathConstants.STRING ) ) { - return resultObject.str(); - } - // XPathConstants.NUMBER - if ( returnType.equals( XPathConstants.NUMBER ) ) { - return new Double ( resultObject.num()); - } - // XPathConstants.BOOLEAN - if ( returnType.equals( XPathConstants.BOOLEAN ) ) { - return new Boolean( resultObject.bool()); - } - // XPathConstants.NODESET ---ORdered, UNOrdered??? - if ( returnType.equals( XPathConstants.NODESET ) ) { - return resultObject.nodelist(); - } - // XPathConstants.NODE - if ( returnType.equals( XPathConstants.NODE ) ) { - NodeIterator ni = resultObject.nodeset(); - //Return the first node, or null - return ni.nextNode(); - } - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString()}); - throw new IllegalArgumentException( fmsg ); + //-Override- + public String evaluate(String expression, Object item) + throws XPathExpressionException { + return (String)this.evaluate(expression, item, XPathConstants.STRING); } - - - /** - *

Evaluate an XPath expression in the specified context and return the result as a String.

- * - *

This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a returnType of - * {@link XPathConstants#STRING}.

- * - *

See "Evaluation of XPath Expressions" of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If expression is null, then a NullPointerException is thrown.

- * - * @param expression The XPath expression. - * @param item The starting context (node or node list, for example). - * - * @return The String that is the result of evaluating the expression and - * converting the result to a String. - * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws NullPointerException If expression is null. - */ - public String evaluate(String expression, Object item) - throws XPathExpressionException { - return (String)this.evaluate( expression, item, XPathConstants.STRING ); - } - - /** - *

Compile an XPath expression for later evaluation.

- * - *

If expression contains any {@link XPathFunction}s, - * they must be available via the {@link XPathFunctionResolver}. - * An {@link XPathExpressionException} will be thrown if the XPathFunction - * cannot be resovled with the XPathFunctionResolver.

- * - *

If expression is null, a NullPointerException is thrown.

- * - * @param expression The XPath expression. - * - * @return Compiled XPath expression. - - * @throws XPathExpressionException If expression cannot be compiled. - * @throws NullPointerException If expression is null. - */ + //-Override- public XPathExpression compile(String expression) throws XPathExpressionException { - if ( expression == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"XPath expression"} ); - throw new NullPointerException ( fmsg ); - } + requireNonNull(expression, "XPath expression"); try { com.sun.org.apache.xpath.internal.XPath xpath = new XPath (expression, null, - prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT ); + prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT); // Can have errorListener XPathExpressionImpl ximpl = new XPathExpressionImpl (xpath, prefixResolver, functionResolver, variableResolver, - featureSecureProcessing, useServiceMechanism, featureManager ); + featureSecureProcessing, useServiceMechanism, featureManager); return ximpl; - } catch ( javax.xml.transform.TransformerException te ) { - throw new XPathExpressionException ( te ) ; + } catch (TransformerException te) { + throw new XPathExpressionException (te) ; } } - - /** - *

Evaluate an XPath expression in the context of the specified InputSource - * and return the result as the specified type.

- * - *

This method builds a data model for the {@link InputSource} and calls - * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If returnType is not one of the types defined in {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

- * - *

If expression, source or returnType is null, - * then a NullPointerException is thrown.

- * - * @param expression The XPath expression. - * @param source The input source of the document to evaluate over. - * @param returnType The desired return type. - * - * @return The Object that encapsulates the result of evaluating the expression. - * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If expression, source or returnType - * is null. - */ + //-Override- public Object evaluate(String expression, InputSource source, QName returnType) throws XPathExpressionException { - // Checking validity of different parameters - if( source== null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"source"} ); - throw new NullPointerException ( fmsg ); - } - if ( expression == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"XPath expression"} ); - throw new NullPointerException ( fmsg ); - } - if ( returnType == null ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, - new Object[] {"returnType"} ); - throw new NullPointerException ( fmsg ); - } - - //Checking if requested returnType is supported. - //returnType need to be defined in XPathConstants - if ( !isSupported ( returnType ) ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, - new Object[] { returnType.toString() } ); - throw new IllegalArgumentException ( fmsg ); - } + isSupported(returnType); try { - - Document document = getParser().parse( source ); - - XObject resultObject = eval( expression, document ); - return getResultAsType( resultObject, returnType ); - } catch ( SAXException e ) { - throw new XPathExpressionException ( e ); - } catch( IOException e ) { - throw new XPathExpressionException ( e ); - } catch ( javax.xml.transform.TransformerException te ) { + Document document = getDocument(source); + XObject resultObject = eval(expression, document); + return getResultAsType(resultObject, returnType); + } catch (TransformerException te) { Throwable nestedException = te.getException(); - if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { + if (nestedException instanceof javax.xml.xpath.XPathFunctionException) { throw (javax.xml.xpath.XPathFunctionException)nestedException; } else { - throw new XPathExpressionException ( te ); + throw new XPathExpressionException (te); } } - } - - - - /** - *

Evaluate an XPath expression in the context of the specified InputSource - * and return the result as a String.

- * - *

This method calls {@link #evaluate(String expression, InputSource source, QName returnType)} with a - * returnType of {@link XPathConstants#STRING}.

- * - *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec - * for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If expression or source is null, - * then a NullPointerException is thrown.

- * - * @param expression The XPath expression. - * @param source The InputSource of the document to evaluate over. - * - * @return The String that is the result of evaluating the expression and - * converting the result to a String. - * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws NullPointerException If expression or source is null. - */ + //-Override- public String evaluate(String expression, InputSource source) throws XPathExpressionException { - return (String)this.evaluate( expression, source, XPathConstants.STRING ); + return (String)this.evaluate(expression, source, XPathConstants.STRING); } - /** - *

Reset this XPath to its original configuration.

- * - *

XPath is reset to the same state as when it was created with - * {@link XPathFactory#newXPath()}. - * reset() is designed to allow the reuse of existing XPaths - * thus saving resources associated with the creation of new XPaths.

- * - *

The reset XPath is not guaranteed to have the same - * {@link XPathFunctionResolver}, {@link XPathVariableResolver} - * or {@link NamespaceContext} Objects, e.g. {@link Object#equals(Object obj)}. - * It is guaranteed to have a functionally equal XPathFunctionResolver, - * XPathVariableResolver - * and NamespaceContext.

- */ + //-Override- public void reset() { this.variableResolver = this.origVariableResolver; this.functionResolver = this.origFunctionResolver; this.namespaceContext = null; } + //-Override- + public T evaluateExpression(String expression, Object item, Class type) + throws XPathExpressionException { + isSupportedClassType(type); + try { + XObject resultObject = eval(expression, item); + if (type.isAssignableFrom(XPathEvaluationResult.class)) { + return getXPathResult(resultObject, type); + } else { + return XPathResultImpl.getValue(resultObject, type); + } + } catch (TransformerException te) { + throw new XPathExpressionException (te); + } + } + + //-Override- + public XPathEvaluationResult evaluateExpression(String expression, Object item) + throws XPathExpressionException { + return evaluateExpression(expression, item, XPathEvaluationResult.class); + } + + //-Override- + public T evaluateExpression(String expression, InputSource source, Class type) + throws XPathExpressionException { + Document document = getDocument(source); + return evaluateExpression(expression, document, type); + } + + //-Override- + public XPathEvaluationResult evaluateExpression(String expression, InputSource source) + throws XPathExpressionException { + return evaluateExpression(expression, source, XPathEvaluationResult.class); + } } diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2015, 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.org.apache.xpath.internal.jaxp; + +import com.sun.org.apache.xalan.internal.res.XSLMessages; +import com.sun.org.apache.xalan.internal.utils.FactoryImpl; +import com.sun.org.apache.xalan.internal.utils.FeatureManager; +import com.sun.org.apache.xml.internal.dtm.DTM; +import com.sun.org.apache.xpath.internal.objects.XObject; +import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; +import java.io.IOException; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathEvaluationResult; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFunctionResolver; +import javax.xml.xpath.XPathNodes; +import javax.xml.xpath.XPathVariableResolver; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * This class contains several utility methods used by XPathImpl and + * XPathExpressionImpl + */ +class XPathImplUtil { + XPathFunctionResolver functionResolver; + XPathVariableResolver variableResolver; + JAXPPrefixResolver prefixResolver; + boolean useServiceMechanism = true; + // By default Extension Functions are allowed in XPath Expressions. If + // Secure Processing Feature is set on XPathFactory then the invocation of + // extensions function need to throw XPathFunctionException + boolean featureSecureProcessing = false; + FeatureManager featureManager; + + /** + * Evaluate an XPath context using the internal XPath engine + * @param contextItem The XPath context + * @param xpath The internal XPath engine + * @return an XObject + * @throws javax.xml.transform.TransformerException If the expression cannot be evaluated. + */ + XObject eval(Object contextItem, com.sun.org.apache.xpath.internal.XPath xpath) + throws javax.xml.transform.TransformerException { + com.sun.org.apache.xpath.internal.XPathContext xpathSupport; + if (functionResolver != null) { + JAXPExtensionsProvider jep = new JAXPExtensionsProvider( + functionResolver, featureSecureProcessing, featureManager); + xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(jep); + } else { + xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(); + } + + xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); + XObject xobj; + + Node contextNode = (Node)contextItem; + // We always need to have a ContextNode with Xalan XPath implementation + // To allow simple expression evaluation like 1+1 we are setting + // dummy Document as Context Node + if (contextNode == null) { + xobj = xpath.execute(xpathSupport, DTM.NULL, prefixResolver); + } else { + xobj = xpath.execute(xpathSupport, contextNode, prefixResolver); + } + + return xobj; + } + + /** + * Parse the input source and return a Document. + * @param source The {@code InputSource} of the document + * @return a DOM Document + * @throws XPathExpressionException if there is an error parsing the source. + */ + Document getDocument(InputSource source) + throws XPathExpressionException { + requireNonNull(source, "Source"); + try { + // we'd really like to cache those DocumentBuilders, but we can't because: + // 1. thread safety. parsers are not thread-safe, so at least + // we need one instance per a thread. + // 2. parsers are non-reentrant, so now we are looking at having a + // pool of parsers. + // 3. then the class loading issue. The look-up procedure of + // DocumentBuilderFactory.newInstance() depends on context class loader + // and system properties, which may change during the execution of JVM. + // + // so we really have to create a fresh DocumentBuilder every time we need one + // - KK + DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(useServiceMechanism); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + return dbf.newDocumentBuilder().parse(source); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new XPathExpressionException (e); + } + } + + /** + * Get result depending on the QName type defined in XPathConstants + * @param resultObject the result of an evaluation + * @param returnType the return type + * @return result per the return type + * @throws TransformerException if the result can not be converted to + * the specified return type. + */ + Object getResultAsType(XObject resultObject, QName returnType) + throws TransformerException { + // XPathConstants.STRING + if (returnType.equals(XPathConstants.STRING)) { + return resultObject.str(); + } + // XPathConstants.NUMBER + if (returnType.equals(XPathConstants.NUMBER)) { + return resultObject.num(); + } + // XPathConstants.BOOLEAN + if (returnType.equals(XPathConstants.BOOLEAN)) { + return resultObject.bool(); + } + // XPathConstants.NODESET ---ORdered, UNOrdered??? + if (returnType.equals(XPathConstants.NODESET)) { + return resultObject.nodelist(); + } + // XPathConstants.NODE + if (returnType.equals(XPathConstants.NODE)) { + NodeIterator ni = resultObject.nodeset(); + //Return the first node, or null + return ni.nextNode(); + } + // If isSupported check is already done then the execution path + // shouldn't come here. Being defensive + String fmsg = XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, + new Object[] { returnType.toString()}); + throw new IllegalArgumentException (fmsg); + } + + /** + * Construct an XPathExpressionResult object based on the result of the + * evaluation and cast to the specified class type. + * @param The class type + * @param resultObject the result of an evaluation + * @param type The class type expected to be returned by the XPath expression. + * @return an instance of the specified type or null if the XObject returned + * an UNKNOWN object type. + * @throws TransformerException if there is an error converting the result + * to the specified type. It's unlikely to happen. This is mostly needed + * by the internal XPath engine. + */ + T getXPathResult(XObject resultObject, Class type) + throws TransformerException { + int resultType = resultObject.getType(); + + switch (resultType) { + case XObject.CLASS_BOOLEAN : + return type.cast(new XPathResultImpl<>(resultObject, Boolean.class)); + case XObject.CLASS_NUMBER : + return type.cast(new XPathResultImpl<>(resultObject, Double.class)); + case XObject.CLASS_STRING : + return type.cast(new XPathResultImpl<>(resultObject, String.class)); + case XObject.CLASS_NODESET : + return type.cast(new XPathResultImpl<>(resultObject, XPathNodes.class)); + case XObject.CLASS_RTREEFRAG : //NODE + return type.cast(new XPathResultImpl<>(resultObject, Node.class)); + } + + return null; + } + + /** + * Check whether or not the specified type is supported + * @param The class type + * @param type The type to be checked + * @throws IllegalArgumentException if the type is not supported + */ + void isSupportedClassType(Class type) { + requireNonNull(type, "The class type"); + if (type.isAssignableFrom(Boolean.class) || + type.isAssignableFrom(Double.class) || + type.isAssignableFrom(Integer.class) || + type.isAssignableFrom(Long.class) || + type.isAssignableFrom(String.class) || + type.isAssignableFrom(XPathNodes.class) || + type.isAssignableFrom(Node.class) || + type.isAssignableFrom(XPathEvaluationResult.class)) { + return; + } + String fmsg = XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, + new Object[] { type.toString() }); + throw new IllegalArgumentException (fmsg); + } + + /** + * Check if the requested returnType is supported. + * @param returnType the return type + * @throws IllegalArgumentException if the return type is not supported + */ + void isSupported(QName returnType) { + requireNonNull(returnType, "returnType"); + if (returnType.equals(XPathConstants.STRING) || + returnType.equals(XPathConstants.NUMBER) || + returnType.equals(XPathConstants.BOOLEAN) || + returnType.equals(XPathConstants.NODE) || + returnType.equals(XPathConstants.NODESET)) { + return; + } + String fmsg = XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, + new Object[] { returnType.toString() }); + throw new IllegalArgumentException (fmsg); + } + + /** + * Checks that the specified parameter is not {@code null}. + * + * @param the type of the reference + * @param param the parameter to check for nullity + * @param paramName the parameter name + * @throws NullPointerException if {@code param} is {@code null} + */ + void requireNonNull(T param, String paramName) { + if (param == null) { + String fmsg = XSLMessages.createXPATHMessage( + XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, + new Object[] {paramName}); + throw new NullPointerException (fmsg); + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathNodesImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathNodesImpl.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, 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.org.apache.xpath.internal.jaxp; + +import java.util.Iterator; +import javax.xml.xpath.XPathException; +import javax.xml.xpath.XPathNodes; +import javax.xml.xpath.XPathEvaluationResult.XPathResultType; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * This class implements XPathNodes that represents a set of nodes selected by + * evaluating an expression. + */ +public class XPathNodesImpl implements XPathNodes { + Class elementType; + NodeList nodeList = null; + + public XPathNodesImpl(NodeList nodeList, Class elementType) { + this.nodeList = nodeList; + this.elementType = elementType; + } + + @Override + public Iterator iterator() { + return new NodeSetIterator<>(elementType); + } + + class NodeSetIterator implements Iterator { + int currentIndex; + Class elementType; + NodeSetIterator(Class elementType) { + this.elementType = elementType; + } + public boolean hasNext() { + if (nodeList != null) { + return currentIndex < nodeList.getLength(); + } + + return false; + } + + public E next() { + if (nodeList != null && nodeList.getLength() > 0) { + return elementType.cast(nodeList.item(currentIndex++)); + } + return null; + } + } + + @Override + public int size() { + if (nodeList != null) { + return nodeList.getLength(); + } + return 0; + } + + @Override + public Node get(int index) throws XPathException { + if (index <0 || index >= size()) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds"); + } + if (nodeList != null) { + return nodeList.item(index); + } + return null; + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, 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.org.apache.xpath.internal.jaxp; + +import com.sun.org.apache.xpath.internal.objects.XObject; +import java.util.Objects; +import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPathNodes; +import javax.xml.xpath.XPathEvaluationResult; +import javax.xml.xpath.XPathEvaluationResult.XPathResultType; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; + + +/** + * This is the implementation of XPathEvaluationResult that represents the + * result of the evaluation of an XPath expression within the context of a + * particular node. + */ +class XPathResultImpl implements XPathEvaluationResult { + + XObject resultObject; + int resultType; + Class type; + XPathResultType mapToType; + NodeList nodeList = null; + int currentIndex; + Node currentNode; + + boolean boolValue = false; + Node node = null; + double numValue; + String strValue; + + /** + * Construct an XPathEvaluationResult object. + * + * @param resultObject internal XPath result object + * @param type class type + * @throws TransformerException if there is an error reading the XPath + * result. + */ + public XPathResultImpl(XObject resultObject, Class type) + throws TransformerException { + this.resultObject = resultObject; + resultType = resultObject.getType(); + this.type = type; + getResult(resultObject); + } + + /** + * Return the result type as an enum specified by {@code XPathResultType} + * @return the result type + */ + @Override + public XPathResultType type() { + return mapToType; + } + + /** + * Returns the value of the result as the type <T> specified for the class. + * + * @return The value of the result. + */ + @Override + public T value() { + Objects.requireNonNull(type); + try { + return getValue(resultObject, type); + } catch (TransformerException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Read the XObject and set values in accordance with the result type + * @param resultObject internal XPath result object + * @throws TransformerException if there is an error reading the XPath + * result. + */ + private void getResult(XObject resultObject) throws TransformerException { + switch (resultType) { + case XObject.CLASS_BOOLEAN: + boolValue = resultObject.bool(); + mapToType = XPathResultType.BOOLEAN; + break; + case XObject.CLASS_NUMBER: + numValue = resultObject.num(); + mapToType = XPathResultType.NUMBER; + break; + case XObject.CLASS_STRING: + strValue = resultObject.str(); + mapToType = XPathResultType.STRING; + break; + case XObject.CLASS_NODESET: + mapToType = XPathResultType.NODESET; + nodeList = resultObject.nodelist(); + break; + case XObject.CLASS_RTREEFRAG: //NODE + mapToType = XPathResultType.NODE; + NodeIterator ni = resultObject.nodeset(); + //Return the first node, or null + node = ni.nextNode(); + break; + } + } + + /** + * Read the internal result object and return the value in accordance with + * the type specified. + * + * @param The expected class type. + * @param resultObject internal XPath result object + * @param type the class type + * @return The value of the result, null in case of unexpected type. + * @throws TransformerException if there is an error reading the XPath + * result. + */ + static T getValue(XObject resultObject, Class type) throws TransformerException { + Objects.requireNonNull(type); + if (type.isAssignableFrom(XPathEvaluationResult.class)) { + return type.cast(new XPathResultImpl(resultObject, type)); + } + int resultType = classToInternalType(type); + switch (resultType) { + case XObject.CLASS_BOOLEAN: + return type.cast(resultObject.bool()); + case XObject.CLASS_NUMBER: + if (Double.class.isAssignableFrom(type)) { + return type.cast(resultObject.num()); + } else if (Integer.class.isAssignableFrom(type)) { + return type.cast((int)resultObject.num()); + } else if (Long.class.isAssignableFrom(type)) { + return type.cast((long)resultObject.num()); + } + /* + This is to suppress warnings. By the current specification, + among numeric types, only Double, Integer and Long are supported. + */ + break; + case XObject.CLASS_STRING: + return type.cast(resultObject.str()); + case XObject.CLASS_NODESET: + XPathNodes nodeSet = new XPathNodesImpl(resultObject.nodelist(), + Node.class); + return type.cast(nodeSet); + case XObject.CLASS_RTREEFRAG: //NODE + NodeIterator ni = resultObject.nodeset(); + //Return the first node, or null + return type.cast(ni.nextNode()); + } + + return null; + } + + /** + * Map the specified class type to the internal result type + * + * @param The expected class type. + * @param type the class type + * @return the internal XObject type. + */ + static int classToInternalType(Class type) { + if (type.isAssignableFrom(Boolean.class)) { + return XObject.CLASS_BOOLEAN; + } else if (Number.class.isAssignableFrom(type)) { + return XObject.CLASS_NUMBER; + } else if (type.isAssignableFrom(String.class)) { + return XObject.CLASS_STRING; + } else if (type.isAssignableFrom(XPathNodes.class)) { + return XObject.CLASS_NODESET; + } else if (type.isAssignableFrom(Node.class)) { + return XObject.CLASS_RTREEFRAG; + } + return XObject.CLASS_NULL; + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPath.java --- a/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPath.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPath.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -25,12 +25,12 @@ package javax.xml.xpath; -import org.xml.sax.InputSource; +import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; -import javax.xml.namespace.NamespaceContext; +import org.xml.sax.InputSource; /** - *

XPath provides access to the XPath evaluation environment and expressions.

+ * {@code XPath} provides access to the XPath evaluation environment and expressions. * * * @@ -39,7 +39,6 @@ * * * - * * * * * * @@ -65,7 +64,7 @@ * If the expression contains a function reference, the function will be found through the {@link XPathFunctionResolver} * set with {@link #setXPathFunctionResolver(XPathFunctionResolver resolver)}. * An {@link XPathExpressionException} is raised if the function resolver is undefined or - * the function resolver returns null for the function.

+ * the function resolver returns {@code null} for the function. * * * @@ -80,7 +79,7 @@ * * *
Evaluation of XPath Expressions.
context @@ -55,8 +54,8 @@ * If the expression contains a variable reference, its value will be found through the {@link XPathVariableResolver} * set with {@link #setXPathVariableResolver(XPathVariableResolver resolver)}. * An {@link XPathExpressionException} is raised if the variable resolver is undefined or - * the resolver returns null for the variable. - * The value of a variable must be immutable through the course of any single evaluation.

+ * the resolver returns {@code null} for the variable. + * The value of a variable must be immutable through the course of any single evaluation. *
* This result of evaluating an expression is converted to an instance of the desired return type. * Valid return types are defined in {@link XPathConstants}. - * Conversion to the return type follows XPath conversion rules.

+ * Conversion to the return type follows XPath conversion rules. *
@@ -88,9 +87,9 @@ *

An XPath object is not thread-safe and not reentrant. * In other words, it is the application's responsibility to make * sure that one {@link XPath} object is not used from - * more than one thread at any given time, and while the evaluate + * more than one thread at any given time, and while the {@code evaluate} * method is invoked, applications may not recursively call - * the evaluate method. + * the {@code evaluate} method. *

* * @author Norman Walsh @@ -100,191 +99,189 @@ */ public interface XPath { - /** - *

Reset this XPath to its original configuration.

- * - *

XPath is reset to the same state as when it was created with - * {@link XPathFactory#newXPath()}. - * reset() is designed to allow the reuse of existing XPaths - * thus saving resources associated with the creation of new XPaths.

- * - *

The reset XPath is not guaranteed to have the same {@link XPathFunctionResolver}, {@link XPathVariableResolver} - * or {@link NamespaceContext} Objects, e.g. {@link Object#equals(Object obj)}. - * It is guaranteed to have a functionally equal XPathFunctionResolver, XPathVariableResolver - * and NamespaceContext.

- */ - public void reset(); /** - *

Establish a variable resolver.

+ * Reset this {@code XPath} to its original configuration. + * + *

{@code XPath} is reset to the same state as when it was created with + * {@link XPathFactory#newXPath()}. + * {@code reset()} is designed to allow the reuse of existing {@code XPath}s + * thus saving resources associated with the creation of new {@code XPath}s. * - *

A NullPointerException is thrown if resolver is null.

+ *

The reset {@code XPath} is not guaranteed to have the same + * {@link XPathFunctionResolver}, {@link XPathVariableResolver} + * or {@link NamespaceContext} {@code Object}s, e.g. {@link Object#equals(Object obj)}. + * It is guaranteed to have a functionally equal {@code XPathFunctionResolver}, + * {@code XPathVariableResolver} and {@code NamespaceContext}. + */ + public void reset(); + + /** + * Establish a variable resolver. + * + *

A {@code NullPointerException} is thrown if {@code resolver} is {@code null}. * * @param resolver Variable resolver. * - * @throws NullPointerException If resolver is null. + * @throws NullPointerException If {@code resolver} is {@code null}. */ public void setXPathVariableResolver(XPathVariableResolver resolver); /** - *

Return the current variable resolver.

+ * Return the current variable resolver. * - *

null is returned in no variable resolver is in effect.

+ *

{@code null} is returned in no variable resolver is in effect. * * @return Current variable resolver. */ public XPathVariableResolver getXPathVariableResolver(); /** - *

Establish a function resolver.

+ * Establish a function resolver. * - *

A NullPointerException is thrown if resolver is null.

+ *

A {@code NullPointerException} is thrown if {@code resolver} is {@code null}. * * @param resolver XPath function resolver. * - * @throws NullPointerException If resolver is null. + * @throws NullPointerException If {@code resolver} is {@code null}. */ public void setXPathFunctionResolver(XPathFunctionResolver resolver); /** - *

Return the current function resolver.

- * - *

null is returned in no function resolver is in effect.

+ * Return the current function resolver. + *

+ * {@code null} is returned in no function resolver is in effect. * * @return Current function resolver. */ public XPathFunctionResolver getXPathFunctionResolver(); /** - *

Establish a namespace context.

+ * Establish a namespace context. * - *

A NullPointerException is thrown if nsContext is null.

+ *

A {@code NullPointerException} is thrown if {@code nsContext} is {@code null}. * * @param nsContext Namespace context to use. * - * @throws NullPointerException If nsContext is null. + * @throws NullPointerException If {@code nsContext} is {@code null}. */ public void setNamespaceContext(NamespaceContext nsContext); /** - *

Return the current namespace context.

+ * Return the current namespace context. * - *

null is returned in no namespace context is in effect.

+ *

{@code null} is returned in no namespace context is in effect. * * @return Current Namespace context. */ public NamespaceContext getNamespaceContext(); /** - *

Compile an XPath expression for later evaluation.

+ * Compile an XPath expression for later evaluation. * - *

If expression contains any {@link XPathFunction}s, + *

If {@code expression} contains any {@link XPathFunction}s, * they must be available via the {@link XPathFunctionResolver}. * An {@link XPathExpressionException} will be thrown if the - * XPathFunction - * cannot be resovled with the XPathFunctionResolver.

+ * {@code XPathFunction} + * cannot be resovled with the {@code XPathFunctionResolver}. * - *

If expression contains any variables, the + *

If {@code expression} contains any variables, the * {@link XPathVariableResolver} in effect - * at compile time will be used to resolve them.

- * - *

If expression is null, a NullPointerException is thrown.

+ * at compile time will be used to resolve them. * * @param expression The XPath expression. * * @return Compiled XPath expression. - * @throws XPathExpressionException If expression cannot be compiled. - * @throws NullPointerException If expression is null. + * @throws XPathExpressionException If {@code expression} cannot be compiled. + * @throws NullPointerException If {@code expression} is {@code null}. */ public XPathExpression compile(String expression) throws XPathExpressionException; /** - *

Evaluate an XPath expression in the specified context and return the result as the specified type.

+ * Evaluate an {@code XPath} expression in the specified context and + * return the result as the specified type. * - *

See Evaluation of XPath Expressions for context item evaluation, - * variable, function and QName resolution and return type conversion.

+ *

+ * See Evaluation of XPath Expressions + * for context item evaluation, variable, function and {@code QName} resolution + * and return type conversion. + *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. * - *

If returnType is not one of the types defined in {@link XPathConstants} ( + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. + * + * @param expression The XPath expression. + * @param item The context the XPath expression will be evaluated in. + * @param returnType The result type expected to be returned by the XPath expression. + * + * @return The result of evaluating an XPath expression as an {@code Object} of {@code returnType}. + * + * @throws XPathExpressionException If {@code expression} cannot be evaluated. + * @throws IllegalArgumentException If {@code returnType} is not one of the types defined in {@link XPathConstants} ( * {@link XPathConstants#NUMBER NUMBER}, * {@link XPathConstants#STRING STRING}, * {@link XPathConstants#BOOLEAN BOOLEAN}, * {@link XPathConstants#NODE NODE} or - * {@link XPathConstants#NODESET NODESET}) - * then an IllegalArgumentException is thrown.

- * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If expression or returnType is null, then a - * NullPointerException is thrown.

- * - * @param expression The XPath expression. - * @param item The starting context (a node, for example). - * @param returnType The desired return type. - * - * @return Result of evaluating an XPath expression as an Object of returnType. - * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If expression or returnType is null. + * {@link XPathConstants#NODESET NODESET}). + * @throws NullPointerException If {@code expression or returnType} is {@code null}. */ public Object evaluate(String expression, Object item, QName returnType) throws XPathExpressionException; /** - *

Evaluate an XPath expression in the specified context and return the result as a String.

+ * Evaluate an XPath expression in the specified context and return the result as a {@code String}. * - *

This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a returnType of - * {@link XPathConstants#STRING}.

+ *

This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a {@code returnType} of + * {@link XPathConstants#STRING}. * *

See Evaluation of XPath Expressions for context item evaluation, - * variable, function and QName resolution and return type conversion.

+ * variable, function and QName resolution and return type conversion. * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If expression is null, then a NullPointerException is thrown.

+ *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. + * + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. * * @param expression The XPath expression. - * @param item The starting context (a node, for example). + * @param item The context the XPath expression will be evaluated in. * - * @return The String that is the result of evaluating the expression and - * converting the result to a String. + * @return The result of evaluating an XPath expression as a {@code String}. * - * @throws XPathExpressionException If expression cannot be evaluated. - * @throws NullPointerException If expression is null. + * @throws XPathExpressionException If {@code expression} cannot be evaluated. + * @throws NullPointerException If {@code expression} is {@code null}. */ public String evaluate(String expression, Object item) throws XPathExpressionException; /** - *

Evaluate an XPath expression in the context of the specified InputSource - * and return the result as the specified type.

+ * Evaluate an XPath expression in the context of the specified {@code InputSource} + * and return the result as the specified type. * *

This method builds a data model for the {@link InputSource} and calls - * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object.

+ * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object. * *

See Evaluation of XPath Expressions for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If returnType is not one of the types defined in {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

- * - *

If expression, source or returnType is null, - * then a NullPointerException is thrown.

+ * variable, function and QName resolution and return type conversion. * * @param expression The XPath expression. * @param source The input source of the document to evaluate over. * @param returnType The desired return type. * - * @return The Object that encapsulates the result of evaluating the expression. + * @return The {@code Object} that encapsulates the result of evaluating the expression. * * @throws XPathExpressionException If expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If expression, source or returnType - * is null. + * @throws IllegalArgumentException If {@code returnType} is not one of the types defined in {@link XPathConstants}. + * @throws NullPointerException If {@code expression, source or returnType} is {@code null}. */ public Object evaluate( String expression, @@ -293,27 +290,209 @@ throws XPathExpressionException; /** - *

Evaluate an XPath expression in the context of the specified InputSource - * and return the result as a String.

+ * Evaluate an XPath expression in the context of the specified {@code InputSource} + * and return the result as a {@code String}. * *

This method calls {@link #evaluate(String expression, InputSource source, QName returnType)} with a - * returnType of {@link XPathConstants#STRING}.

+ * {@code returnType} of {@link XPathConstants#STRING}. * *

See Evaluation of XPath Expressions for context item evaluation, - * variable, function and QName resolution and return type conversion.

- * - *

If expression or source is null, - * then a NullPointerException is thrown.

+ * variable, function and QName resolution and return type conversion. * * @param expression The XPath expression. - * @param source The InputSource of the document to evaluate over. + * @param source The {@code InputSource} of the document to evaluate over. * - * @return The String that is the result of evaluating the expression and - * converting the result to a String. + * @return The {@code String} that is the result of evaluating the expression and + * converting the result to a {@code String}. * * @throws XPathExpressionException If expression cannot be evaluated. - * @throws NullPointerException If expression or source is null. + * @throws NullPointerException If {@code expression or source} is {@code null}. */ public String evaluate(String expression, InputSource source) throws XPathExpressionException; + + /** + * Evaluate an XPath expression in the specified context and return + * the result with the type specified through the {@code class type} + * + *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. + * + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     (T)evaluate(expression, item,
+     *           XPathEvaluationResult.XPathResultType.getQNameType(type));
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type, specifying + * XPathEvaluationResult as the type will result in IllegalArgumentException. + * Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must override + * this method. + * + * @param The class type that will be returned by the XPath expression. + * @param expression The XPath expression. + * @param item The context the XPath expression will be evaluated in. + * @param type The class type expected to be returned by the XPath expression. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If {@code type} is not of the types + * corresponding to the types defined in the {@link XPathEvaluationResult.XPathResultType}, + * or XPathEvaluationResult is specified as the type but an implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type is not available. + * @throws NullPointerException If {@code expression or type} is {@code null}. + * + * @since 1.9 + */ + default T evaluateExpression(String expression, Object item, Class type) + throws XPathExpressionException { + return type.cast(evaluate(expression, item, + XPathEvaluationResult.XPathResultType.getQNameType(type))); + } + + /** + * Evaluate an XPath expression in the specified context. This is equivalent to + * calling {@link #evaluateExpression(String expression, Object item, Class type)} + * with type {@link XPathEvaluationResult}: + *
 {@code
+     *     evaluateExpression(expression, item, XPathEvaluationResult.class);
+     * }
+ *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. + * + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     evaluateExpression(expression, item, XPathEvaluationResult.class);
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} + * type, the default implementation of this method will always throw an + * IllegalArgumentException. Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must therefore + * override this method. + * + * @param expression The XPath expression. + * @param item The context the XPath expression will be evaluated in. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If the implementation of this method + * does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type. + * @throws NullPointerException If {@code expression} is {@code null}. + * + * @since 1.9 + */ + default XPathEvaluationResult evaluateExpression(String expression, Object item) + throws XPathExpressionException + { + return evaluateExpression(expression, item, XPathEvaluationResult.class); + } + + /** + * Evaluate an XPath expression in the context of the specified {@code source} + * and return the result as specified. + *

+ * This method builds a data model for the {@link InputSource} and calls + * {@link #evaluateExpression(String expression, Object item, Class type)} + * on the resulting document object. The data model is usually + * {@link org.w3c.dom.Document} + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+           (T)evaluate(expression, source,
+                XPathEvaluationResult.XPathResultType.getQNameType(type));
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type, specifying + * XPathEvaluationResult as the type will result in IllegalArgumentException. + * Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must override + * this method. + * + * @param The class type that will be returned by the XPath expression. + * @param expression The XPath expression. + * @param source The input source of the document to evaluate over. + * @param type The class type expected to be returned by the XPath expression. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If {@code type} is not of the types + * corresponding to the types defined in the {@link XPathEvaluationResult.XPathResultType + * XPathResultType}, or XPathEvaluationResult is specified as the type but an + * implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type is not available. + * @throws NullPointerException If {@code expression, source or type}is {@code null}. + * + * @since 1.9 + */ + default T evaluateExpression(String expression, InputSource source, Class type) + throws XPathExpressionException + { + return type.cast(evaluate(expression, source, + XPathEvaluationResult.XPathResultType.getQNameType(type))); + } + + /** + * Evaluate an XPath expression in the specified context. This is equivalent to + * calling {@link #evaluateExpression(String expression, Object item, Class type)} + * with type {@link XPathEvaluationResult}: + *
 {@code
+     *     evaluateExpression(expression, item, XPathEvaluationResult.class);
+     * }
+ *

+ * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     evaluateExpression(expression, source, XPathEvaluationResult.class);
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} + * type, the default implementation of this method will always throw an + * IllegalArgumentException. Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must therefore + * override this method. + * + * @param expression The XPath expression. + * @param source The input source of the document to evaluate over. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If the implementation of this method + * does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type. + * @throws NullPointerException If {@code expression or source} is {@code null}. + * + * @since 1.9 + */ + default XPathEvaluationResult evaluateExpression(String expression, InputSource source) + throws XPathExpressionException + { + return evaluateExpression(expression, source, XPathEvaluationResult.class); + } } diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathEvaluationResult.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathEvaluationResult.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015 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 javax.xml.xpath; + +import java.util.Objects; +import javax.xml.namespace.QName; +import org.w3c.dom.Node; +/** + * The {@code XPathEvaluationResult} interface represents the result of the + * evaluation of an XPath expression within the context of a particular node. + * The evaluation of an XPath expression can result in various result types as + * defined in XML Path Language (XPath) Version 1.0. + *

+ * + * @param the object type returned by the XPath evaluation. + * @see XML Path Language (XPath) Version + * 1.0 + * + * @since 1.9 + */ +public interface XPathEvaluationResult { + + /** + * XPathResultType represents possible return types of an XPath evaluation. + * Provided as an enum type, it allows the use of switch statement. At the + * same time, a mapping is provided between the original QName types in + * {@link XPathConstants} and class types used in the generic methods. + */ + public static enum XPathResultType { + /** + * Any type that represents any of the 5 other types listed below. + * Maps to {@link XPathEvaluationResult}. + */ + ANY(new QName("http://www.w3.org/1999/XSL/Transform", "any"), XPathEvaluationResult.class), + /** + * The XPath 1.0 boolean data type. Maps to Java {@link Boolean}. + */ + BOOLEAN(XPathConstants.BOOLEAN, Boolean.class), + /** + * The XPath 1.0 Number data type. Maps to Java {@link Number}. Of the + * subtypes of Number, only Double, Integer and Long are required. + */ + NUMBER(XPathConstants.NUMBER, Number.class), + /** + * The XPath 1.0 String data type. Maps to Java {@link String}. + */ + STRING(XPathConstants.STRING, String.class), + /** + * The XPath 1.0 NodeSet data type. Maps to {@link org.w3c.dom.NodeList}. + */ + NODESET(XPathConstants.NODESET, XPathNodes.class), + /** + * The XPath 1.0 NodeSet data type. Maps to {@link org.w3c.dom.Node}. + */ + NODE(XPathConstants.NODE, Node.class); + + final QName qnameType; + final Class clsType; + XPathResultType(QName qnameType, Class clsType) { + this.qnameType = qnameType; + this.clsType = clsType; + } + + /** + * Compares this type to the specified class type. + * @param clsType class type + * @return true if the argument is not null and is a class type that + * matches that this type represents, false otherwise. + */ + private boolean equalsClassType(Class clsType) { + Objects.nonNull(clsType); + if (clsType.isAssignableFrom(this.clsType)) { + return true; + } + return false; + } + + /** + * Returns the QName type as specified in {@link XPathConstants} that + * corresponds to the specified class type. + * @param clsType a class type that the enum type supports + * @return the QName type that matches with the specified class type, + * null if there is no match + */ + static public QName getQNameType(Class clsType) { + for (XPathResultType type : XPathResultType.values()) { + if (type.equalsClassType(clsType)) { + return type.qnameType; + } + } + return null; + } + } + + /** + * Return the result type as an enum specified by {@code XPathResultType} + * @return the result type + */ + public XPathResultType type(); + + /** + * Returns the value of the result as the type <T> specified for the class. + * + * @return The value of the result. + */ + public T value(); + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathExpression.java --- a/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathExpression.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathExpression.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -25,11 +25,11 @@ package javax.xml.xpath; +import javax.xml.namespace.QName; import org.xml.sax.InputSource; -import javax.xml.namespace.QName; /** - *

XPathExpression provides access to compiled XPath expressions.

+ *

{@code XPathExpression} provides access to compiled XPath expressions.

* * * @@ -53,7 +53,7 @@ * * @@ -62,7 +62,7 @@ * * * @@ -84,9 +84,9 @@ *

An XPath expression is not thread-safe and not reentrant. * In other words, it is the application's responsibility to make * sure that one {@link XPathExpression} object is not used from - * more than one thread at any given time, and while the evaluate + * more than one thread at any given time, and while the {@code evaluate} * method is invoked, applications may not recursively call - * the evaluate method. + * the {@code evaluate} method. *

* * @author Norman Walsh @@ -96,50 +96,56 @@ */ public interface XPathExpression { + /** *

Evaluate the compiled XPath expression in the specified context and return the result as the specified type.

* *

See Evaluation of XPath Expressions for context item evaluation, * variable, function and QName resolution and return type conversion.

* - *

If returnType is not one of the types defined in {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

+ *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. * - *

If a null value is provided for - * item, an empty document will be used for the - * context. - * If returnType is null, then a NullPointerException is thrown.

+ * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. * - * @param item The starting context (a node, for example). - * @param returnType The desired return type. + * @param item The context the XPath expression will be evaluated in. + * @param returnType The result type expected to be returned by the XPath expression. * - * @return The Object that is the result of evaluating the expression and converting the result to - * returnType. + * @return The {@code Object} that is the result of evaluating the expression and converting the result to + * {@code returnType}. * * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If returnType is null. + * @throws IllegalArgumentException If {@code returnType} is not one of the types defined in {@link XPathConstants}. + * @throws NullPointerException If {@code returnType} is {@code null}. */ public Object evaluate(Object item, QName returnType) throws XPathExpressionException; /** - *

Evaluate the compiled XPath expression in the specified context and return the result as a String.

+ *

Evaluate the compiled XPath expression in the specified context and return the result as a {@code String}.

* - *

This method calls {@link #evaluate(Object item, QName returnType)} with a returnType of + *

This method calls {@link #evaluate(Object item, QName returnType)} with a {@code returnType} of * {@link XPathConstants#STRING}.

* *

See Evaluation of XPath Expressions for context item evaluation, * variable, function and QName resolution and return type conversion.

* - *

If a null value is provided for - * item, an empty document will be used for the - * context. + *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. * - * @param item The starting context (a node, for example). + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. * - * @return The String that is the result of evaluating the expression and converting the result to a - * String. + * @param item The context the XPath expression will be evaluated in. + * + * @return The result of evaluating an XPath expression as a {@code String}. * * @throws XPathExpressionException If the expression cannot be evaluated. */ @@ -147,7 +153,7 @@ throws XPathExpressionException; /** - *

Evaluate the compiled XPath expression in the context of the specified InputSource and return the result as the + *

Evaluate the compiled XPath expression in the context of the specified {@code InputSource} and return the result as the * specified type.

* *

This method builds a data model for the {@link InputSource} and calls @@ -156,45 +162,225 @@ *

See Evaluation of XPath Expressions for context item evaluation, * variable, function and QName resolution and return type conversion.

* - *

If returnType is not one of the types defined in {@link XPathConstants}, - * then an IllegalArgumentException is thrown.

+ *

If {@code returnType} is not one of the types defined in {@link XPathConstants}, + * then an {@code IllegalArgumentException} is thrown.

* - *

If source or returnType is null, - * then a NullPointerException is thrown.

+ *

If {@code source} or {@code returnType} is {@code null}, + * then a {@code NullPointerException} is thrown.

* - * @param source The InputSource of the document to evaluate over. + * @param source The {@code InputSource} of the document to evaluate over. * @param returnType The desired return type. * - * @return The Object that is the result of evaluating the expression and converting the result to - * returnType. + * @return The {@code Object} that is the result of evaluating the expression and converting the result to + * {@code returnType}. * * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. - * @throws NullPointerException If source or returnType is null. + * @throws IllegalArgumentException If {@code returnType} is not one of the types defined in {@link XPathConstants}. + * @throws NullPointerException If {@code source or returnType} is {@code null}. */ public Object evaluate(InputSource source, QName returnType) throws XPathExpressionException; /** - *

Evaluate the compiled XPath expression in the context of the specified InputSource and return the result as a - * String.

+ *

Evaluate the compiled XPath expression in the context of the specified {@code InputSource} and return the result as a + * {@code String}.

* - *

This method calls {@link #evaluate(InputSource source, QName returnType)} with a returnType of + *

This method calls {@link #evaluate(InputSource source, QName returnType)} with a {@code returnType} of * {@link XPathConstants#STRING}.

* *

See Evaluation of XPath Expressions for context item evaluation, * variable, function and QName resolution and return type conversion.

* - *

If source is null, then a NullPointerException is thrown.

+ *

If {@code source} is {@code null}, then a {@code NullPointerException} is thrown.

* - * @param source The InputSource of the document to evaluate over. + * @param source The {@code InputSource} of the document to evaluate over. * - * @return The String that is the result of evaluating the expression and converting the result to a - * String. + * @return The {@code String} that is the result of evaluating the expression and converting the result to a + * {@code String}. * * @throws XPathExpressionException If the expression cannot be evaluated. - * @throws NullPointerException If source is null. + * @throws NullPointerException If {@code source} is {@code null}. */ public String evaluate(InputSource source) throws XPathExpressionException; + + /** + * Evaluate the compiled XPath expression in the specified context, and return + * the result with the type specified through the {@code class type}. + * + *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. + * + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     (T)evaluate(item, XPathEvaluationResult.XPathResultType.getQNameType(type));
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type, specifying + * XPathEvaluationResult as the type will result in IllegalArgumentException. + * Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must override + * this method. + * + * @param The class type that will be returned by the XPath expression. + * @param item The context the XPath expression will be evaluated in. + * @param type The class type expected to be returned by the XPath expression. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If {@code type} is not of the types + * corresponding to the types defined in the {@link XPathEvaluationResult.XPathResultType + * XPathResultType}, or XPathEvaluationResult is specified as the type but an + * implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type is not available. + * @throws NullPointerException If {@code type} is {@code null}. + * + * @since 1.9 + */ + default T evaluateExpression(Object item, Class type) + throws XPathExpressionException + { + return type.cast(evaluate(item, XPathEvaluationResult.XPathResultType.getQNameType(type))); + } + + /** + * Evaluate the compiled XPath expression in the specified context. This is + * equivalent to calling {@link #evaluateExpression(Object item, Class type)} + * with type {@link XPathEvaluationResult}: + *
 {@code
+     *     evaluateExpression(item, XPathEvaluationResult.class);
+     * }
+ *

+ * The parameter {@code item} represents the context the XPath expression + * will be operated on. The type of the context is implementation-dependent. + * If the value is {@code null}, the operation must have no dependency on + * the context, otherwise an XPathExpressionException will be thrown. + * + * @implNote + * The type of the context is usually {@link org.w3c.dom.Node}. + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     evaluateExpression(item, XPathEvaluationResult.class);
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} + * type, the default implementation of this method will always throw an + * IllegalArgumentException. Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must therefore + * override this method. + * + * @param item The context the XPath expression will be evaluated in. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If the implementation of this method + * does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type. + * + * @since 1.9 + */ + default XPathEvaluationResult evaluateExpression(Object item) + throws XPathExpressionException + { + return evaluateExpression(item, XPathEvaluationResult.class); + } + + /** + * Evaluate the compiled XPath expression in the specified context, + * and return the result with the type specified through the {@code class type} + *

+ * This method builds a data model for the {@link InputSource} and calls + * {@link #evaluateExpression(Object item, Class type)} on the resulting + * document object. + *

+ * By default, the JDK's data model is {@link org.w3c.dom.Document}. + * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+           (T)evaluate(source, XPathEvaluationResult.XPathResultType.getQNameType(type));
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type, specifying + * XPathEvaluationResult as the type will result in IllegalArgumentException. + * Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must override + * this method. + * + * @param The class type that will be returned by the XPath expression. + * @param source The {@code InputSource} of the document to evaluate over. + * @param type The class type expected to be returned by the XPath expression. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If {@code type} is not of the types + * corresponding to the types defined in the {@link XPathEvaluationResult.XPathResultType + * XPathResultType}, or XPathEvaluationResult is specified as the type but an + * implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type + * is not available. + * @throws NullPointerException If {@code source or type} is {@code null}. + * + * @since 1.9 + */ + default T evaluateExpression(InputSource source, Class type) + throws XPathExpressionException + { + return type.cast(evaluate(source, XPathEvaluationResult.XPathResultType.getQNameType(type))); + } + + /** + * Evaluate the compiled XPath expression in the specified context. This is + * equivalent to calling {@link #evaluateExpression(InputSource source, Class type)} + * with type {@link XPathEvaluationResult}: + *
 {@code
+     *     evaluateExpression(source, XPathEvaluationResult.class);
+     * }
+ *

+ * + * @implSpec + * The default implementation in the XPath API is equivalent to: + *

 {@code
+     *     (XPathEvaluationResult)evaluateExpression(source, XPathEvaluationResult.class);
+     * }
+ * + * Since the {@code evaluate} method does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} + * type, the default implementation of this method will always throw an + * IllegalArgumentException. Any implementation supporting the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type must therefore + * override this method. + * + * @param source The {@code InputSource} of the document to evaluate over. + * + * @return The result of evaluating the expression. + * + * @throws XPathExpressionException If the expression cannot be evaluated. + * @throws IllegalArgumentException If the implementation of this method + * does not support the + * {@link XPathEvaluationResult.XPathResultType#ANY ANY} type. + * @throws NullPointerException If {@code source} is {@code null}. + * + * @since 1.9 + */ + default XPathEvaluationResult evaluateExpression(InputSource source) + throws XPathExpressionException + { + return evaluateExpression(source, XPathEvaluationResult.class); + } } diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactory.java --- a/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactory.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactory.java Wed Jul 05 20:17:28 2017 +0200 @@ -117,7 +117,7 @@ * and returns it if it is successfully created. * *
  • - * ${java.home}/conf/jaxp.properties is read and the value associated with the key being the system property above is looked for. + * ${java.home}/lib/jaxp.properties is read and the value associated with the key being the system property above is looked for. * If present, the value is processed just like above. *
  • *
  • diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactoryFinder.java --- a/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactoryFinder.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathFactoryFinder.java Wed Jul 05 20:17:28 2017 +0200 @@ -176,9 +176,9 @@ String javah = ss.getSystemProperty( "java.home" ); String configFile = javah + File.separator + - "conf" + File.separator + "jaxp.properties"; + "lib" + File.separator + "jaxp.properties"; - // try to read from $java.home/conf/jaxp.properties + // try to read from $java.home/lib/jaxp.properties try { if(firstTime){ synchronized(cacheProps){ @@ -193,7 +193,7 @@ } } final String factoryClassName = cacheProps.getProperty(propertyName); - debugPrintln("found " + factoryClassName + " in $java.home/conf/jaxp.properties"); + debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); if (factoryClassName != null) { xpathFactory = createInstance(factoryClassName, true); diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/XPathNodes.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 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 javax.xml.xpath; + +import java.util.Iterator; +import org.w3c.dom.Node; + +/** + * XPathNodes represents a set of nodes selected by a location path as specified + * in XML Path Language (XPath) + * Version 1.0, 3.3 Node-sets. + * + * @since 1.9 + */ +public interface XPathNodes extends Iterable { + + /** + * Returns an iterator of the Nodes. + * + * @return an Iterator. + */ + @Override + public abstract Iterator iterator(); + + /** + * Returns the number of items in the result + * + * @return The number of items in the result + */ + public abstract int size(); + + /** + * Returns a Node at the specified position + * + * @param index Index of the element to return. + * @return The Node at the specified position. + * @throws javax.xml.xpath.XPathException If the index is out of range + * (index < 0 || index >= size()) + */ + public abstract Node get(int index) + throws XPathException; +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/src/java.xml/share/classes/javax/xml/xpath/package.html --- a/jaxp/src/java.xml/share/classes/javax/xml/xpath/package.html Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxp/src/java.xml/share/classes/javax/xml/xpath/package.html Wed Jul 05 20:17:28 2017 +0200 @@ -1,6 +1,8 @@ - + + + + + - - - -javax.xml.xpath - - - - - - - - - -

    This package provides an object-model neutral API for the +This package provides an object-model neutral API for the evaluation of XPath expressions and access to the evaluation environment. -

    -

    The following XML standards apply:

    - - +

    +The XPath API supports + XML Path Language (XPath) Version 1.0


    -

    XPath Overview

    + +

    + +

    1. XPath Overview

    The XPath language provides a simple, concise syntax for selecting nodes from an XML document. XPath also provides rules for converting a @@ -67,7 +69,8 @@ replace many lines of DOM API code.

    -

    XPath Expressions

    + +

    2. XPath Expressions

    An XPath expression is composed of a location path and one or more optional predicates. Expressions @@ -76,18 +79,22 @@

    The following is an example of a simple XPath expression:

    +
     /foo/bar
     
    +

    This example would select the <bar> element in an XML document such as the following:

    +
     <foo>
    -<bar/>
    +    <bar/>
     </foo>
     
    +

    The expression /foo/bar is an example of a location path. While XPath location paths resemble Unix-style file system @@ -96,30 +103,36 @@ <bar> elements in the following document would be selected by the /foo/bar expression:

    +
     <foo>
    -<bar/>
    -<bar/>
    -<bar/>
    +    <bar/>
    +    <bar/>
    +    <bar/>
     </foo>
     
    +

    A special location path operator, //, selects nodes at any depth in an XML document. The following example selects all <bar> elements regardless of their location in a document:

    +
     //bar
     
    +

    A wildcard operator, *, causes all element nodes to be selected. The following example selects all children elements of a -<foo> element:

    +<foo> element: +
     /foo/*
     
    +

    In addition to element nodes, XPath location paths may also address attribute nodes, text nodes, comment nodes, and processing instruction @@ -166,35 +179,27 @@ <foo> elements that contain an include attribute with the value of true:

    +
     //foo[@include='true']
     
    +

    Predicates may be appended to each other to further refine an expression, such as:

    +
     //foo[@include='true'][@mode='bar']
     
    - -

    Using the XPath API

    - -

    -The following example demonstrates using the XPath API to select one -or more nodes from an XML document:

    +
    -
    -XPath xpath = XPathFactory.newInstance().newXPath();
    -String expression = "/widgets/widget";
    -InputSource inputSource = new InputSource("widgets.xml");
    -NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
    -
    - -

    XPath Expressions and Types

    + +

    3. XPath Data Types

    While XPath expressions select nodes in the XML document, the XPath API allows the selected nodes to be coalesced into one of the -following other data types:

    +following data types:

    • Boolean
    • @@ -202,14 +207,10 @@
    • String
    -

    The desired return type is specified by a {@link -javax.xml.namespace.QName} parameter in method call used to evaluate -the expression, which is either a call to -XPathExpression.evalute(...) or to one of the -XPath.evaluate(...) convenience methods. The allowed -QName values are specified as constants in the {@link -javax.xml.xpath.XPathConstants} class; they are:

    - + +

    3.1 QName types

    +The XPath API defines the following {@link javax.xml.namespace.QName} types to +represent return types of an XPath evaluation:
    • {@link javax.xml.xpath.XPathConstants#NODESET}
    • {@link javax.xml.xpath.XPathConstants#NODE}
    • @@ -218,26 +219,71 @@
    • {@link javax.xml.xpath.XPathConstants#NUMBER}
    +

    The return type is specified by a {@link javax.xml.namespace.QName} parameter +in method call used to evaluate the expression, which is either a call to +XPathExpression.evalute(...) or XPath.evaluate(...) +methods. +

    When a Boolean return type is requested, Boolean.TRUE is returned if one or more nodes were -selected; otherwise, Boolean.FALSE is returned.

    +selected; otherwise, Boolean.FALSE is returned.

    The String return type is a convenience for retrieving the character data from a text node, attribute node, comment node, or processing-instruction node. When used on an element node, the value of the child text nodes is returned. -

    The Number return type attempts to coalesce the text of a node to a double data type. -

    + + +

    3.2 Class types

    +In addition to the QName types, the XPath API supports the use of Class types +through the XPathExpression.evaluteExpression(...) or +XPath.evaluateExpression(...) methods. -

    XPath Context

    +The XPath data types are mapped to Class types as follows: +
      +
    • Boolean -- Boolean.class
    • +
    • Number -- Number.class
    • +
    • String -- String.class
    • +
    • Nodeset -- XPathNodes.class
    • +
    • Node -- Node.class
    • +
    + +

    +Of the subtypes of Number, only Double, Integer and Long are supported. + + +

    3.3 Enum types

    +Enum types are defined in {@link javax.xml.xpath.XPathEvaluationResult.XPathResultType} +that provide mappings between the QName and Class types above. The result of +evaluating an expression using the XPathExpression.evaluteExpression(...) +or XPath.evaluateExpression(...) methods will be of one of these types. + + +

    4. XPath Context

    XPath location paths may be relative to a particular node in the -document, known as the context. Consider the following -XML document:

    +document, known as the context. A context consists of: +
      +
    • a node (the context node)
    • +
    • a pair of non-zero positive integers (the context position and the context size)
    • +
    • a set of variable bindings
    • +
    • a function library
    • +
    • the set of namespace declarations in scope for the expression
    • +
    +

    +It is an XML document tree represented as a hierarchy of nodes, a +{@link org.w3c.dom.Node} for example, in the JDK implementation. + + +

    5. Using the XPath API

    + +Consider the following XML document: +

    +

     <widgets>
     <widget>
    @@ -246,36 +292,88 @@
     </widget>
     </widgets>
     
    +
    -

    The <widget> element can be selected with the -following XPath API code:

    +

    +The <widget> element can be selected with the following process: +

     // parse the XML as a W3C Document
     DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
     Document document = builder.parse(new File("/widgets.xml"));
     
    +//Get an XPath object and evaluate the expression
     XPath xpath = XPathFactory.newInstance().newXPath();
     String expression = "/widgets/widget";
     Node widgetNode = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
    +
    +//or using the evaluateExpression method
    +Node widgetNode = xpath.evaluateExpression(expression, document, Node.class);
     
    +

    With a reference to the <widget> element, a -relative XPath expression can now written to select the +relative XPath expression can be written to select the <manufacturer> child element:

    +
     XPath xpath = XPathFactory.newInstance().newXPath();
     String expression = "manufacturer";
     Node manufacturerNode = (Node) xpath.evaluate(expression, widgetNode, XPathConstants.NODE);
    +
    +//or using the evaluateExpression method
    +Node manufacturerNode = xpath.evaluateExpression(expression, widgetNode, Node.class);
     
    +
    + +

    +In the above example, the XML file is read into a DOM Document before being passed +to the XPath API. The following code demonstrates the use of InputSource to +leave it to the XPath implementation to process it: + +

    +
    +XPath xpath = XPathFactory.newInstance().newXPath();
    +String expression = "/widgets/widget";
    +InputSource inputSource = new InputSource("widgets.xml");
    +NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
    +
    +//or using the evaluateExpression method
    +XPathNodes nodes = xpath.evaluate(expression, inputSource, XPathNodes.class);
    +
    +
    - +

    +In the above cases, the type of the expected results are known. In case where +the result type is unknown or any type, the {@link javax.xml.xpath.XPathEvaluationResult} +may be used to determine the return type. The following code demonstrates the usage: +

    +
    +XPathEvaluationResult<?> result = xpath.evaluateExpression(expression, document);
    +switch (result.type()) {
    +    case NODESET:
    +        XPathNodes nodes = (XPathNodes)result.value();
    +        ...
    +        break;
    +}
    +
    +
    + +

    +The XPath 1.0 Number data type is defined as a double. However, the XPath +specification also provides functions that returns Integer type. To facilitate +such operations, the XPath API allows Integer and Long to be used in +{@code evaluateExpression} method such as the following code: +

    +

    +
    +int count = xpath.evaluate("count(/widgets/widget)", document, Integer.class);
    +
    +
    + +@since 1.5 + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/SchemaFactoryTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,297 @@ +/* + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; + +/* + * @summary Class containing the test cases for SchemaFactory + */ +@Test(singleThreaded = true) +public class SchemaFactoryTest { + + @BeforeClass + public void setup() throws SAXException, IOException, ParserConfigurationException { + sf = newSchemaFactory(); + + assertNotNull(sf); + + xsd1 = Files.readAllBytes(Paths.get(XML_DIR + "test.xsd")); + xsd2 = Files.readAllBytes(Paths.get(XML_DIR + "test1.xsd")); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + xsdDoc1 = db.parse(newInputStream(xsd1)); + xsdDoc2 = db.parse(newInputStream(xsd2)); + + xml = Files.readAllBytes(Paths.get(XML_DIR + "test.xml")); + } + + @Test(expectedExceptions = SAXParseException.class) + public void testNewSchemaDefault() throws SAXException, IOException { + validate(sf.newSchema()); + } + + @Test + public void testNewSchemaWithFile() throws SAXException, IOException { + validate(sf.newSchema(new File(XML_DIR + "test.xsd"))); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNewSchemaWithNullFile() throws SAXException { + sf.newSchema((File) null); + } + + @DataProvider(name = "valid-source") + public Object[][] getValidSource() { + return new Object[][] { + { streamSource(xsd1) }, + { saxSource(xsd1) }, + { domSource(xsdDoc1) } }; + + } + + @Test(dataProvider = "valid-source") + public void testNewSchemaWithValidSource(Source schema) throws SAXException, IOException { + validate(sf.newSchema(schema)); + } + + @DataProvider(name = "invalid-source") + public Object[][] getInvalidSource() { + return new Object[][] { + { nullStreamSource() }, + { nullSaxSource() } }; + } + + @Test(dataProvider = "invalid-source", expectedExceptions = SAXParseException.class) + public void testNewSchemaWithInvalidSource(Source schema) throws SAXException { + sf.newSchema(schema); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNewSchemaWithNullSource() throws SAXException { + sf.newSchema((Source)null); + } + + @DataProvider(name = "valid-sources") + public Object[][] getValidSources() { + return new Object[][] { + { streamSource(xsd1), streamSource(xsd2) }, + { saxSource(xsd1), saxSource(xsd2) }, + { domSource(xsdDoc1), domSource(xsdDoc2) } }; + + } + + @Test(dataProvider = "valid-sources") + public void testNewSchemaWithValidSourceArray(Source schema1, Source schema2) throws SAXException, IOException { + validate(sf.newSchema(new Source[] { schema1, schema2 })); + } + + @DataProvider(name = "invalid-sources") + public Object[][] getInvalidSources() { + return new Object[][] { + { streamSource(xsd1), nullStreamSource() }, + { nullStreamSource(), nullStreamSource() }, + { saxSource(xsd1), nullSaxSource() }, + { nullSaxSource(), nullSaxSource() } }; + } + + @Test(dataProvider = "invalid-sources", expectedExceptions = SAXParseException.class) + public void testNewSchemaWithInvalidSourceArray(Source schema1, Source schema2) throws SAXException { + sf.newSchema(new Source[] { schema1, schema2 }); + } + + @DataProvider(name = "null-sources") + public Object[][] getNullSources() { + return new Object[][] { + { new Source[] { domSource(xsdDoc1), null } }, + { new Source[] { null, null } }, + { null } }; + + } + + @Test(dataProvider = "null-sources", expectedExceptions = NullPointerException.class) + public void testNewSchemaWithNullSourceArray(Source[] schemas) throws SAXException { + sf.newSchema(schemas); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNewSchemaWithNullUrl() throws SAXException { + sf.newSchema((URL) null); + } + + + @Test + public void testErrorHandler() { + SchemaFactory sf = newSchemaFactory(); + assertNull(sf.getErrorHandler(), "When SchemaFactory is created, initially ErrorHandler should not be set."); + + ErrorHandler handler = new MyErrorHandler(); + sf.setErrorHandler(handler); + assertSame(sf.getErrorHandler(), handler); + + sf.setErrorHandler(null); + assertNull(sf.getErrorHandler()); + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + sf.getProperty(UNRECOGNIZED_NAME); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + sf.setProperty(UNRECOGNIZED_NAME, "test"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + assertNotNull(sf); + sf.getProperty(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + assertNotNull(sf); + sf.setProperty(null, "test"); + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + sf.getFeature(UNRECOGNIZED_NAME); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + sf.setFeature(UNRECOGNIZED_NAME, true); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + assertNotNull(sf); + sf.getFeature(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + SchemaFactory sf = newSchemaFactory(); + assertNotNull(sf); + sf.setFeature(null, true); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testInvalidSchemaLanguage() { + final String INVALID_SCHEMA_LANGUAGE = "http://relaxng.org/ns/structure/1.0"; + SchemaFactory.newInstance(INVALID_SCHEMA_LANGUAGE); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullSchemaLanguage() { + SchemaFactory.newInstance(null); + } + + private void validate(Schema schema) throws SAXException, IOException { + schema.newValidator().validate(new StreamSource(new ByteArrayInputStream(xml))); + } + private InputStream newInputStream(byte[] xsd) { + return new ByteArrayInputStream(xsd); + } + + private Source streamSource(byte[] xsd) { + return new StreamSource(newInputStream(xsd)); + } + + private Source nullStreamSource() { + return new StreamSource((InputStream) null); + } + + private Source saxSource(byte[] xsd) { + return new SAXSource(new InputSource(newInputStream(xsd))); + } + + private Source nullSaxSource() { + return new SAXSource(new InputSource((InputStream) null)); + } + + private Source domSource(Document xsdDoc) { + return new DOMSource(xsdDoc); + } + + private SchemaFactory newSchemaFactory() { + return SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); + } + + private static final String UNRECOGNIZED_NAME = "http://xml.org/sax/features/namespace-prefixes"; + + private SchemaFactory sf; + private byte[] xsd1; + private byte[] xsd2; + private Document xsdDoc1; + private Document xsdDoc2; + private byte[] xml; +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/TypeInfoProviderTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static jaxp.library.JAXPTestUtilities.filenameToURL; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.TypeInfoProvider; +import javax.xml.validation.ValidatorHandler; + +import jaxp.library.JAXPFileBaseTest; + +import org.testng.annotations.Test; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/* + * @summary test ValidatorHandler.getTypeInfoProvider() + */ +public class TypeInfoProviderTest extends JAXPFileBaseTest { + + private ValidatorHandler validatorHandler; + + @Test + public void test() throws SAXException, ParserConfigurationException, IOException { + + SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); + Schema schema = sf.newSchema(new File(XML_DIR + "shiporder11.xsd")); + validatorHandler = schema.newValidatorHandler(); + MyDefaultHandler myDefaultHandler = new MyDefaultHandler(); + validatorHandler.setContentHandler(myDefaultHandler); + + InputSource is = new InputSource(filenameToURL(XML_DIR + "shiporder11.xml")); + + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + XMLReader xmlReader = parserFactory.newSAXParser().getXMLReader(); + xmlReader.setContentHandler(validatorHandler); + xmlReader.parse(is); + + } + + private class MyDefaultHandler extends DefaultHandler { + + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + TypeInfoProvider typeInfoProvider = validatorHandler.getTypeInfoProvider(); + int index = atts.getIndex("orderid"); + if (index != -1) { + System.out.println(" Index " + index); + System.out.println(" ElementType " + typeInfoProvider.getElementTypeInfo().getTypeName()); + assertEquals(typeInfoProvider.getAttributeTypeInfo(index).getTypeName(), "string"); + assertTrue(typeInfoProvider.isSpecified(index)); + assertFalse(typeInfoProvider.isIdAttribute(index)); + } + + } + + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorHandlerTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.File; + +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.ValidatorHandler; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.xml.sax.ContentHandler; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.helpers.DefaultHandler; + +/* + * @summary Class containing the test cases for ValidatorHandler API + */ +public class ValidatorHandlerTest { + @BeforeClass + public void setup() throws SAXException { + schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); + + assertNotNull(schema); + } + + @Test + public void testErrorHandler() { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNull(validatorHandler.getErrorHandler(), "When ValidatorHandler is created, initially ErrorHandler should not be set."); + + ErrorHandler handler = new MyErrorHandler(); + validatorHandler.setErrorHandler(handler); + assertSame(validatorHandler.getErrorHandler(), handler); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + validatorHandler.getProperty(FEATURE_NAME); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + validatorHandler.setProperty(FEATURE_NAME, "test"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNotNull(validatorHandler); + validatorHandler.getProperty(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNotNull(validatorHandler); + validatorHandler.setProperty(null, "test"); + } + + public void testFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertFalse(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); + + validatorHandler.setFeature(FEATURE_NAME, true); + assertTrue(validatorHandler.getFeature(FEATURE_NAME), "The feature should be false by default."); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNotNull(validatorHandler); + validatorHandler.getFeature(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNotNull(validatorHandler); + validatorHandler.setFeature(null, true); + } + + @Test + public void testContentHandler() { + ValidatorHandler validatorHandler = getValidatorHandler(); + assertNull(validatorHandler.getContentHandler(), "When ValidatorHandler is created, initially ContentHandler should not be set."); + + ContentHandler handler = new DefaultHandler(); + validatorHandler.setContentHandler(handler); + assertSame(validatorHandler.getContentHandler(), handler); + + validatorHandler.setContentHandler(null); + assertNull(validatorHandler.getContentHandler()); + + } + + private ValidatorHandler getValidatorHandler() { + return schema.newValidatorHandler(); + } + + private static final String FEATURE_NAME = "http://xml.org/sax/features/namespace-prefixes"; + + private Schema schema; + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/ptests/ValidatorTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static javax.xml.validation.ptests.ValidationTestConst.XML_DIR; +import static jaxp.library.JAXPTestUtilities.filenameToURL; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; + +import java.io.File; +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import jaxp.library.JAXPFileBaseTest; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.helpers.DefaultHandler; + +/* + * @summary Class containing the test cases for Validator API + */ +public class ValidatorTest extends JAXPFileBaseTest { + + @BeforeClass + public void setup() throws SAXException, IOException, ParserConfigurationException { + schema = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI).newSchema(new File(XML_DIR + "test.xsd")); + + assertNotNull(schema); + + xmlFileUri = filenameToURL(XML_DIR + "test.xml"); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + xmlDoc = dbf.newDocumentBuilder().parse(xmlFileUri); + } + + @Test + public void testValidateStreamSource() throws SAXException, IOException { + Validator validator = getValidator(); + validator.setErrorHandler(new MyErrorHandler()); + validator.validate(getStreamSource()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testValidateNullSource() throws SAXException, IOException { + Validator validator = getValidator(); + assertNotNull(validator); + validator.validate(null); + } + + @Test + public void testErrorHandler() { + Validator validator = getValidator(); + assertNull(validator.getErrorHandler(), "When Validator is created, initially ErrorHandler should not be set."); + + ErrorHandler mh = new MyErrorHandler(); + validator.setErrorHandler(mh); + assertSame(validator.getErrorHandler(), mh); + + } + + @DataProvider(name = "source-result") + public Object[][] getSourceAndResult() { + return new Object[][] { + { getStreamSource(), null }, + { getSAXSource(), getSAXResult() }, + { getDOMSource(), getDOMResult() }, + { getSAXSource(), null }, + { getDOMSource(), null } }; + } + + @Test(dataProvider = "source-result") + public void testValidateWithResult(Source source, Result result) throws SAXException, IOException { + Validator validator = getValidator(); + validator.validate(source, result); + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testGetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + validator.getProperty(UNRECOGNIZED_NAME); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testSetUnrecognizedProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + validator.setProperty(UNRECOGNIZED_NAME, "test"); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + assertNotNull(validator); + validator.getProperty(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullProperty() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + assertNotNull(validator); + validator.setProperty(null, "test"); + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testGetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + validator.getFeature(UNRECOGNIZED_NAME); + + } + + @Test(expectedExceptions = SAXNotRecognizedException.class) + public void testSetUnrecognizedFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + validator.setFeature(UNRECOGNIZED_NAME, true); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testGetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + assertNotNull(validator); + validator.getFeature(null); + + } + + @Test(expectedExceptions = NullPointerException.class) + public void testSetNullFeature() throws SAXNotRecognizedException, SAXNotSupportedException { + Validator validator = getValidator(); + assertNotNull(validator); + validator.setFeature(null, true); + } + + private Validator getValidator() { + return schema.newValidator(); + } + + private Source getStreamSource() { + return new StreamSource(xmlFileUri); + } + + private Source getSAXSource() { + return new SAXSource(new InputSource(xmlFileUri)); + } + + private Result getSAXResult() { + SAXResult saxResult = new SAXResult(); + saxResult.setHandler(new DefaultHandler()); + return saxResult; + } + + private Source getDOMSource() { + return new DOMSource(xmlDoc); + } + + private Result getDOMResult() { + return new DOMResult(); + } + + private static final String UNRECOGNIZED_NAME = "http://xml.org/sax/features/namespace-prefixes"; + private String xmlFileUri; + private Schema schema; + private Document xmlDoc; + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder11.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder11.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,24 @@ + + + +John Smith + +Ola Nordmann +
    Langgt 23
    +4000 Stavanger +Norway +
    + +Empire Burlesque +Special Edition +1 +10.90 + + +Hide your heart +1 +9.90 + +
    \ No newline at end of file diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder11.xsd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder11.xsd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder12.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder12.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,24 @@ + + + +John Smith + +Ola Nordmann +
    Langgt 23
    +4000 Stavanger +Norway +
    + +Empire Burlesque +Special Edition +1 +10.90 + + +Hide your heart +1 +9.90 + +
    \ No newline at end of file diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder12.xsd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/shiporder12.xsd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,5 @@ + + + John +444-121-3434 + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test.xsd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test.xsd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,11 @@ + + + + + + + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test1.xsd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/javax/xml/validation/xmlfiles/test1.xsd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,11 @@ + + + + + + + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4511326.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.gaptest; + +import java.io.StringReader; + +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamSource; + +import jaxp.library.JAXPBaseTest; + +import org.testng.annotations.Test; + +/* + * @bug 4511326 + * @summary In forwards-compatible mode the attribute isn't ignored + */ + +public class Bug4511326 extends JAXPBaseTest { + + private static final String XSL = "" + + "" + + "

    " + + "

    " + + "
    " + + "
    "; + + + @Test + public void ignoreAttTest() throws TransformerConfigurationException { + /* Create a TransformFactory instance */ + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + + /* Create and init a StreamSource instance */ + StreamSource source = new StreamSource(new StringReader(XSL)); + + transformerFactory.newTransformer(source); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4512806.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.gaptest; + +import static javax.xml.transform.OutputKeys.ENCODING; +import static javax.xml.transform.OutputKeys.INDENT; +import static org.testng.Assert.assertEquals; + +import java.io.StringReader; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamSource; + +import jaxp.library.JAXPBaseTest; + +import org.testng.annotations.Test; + +/* + * @bug 4512806 + * @summary test transformer.setOutputProperties(null) + */ +public class Bug4512806 extends JAXPBaseTest { + + @Test + public void testProperty() throws TransformerConfigurationException { + /* Create a transform factory instance */ + TransformerFactory tfactory = TransformerFactory.newInstance(); + + /* Create a StreamSource instance */ + StreamSource streamSource = new StreamSource(new StringReader(xslData)); + + transformer = tfactory.newTransformer(streamSource); + transformer.setOutputProperty(INDENT, "no"); + transformer.setOutputProperty(ENCODING, "UTF-16"); + + assertEquals(printPropertyValue(INDENT), "indent=no"); + assertEquals(printPropertyValue(ENCODING), "encoding=UTF-16"); + + transformer.setOutputProperties(null); + + assertEquals(printPropertyValue(INDENT), "indent=yes"); + assertEquals(printPropertyValue(ENCODING), "encoding=UTF-8"); + + } + + private String printPropertyValue(String name) { + return name + "=" + transformer.getOutputProperty(name); + } + + private Transformer transformer; + + private static final String xslData = "" + + "\n" + + " \n" + + " \n" + + " Hello World! \n" + + " \n" + + ""; + + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4515047.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.gaptest; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import jaxp.library.JAXPBaseTest; + +import org.testng.annotations.Test; + +/* + * @bug 4515047 + * @summary test transform an empty dom source + */ + +public class Bug4515047 extends JAXPBaseTest { + + @Test + public void testCreateTxDoc() throws TransformerException, ParserConfigurationException { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + + StreamResult result = new StreamResult(System.out); + DOMSource source = new DOMSource(); + + /* This should not throw an Illegal Argument Exception */ + //Test empty DOMSource + transformer.transform(source, result); + + //Test DOMSource having only an empty node + source.setNode(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()); + transformer.transform(source, result); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4515660.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.gaptest; + +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.stream.StreamResult; + +import jaxp.library.JAXPBaseTest; + +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; + +/* + * @bug 4515660 + * @summary verify property org.xml.sax.driver is used by SAXTransformerFactory + */ +@Test(singleThreaded = true) +public class Bug4515660 extends JAXPBaseTest { + + @BeforeClass + public void setSaxDrier() { + setSystemProperty("org.xml.sax.driver", ReaderStub.class.getName()); + } + + @AfterClass + public void clearSaxDrier() { + setSystemProperty("org.xml.sax.driver", null); + } + + @Test + public void testTransformer() throws TransformerException { + String xml = ""; + ReaderStub.used = false; + + TransformerFactory transFactory = TransformerFactory.newInstance(); + Transformer transformer = transFactory.newTransformer(); + InputSource in = new InputSource(new StringReader(xml)); + SAXSource source = new SAXSource(in); + StreamResult result = new StreamResult(new StringWriter()); + + transformer.transform(source, result); + + assertTrue(ReaderStub.used); + + } + + @Test + public void testSAXTransformerFactory() throws TransformerConfigurationException { + final String xsl = "\n" + "\n" + + " Hello World!\n" + "\n"; + + ReaderStub.used = false; + + TransformerFactory transFactory = TransformerFactory.newInstance(); + assertTrue(transFactory.getFeature(SAXTransformerFactory.FEATURE)); + + InputSource in = new InputSource(new StringReader(xsl)); + SAXSource source = new SAXSource(in); + + transFactory.newTransformer(source); + assertTrue(ReaderStub.used); + + } + + public static class ReaderStub extends XMLFilterImpl { + static boolean used = false; + + public ReaderStub() throws ParserConfigurationException, SAXException { + super(); + super.setParent(SAXParserFactory.newInstance().newSAXParser().getXMLReader()); + used = true; + } + + public void parse(InputSource input) throws SAXException, IOException { + used = true; + super.parse(input); + } + + public void parse(String systemId) throws SAXException, IOException { + used = true; + super.parse(systemId); + } + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4693341.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4693341.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.gaptest; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static jaxp.library.JAXPTestUtilities.USER_DIR; +import static jaxp.library.JAXPTestUtilities.compareDocumentWithGold; +import static org.testng.Assert.assertTrue; +import static test.gaptest.GapTestConst.GOLDEN_DIR; +import static test.gaptest.GapTestConst.XML_DIR; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import jaxp.library.JAXPFileBaseTest; + +import org.testng.annotations.Test; +import org.xml.sax.SAXException; + +/* + * @bug 4693341 + * @summary test transforming to stream with external dtd + */ + +public class Bug4693341 extends JAXPFileBaseTest { + + @Test + public void test() throws TransformerException, ParserConfigurationException, SAXException, IOException { + + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + + String out = USER_DIR + File.separator + "Bug4693341.out"; + StreamResult result = new StreamResult(new File(out)); + + String in = XML_DIR + "Bug4693341.xml"; + String golden = GOLDEN_DIR + "Bug4693341.xml"; + File file = new File(in); + StreamSource source = new StreamSource(file); + System.out.println(source.getSystemId()); + + Files.copy(Paths.get(XML_DIR + "Bug4693341.dtd"), + Paths.get(USER_DIR + File.separator + "Bug4693341.dtd"), REPLACE_EXISTING); + + transformer.transform(source, result); + + assertTrue(compareDocumentWithGold(golden, out)); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4848653.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package test.gaptest; + +import static jaxp.library.JAXPTestUtilities.filenameToURL; +import static test.gaptest.GapTestConst.XML_DIR; + +import java.io.IOException; + +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import jaxp.library.JAXPFileBaseTest; + +import org.testng.annotations.Test; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; + +/* + * @bug 4848653 + * @summary Verify JAXP schemaLanguage property is ignored if setValidating(false) + */ + +public class Bug4848653 extends JAXPFileBaseTest { + + @Test + public void test() throws IOException, SAXException, ParserConfigurationException { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(false); + SAXParser parser = factory.newSAXParser(); + parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", XMLConstants.W3C_XML_SCHEMA_NS_URI); + + String filename = XML_DIR + "Bug4848653.xml"; + InputSource is = new InputSource(filenameToURL(filename)); + XMLReader xmlReader = parser.getXMLReader(); + xmlReader.setErrorHandler(new MyErrorHandler()); + xmlReader.parse(is); + } + + class MyErrorHandler implements ErrorHandler { + public void error(SAXParseException exception) throws SAXParseException { + throw exception; + } + + public void warning(SAXParseException exception) throws SAXParseException { + throw exception; + } + + public void fatalError(SAXParseException exception) throws SAXParseException { + throw exception; + } + + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/Bug4858685.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package test.gaptest; + +import static jaxp.library.JAXPTestUtilities.filenameToURL; +import static org.testng.Assert.assertEquals; +import static test.gaptest.GapTestConst.GOLDEN_DIR; +import static test.gaptest.GapTestConst.XML_DIR; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.stream.StreamSource; + +import jaxp.library.JAXPFileBaseTest; + +import org.testng.annotations.Test; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/* + * @bug 4858685 4894410 + * @summary test transforming text node + */ + +public class Bug4858685 extends JAXPFileBaseTest { + @Test + public void test() throws TransformerException, IOException { + String uri = XML_DIR + "certificate.xml"; + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + + Transformer transformer = transformerFactory.newTransformer(); + + // use URI as a StreamSource + StreamSource streamSource = new StreamSource(filenameToURL(uri)); + + DOMResult domResult = new DOMResult(); + + // StreamSource -> DOMResult + transformer.transform(streamSource, domResult); + + // dump DOM in a human readable form + String gotString = DOMDump.dumpDom(domResult.getNode()); + + String goldenString = new String(Files.readAllBytes(Paths.get(GOLDEN_DIR + "Bug4858685.txt"))); + + assertEquals(gotString, goldenString); + + } + + /** + * DOMDump: dump a DOM to a String in human readable form. method dumpDOM() + * is static for easy calling: + */ + private static class DOMDump { + + /** + * the maximum level to indent with blanks + */ + private static final int BLANKS_LEN = 64; + + /** + * each level of the tree will be indented with blanks for readability + */ + private static final String BLANKS = " "; + + /** + * dumpDOM will dump the DOM into a String for human readability + * + * @param domNode + * the DOM Node to dump + * @return human readabile DOM as a String + */ + public static String dumpDom(Node domNode) { + return dumpInternal(domNode, 0); + } + + /** + * dumpInternal is used internaly to recursively dump DOM Nodes + * + * @param domNode + * to dump + * @param indent + * level + * @return domNode as human readable String + */ + private static String dumpInternal(Node domNode, int indent) { + + String result = ""; + + // indent for readability + result += indentBlanks(indent); + indent += 2; + + // protect against null + if (domNode == null) { + result = result + "[null]" + "\n"; + return result; + } + + // what to output depends on NodeType + short type = domNode.getNodeType(); + switch (type) { + case Node.ATTRIBUTE_NODE: { + result += "[attribute] " + domNode.getNodeName() + "=\"" + domNode.getNodeValue() + "\""; + break; + } + case Node.CDATA_SECTION_NODE: { + result += "[cdata] " + domNode.getNodeValue(); + break; + } + case Node.COMMENT_NODE: { + result += "[comment] " + domNode.getNodeValue(); + break; + } + case Node.DOCUMENT_FRAGMENT_NODE: { + result += "[document fragment]"; + break; + } + case Node.DOCUMENT_NODE: { + result += "[document]"; + break; + } + case Node.DOCUMENT_TYPE_NODE: { + result += "[document type] " + domNode.getNodeName(); + break; + } + case Node.ELEMENT_NODE: { + result += "[element] " + domNode.getNodeName(); + // output all attributes for Element + if (domNode.hasAttributes()) { + NamedNodeMap attributes = domNode.getAttributes(); + for (int onAttribute = 0; onAttribute < attributes.getLength(); onAttribute++) { + + // seprate each attribute with a space + result += " "; + + Node attribute = attributes.item(onAttribute); + String namespaceURI = attribute.getNamespaceURI(); + String prefix = attribute.getPrefix(); + String localName = attribute.getLocalName(); + String name = attribute.getNodeName(); + String value = attribute.getNodeValue(); + + // using Namespaces? + if (namespaceURI != null) { + result += "{" + namespaceURI + "}"; + } + if (prefix != null) { + result += prefix + ":"; + } + + // name="value" + result += attribute.getNodeName() + "=\"" + attribute.getNodeValue() + "\""; + } + } + + break; + } + case Node.ENTITY_NODE: { + result += "[entity] " + domNode.getNodeName(); + break; + } + case Node.ENTITY_REFERENCE_NODE: { + result += "[entity reference] " + domNode.getNodeName(); + break; + } + case Node.NOTATION_NODE: { + result += "[notation] " + domNode.getNodeName(); + break; + } + case Node.PROCESSING_INSTRUCTION_NODE: { + result += "[pi] target=\"" + domNode.getNodeName() + "\" content=\"" + domNode.getNodeValue() + "\""; + break; + } + case Node.TEXT_NODE: { + result += "[text] " + domNode.getNodeValue(); + break; + } + default: { + result += "[unknown]"; + break; + } + } + + // humans read in lines + result += "\n"; + + // process children + NodeList children = domNode.getChildNodes(); + for (int onChild = 0; onChild < children.getLength(); onChild++) { + Node child = children.item(onChild); + result += dumpInternal(child, indent); + } + + // return human readable DOM as String + return result; + } + + /** + * indentBlanks will return a String of indent blanks + * + * @param indent + * level + * @return String of blanks + */ + private static String indentBlanks(int indent) { + if (indent == 0) { + return ""; + } + + if (indent > BLANKS_LEN) { + return BLANKS; + } + + return BLANKS.substring(0, indent + 1); + } + + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4693341.dtd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4693341.dtd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4693341.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4693341.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,20 @@ + + + +10016 +Wed May 29 12:45:00 PDT 2002 + +ABC +XYZ +1234 Anywhere Street +Palo Alto +California +USA +94303 +NULL +NULL + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4848653.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/Bug4848653.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,1 @@ + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/certificate.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/certificate.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,27 @@ + + + + + +MIIDVjCCAxICBD6kKrMwCwYHKoZIzjgEAwUAMIGPMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FDASBgNVBAcTC1NhbnRhIENsYXJhMR8wHQYDVQQKExZTdW4gTWljcm9zeXN0ZW1zLCBJbmMuMR4w +HAYDVQQLExVKYXZhIGFuZCBYTUwgU29mdHdhcmUxHDAaBgNVBAMTE0pXUy1TZWN1cml0eSBDbGll +bnQwHhcNMDMwNDIxMTczMDI3WhcNMDMwNzIwMTczMDI3WjCBjzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEfMB0GA1UEChMWU3VuIE1pY3Jvc3lzdGVtcywg +SW5jLjEeMBwGA1UECxMVSmF2YSBhbmQgWE1MIFNvZnR3YXJlMRwwGgYDVQQDExNKV1MtU2VjdXJp +dHkgQ2xpZW50MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E +AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up +1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUj +C8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZ +T+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7 +zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAV3R+bUfh+u0yaPBV75umKvFB +ucv37ETDak889b7k72kZdGoHz3oDmp69tiNDg5r7IvKtjHGbZ6C3Nv0ycNR7Sed1QPOF4nn/tgUl +j+BvtVW3iiIRgBJ82KP+28QtwPkkxSp7n5HG0v7bE29E/juLduuhKBQTaaCvajuCFxiBrmAwCwYH +KoZIzjgEAwUAAzEAMC4CFQCCuDNmMKjgY6MV1SmAcCdnhuT6VwIVAJBOiPDnDWp2WlKAERF6nOAf +vKz9 + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4693341.dtd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4693341.dtd Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4693341.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4693341.xml Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,20 @@ + + + +10016 +Wed May 29 12:45:00 PDT 2002 + +ABC +XYZ +1234 Anywhere Street +Palo Alto +California +USA +94303 +NULL +NULL + + + + + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4858685.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/functional/test/gaptest/xmlfiles/out/Bug4858685.txt Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,30 @@ +[document] + [element] env:Envelope {http://www.w3.org/2000/xmlns/}xmlns:xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" {http://www.w3.org/2000/xmlns/}xmlns:xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" {http://www.w3.org/2000/xmlns/}xmlns:xmlns:ns0="http://headertest.org/" {http://www.w3.org/2000/xmlns/}xmlns:xmlns:xsd="http://www.w3.org/2001/XMLSchema" {http://www.w3.org/2000/xmlns/}xmlns:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + [text] + + [element] env:Body + [text] + + [element] ds:X509Certificate xmlns="" {http://www.w3.org/2000/xmlns/}xmlns:xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + [text] +MIIDVjCCAxICBD6kKrMwCwYHKoZIzjgEAwUAMIGPMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FDASBgNVBAcTC1NhbnRhIENsYXJhMR8wHQYDVQQKExZTdW4gTWljcm9zeXN0ZW1zLCBJbmMuMR4w +HAYDVQQLExVKYXZhIGFuZCBYTUwgU29mdHdhcmUxHDAaBgNVBAMTE0pXUy1TZWN1cml0eSBDbGll +bnQwHhcNMDMwNDIxMTczMDI3WhcNMDMwNzIwMTczMDI3WjCBjzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEfMB0GA1UEChMWU3VuIE1pY3Jvc3lzdGVtcywg +SW5jLjEeMBwGA1UECxMVSmF2YSBhbmQgWE1MIFNvZnR3YXJlMRwwGgYDVQQDExNKV1MtU2VjdXJp +dHkgQ2xpZW50MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E +AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up +1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUj +C8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZ +T+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7 +zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAV3R+bUfh+u0yaPBV75umKvFB +ucv37ETDak889b7k72kZdGoHz3oDmp69tiNDg5r7IvKtjHGbZ6C3Nv0ycNR7Sed1QPOF4nn/tgUl +j+BvtVW3iiIRgBJ82KP+28QtwPkkxSp7n5HG0v7bE29E/juLduuhKBQTaaCvajuCFxiBrmAwCwYH +KoZIzjgEAwUAAzEAMC4CFQCCuDNmMKjgY6MV1SmAcCdnhuT6VwIVAJBOiPDnDWp2WlKAERF6nOAf +vKz9 + + [text] + + [text] + diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/libs/javax/xml/validation/ptests/MyErrorHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/libs/javax/xml/validation/ptests/MyErrorHandler.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXParseException; + +class MyErrorHandler implements ErrorHandler { + public void error(SAXParseException exception) throws SAXParseException { + throw exception; + } + + public void warning(SAXParseException exception) throws SAXParseException { + throw exception; + } + + public void fatalError(SAXParseException exception) throws SAXParseException { + throw exception; + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/libs/javax/xml/validation/ptests/ValidationTestConst.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.validation.ptests; + +import static jaxp.library.JAXPTestUtilities.FILE_SEP; +import static jaxp.library.JAXPTestUtilities.getPathByClassName; + +/** + * This class defines the path constant + */ +public class ValidationTestConst { + /** + * XML source file directory. + */ + public static final String XML_DIR = getPathByClassName(ValidationTestConst.class, + ".." + FILE_SEP + "xmlfiles"); + + /** + * Golden validation files directory. + */ + public static final String GOLDEN_DIR = getPathByClassName(ValidationTestConst.class, + ".." + FILE_SEP + "xmlfiles" + FILE_SEP + "out"); +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/libs/test/gaptest/GapTestConst.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package test.gaptest; + +import static jaxp.library.JAXPTestUtilities.FILE_SEP; +import static jaxp.library.JAXPTestUtilities.getPathByClassName; + +/** + * This class defines the path constant + */ +public class GapTestConst { + /** + * XML source file directory. + */ + public static final String XML_DIR = getPathByClassName(GapTestConst.class, "xmlfiles"); + + /** + * Golden validation files directory. + */ + public static final String GOLDEN_DIR = getPathByClassName(GapTestConst.class, "xmlfiles" + FILE_SEP + "out"); +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathAnyTypeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathAnyTypeTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.xpath; + +import java.io.File; +import javax.xml.xpath.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/* + * @bug 8054196 + * @summary Test for the project XPath: support any type. This test covers the new + * evaluateExpression methods of XPath, as well as XPathNodes and XPathEvaluationResult. + */ +public class XPathAnyTypeTest extends XPathTestBase { + /* + Test for resolveFunction(QName functionName,int arity); evaluate throws + NPE if functionName is null. + */ + + @Test(dataProvider = "xpath", expectedExceptions = NullPointerException.class) + public void testCheckXPathFunctionResolver02(XPath xpath) throws XPathExpressionException { + xpath.setXPathFunctionResolver((functionName, arity) -> null); + assertEquals(xpath.evaluate(null, "5"), "2"); + } + /* + Check that NPE is thrown when expression is null. + */ + + @Test(dataProvider = "xpath", expectedExceptions = NullPointerException.class) + public void test01(XPath xpath) throws XPathExpressionException { + double result = xpath.evaluateExpression(null, (Object) null, Double.class); + } + + /* + Check that NPE is thrown when the class type is null. + */ + @Test(dataProvider = "xpath", expectedExceptions = NullPointerException.class) + public void test02(XPath xpath) throws XPathExpressionException { + double result = xpath.evaluateExpression("1+1", (Object) null, null); + } + + /* + Parameter item can be null when the expression does not depends on the + context. + */ + @Test(dataProvider = "xpath") + public void test03(XPath xpath) throws XPathExpressionException { + int result = xpath.evaluateExpression("1+1", (Object) null, Integer.class); + assertTrue(result == 2); + } + + /* + * Test return type: boolean. + */ + @Test(dataProvider = "document") + public void test04(XPath xpath, Document doc) throws XPathExpressionException { + boolean result1 = xpath.evaluateExpression("boolean(/Customers/Customer[@id=3])", doc, Boolean.class); + assertTrue(result1); + } + + /* + * Test return type: numeric. Subtypes supported: Double, Integer and Long + */ + @Test(dataProvider = "document") + public void test05(XPath xpath, Document doc) throws XPathExpressionException { + double result1 = xpath.evaluateExpression("count(/Customers/Customer)", doc, Double.class); + assertTrue(result1 == 3.0); + int result2 = xpath.evaluateExpression("count(/Customers/Customer)", doc, Integer.class); + assertTrue(result2 == 3); + long result3 = xpath.evaluateExpression("count(/Customers/Customer)", doc, Long.class); + assertTrue(result3 == 3); + } + + /* + * Test return type: numeric. Of the subtypes of Number, only Double, + * Integer and Long are required. + */ + @Test(dataProvider = "invalidNumericTypes", expectedExceptions = IllegalArgumentException.class) + public void test06(XPath xpath, Class type) throws XPathExpressionException { + xpath.evaluateExpression("1+1", (Object) null, type); + } + + /* + * Test return type: String. + */ + @Test(dataProvider = "document") + public void test07(XPath xpath, Document doc) throws XPathExpressionException { + String result1 = xpath.evaluateExpression("string(/Customers/Customer[@id=3]/Phone/text())", doc, String.class); + assertTrue(result1.equals("3333333333")); + } + + /* + * Test return type: NodeSet. + */ + @Test(dataProvider = "document") + public void test08(XPath xpath, Document doc) throws XPathExpressionException { + XPathNodes nodes = xpath.evaluateExpression("/Customers/Customer", doc, XPathNodes.class); + assertTrue(nodes.size() == 3); + for (Node n : nodes) { + assertEquals(n.getLocalName(), "Customer"); + } + } + + /* + * Test return type: Node. + */ + @Test(dataProvider = "document") + public void test09(XPath xpath, Document doc) throws XPathExpressionException { + Node n = xpath.evaluateExpression("/Customers/Customer[@id=3]", doc, Node.class); + assertEquals(n.getLocalName(), "Customer"); + } + + /* + * Test return type: Unsupported type. + */ + @Test(dataProvider = "document", expectedExceptions = IllegalArgumentException.class) + public void test10(XPath xpath, Document doc) throws XPathExpressionException { + File n = xpath.evaluateExpression("/Customers/Customer[@id=3]", doc, File.class); + } + + /* + * Test return type: Any::Boolean. + */ + @Test(dataProvider = "document") + public void test11(XPath xpath, Document doc) throws XPathExpressionException { + XPathEvaluationResult result = xpath.evaluateExpression("boolean(/Customers/Customer[@id=3])", doc); + verifyResult(result, true); + } + + /* + * Test return type: Any::Number. + */ + @Test(dataProvider = "document") + public void test12(XPath xpath, Document doc) throws XPathExpressionException { + XPathEvaluationResult result = xpath.evaluateExpression("count(/Customers/Customer)", doc); + verifyResult(result, 3.0); + } + + /* + * Test return type: Any::String. + */ + @Test(dataProvider = "document") + public void test13(XPath xpath, Document doc) throws XPathExpressionException { + XPathEvaluationResult result = xpath.evaluateExpression( + "string(/Customers/Customer[@id=3]/Phone/text())", doc, XPathEvaluationResult.class); + verifyResult(result, "3333333333"); + } + + /* + * Test return type: Any::Nodeset. + */ + @Test(dataProvider = "document") + public void test14(XPath xpath, Document doc) throws XPathExpressionException { + XPathEvaluationResult result = xpath.evaluateExpression("/Customers/Customer", doc); + verifyResult(result, "Customer"); + } + + /* + * Test return type: Any::Node. + */ + @Test(dataProvider = "document") + public void test15(XPath xpath, Document doc) throws XPathExpressionException { + XPathEvaluationResult result = xpath.evaluateExpression("/Customers/Customer[@id=3]", doc); + verifyResult(result, "Customer"); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathExpAnyTypeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathExpAnyTypeTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.xml.xpath; + +import java.io.File; +import javax.xml.xpath.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/* + * @bug 8054196 + * @summary Test for the project XPath: support any type. This test covers the new + * evaluateExpression methods of XPathExpression. + */ +public class XPathExpAnyTypeTest extends XPathTestBase { + + /* + * Check that NPE is thrown when the class type is null. + */ + @Test(dataProvider = "xpath", expectedExceptions = NullPointerException.class) + public void test02(XPath xpath) throws XPathExpressionException { + XPathExpression exp = xpath.compile("1+1"); + double result = exp.evaluateExpression((Object)null, null); + } + + /* + * Parameter item can be null when the expression does not depends on the + * context. + */ + @Test(dataProvider = "xpath") + public void test03(XPath xpath) throws XPathExpressionException { + XPathExpression exp = xpath.compile("1+1"); + int result = exp.evaluateExpression((Object)null, Integer.class); + assertTrue(result == 2); + } + + /* + * Test return type: boolean. + */ + @Test(dataProvider = "document") + public void test04(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("boolean(/Customers/Customer[@id=3])"); + boolean result1 = exp.evaluateExpression(doc, Boolean.class); + assertTrue(result1); + } + + /* + * Test return type: numeric. + */ + @Test(dataProvider = "document") + public void test05(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("count(/Customers/Customer)"); + double result1 = exp.evaluateExpression(doc, Double.class); + assertTrue(result1 == 3.0); + + int result2 = exp.evaluateExpression(doc, Integer.class); + assertTrue(result2 == 3); + } + + /* + * Test return type: String. + */ + @Test(dataProvider = "document") + public void test06(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("string(/Customers/Customer[@id=3]/Phone/text())"); + String result1 = exp.evaluateExpression(doc, String.class); + assertTrue(result1.equals("3333333333")); + } + + /* + * Test return type: NodeSet. + */ + @Test(dataProvider = "document") + public void test07(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("/Customers/Customer"); + XPathNodes nodes = exp.evaluateExpression(doc, XPathNodes.class); + assertTrue(nodes.size() == 3); + for (Node n : nodes) { + assertEquals(n.getLocalName(), "Customer"); + } + } + + /* + * Test return type: Node. + */ + @Test(dataProvider = "document") + public void test08(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("/Customers/Customer[@id=3]"); + Node n = exp.evaluateExpression(doc, Node.class); + assertEquals(n.getLocalName(), "Customer"); + } + + /* + * Test return type: Unsupported type. + */ + @Test(dataProvider = "document", expectedExceptions = IllegalArgumentException.class) + public void test09(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("/Customers/Customer[@id=3]"); + File n = exp.evaluateExpression(doc, File.class); + } + + /* + * Test return type: Any::Boolean. + */ + @Test(dataProvider = "document") + public void test10(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("boolean(/Customers/Customer[@id=3])"); + XPathEvaluationResult result = exp.evaluateExpression(doc); + verifyResult(result, true); + } + + /* + * Test return type: Any::Number. + */ + @Test(dataProvider = "document") + public void test11(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("count(/Customers/Customer)"); + XPathEvaluationResult result = exp.evaluateExpression(doc); + verifyResult(result, 3.0); + } + + /* + * Test return type: Any::String. + */ + @Test(dataProvider = "document") + public void test12(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("string(/Customers/Customer[@id=3]/Phone/text())"); + XPathEvaluationResult result = exp.evaluateExpression(doc, XPathEvaluationResult.class); + verifyResult(result, "3333333333"); + } + + /* + * Test return type: Any::Nodeset. + */ + @Test(dataProvider = "document") + public void test13(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("/Customers/Customer"); + XPathEvaluationResult result = exp.evaluateExpression(doc); + verifyResult(result, "Customer"); + } + + /* + * Test return type: Any::Node. + */ + @Test(dataProvider = "document") + public void test14(XPath xpath, Document doc) throws XPathExpressionException { + XPathExpression exp = xpath.compile("/Customers/Customer[@id=3]"); + XPathEvaluationResult result = exp.evaluateExpression(doc); + verifyResult(result, "Customer"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathTestBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/test/javax/xml/jaxp/unittest/javax/xml/xpath/XPathTestBase.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.xml.xpath; + +import java.io.ByteArrayInputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import javax.xml.parsers.DocumentBuilderFactory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import org.testng.annotations.DataProvider; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/* + * Base class for XPath test + */ +class XPathTestBase { + + static final String rawXML + = "" + + "" + + " " + + " name1" + + " 1111111111" + + " 123@xyz.com" + + "
    " + + " 1111 111st ave" + + " The City" + + " The State" + + "
    " + + "
    " + + " " + + " name1" + + " 2222222222" + + " 123@xyz.com" + + "
    " + + " 2222 222nd ave" + + " The City" + + " The State" + + "
    " + + "
    " + + " " + + " name1" + + " 3333333333" + + " 123@xyz.com" + + "
    " + + " 3333 333rd ave" + + " The City" + + " The State" + + "
    " + + "
    " + + "
    "; + + void verifyResult(XPathEvaluationResult result, Object expected) { + switch (result.type()) { + case BOOLEAN: + assertTrue(((Boolean) result.value()).equals(expected)); + return; + case NUMBER: + assertTrue(((Double) result.value()).equals(expected)); + return; + case STRING: + assertTrue(((String) result.value()).equals(expected)); + return; + case NODESET: + XPathNodes nodes = (XPathNodes) result.value(); + for (Node n : nodes) { + assertEquals(n.getLocalName(), expected); + } + return; + case NODE: + assertTrue(((Node) result.value()).getLocalName().equals(expected)); + return; + } + assertFalse(true, "Unsupported type"); + } + + /* + * DataProvider: XPath object + */ + @DataProvider(name = "xpath") + Object[][] getXPath() { + return new Object[][]{{XPathFactory.newInstance().newXPath()}}; + } + + /* + * DataProvider: Numeric types not supported + */ + @DataProvider(name = "invalidNumericTypes") + Object[][] getInvalidNumericTypes() { + XPath xpath = XPathFactory.newInstance().newXPath(); + return new Object[][]{{xpath, AtomicInteger.class}, + {xpath, AtomicInteger.class}, + {xpath, AtomicLong.class}, + {xpath, BigDecimal.class}, + {xpath, BigInteger.class}, + {xpath, Byte.class}, + {xpath, Float.class}, + {xpath, Short.class} + }; + } + + /* + * DataProvider: XPath and Document objects + */ + @DataProvider(name = "document") + Object[][] getDocument() throws Exception { + DocumentBuilderFactory dBF = DocumentBuilderFactory.newInstance(); + dBF.setValidating(false); + dBF.setNamespaceAware(true); + Document doc = dBF.newDocumentBuilder().parse( + new ByteArrayInputStream(rawXML.getBytes("UTF-8"))); + + return new Object[][]{{XPathFactory.newInstance().newXPath(), doc}}; + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jaxws/.hgtags --- a/jaxws/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxws/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -293,3 +293,4 @@ e529374fbe526dbd668e5e98fc047b42b3bc6d33 jdk9-b45 64ca52b0bda8028636e4ccafbe1107befcdda47d jdk9-b46 6c17d648d03e4bf4729c3645f8db55d34115e0b7 jdk9-b47 +33e7e699804892c0496adf60ad67cc12855aeb61 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 jaxws/src/jdk.xml.bind/share/classes/com/sun/tools/internal/jxc/ap/SchemaGenerator.java --- a/jaxws/src/jdk.xml.bind/share/classes/com/sun/tools/internal/jxc/ap/SchemaGenerator.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jaxws/src/jdk.xml.bind/share/classes/com/sun/tools/internal/jxc/ap/SchemaGenerator.java Wed Jul 05 20:17:28 2017 +0200 @@ -135,7 +135,7 @@ private void filterClass(List classes, Collection elements) { for (Element element : elements) { - if (element.getKind().equals(ElementKind.CLASS)) { + if (element.getKind().equals(ElementKind.CLASS) || element.getKind().equals(ElementKind.ENUM)) { classes.add(new Reference((TypeElement) element, processingEnv)); filterClass(classes, ElementFilter.typesIn(element.getEnclosedElements())); } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/.hgtags --- a/jdk/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -290,3 +290,4 @@ 9acaa4f57b0b9e3757a7b4576ca9418a75ea8287 jdk9-b45 efedac7f44ed41cea2b1038138047271f55aacba jdk9-b46 b641c14730ac05d9ec8b4f66e6fca3dc21adb403 jdk9-b47 +ebb2eb7f1aec78eb6d8cc4c96f018afa11093cde jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/Tools.gmk --- a/jdk/make/Tools.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/Tools.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -34,28 +34,23 @@ include NativeCompilation.gmk include SetupJavaCompilers.gmk -# The exception handling of swing beaninfo which have the own tool directory -ifeq (, $(BUILD_TOOLS_JDK)) - $(eval $(call SetupJavaCompilation,BUILD_TOOLS_JDK, \ - SETUP := GENERATE_OLDBYTECODE, \ - ADD_JAVAC_FLAGS := "-Xbootclasspath/p:$(BUILDTOOLS_OUTPUTDIR)/interim_jimage_classes", \ - SRC := $(JDK_TOPDIR)/make/src/classes, \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes, \ - COPY := boot.modules ext.modules)) -endif +################################################################################ + +$(eval $(call SetupJavaCompilation,BUILD_TOOLS_JDK, \ + SETUP := GENERATE_OLDBYTECODE, \ + ADD_JAVAC_FLAGS := "-Xbootclasspath/p:$(BUILDTOOLS_OUTPUTDIR)/interim_jimage_classes", \ + SRC := $(JDK_TOPDIR)/make/src/classes, \ + BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes, \ + COPY := boot.modules ext.modules)) -$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes/build/tools/generatenimbus/resources/%.template: \ - $(JDK_TOPDIR)/src/java.desktop/share/classes/javax/swing/plaf/nimbus/%.template - $(call install-file) - -BUILD_TOOLS_JDK += $(foreach i, $(wildcard $(JDK_TOPDIR)/src/java.desktop/share/classes/javax/swing/plaf/nimbus/*.template), $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes/build/tools/generatenimbus/resources/$(notdir $i)) +$(eval $(call SetupCopyFiles,COPY_NIMBUS_TEMPLATES, \ + SRC := $(JDK_TOPDIR)/src/java.desktop/share/classes/javax/swing/plaf/nimbus, \ + DEST := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes/build/tools/generatenimbus/resources, \ + FILES := $(wildcard $(JDK_TOPDIR)/src/java.desktop/share/classes/javax/swing/plaf/nimbus/*.template))) -# Resource used by CheckDeps tool -$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes/build/tools/deps/refs.allowed: \ - $(JDK_TOPDIR)/make/data/checkdeps/refs.allowed - $(call install-file) +BUILD_TOOLS_JDK += $(COPY_NIMBUS_TEMPLATES) -BUILD_TOOLS_JDK += $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes/build/tools/deps/refs.allowed +################################################################################ # Add a checksum ("jsum") to the end of a text file. Prevents trivial tampering with class lists. TOOL_ADDJSUM = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ @@ -130,10 +125,6 @@ TOOL_CLDRCONVERTER = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.cldrconverter.CLDRConverter -TOOL_CHECKDEPS = $(JAVA_SMALL) -Xbootclasspath/p:$(INTERIM_LANGTOOLS_JAR) \ - -cp "$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes$(PATH_SEP)$(JDK_OUTPUTDIR)" \ - build.tools.deps.CheckDeps - TOOL_GENMODULESXML = $(JAVA_SMALL) -Xbootclasspath/p:$(INTERIM_LANGTOOLS_JAR) \ -cp "$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes$(PATH_SEP)$(JDK_OUTPUTDIR)" \ build.tools.module.GenJdepsModulesXml @@ -161,25 +152,25 @@ # Tools needed on solaris because OBJCOPY is broken. ifeq ($(OPENJDK_TARGET_OS), solaris) -$(eval $(call SetupNativeCompilation,ADD_GNU_DEBUGLINK, \ - SRC := $(JDK_TOPDIR)/make/src/native/add_gnu_debuglink, \ - LANG := C, \ - CC := $(BUILD_CC), \ - LDEXE := $(BUILD_LD), \ - LDFLAGS := -lelf, \ - OBJECT_DIR := $(BUILDTOOLS_OUTPUTDIR)/objs/add_gnu_debuglink, \ - OUTPUT_DIR := $(BUILDTOOLS_OUTPUTDIR)/bin, \ - PROGRAM := add_gnu_debuglink)) + $(eval $(call SetupNativeCompilation,ADD_GNU_DEBUGLINK, \ + SRC := $(JDK_TOPDIR)/make/src/native/add_gnu_debuglink, \ + LANG := C, \ + CC := $(BUILD_CC), \ + LDEXE := $(BUILD_LD), \ + LDFLAGS := -lelf, \ + OBJECT_DIR := $(BUILDTOOLS_OUTPUTDIR)/objs/add_gnu_debuglink, \ + OUTPUT_DIR := $(BUILDTOOLS_OUTPUTDIR)/bin, \ + PROGRAM := add_gnu_debuglink)) -$(eval $(call SetupNativeCompilation,FIX_EMPTY_SEC_HDR_FLAGS, \ - SRC := $(JDK_TOPDIR)/make/src/native/fix_empty_sec_hdr_flags, \ - LANG := C, \ - CC := $(BUILD_CC), \ - LDEXE := $(BUILD_LD), \ - LDFLAGS := -lelf, \ - OBJECT_DIR := $(BUILDTOOLS_OUTPUTDIR)/objs/fix_empty_sec_hdr_flags, \ - OUTPUT_DIR := $(BUILDTOOLS_OUTPUTDIR)/bin, \ - PROGRAM := fix_empty_sec_hdr_flags)) + $(eval $(call SetupNativeCompilation,FIX_EMPTY_SEC_HDR_FLAGS, \ + SRC := $(JDK_TOPDIR)/make/src/native/fix_empty_sec_hdr_flags, \ + LANG := C, \ + CC := $(BUILD_CC), \ + LDEXE := $(BUILD_LD), \ + LDFLAGS := -lelf, \ + OBJECT_DIR := $(BUILDTOOLS_OUTPUTDIR)/objs/fix_empty_sec_hdr_flags, \ + OUTPUT_DIR := $(BUILDTOOLS_OUTPUTDIR)/bin, \ + PROGRAM := fix_empty_sec_hdr_flags)) endif $(BUILD_TOOLS_JDK): $(BUILD_INTERIM_JIMAGE) diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/data/checkdeps/refs.allowed --- a/jdk/make/data/checkdeps/refs.allowed Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -# -# This properties-formatted file contains the names of the non-existent types -# that are allowed to be referenced from classes in a profiles image. -# -# The property key is a type that does not exist. The property value is one or -# more types that reference the missing type. The property value also encodes -# the names of the profiles where this reference is allowed. - -# jsse.jar is not subsetted by the profiles build. For compact1 and compact2 -# then this means that there are references to Kerberos types that do not -# exist. These references are harmless. -# -javax.security.auth.kerberos.KerberosKey=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -javax.security.auth.kerberos.KerberosPrincipal=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -javax.security.auth.kerberos.KerberosTicket=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -javax.security.auth.kerberos.KeyTab=sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -javax.security.auth.kerberos.ServicePermission=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -sun.security.jgss.GSSCaller=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -sun.security.jgss.krb5.Krb5Util=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2 -sun.security.jgss.krb5.ServiceCreds=sun.security.ssl.krb5.Krb5ProxyImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.EncryptedData= sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.EncryptionKey=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.internal.crypto.KeyUsage=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.internal.EncTicketPart=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.internal.Krb5=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.internal.Ticket=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.KrbException=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.PrincipalName=sun.security.ssl.krb5.Krb5ProxyImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 -sun.security.krb5.Realm=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2 - -# Residual references to java.beans. -# The RemoveMethods tool does not yet purge the constant pool. -# -java.beans.PropertyChangeListener=java.util.logging.LogManager,compact1,compact2,compact3 diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/gensrc/GensrcMisc.gmk --- a/jdk/make/gensrc/GensrcMisc.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/gensrc/GensrcMisc.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -28,6 +28,13 @@ # string and the runtime name into the Version.java file. # To be printed by java -version +# These dependencies should ideally be added to prerequesites for Version.java +# but skip for now until we have better incremental build for java. +# $(call DependOnVariable, LAUNCHER_NAME) \ +# $(call DependOnVariable, RELEASE) \ +# $(call DependOnVariable, FULL_VERSION) \ +# $(call DependOnVariable, RUNTIME_VERSION) + $(SUPPORT_OUTPUTDIR)/gensrc/java.base/sun/misc/Version.java: \ $(JDK_TOPDIR)/src/java.base/share/classes/sun/misc/Version.java.template $(MKDIR) -p $(@D) diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/lib/CoreLibraries.gmk --- a/jdk/make/lib/CoreLibraries.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/lib/CoreLibraries.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -74,8 +74,6 @@ ########################################################################################## -BUILD_LIBVERIFY_SRC := check_code.c check_format.c - ifeq ($(OPENJDK_TARGET_OS), solaris) ifneq ($(OPENJDK_TARGET_CPU), x86_64) BUILD_LIBVERIFY_REORDER := $(JDK_TOPDIR)/make/mapfiles/libverify/reorder-$(OPENJDK_TARGET_CPU) @@ -116,10 +114,6 @@ LIBJAVA_SRC_DIRS := $(call FindSrcDirsForLib, java.base, java) -ifeq ($(OPENJDK_TARGET_OS), macosx) - LIBJAVA_EXCLUDE_FILES += $(JDK_TOPDIR)/src/java.base/unix/native/libjava/HostLocaleProviderAdapter_md.c -endif - LIBJAVA_CFLAGS := $(addprefix -I, $(LIBJAVA_SRC_DIRS)) \ -I$(JDK_TOPDIR)/src/java.base/share/native/libfdlibm \ -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ @@ -134,9 +128,7 @@ LIBJAVA_CFLAGS += -DJDK_UPDATE_VERSION='"$(JDK_UPDATE_VERSION)"' endif -ifneq ($(OPENJDK_TARGET_OS), macosx) - LIBJAVA_EXCLUDE_FILES += java_props_macosx.c -else +ifeq ($(OPENJDK_TARGET_OS), macosx) BUILD_LIBJAVA_java_props_md.c_CFLAGS := -x objective-c BUILD_LIBJAVA_java_props_macosx.c_CFLAGS := -x objective-c endif @@ -151,8 +143,6 @@ LIBRARY := java, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBJAVA_SRC_DIRS), \ - EXCLUDES := fdlibm/src zip prefs, \ - EXCLUDE_FILES := $(LIBJAVA_EXCLUDE_FILES), \ LANG := C, \ OPTIMIZATION := HIGH, \ CFLAGS := $(CFLAGS_JDKLIB) \ @@ -247,19 +237,10 @@ ########################################################################################## -BUILD_LIBJLI_SRC_DIRS := $(JDK_TOPDIR)/src/java.base/share/native/libjli \ - $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli +LIBJLI_SRC_DIRS := $(call FindSrcDirsForLib, java.base, jli) LIBJLI_CFLAGS := $(CFLAGS_JDKLIB) -BUILD_LIBJLI_FILES := \ - java.c \ - splashscreen_stubs.c \ - parse_manifest.c \ - version_comp.c \ - wildcard.c \ - jli_util.c - ifeq ($(JVM_VARIANT_ZERO), true) ERGO_FAMILY := zero else @@ -269,68 +250,55 @@ ERGO_FAMILY := $(OPENJDK_TARGET_CPU_ARCH) endif endif +LIBJLI_ALL_ERGO := $(wildcard $(addsuffix /ergo_*.c, $(LIBJLI_SRC_DIRS))) +LIBJLI_EXCLUDE_ERGO := $(filter-out %/ergo_$(ERGO_FAMILY).c, $(LIBJLI_ALL_ERGO)) +# If all specialized ergo files are excluded, use generic ergo +ifeq ($(LIBJLI_ALL_ERGO), $(LIBJLI_EXCLUDE_ERGO)) + LIBJLI_CFLAGS += -DUSE_GENERIC_ERGO +endif +LIBJLI_EXCLUDE_FILES += $(notdir $(LIBJLI_EXCLUDE_ERGO)) ifeq ($(OPENJDK_TARGET_OS), macosx) - BUILD_LIBJLI_SRC_DIRS += $(JDK_TOPDIR)/src/java.base/macosx/native/libjli - BUILD_LIBJLI_FILES += java_md_common.c java_md_macosx.c + LIBJLI_EXCLUDE_FILES += java_md_solinux.c ergo.c BUILD_LIBJLI_java_md_macosx.c_CFLAGS := -x objective-c BUILD_LIBJLI_STATIC_java_md_macosx.c_CFLAGS := -x objective-c + + LIBJLI_CFLAGS += -DPACKAGE_PATH=\"$(PACKAGE_PATH)\" endif ifeq ($(OPENJDK_TARGET_OS), windows) - BUILD_LIBJLI_FILES += java_md.c \ - cmdtoargs.c # Staticically link with c runtime on windows. LIBJLI_CFLAGS := $(filter-out -MD, $(LIBJLI_CFLAGS)) -else ifneq ($(OPENJDK_TARGET_OS), macosx) - - BUILD_LIBJLI_FILES += java_md_common.c - BUILD_LIBJLI_FILES += java_md_solinux.c ergo.c - - ERGO_ARCH_FILE = ergo_$(ERGO_FAMILY).c - - # if the architecture specific ergo file exists then - # use it, else use the generic definitions from ergo.c - ifneq ($(wildcard $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli/$(ERGO_ARCH_FILE)), ) - BUILD_LIBJLI_FILES += $(ERGO_ARCH_FILE) - else # !ERGO_ARCH_FILE - LIBJLI_CFLAGS += -DUSE_GENERIC_ERGO - endif # ERGO_ARCH_FILE -endif #WINDOWS - -LIBJLI_CFLAGS += $(foreach dir, $(BUILD_LIBJLI_SRC_DIRS), -I$(dir)) - -# Append defines depending on target platform -LIBJLI_CFLAGS += $(OPENJDK_TARGET_CPU_JLI_CFLAGS) - -ifeq ($(OPENJDK_TARGET_OS), macosx) - LIBJLI_CFLAGS += -DPACKAGE_PATH=\"$(PACKAGE_PATH)\" -endif - -ifneq ($(USE_EXTERNAL_LIBZ), true) - BUILD_LIBJLI_SRC_DIRS += $(JDK_TOPDIR)/src/java.base/share/native/libzip/zlib-1.2.8 - LIBJLI_CFLAGS += $(ZLIB_CPPFLAGS) - BUILD_LIBJLI_FILES += \ - inflate.c \ - inftrees.c \ - inffast.c \ - zadler32.c \ - zcrc32.c \ - zutil.c -endif - -ifeq ($(OPENJDK_TARGET_OS), windows) LIBJLI_OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE) else LIBJLI_OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE)/jli endif +LIBJLI_CFLAGS += $(addprefix -I, $(LIBJLI_SRC_DIRS)) + +# Append defines depending on target platform +LIBJLI_CFLAGS += $(OPENJDK_TARGET_CPU_JLI_CFLAGS) + +ifneq ($(USE_EXTERNAL_LIBZ), true) + LIBJLI_CFLAGS += $(ZLIB_CPPFLAGS) + LIBJLI_EXTRA_FILES += \ + $(addprefix $(JDK_TOPDIR)/src/java.base/share/native/libzip/zlib-1.2.8/, \ + inflate.c \ + inftrees.c \ + inffast.c \ + zadler32.c \ + zcrc32.c \ + zutil.c \ + ) +endif + $(eval $(call SetupNativeCompilation,BUILD_LIBJLI, \ LIBRARY := jli, \ OUTPUT_DIR := $(LIBJLI_OUTPUT_DIR), \ - SRC := $(BUILD_LIBJLI_SRC_DIRS), \ - INCLUDE_FILES := $(BUILD_LIBJLI_FILES), \ + SRC := $(LIBJLI_SRC_DIRS), \ + EXCLUDE_FILES := $(LIBJLI_EXCLUDE_FILES), \ + EXTRA_FILES := $(LIBJLI_EXTRA_FILES), \ LANG := C, \ OPTIMIZATION := HIGH, \ CFLAGS := $(LIBJLI_CFLAGS), \ @@ -376,8 +344,9 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJLI_STATIC, \ STATIC_LIBRARY := jli_static, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE), \ - SRC := $(BUILD_LIBJLI_SRC_DIRS), \ - INCLUDE_FILES := $(BUILD_LIBJLI_FILES), \ + SRC := $(LIBJLI_SRC_DIRS), \ + EXCLUDE_FILES := $(LIBJLI_EXCLUDE_FILES), \ + EXTRA_FILES := $(LIBJLI_EXTRA_FILES), \ LANG := C, \ OPTIMIZATION := HIGH, \ CFLAGS := $(STATIC_LIBRARY_FLAGS) $(LIBJLI_CFLAGS), \ @@ -395,8 +364,9 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJLI_STATIC, \ LIBRARY := jli_static, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE), \ - SRC := $(BUILD_LIBJLI_SRC_DIRS), \ - INCLUDE_FILES := $(BUILD_LIBJLI_FILES), \ + SRC := $(LIBJLI_SRC_DIRS), \ + EXCLUDE_FILES := $(LIBJLI_EXCLUDE_FILES), \ + EXTRA_FILES := $(LIBJLI_EXTRA_FILES), \ LANG := C, \ OPTIMIZATION := HIGH, \ CFLAGS := $(CFLAGS_JDKLIB) $(LIBJLI_CFLAGS), \ @@ -411,16 +381,17 @@ else ifeq ($(OPENJDK_TARGET_OS), aix) # AIX also requires a static libjli because the compiler doesn't support '-rpath' - $(eval $(call SetupNativeCompilation,BUILD_LIBJLI_STATIC,\ - STATIC_LIBRARY:=jli_static,\ - OUTPUT_DIR:=$(SUPPORT_OUTPUTDIR)/native/$(MODULE),\ - SRC:=$(BUILD_LIBJLI_SRC_DIRS),\ - INCLUDE_FILES:=$(BUILD_LIBJLI_FILES),\ - LANG:=C,\ - OPTIMIZATION:=HIGH, \ - CFLAGS:=$(STATIC_LIBRARY_FLAGS) $(LIBJLI_CFLAGS),\ - ARFLAGS:=$(ARFLAGS),\ - OBJECT_DIR:=$(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static)) + $(eval $(call SetupNativeCompilation,BUILD_LIBJLI_STATIC, \ + STATIC_LIBRARY := jli_static, \ + OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE), \ + SRC := $(LIBJLI_SRC_DIRS), \ + EXCLUDE_FILES := $(LIBJLI_EXCLUDE_FILES), \ + EXTRA_FILES := $(LIBJLI_EXTRA_FILES), \ + LANG := C, \ + OPTIMIZATION := HIGH, \ + CFLAGS := $(STATIC_LIBRARY_FLAGS) $(LIBJLI_CFLAGS), \ + ARFLAGS := $(ARFLAGS), \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static)) TARGETS += $(BUILD_LIBJLI_STATIC) diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/lib/Lib-jdk.attach.gmk --- a/jdk/make/lib/Lib-jdk.attach.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/lib/Lib-jdk.attach.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -31,7 +31,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBATTACH, \ LIBRARY := attach, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ - SRC := $(JDK_TOPDIR)/src/jdk.attach/$(OPENJDK_TARGET_OS)/native/libattach, \ + SRC := $(call FindSrcDirsForLib, jdk.attach, attach), \ LANG := C, \ OPTIMIZATION := LOW, \ CFLAGS := $(CFLAGS_JDKLIB) $(CFLAGS_WARNINGS_ARE_ERRORS) \ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/lib/Lib-jdk.security.auth.gmk --- a/jdk/make/lib/Lib-jdk.security.auth.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/lib/Lib-jdk.security.auth.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -28,9 +28,7 @@ ################################################################################ LIBJAAS_MAPFILE := -ifneq ($(OPENJDK_TARGET_OS), solaris) - LIBJAAS_EXCLUDE_FILES := Solaris.c -else +ifeq ($(OPENJDK_TARGET_OS), solaris) # only on solaris...wonder why LIBJAAS_MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libjaas/mapfile-vers endif @@ -43,7 +41,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBJAAS, \ LIBRARY := $(LIBJAAS_NAME), \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ - SRC := $(JDK_TOPDIR)/src/jdk.security.auth/$(OPENJDK_TARGET_OS_TYPE)/native/libjaas, \ + SRC := $(call FindSrcDirsForLib, jdk.security.auth, jaas), \ LANG := C, \ OPTIMIZATION := LOW, \ CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/jdk.security.auth, \ @@ -53,7 +51,6 @@ LDFLAGS_windows := netapi32.lib user32.lib mpr.lib advapi32.lib, \ LDFLAGS_SUFFIX_windows := $(LDFLAGS_JDKLIB_SUFFIX), \ LDFLAGS_SUFFIX_solaris := -lc, \ - EXCLUDE_FILES := $(LIBJAAS_EXCLUDE_FILES), \ VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ RC_FLAGS := $(RC_FLAGS) \ -D "JDK_FNAME=$(LIBJAAS_NAME).dll" \ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/lib/NetworkingLibraries.gmk --- a/jdk/make/lib/NetworkingLibraries.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/lib/NetworkingLibraries.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -23,39 +23,16 @@ # questions. # -LIBNET_SRC_DIRS := $(JDK_TOPDIR)/src/java.base/share/native/libnet \ - $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libnet -LIBNET_CFLAGS += -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ - $(LIBJAVA_HEADER_FLAGS) - -LIBNET_CFLAGS += $(foreach dir, $(LIBNET_SRC_DIRS), -I$(dir)) - -LIBNET_EXCLUDE_FILES := -ifneq ($(OPENJDK_TARGET_OS), solaris) - LIBNET_EXCLUDE_FILES += solaris_close.c -endif - -ifneq ($(OPENJDK_TARGET_OS), linux) - LIBNET_EXCLUDE_FILES += linux_close.c -endif - -ifneq ($(OPENJDK_TARGET_OS), macosx) - LIBNET_EXCLUDE_FILES += bsd_close.c -endif - -ifeq ($(OPENJDK_TARGET_OS), aix) - LIBNET_SRC_DIRS += $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libnet/java/net/ -endif +LIBNET_SRC_DIRS := $(call FindSrcDirsForLib, java.base, net) $(eval $(call SetupNativeCompilation,BUILD_LIBNET, \ LIBRARY := net, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBNET_SRC_DIRS), \ - EXCLUDE_FILES := $(LIBNET_EXCLUDE_FILES), \ LANG := C, \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) \ - $(LIBNET_CFLAGS), \ + CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ + $(LIBJAVA_HEADER_FLAGS) $(addprefix -I, $(LIBNET_SRC_DIRS)), \ MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libnet/mapfile-vers, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/lib/NioLibraries.gmk --- a/jdk/make/lib/NioLibraries.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/lib/NioLibraries.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -65,7 +65,6 @@ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(BUILD_LIBNIO_SRC), \ EXCLUDE_FILES := $(BUILD_LIBNIO_EXFILES), \ - EXCLUDES := sctp, \ LANG := C, \ OPTIMIZATION := HIGH, \ CFLAGS := $(CFLAGS_JDKLIB) \ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/mapfiles/libjava/mapfile-vers --- a/jdk/make/mapfiles/libjava/mapfile-vers Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/make/mapfiles/libjava/mapfile-vers Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2015, 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 @@ -214,10 +214,10 @@ Java_java_lang_Throwable_fillInStackTrace; Java_java_lang_Throwable_getStackTraceDepth; Java_java_lang_Throwable_getStackTraceElement; - Java_java_lang_UNIXProcess_init; - Java_java_lang_UNIXProcess_waitForProcessExit; - Java_java_lang_UNIXProcess_forkAndExec; - Java_java_lang_UNIXProcess_destroyProcess; + Java_java_lang_ProcessImpl_init; + Java_java_lang_ProcessImpl_waitForProcessExit; + Java_java_lang_ProcessImpl_forkAndExec; + Java_java_lang_ProcessImpl_destroyProcess; Java_java_nio_Bits_copyFromShortArray; Java_java_nio_Bits_copyToShortArray; Java_java_nio_Bits_copyFromIntArray; diff -r ac59fe6cd98f -r 4746a63313f6 jdk/make/src/classes/build/tools/deps/CheckDeps.java --- a/jdk/make/src/classes/build/tools/deps/CheckDeps.java Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2013, 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.deps; - -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.charset.StandardCharsets; -import java.util.Set; -import java.util.HashSet; -import java.util.Map; -import java.util.HashMap; -import java.util.Enumeration; -import java.util.Properties; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; - -import com.sun.tools.classfile.ClassFile; -import com.sun.tools.classfile.Dependencies; -import com.sun.tools.classfile.Dependency; - -/** - * A simple tool to check the JAR files in a JRE image to ensure that there - * aren't any references to types that do not exist. The tool is intended to - * be used in the JDK "profiles" build to help ensure that the profile - * definitions are kept up to date. - */ - -public class CheckDeps { - - // classfile API for finding dependencies - static final Dependency.Finder finder = Dependencies.getClassDependencyFinder(); - - // "known types", found in rt.jar or other JAR files - static final Set knownTypes = new HashSet<>(); - - // References to unknown types. The map key is the unknown type, the - // map value is the set of classes that reference it. - static final Map> unknownRefs = new HashMap<>(); - - // The property name is the name of an unknown type that is allowed to be - // references. The property value is a comma separated list of the types - // that are allowed to reference it. The list also includes the names of - // the profiles that the reference is allowed. - static final Properties allowedBadRefs = new Properties(); - - /** - * Returns the class name for the given class file. In the case of inner - * classes then the enclosing class is returned in order to keep the - * rules simple. - */ - static String toClassName(String s) { - int i = s.indexOf('$'); - if (i > 0) - s = s.substring(0, i); - return s.replace("/", "."); - } - - /** - * Analyze the dependencies of all classes in the given JAR file. The - * method updates knownTypes and unknownRefs as part of the analysis. - */ - static void analyzeDependencies(Path jarpath) throws Exception { - System.out.format("Analyzing %s%n", jarpath); - try (JarFile jf = new JarFile(jarpath.toFile())) { - Enumeration entries = jf.entries(); - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - String name = e.getName(); - if (name.endsWith(".class")) { - ClassFile cf = ClassFile.read(jf.getInputStream(e)); - for (Dependency d : finder.findDependencies(cf)) { - String origin = toClassName(d.getOrigin().getName()); - String target = toClassName(d.getTarget().getName()); - - // origin is now known - unknownRefs.remove(origin); - knownTypes.add(origin); - - // if the target is not known then record the reference - if (!knownTypes.contains(target)) { - Set refs = unknownRefs.get(target); - if (refs == null) { - // first time seeing this unknown type - refs = new HashSet<>(); - unknownRefs.put(target, refs); - } - refs.add(origin); - } - } - } - } - } - } - - /** - * We have closure (no references to types that do not exist) if - * unknownRefs is empty. When unknownRefs is not empty then it should - * only contain references that are allowed to be present (these are - * loaded from the refs.allowed properties file). - * - * @param the profile that is being tested, this determines the exceptions - * in {@code allowedBadRefs} that apply. - * - * @return {@code true} if there are no missing types or the only references - * to missing types are described by {@code allowedBadRefs}. - */ - static boolean checkClosure(String profile) { - // process the references to types that do not exist. - boolean fail = false; - for (Map.Entry> entry: unknownRefs.entrySet()) { - String target = entry.getKey(); - for (String origin: entry.getValue()) { - // check if origin -> target allowed - String value = allowedBadRefs.getProperty(target); - if (value == null) { - System.err.format("%s -> %s (unknown type)%n", origin, target); - fail = true; - } else { - // target is known, check if the origin is one that we - // expect and that the exception applies to the profile. - boolean found = false; - boolean applicable = false; - for (String s: value.split(",")) { - s = s.trim(); - if (s.equals(origin)) - found = true; - if (s.equals(profile)) - applicable = true; - } - if (!found || !applicable) { - if (!found) { - System.err.format("%s -> %s (not allowed)%n", origin, target); - } else { - System.err.format("%s -> %s (reference not applicable to %s)%n", - origin, target, profile); - } - fail = true; - } - } - - } - } - - return !fail; - } - - static void fail(URL url) throws Exception { - System.err.println("One or more unexpected references encountered"); - if (url != null) - System.err.format("Check %s is up to date%n", Paths.get(url.toURI())); - System.exit(-1); - } - - public static void main(String[] args) throws Exception { - // load properties file so that we know what missing types that are - // allowed to be referenced. - URL url = CheckDeps.class.getResource("refs.allowed"); - if (url != null) { - try (InputStream in = url.openStream()) { - allowedBadRefs.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - } - } - - if (args.length != 2) { - System.err.println("Usage: java CheckDeps "); - System.exit(-1); - } - - String image = args[0]; - String profile = args[1]; - - // process JAR files on boot class path - Path lib = Paths.get(image, "lib"); - try (DirectoryStream stream = Files.newDirectoryStream(lib, "*.jar")) { - for (Path jarpath: stream) { - analyzeDependencies(jarpath); - } - } - - // classes on boot class path should not reference other types - boolean okay = checkClosure(profile); - if (!okay) - fail(url); - - // process JAR files in the extensions directory - try (DirectoryStream stream = Files.newDirectoryStream(lib.resolve("ext"), "*.jar")) { - for (Path jarpath: stream) { - analyzeDependencies(jarpath); - } - } - - // re-check to ensure that the extensions doesn't reference types that - // do not exist. - okay = checkClosure(profile); - if (!okay) - fail(url); - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/aix/native/libnet/aix_close.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/aix/native/libnet/aix_close.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2001, 2013, 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. + */ + +/* + * This file contains implementations of NET_... functions. The NET_.. functions are + * wrappers for common file- and socket functions plus provisions for non-blocking IO. + * + * (basically, the layers remember all file descriptors waiting for a particular fd; + * all threads waiting on a certain fd can be woken up by sending them a signal; this + * is done e.g. when the fd is closed.) + * + * This was originally copied from the linux_close.c implementation. + * + * Side Note: This coding needs initialization. Under Linux this is done + * automatically via __attribute((constructor)), on AIX this is done manually + * (see aix_close_init). + * + */ + +/* + AIX needs a workaround for I/O cancellation, see: + http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/close.htm + ... + The close subroutine is blocked until all subroutines which use the file + descriptor return to usr space. For example, when a thread is calling close + and another thread is calling select with the same file descriptor, the + close subroutine does not return until the select call returns. + ... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Stack allocated by thread when doing blocking operation + */ +typedef struct threadEntry { + pthread_t thr; /* this thread */ + struct threadEntry *next; /* next thread */ + int intr; /* interrupted */ +} threadEntry_t; + +/* + * Heap allocated during initialized - one entry per fd + */ +typedef struct { + pthread_mutex_t lock; /* fd lock */ + threadEntry_t *threads; /* threads blocked on fd */ +} fdEntry_t; + +/* + * Signal to unblock thread + */ +static int sigWakeup = (SIGRTMAX - 1); + +/* + * The fd table and the number of file descriptors + */ +static fdEntry_t *fdTable = NULL; +static int fdCount = 0; + +/* + * Null signal handler + */ +static void sig_wakeup(int sig) { +} + +/* + * Initialization routine (executed when library is loaded) + * Allocate fd tables and sets up signal handler. + * + * On AIX we don't have __attribute((constructor)) so we need to initialize + * manually (from JNI_OnLoad() in 'src/share/native/java/net/net_util.c') + */ +void aix_close_init() { + struct rlimit nbr_files; + sigset_t sigset; + struct sigaction sa; + + /* Check already initialized */ + if (fdCount > 0 && fdTable != NULL) { + return; + } + + /* + * Allocate table based on the maximum number of + * file descriptors. + */ + if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) { + fprintf(stderr, "library initialization failed - " + "unable to get max # of allocated fds\n"); + abort(); + } + fdCount = nbr_files.rlim_max; + /* + * We have a conceptual problem here, when the number of files is + * unlimited. As a kind of workaround, we ensure the table is big + * enough for handle even a large number of files. Since SAP itself + * recommends a limit of 32000 files, we just use 64000 as 'infinity'. + */ + if (nbr_files.rlim_max == RLIM_INFINITY) { + fdCount = 64000; + } + fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); + if (fdTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor table - out of memory"); + abort(); + } + + { + int i; + for (i=0; i < fdCount; i++) { + pthread_mutex_init(&fdTable[i].lock, NULL); + } + } + + /* + * Setup the signal handler + */ + sa.sa_handler = sig_wakeup; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sigWakeup, &sa, NULL); + + sigemptyset(&sigset); + sigaddset(&sigset, sigWakeup); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +} + +/* + * Return the fd table for this fd or NULL is fd out + * of range. + */ +static inline fdEntry_t *getFdEntry(int fd) +{ + if (fd < 0 || fd >= fdCount) { + return NULL; + } + return &fdTable[fd]; +} + +/* + * Start a blocking operation :- + * Insert thread onto thread list for the fd. + */ +static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) +{ + self->thr = pthread_self(); + self->intr = 0; + + pthread_mutex_lock(&(fdEntry->lock)); + { + self->next = fdEntry->threads; + fdEntry->threads = self; + } + pthread_mutex_unlock(&(fdEntry->lock)); +} + +/* + * End a blocking operation :- + * Remove thread from thread list for the fd + * If fd has been interrupted then set errno to EBADF + */ +static inline void endOp + (fdEntry_t *fdEntry, threadEntry_t *self) +{ + int orig_errno = errno; + pthread_mutex_lock(&(fdEntry->lock)); + { + threadEntry_t *curr, *prev=NULL; + curr = fdEntry->threads; + while (curr != NULL) { + if (curr == self) { + if (curr->intr) { + orig_errno = EBADF; + } + if (prev == NULL) { + fdEntry->threads = curr->next; + } else { + prev->next = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + } + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; +} + +/* + * Close or dup2 a file descriptor ensuring that all threads blocked on + * the file descriptor are notified via a wakeup signal. + * + * fd1 < 0 => close(fd2) + * fd1 >= 0 => dup2(fd1, fd2) + * + * Returns -1 with errno set if operation fails. + */ +static int closefd(int fd1, int fd2) { + int rv, orig_errno; + fdEntry_t *fdEntry = getFdEntry(fd2); + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Lock the fd to hold-off additional I/O on this fd. + */ + pthread_mutex_lock(&(fdEntry->lock)); + + { + /* On fast machines we see that we enter dup2 before the + * accepting thread had a chance to get and process the signal. + * So in case we woke a thread up, give it some time to cope. + * Also see https://bugs.openjdk.java.net/browse/JDK-8006395 */ + int num_woken = 0; + + /* + * Send a wakeup signal to all threads blocked on this + * file descriptor. + */ + threadEntry_t *curr = fdEntry->threads; + while (curr != NULL) { + curr->intr = 1; + pthread_kill( curr->thr, sigWakeup ); + num_woken ++; + curr = curr->next; + } + + if (num_woken > 0) { + usleep(num_woken * 50); + } + + /* + * And close/dup the file descriptor + * (restart if interrupted by signal) + */ + do { + if (fd1 < 0) { + rv = close(fd2); + } else { + rv = dup2(fd1, fd2); + } + } while (rv == -1 && errno == EINTR); + } + + /* + * Unlock without destroying errno + */ + orig_errno = errno; + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; + + return rv; +} + +/* + * Wrapper for dup2 - same semantics as dup2 system call except + * that any threads blocked in an I/O system call on fd2 will be + * preempted and return -1/EBADF; + */ +int NET_Dup2(int fd, int fd2) { + if (fd < 0) { + errno = EBADF; + return -1; + } + return closefd(fd, fd2); +} + +/* + * Wrapper for close - same semantics as close system call + * except that any threads blocked in an I/O on fd will be + * preempted and the I/O system call will return -1/EBADF. + */ +int NET_SocketClose(int fd) { + return closefd(-1, fd); +} + +/************** Basic I/O operations here ***************/ + +/* + * Macro to perform a blocking IO operation. Restarts + * automatically if interrupted by signal (other than + * our wakeup signal) + */ +#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + int ret; \ + threadEntry_t self; \ + fdEntry_t *fdEntry = getFdEntry(FD); \ + if (fdEntry == NULL) { \ + errno = EBADF; \ + return -1; \ + } \ + do { \ + startOp(fdEntry, &self); \ + ret = FUNC; \ + endOp(fdEntry, &self); \ + } while (ret == -1 && errno == EINTR); \ + return ret; \ +} + +int NET_Read(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); +} + +int NET_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); +} + +int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, int *fromlen) { + socklen_t socklen = *fromlen; + BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, &socklen) ); + *fromlen = socklen; +} + +int NET_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); +} + +int NET_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); +} + +int NET_SendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen) { + BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); +} + +int NET_Accept(int s, struct sockaddr *addr, int *addrlen) { + socklen_t socklen = *addrlen; + BLOCKING_IO_RETURN_INT( s, accept(s, addr, &socklen) ); + *addrlen = socklen; +} + +int NET_Connect(int s, struct sockaddr *addr, int addrlen) { + int crc = -1, prc = -1; + threadEntry_t self; + fdEntry_t* fdEntry = getFdEntry(s); + + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* On AIX, when the system call connect() is interrupted, the connection + * is not aborted and it will be established asynchronously by the kernel. + * Hence, no need to restart connect() when EINTR is received + */ + startOp(fdEntry, &self); + crc = connect(s, addr, addrlen); + endOp(fdEntry, &self); + + if (crc == -1 && errno == EINTR) { + struct pollfd s_pollfd; + int sockopt_arg = 0; + socklen_t len; + + s_pollfd.fd = s; + s_pollfd.events = POLLOUT | POLLERR; + + /* poll the file descriptor */ + do { + startOp(fdEntry, &self); + prc = poll(&s_pollfd, 1, -1); + endOp(fdEntry, &self); + } while (prc == -1 && errno == EINTR); + + if (prc < 0) + return prc; + + len = sizeof(sockopt_arg); + + /* Check whether the connection has been established */ + if (getsockopt(s, SOL_SOCKET, SO_ERROR, &sockopt_arg, &len) == -1) + return -1; + + if (sockopt_arg != 0 ) { + errno = sockopt_arg; + return -1; + } + } else { + return crc; + } + + /* At this point, fd is connected. Set successful return code */ + return 0; +} + +int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); +} + +/* + * Wrapper for poll(s, timeout). + * Auto restarts with adjusted timeout if interrupted by + * signal other than our wakeup signal. + */ +int NET_Timeout(int s, long timeout) { + long prevtime = 0, newtime; + struct timeval t; + fdEntry_t *fdEntry = getFdEntry(s); + + /* + * Check that fd hasn't been closed. + */ + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Pick up current time as may need to adjust timeout + */ + if (timeout > 0) { + gettimeofday(&t, NULL); + prevtime = t.tv_sec * 1000 + t.tv_usec / 1000; + } + + for(;;) { + struct pollfd pfd; + int rv; + threadEntry_t self; + + /* + * Poll the fd. If interrupted by our wakeup signal + * errno will be set to EBADF. + */ + pfd.fd = s; + pfd.events = POLLIN | POLLERR; + + startOp(fdEntry, &self); + rv = poll(&pfd, 1, timeout); + endOp(fdEntry, &self); + + /* + * If interrupted then adjust timeout. If timeout + * has expired return 0 (indicating timeout expired). + */ + if (rv < 0 && errno == EINTR) { + if (timeout > 0) { + gettimeofday(&t, NULL); + newtime = t.tv_sec * 1000 + t.tv_usec / 1000; + timeout -= newtime - prevtime; + if (timeout <= 0) { + return 0; + } + prevtime = newtime; + } + } else { + return rv; + } + + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/aix/native/libnet/java/net/aix_close.c --- a/jdk/src/java.base/aix/native/libnet/java/net/aix_close.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,491 +0,0 @@ -/* - * Copyright (c) 2001, 2013, 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. - */ - -/* - * This file contains implementations of NET_... functions. The NET_.. functions are - * wrappers for common file- and socket functions plus provisions for non-blocking IO. - * - * (basically, the layers remember all file descriptors waiting for a particular fd; - * all threads waiting on a certain fd can be woken up by sending them a signal; this - * is done e.g. when the fd is closed.) - * - * This was originally copied from the linux_close.c implementation. - * - * Side Note: This coding needs initialization. Under Linux this is done - * automatically via __attribute((constructor)), on AIX this is done manually - * (see aix_close_init). - * - */ - -/* - AIX needs a workaround for I/O cancellation, see: - http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/close.htm - ... - The close subroutine is blocked until all subroutines which use the file - descriptor return to usr space. For example, when a thread is calling close - and another thread is calling select with the same file descriptor, the - close subroutine does not return until the select call returns. - ... -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Stack allocated by thread when doing blocking operation - */ -typedef struct threadEntry { - pthread_t thr; /* this thread */ - struct threadEntry *next; /* next thread */ - int intr; /* interrupted */ -} threadEntry_t; - -/* - * Heap allocated during initialized - one entry per fd - */ -typedef struct { - pthread_mutex_t lock; /* fd lock */ - threadEntry_t *threads; /* threads blocked on fd */ -} fdEntry_t; - -/* - * Signal to unblock thread - */ -static int sigWakeup = (SIGRTMAX - 1); - -/* - * The fd table and the number of file descriptors - */ -static fdEntry_t *fdTable = NULL; -static int fdCount = 0; - -/* - * Null signal handler - */ -static void sig_wakeup(int sig) { -} - -/* - * Initialization routine (executed when library is loaded) - * Allocate fd tables and sets up signal handler. - * - * On AIX we don't have __attribute((constructor)) so we need to initialize - * manually (from JNI_OnLoad() in 'src/share/native/java/net/net_util.c') - */ -void aix_close_init() { - struct rlimit nbr_files; - sigset_t sigset; - struct sigaction sa; - - /* Check already initialized */ - if (fdCount > 0 && fdTable != NULL) { - return; - } - - /* - * Allocate table based on the maximum number of - * file descriptors. - */ - if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) { - fprintf(stderr, "library initialization failed - " - "unable to get max # of allocated fds\n"); - abort(); - } - fdCount = nbr_files.rlim_max; - /* - * We have a conceptual problem here, when the number of files is - * unlimited. As a kind of workaround, we ensure the table is big - * enough for handle even a large number of files. Since SAP itself - * recommends a limit of 32000 files, we just use 64000 as 'infinity'. - */ - if (nbr_files.rlim_max == RLIM_INFINITY) { - fdCount = 64000; - } - fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); - if (fdTable == NULL) { - fprintf(stderr, "library initialization failed - " - "unable to allocate file descriptor table - out of memory"); - abort(); - } - - { - int i; - for (i=0; i < fdCount; i++) { - pthread_mutex_init(&fdTable[i].lock, NULL); - } - } - - /* - * Setup the signal handler - */ - sa.sa_handler = sig_wakeup; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(sigWakeup, &sa, NULL); - - sigemptyset(&sigset); - sigaddset(&sigset, sigWakeup); - sigprocmask(SIG_UNBLOCK, &sigset, NULL); -} - -/* - * Return the fd table for this fd or NULL is fd out - * of range. - */ -static inline fdEntry_t *getFdEntry(int fd) -{ - if (fd < 0 || fd >= fdCount) { - return NULL; - } - return &fdTable[fd]; -} - -/* - * Start a blocking operation :- - * Insert thread onto thread list for the fd. - */ -static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) -{ - self->thr = pthread_self(); - self->intr = 0; - - pthread_mutex_lock(&(fdEntry->lock)); - { - self->next = fdEntry->threads; - fdEntry->threads = self; - } - pthread_mutex_unlock(&(fdEntry->lock)); -} - -/* - * End a blocking operation :- - * Remove thread from thread list for the fd - * If fd has been interrupted then set errno to EBADF - */ -static inline void endOp - (fdEntry_t *fdEntry, threadEntry_t *self) -{ - int orig_errno = errno; - pthread_mutex_lock(&(fdEntry->lock)); - { - threadEntry_t *curr, *prev=NULL; - curr = fdEntry->threads; - while (curr != NULL) { - if (curr == self) { - if (curr->intr) { - orig_errno = EBADF; - } - if (prev == NULL) { - fdEntry->threads = curr->next; - } else { - prev->next = curr->next; - } - break; - } - prev = curr; - curr = curr->next; - } - } - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; -} - -/* - * Close or dup2 a file descriptor ensuring that all threads blocked on - * the file descriptor are notified via a wakeup signal. - * - * fd1 < 0 => close(fd2) - * fd1 >= 0 => dup2(fd1, fd2) - * - * Returns -1 with errno set if operation fails. - */ -static int closefd(int fd1, int fd2) { - int rv, orig_errno; - fdEntry_t *fdEntry = getFdEntry(fd2); - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Lock the fd to hold-off additional I/O on this fd. - */ - pthread_mutex_lock(&(fdEntry->lock)); - - { - /* On fast machines we see that we enter dup2 before the - * accepting thread had a chance to get and process the signal. - * So in case we woke a thread up, give it some time to cope. - * Also see https://bugs.openjdk.java.net/browse/JDK-8006395 */ - int num_woken = 0; - - /* - * Send a wakeup signal to all threads blocked on this - * file descriptor. - */ - threadEntry_t *curr = fdEntry->threads; - while (curr != NULL) { - curr->intr = 1; - pthread_kill( curr->thr, sigWakeup ); - num_woken ++; - curr = curr->next; - } - - if (num_woken > 0) { - usleep(num_woken * 50); - } - - /* - * And close/dup the file descriptor - * (restart if interrupted by signal) - */ - do { - if (fd1 < 0) { - rv = close(fd2); - } else { - rv = dup2(fd1, fd2); - } - } while (rv == -1 && errno == EINTR); - } - - /* - * Unlock without destroying errno - */ - orig_errno = errno; - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; - - return rv; -} - -/* - * Wrapper for dup2 - same semantics as dup2 system call except - * that any threads blocked in an I/O system call on fd2 will be - * preempted and return -1/EBADF; - */ -int NET_Dup2(int fd, int fd2) { - if (fd < 0) { - errno = EBADF; - return -1; - } - return closefd(fd, fd2); -} - -/* - * Wrapper for close - same semantics as close system call - * except that any threads blocked in an I/O on fd will be - * preempted and the I/O system call will return -1/EBADF. - */ -int NET_SocketClose(int fd) { - return closefd(-1, fd); -} - -/************** Basic I/O operations here ***************/ - -/* - * Macro to perform a blocking IO operation. Restarts - * automatically if interrupted by signal (other than - * our wakeup signal) - */ -#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ - int ret; \ - threadEntry_t self; \ - fdEntry_t *fdEntry = getFdEntry(FD); \ - if (fdEntry == NULL) { \ - errno = EBADF; \ - return -1; \ - } \ - do { \ - startOp(fdEntry, &self); \ - ret = FUNC; \ - endOp(fdEntry, &self); \ - } while (ret == -1 && errno == EINTR); \ - return ret; \ -} - -int NET_Read(int s, void* buf, size_t len) { - BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); -} - -int NET_ReadV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); -} - -int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen) { - socklen_t socklen = *fromlen; - BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, &socklen) ); - *fromlen = socklen; -} - -int NET_Send(int s, void *msg, int len, unsigned int flags) { - BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); -} - -int NET_WriteV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); -} - -int NET_SendTo(int s, const void *msg, int len, unsigned int - flags, const struct sockaddr *to, int tolen) { - BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); -} - -int NET_Accept(int s, struct sockaddr *addr, int *addrlen) { - socklen_t socklen = *addrlen; - BLOCKING_IO_RETURN_INT( s, accept(s, addr, &socklen) ); - *addrlen = socklen; -} - -int NET_Connect(int s, struct sockaddr *addr, int addrlen) { - int crc = -1, prc = -1; - threadEntry_t self; - fdEntry_t* fdEntry = getFdEntry(s); - - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* On AIX, when the system call connect() is interrupted, the connection - * is not aborted and it will be established asynchronously by the kernel. - * Hence, no need to restart connect() when EINTR is received - */ - startOp(fdEntry, &self); - crc = connect(s, addr, addrlen); - endOp(fdEntry, &self); - - if (crc == -1 && errno == EINTR) { - struct pollfd s_pollfd; - int sockopt_arg = 0; - socklen_t len; - - s_pollfd.fd = s; - s_pollfd.events = POLLOUT | POLLERR; - - /* poll the file descriptor */ - do { - startOp(fdEntry, &self); - prc = poll(&s_pollfd, 1, -1); - endOp(fdEntry, &self); - } while (prc == -1 && errno == EINTR); - - if (prc < 0) - return prc; - - len = sizeof(sockopt_arg); - - /* Check whether the connection has been established */ - if (getsockopt(s, SOL_SOCKET, SO_ERROR, &sockopt_arg, &len) == -1) - return -1; - - if (sockopt_arg != 0 ) { - errno = sockopt_arg; - return -1; - } - } else { - return crc; - } - - /* At this point, fd is connected. Set successful return code */ - return 0; -} - -int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { - BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); -} - -/* - * Wrapper for poll(s, timeout). - * Auto restarts with adjusted timeout if interrupted by - * signal other than our wakeup signal. - */ -int NET_Timeout(int s, long timeout) { - long prevtime = 0, newtime; - struct timeval t; - fdEntry_t *fdEntry = getFdEntry(s); - - /* - * Check that fd hasn't been closed. - */ - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Pick up current time as may need to adjust timeout - */ - if (timeout > 0) { - gettimeofday(&t, NULL); - prevtime = t.tv_sec * 1000 + t.tv_usec / 1000; - } - - for(;;) { - struct pollfd pfd; - int rv; - threadEntry_t self; - - /* - * Poll the fd. If interrupted by our wakeup signal - * errno will be set to EBADF. - */ - pfd.fd = s; - pfd.events = POLLIN | POLLERR; - - startOp(fdEntry, &self); - rv = poll(&pfd, 1, timeout); - endOp(fdEntry, &self); - - /* - * If interrupted then adjust timeout. If timeout - * has expired return 0 (indicating timeout expired). - */ - if (rv < 0 && errno == EINTR) { - if (timeout > 0) { - gettimeofday(&t, NULL); - newtime = t.tv_sec * 1000 + t.tv_usec / 1000; - timeout -= newtime - prevtime; - if (timeout <= 0) { - return 0; - } - prevtime = newtime; - } - } else { - return rv; - } - - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/linux/native/libnet/linux_close.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/linux/native/libnet/linux_close.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2001, 2013, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Stack allocated by thread when doing blocking operation + */ +typedef struct threadEntry { + pthread_t thr; /* this thread */ + struct threadEntry *next; /* next thread */ + int intr; /* interrupted */ +} threadEntry_t; + +/* + * Heap allocated during initialized - one entry per fd + */ +typedef struct { + pthread_mutex_t lock; /* fd lock */ + threadEntry_t *threads; /* threads blocked on fd */ +} fdEntry_t; + +/* + * Signal to unblock thread + */ +static int sigWakeup = (__SIGRTMAX - 2); + +/* + * The fd table and the number of file descriptors + */ +static fdEntry_t *fdTable; +static int fdCount; + +/* + * Null signal handler + */ +static void sig_wakeup(int sig) { +} + +/* + * Initialization routine (executed when library is loaded) + * Allocate fd tables and sets up signal handler. + */ +static void __attribute((constructor)) init() { + struct rlimit nbr_files; + sigset_t sigset; + struct sigaction sa; + + /* + * Allocate table based on the maximum number of + * file descriptors. + */ + getrlimit(RLIMIT_NOFILE, &nbr_files); + fdCount = nbr_files.rlim_max; + fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); + if (fdTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor table - out of memory"); + abort(); + } + + /* + * Setup the signal handler + */ + sa.sa_handler = sig_wakeup; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sigWakeup, &sa, NULL); + + sigemptyset(&sigset); + sigaddset(&sigset, sigWakeup); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +} + +/* + * Return the fd table for this fd or NULL is fd out + * of range. + */ +static inline fdEntry_t *getFdEntry(int fd) +{ + if (fd < 0 || fd >= fdCount) { + return NULL; + } + return &fdTable[fd]; +} + +/* + * Start a blocking operation :- + * Insert thread onto thread list for the fd. + */ +static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) +{ + self->thr = pthread_self(); + self->intr = 0; + + pthread_mutex_lock(&(fdEntry->lock)); + { + self->next = fdEntry->threads; + fdEntry->threads = self; + } + pthread_mutex_unlock(&(fdEntry->lock)); +} + +/* + * End a blocking operation :- + * Remove thread from thread list for the fd + * If fd has been interrupted then set errno to EBADF + */ +static inline void endOp + (fdEntry_t *fdEntry, threadEntry_t *self) +{ + int orig_errno = errno; + pthread_mutex_lock(&(fdEntry->lock)); + { + threadEntry_t *curr, *prev=NULL; + curr = fdEntry->threads; + while (curr != NULL) { + if (curr == self) { + if (curr->intr) { + orig_errno = EBADF; + } + if (prev == NULL) { + fdEntry->threads = curr->next; + } else { + prev->next = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + } + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; +} + +/* + * Close or dup2 a file descriptor ensuring that all threads blocked on + * the file descriptor are notified via a wakeup signal. + * + * fd1 < 0 => close(fd2) + * fd1 >= 0 => dup2(fd1, fd2) + * + * Returns -1 with errno set if operation fails. + */ +static int closefd(int fd1, int fd2) { + int rv, orig_errno; + fdEntry_t *fdEntry = getFdEntry(fd2); + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Lock the fd to hold-off additional I/O on this fd. + */ + pthread_mutex_lock(&(fdEntry->lock)); + + { + /* + * And close/dup the file descriptor + * (restart if interrupted by signal) + */ + do { + if (fd1 < 0) { + rv = close(fd2); + } else { + rv = dup2(fd1, fd2); + } + } while (rv == -1 && errno == EINTR); + + /* + * Send a wakeup signal to all threads blocked on this + * file descriptor. + */ + threadEntry_t *curr = fdEntry->threads; + while (curr != NULL) { + curr->intr = 1; + pthread_kill( curr->thr, sigWakeup ); + curr = curr->next; + } + } + + /* + * Unlock without destroying errno + */ + orig_errno = errno; + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; + + return rv; +} + +/* + * Wrapper for dup2 - same semantics as dup2 system call except + * that any threads blocked in an I/O system call on fd2 will be + * preempted and return -1/EBADF; + */ +int NET_Dup2(int fd, int fd2) { + if (fd < 0) { + errno = EBADF; + return -1; + } + return closefd(fd, fd2); +} + +/* + * Wrapper for close - same semantics as close system call + * except that any threads blocked in an I/O on fd will be + * preempted and the I/O system call will return -1/EBADF. + */ +int NET_SocketClose(int fd) { + return closefd(-1, fd); +} + +/************** Basic I/O operations here ***************/ + +/* + * Macro to perform a blocking IO operation. Restarts + * automatically if interrupted by signal (other than + * our wakeup signal) + */ +#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + int ret; \ + threadEntry_t self; \ + fdEntry_t *fdEntry = getFdEntry(FD); \ + if (fdEntry == NULL) { \ + errno = EBADF; \ + return -1; \ + } \ + do { \ + startOp(fdEntry, &self); \ + ret = FUNC; \ + endOp(fdEntry, &self); \ + } while (ret == -1 && errno == EINTR); \ + return ret; \ +} + +int NET_Read(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); +} + +int NET_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); +} + +int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) { + BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) ); +} + +int NET_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); +} + +int NET_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); +} + +int NET_SendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen) { + BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); +} + +int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) ); +} + +int NET_Connect(int s, struct sockaddr *addr, int addrlen) { + BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); +} + +int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); +} + +/* + * Wrapper for poll(s, timeout). + * Auto restarts with adjusted timeout if interrupted by + * signal other than our wakeup signal. + */ +int NET_Timeout(int s, long timeout) { + long prevtime = 0, newtime; + struct timeval t; + fdEntry_t *fdEntry = getFdEntry(s); + + /* + * Check that fd hasn't been closed. + */ + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Pick up current time as may need to adjust timeout + */ + if (timeout > 0) { + gettimeofday(&t, NULL); + prevtime = t.tv_sec * 1000 + t.tv_usec / 1000; + } + + for(;;) { + struct pollfd pfd; + int rv; + threadEntry_t self; + + /* + * Poll the fd. If interrupted by our wakeup signal + * errno will be set to EBADF. + */ + pfd.fd = s; + pfd.events = POLLIN | POLLERR; + + startOp(fdEntry, &self); + rv = poll(&pfd, 1, timeout); + endOp(fdEntry, &self); + + /* + * If interrupted then adjust timeout. If timeout + * has expired return 0 (indicating timeout expired). + */ + if (rv < 0 && errno == EINTR) { + if (timeout > 0) { + gettimeofday(&t, NULL); + newtime = t.tv_sec * 1000 + t.tv_usec / 1000; + timeout -= newtime - prevtime; + if (timeout <= 0) { + return 0; + } + prevtime = newtime; + } + } else { + return rv; + } + + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/macosx/native/libjava/java_props_macosx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,349 @@ +/* + * Copyright (c) 1998, 2013, 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "java_props_macosx.h" + + +// need dlopen/dlsym trick to avoid pulling in JavaRuntimeSupport before libjava.dylib is loaded +static void *getJRSFramework() { + static void *jrsFwk = NULL; + if (jrsFwk == NULL) { + jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); + } + return jrsFwk; +} + +char *getPosixLocale(int cat) { + char *lc = setlocale(cat, NULL); + if ((lc == NULL) || (strcmp(lc, "C") == 0)) { + lc = getenv("LANG"); + } + if (lc == NULL) return NULL; + return strdup(lc); +} + +#define LOCALEIDLENGTH 128 +char *getMacOSXLocale(int cat) { + switch (cat) { + case LC_MESSAGES: + { + void *jrsFwk = getJRSFramework(); + if (jrsFwk == NULL) return NULL; + + char *(*JRSCopyPrimaryLanguage)() = dlsym(jrsFwk, "JRSCopyPrimaryLanguage"); + char *primaryLanguage = JRSCopyPrimaryLanguage ? JRSCopyPrimaryLanguage() : NULL; + if (primaryLanguage == NULL) return NULL; + + char *(*JRSCopyCanonicalLanguageForPrimaryLanguage)(char *) = dlsym(jrsFwk, "JRSCopyCanonicalLanguageForPrimaryLanguage"); + char *canonicalLanguage = JRSCopyCanonicalLanguageForPrimaryLanguage ? JRSCopyCanonicalLanguageForPrimaryLanguage(primaryLanguage) : NULL; + free (primaryLanguage); + + return canonicalLanguage; + } + break; + default: + { + char localeString[LOCALEIDLENGTH]; + if (CFStringGetCString(CFLocaleGetIdentifier(CFLocaleCopyCurrent()), + localeString, LOCALEIDLENGTH, CFStringGetSystemEncoding())) { + return strdup(localeString); + } + } + break; + } + + return NULL; +} + +char *setupMacOSXLocale(int cat) { + char * ret = getMacOSXLocale(cat); + + if (cat == LC_MESSAGES && ret != NULL) { + void *jrsFwk = getJRSFramework(); + if (jrsFwk != NULL) { + void (*JRSSetDefaultLocalization)(char *) = dlsym(jrsFwk, "JRSSetDefaultLocalization"); + if (JRSSetDefaultLocalization) JRSSetDefaultLocalization(ret); + } + } + + if (ret == NULL) { + return getPosixLocale(cat); + } else { + return ret; + } +} + +int isInAquaSession() { + // environment variable to bypass the aqua session check + char *ev = getenv("AWT_FORCE_HEADFUL"); + if (ev && (strncasecmp(ev, "true", 4) == 0)) { + // if "true" then tell the caller we're in an Aqua session without actually checking + return 1; + } + // Is the WindowServer available? + SecuritySessionId session_id; + SessionAttributeBits session_info; + OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info); + if (status == noErr) { + if (session_info & sessionHasGraphicAccess) { + return 1; + } + } + return 0; +} + +void setOSNameAndVersion(java_props_t *sprops) { + /* Don't rely on JRSCopyOSName because there's no guarantee the value will + * remain the same, or even if the JRS functions will continue to be part of + * Mac OS X. So hardcode os_name, and fill in os_version if we can. + */ + sprops->os_name = strdup("Mac OS X"); + + void *jrsFwk = getJRSFramework(); + if (jrsFwk != NULL) { + char *(*copyOSVersion)() = dlsym(jrsFwk, "JRSCopyOSVersion"); + if (copyOSVersion != NULL) { + sprops->os_version = copyOSVersion(); + return; + } + } + sprops->os_version = strdup("Unknown"); +} + + +static Boolean getProxyInfoForProtocol(CFDictionaryRef inDict, CFStringRef inEnabledKey, CFStringRef inHostKey, CFStringRef inPortKey, CFStringRef *outProxyHost, int *ioProxyPort) { + /* See if the proxy is enabled. */ + CFNumberRef cf_enabled = CFDictionaryGetValue(inDict, inEnabledKey); + if (cf_enabled == NULL) { + return false; + } + + int isEnabled = false; + if (!CFNumberGetValue(cf_enabled, kCFNumberIntType, &isEnabled)) { + return isEnabled; + } + + if (!isEnabled) return false; + *outProxyHost = CFDictionaryGetValue(inDict, inHostKey); + + // If cf_host is null, that means the checkbox is set, + // but no host was entered. We'll treat that as NOT ENABLED. + // If cf_port is null or cf_port isn't a number, that means + // no port number was entered. Treat this as ENABLED with the + // protocol's default port. + if (*outProxyHost == NULL) { + return false; + } + + if (CFStringGetLength(*outProxyHost) == 0) { + return false; + } + + int newPort = 0; + CFNumberRef cf_port = NULL; + if ((cf_port = CFDictionaryGetValue(inDict, inPortKey)) != NULL && + CFNumberGetValue(cf_port, kCFNumberIntType, &newPort) && + newPort > 0) { + *ioProxyPort = newPort; + } else { + // bad port or no port - leave *ioProxyPort unchanged + } + + return true; +} + +static char *createUTF8CString(const CFStringRef theString) { + if (theString == NULL) return NULL; + + const CFIndex stringLength = CFStringGetLength(theString); + const CFIndex bufSize = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 1; + char *returnVal = (char *)malloc(bufSize); + + if (CFStringGetCString(theString, returnVal, bufSize, kCFStringEncodingUTF8)) { + return returnVal; + } + + free(returnVal); + return NULL; +} + +// Return TRUE if str is a syntactically valid IP address. +// Using inet_pton() instead of inet_aton() for IPv6 support. +// len is only a hint; cstr must still be nul-terminated +static int looksLikeIPAddress(char *cstr, size_t len) { + if (len == 0 || (len == 1 && cstr[0] == '.')) return FALSE; + + char dst[16]; // big enough for INET6 + return (1 == inet_pton(AF_INET, cstr, dst) || + 1 == inet_pton(AF_INET6, cstr, dst)); +} + + + +// Convert Mac OS X proxy exception entry to Java syntax. +// See Radar #3441134 for details. +// Returns NULL if this exception should be ignored by Java. +// May generate a string with multiple exceptions separated by '|'. +static char * createConvertedException(CFStringRef cf_original) { + // This is done with char* instead of CFString because inet_pton() + // needs a C string. + char *c_exception = createUTF8CString(cf_original); + if (!c_exception) return NULL; + + int c_len = strlen(c_exception); + + // 1. sanitize exception prefix + if (c_len >= 1 && 0 == strncmp(c_exception, ".", 1)) { + memmove(c_exception, c_exception+1, c_len); + c_len -= 1; + } else if (c_len >= 2 && 0 == strncmp(c_exception, "*.", 2)) { + memmove(c_exception, c_exception+2, c_len-1); + c_len -= 2; + } + + // 2. pre-reject other exception wildcards + if (strchr(c_exception, '*')) { + free(c_exception); + return NULL; + } + + // 3. no IP wildcarding + if (looksLikeIPAddress(c_exception, c_len)) { + return c_exception; + } + + // 4. allow domain suffixes + // c_exception is now "str\0" - change to "str|*.str\0" + c_exception = reallocf(c_exception, c_len+3+c_len+1); + if (!c_exception) return NULL; + + strncpy(c_exception+c_len, "|*.", 3); + strncpy(c_exception+c_len+3, c_exception, c_len); + c_exception[c_len+3+c_len] = '\0'; + return c_exception; +} + +/* + * Method for fetching the user.home path and storing it in the property list. + * For signed .apps running in the Mac App Sandbox, user.home is set to the + * app's sandbox container. + */ +void setUserHome(java_props_t *sprops) { + if (sprops == NULL) { return; } + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + sprops->user_home = createUTF8CString((CFStringRef)NSHomeDirectory()); + [pool drain]; +} + +/* + * Method for fetching proxy info and storing it in the property list. + */ +void setProxyProperties(java_props_t *sProps) { + if (sProps == NULL) return; + + char buf[16]; /* Used for %d of an int - 16 is plenty */ + CFStringRef + cf_httpHost = NULL, + cf_httpsHost = NULL, + cf_ftpHost = NULL, + cf_socksHost = NULL, + cf_gopherHost = NULL; + int + httpPort = 80, // Default proxy port values + httpsPort = 443, + ftpPort = 21, + socksPort = 1080, + gopherPort = 70; + + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if (dict == NULL) return; + + /* Read the proxy exceptions list */ + CFArrayRef cf_list = CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList); + + CFMutableStringRef cf_exceptionList = NULL; + if (cf_list != NULL) { + CFIndex len = CFArrayGetCount(cf_list), idx; + + cf_exceptionList = CFStringCreateMutable(NULL, 0); + for (idx = (CFIndex)0; idx < len; idx++) { + CFStringRef cf_ehost; + if ((cf_ehost = CFArrayGetValueAtIndex(cf_list, idx))) { + /* Convert this exception from Mac OS X syntax to Java syntax. + See Radar #3441134 for details. This may generate a string + with multiple Java exceptions separated by '|'. */ + char *c_exception = createConvertedException(cf_ehost); + if (c_exception) { + /* Append the host to the list of exclusions. */ + if (CFStringGetLength(cf_exceptionList) > 0) { + CFStringAppendCString(cf_exceptionList, "|", kCFStringEncodingMacRoman); + } + CFStringAppendCString(cf_exceptionList, c_exception, kCFStringEncodingMacRoman); + free(c_exception); + } + } + } + } + + if (cf_exceptionList != NULL) { + if (CFStringGetLength(cf_exceptionList) > 0) { + sProps->exceptionList = createUTF8CString(cf_exceptionList); + } + CFRelease(cf_exceptionList); + } + +#define CHECK_PROXY(protocol, PROTOCOL) \ + sProps->protocol##ProxyEnabled = \ + getProxyInfoForProtocol(dict, kSCPropNetProxies##PROTOCOL##Enable, \ + kSCPropNetProxies##PROTOCOL##Proxy, \ + kSCPropNetProxies##PROTOCOL##Port, \ + &cf_##protocol##Host, &protocol##Port); \ + if (sProps->protocol##ProxyEnabled) { \ + sProps->protocol##Host = createUTF8CString(cf_##protocol##Host); \ + snprintf(buf, sizeof(buf), "%d", protocol##Port); \ + sProps->protocol##Port = malloc(strlen(buf) + 1); \ + strcpy(sProps->protocol##Port, buf); \ + } + + CHECK_PROXY(http, HTTP); + CHECK_PROXY(https, HTTPS); + CHECK_PROXY(ftp, FTP); + CHECK_PROXY(socks, SOCKS); + CHECK_PROXY(gopher, Gopher); + +#undef CHECK_PROXY + + CFRelease(dict); +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/macosx/native/libjava/java_props_macosx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.h Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1998, 2013, 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 "java_props.h" + +char *setupMacOSXLocale(int cat); +void setOSNameAndVersion(java_props_t *sprops); +void setUserHome(java_props_t *sprops); +void setProxyProperties(java_props_t *sProps); +int isInAquaSession(); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/macosx/native/libnet/bsd_close.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/macosx/native/libnet/bsd_close.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2001, 2012, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Stack allocated by thread when doing blocking operation + */ +typedef struct threadEntry { + pthread_t thr; /* this thread */ + struct threadEntry *next; /* next thread */ + int intr; /* interrupted */ +} threadEntry_t; + +/* + * Heap allocated during initialized - one entry per fd + */ +typedef struct { + pthread_mutex_t lock; /* fd lock */ + threadEntry_t *threads; /* threads blocked on fd */ +} fdEntry_t; + +/* + * Signal to unblock thread + */ +static int sigWakeup = SIGIO; + +/* + * The fd table and the number of file descriptors + */ +static fdEntry_t *fdTable; +static int fdCount; + +/* + * This limit applies if getlimit() returns unlimited. + * Unfortunately, this means if someone wants a higher limit + * then they have to set an explicit limit, higher than this, + * which is probably counter-intuitive. + */ +#define MAX_FD_COUNT 4096 + +/* + * Null signal handler + */ +static void sig_wakeup(int sig) { +} + +/* + * Initialization routine (executed when library is loaded) + * Allocate fd tables and sets up signal handler. + */ +static void __attribute((constructor)) init() { + struct rlimit nbr_files; + sigset_t sigset; + struct sigaction sa; + int i; + + /* + * Allocate table based on the maximum number of + * file descriptors. + */ + getrlimit(RLIMIT_NOFILE, &nbr_files); + if (nbr_files.rlim_max == RLIM_INFINITY) { + fdCount = MAX_FD_COUNT; + } else { + fdCount = nbr_files.rlim_max; + } + fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); + if (fdTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor table - out of memory"); + abort(); + } + for (i=0; i= fdCount) { + return NULL; + } + return &fdTable[fd]; +} + +/* + * Start a blocking operation :- + * Insert thread onto thread list for the fd. + */ +static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) +{ + self->thr = pthread_self(); + self->intr = 0; + + pthread_mutex_lock(&(fdEntry->lock)); + { + self->next = fdEntry->threads; + fdEntry->threads = self; + } + pthread_mutex_unlock(&(fdEntry->lock)); +} + +/* + * End a blocking operation :- + * Remove thread from thread list for the fd + * If fd has been interrupted then set errno to EBADF + */ +static inline void endOp + (fdEntry_t *fdEntry, threadEntry_t *self) +{ + int orig_errno = errno; + pthread_mutex_lock(&(fdEntry->lock)); + { + threadEntry_t *curr, *prev=NULL; + curr = fdEntry->threads; + while (curr != NULL) { + if (curr == self) { + if (curr->intr) { + orig_errno = EBADF; + } + if (prev == NULL) { + fdEntry->threads = curr->next; + } else { + prev->next = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + } + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; +} + +/* + * Close or dup2 a file descriptor ensuring that all threads blocked on + * the file descriptor are notified via a wakeup signal. + * + * fd1 < 0 => close(fd2) + * fd1 >= 0 => dup2(fd1, fd2) + * + * Returns -1 with errno set if operation fails. + */ +static int closefd(int fd1, int fd2) { + int rv, orig_errno; + fdEntry_t *fdEntry = getFdEntry(fd2); + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Lock the fd to hold-off additional I/O on this fd. + */ + pthread_mutex_lock(&(fdEntry->lock)); + + { + /* + * Send a wakeup signal to all threads blocked on this + * file descriptor. + */ + threadEntry_t *curr = fdEntry->threads; + while (curr != NULL) { + curr->intr = 1; + pthread_kill( curr->thr, sigWakeup ); + curr = curr->next; + } + + /* + * And close/dup the file descriptor + * (restart if interrupted by signal) + */ + do { + if (fd1 < 0) { + rv = close(fd2); + } else { + rv = dup2(fd1, fd2); + } + } while (rv == -1 && errno == EINTR); + + } + + /* + * Unlock without destroying errno + */ + orig_errno = errno; + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; + + return rv; +} + +/* + * Wrapper for dup2 - same semantics as dup2 system call except + * that any threads blocked in an I/O system call on fd2 will be + * preempted and return -1/EBADF; + */ +int NET_Dup2(int fd, int fd2) { + if (fd < 0) { + errno = EBADF; + return -1; + } + return closefd(fd, fd2); +} + +/* + * Wrapper for close - same semantics as close system call + * except that any threads blocked in an I/O on fd will be + * preempted and the I/O system call will return -1/EBADF. + */ +int NET_SocketClose(int fd) { + return closefd(-1, fd); +} + +/************** Basic I/O operations here ***************/ + +/* + * Macro to perform a blocking IO operation. Restarts + * automatically if interrupted by signal (other than + * our wakeup signal) + */ +#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + int ret; \ + threadEntry_t self; \ + fdEntry_t *fdEntry = getFdEntry(FD); \ + if (fdEntry == NULL) { \ + errno = EBADF; \ + return -1; \ + } \ + do { \ + startOp(fdEntry, &self); \ + ret = FUNC; \ + endOp(fdEntry, &self); \ + } while (ret == -1 && errno == EINTR); \ + return ret; \ +} + +int NET_Read(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); +} + +int NET_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); +} + +int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) { + BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) ); +} + +int NET_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); +} + +int NET_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); +} + +int NET_SendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen) { + BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); +} + +int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) ); +} + +int NET_Connect(int s, struct sockaddr *addr, int addrlen) { + BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); +} + +int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); +} + +/* + * Wrapper for select(s, timeout). We are using select() on Mac OS due to Bug 7131399. + * Auto restarts with adjusted timeout if interrupted by + * signal other than our wakeup signal. + */ +int NET_Timeout(int s, long timeout) { + long prevtime = 0, newtime; + struct timeval t, *tp = &t; + fd_set fds; + fd_set* fdsp = NULL; + int allocated = 0; + threadEntry_t self; + fdEntry_t *fdEntry = getFdEntry(s); + + /* + * Check that fd hasn't been closed. + */ + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + /* + * Pick up current time as may need to adjust timeout + */ + if (timeout > 0) { + /* Timed */ + struct timeval now; + gettimeofday(&now, NULL); + prevtime = now.tv_sec * 1000 + now.tv_usec / 1000; + t.tv_sec = timeout / 1000; + t.tv_usec = (timeout % 1000) * 1000; + } else if (timeout < 0) { + /* Blocking */ + tp = 0; + } else { + /* Poll */ + t.tv_sec = 0; + t.tv_usec = 0; + } + + if (s < FD_SETSIZE) { + fdsp = &fds; + FD_ZERO(fdsp); + } else { + int length = (howmany(s+1, NFDBITS)) * sizeof(int); + fdsp = (fd_set *) calloc(1, length); + if (fdsp == NULL) { + return -1; // errno will be set to ENOMEM + } + allocated = 1; + } + FD_SET(s, fdsp); + + for(;;) { + int rv; + + /* + * call select on the fd. If interrupted by our wakeup signal + * errno will be set to EBADF. + */ + + startOp(fdEntry, &self); + rv = select(s+1, fdsp, 0, 0, tp); + endOp(fdEntry, &self); + + /* + * If interrupted then adjust timeout. If timeout + * has expired return 0 (indicating timeout expired). + */ + if (rv < 0 && errno == EINTR) { + if (timeout > 0) { + struct timeval now; + gettimeofday(&now, NULL); + newtime = now.tv_sec * 1000 + now.tv_usec / 1000; + timeout -= newtime - prevtime; + if (timeout <= 0) { + if (allocated != 0) + free(fdsp); + return 0; + } + prevtime = newtime; + t.tv_sec = timeout / 1000; + t.tv_usec = (timeout % 1000) * 1000; + } + } else { + if (allocated != 0) + free(fdsp); + return rv; + } + + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -27,6 +27,9 @@ import sun.misc.FloatingDecimal; import java.util.Arrays; +import java.util.Spliterator; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; /** * A mutable sequence of characters. @@ -292,7 +295,7 @@ if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { throw new IndexOutOfBoundsException(); } - return Character.codePointCountImpl(value, beginIndex, endIndex-beginIndex); + return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); } /** @@ -1432,6 +1435,34 @@ public abstract String toString(); /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public IntStream chars() { + // Reuse String-based spliterator. This requires a supplier to + // capture the value and count when the terminal operation is executed + return StreamSupport.intStream( + () -> new String.IntCharArraySpliterator(value, 0, count, 0), + Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, + false); + } + + /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public IntStream codePoints() { + // Reuse String-based spliterator. This requires a supplier to + // capture the value and count when the terminal operation is executed + return StreamSupport.intStream( + () -> new String.CodePointsSpliterator(value, 0, count, 0), + Spliterator.ORDERED, + false); + } + + /** * Needed by {@code String} for the contentEquals method. */ final char[] getValue() { diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/java/lang/Object.java --- a/jdk/src/java.base/share/classes/java/lang/Object.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Object.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2015, 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 @@ -42,6 +42,11 @@ } /** + * Constructs a new object. + */ + public Object() {} + + /** * Returns the runtime class of this {@code Object}. The returned * {@code Class} object is the object that is locked by {@code * static synchronized} methods of the represented class. @@ -86,12 +91,11 @@ * for unequal objects may improve the performance of hash tables. * *

    - * As much as is reasonably practical, the hashCode method defined by - * class {@code Object} does return distinct integers for distinct - * objects. (This is typically implemented by converting the internal - * address of the object into an integer, but this implementation - * technique is not required by the - * Java™ programming language.) + * As much as is reasonably practical, the hashCode method defined + * by class {@code Object} does return distinct integers for + * distinct objects. (The hashCode may or may not be implemented + * as some function of an object's memory address at some point + * in time.) * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) @@ -344,10 +348,12 @@ * ... // Perform action appropriate to condition * } * - * (For more information on this topic, see Section 3.2.3 in Doug Lea's - * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley, - * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming - * Language Guide" (Addison-Wesley, 2001). + * + * (For more information on this topic, see section 14.2, + * Condition Queues, in Brian Goetz and others' "Java Concurrency + * in Practice" (Addison-Wesley, 2006) or Item 69 in Joshua + * Bloch's "Effective Java (Second Edition)" (Addison-Wesley, + * 2008). * *

    If the current thread is {@linkplain java.lang.Thread#interrupt() * interrupted} by any thread before or while it is waiting, then an diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/java/lang/String.java --- a/jdk/src/java.base/share/classes/java/lang/String.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/String.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,14 @@ import java.util.Formatter; import java.util.Locale; import java.util.Objects; +import java.util.Spliterator; import java.util.StringJoiner; +import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; /** * The {@code String} class represents character strings. All @@ -2894,6 +2898,180 @@ return this; } + static class IntCharArraySpliterator implements Spliterator.OfInt { + private final char[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + IntCharArraySpliterator(char[] array, int acs) { + this(array, 0, array.length, acs); + } + + IntCharArraySpliterator(char[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + return (lo >= mid) + ? null + : new IntCharArraySpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + char[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if ((a = array).length >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { action.accept(a[i]); } while (++i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + action.accept(array[index++]); + return true; + } + return false; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + /** + * Returns a stream of {@code int} zero-extending the {@code char} values + * from this sequence. Any char which maps to a surrogate code + * point is passed through uninterpreted. + * + * @return an IntStream of char values from this sequence + * @since 1.9 + */ + @Override + public IntStream chars() { + return StreamSupport.intStream( + new IntCharArraySpliterator(value, Spliterator.IMMUTABLE), false); + } + + static class CodePointsSpliterator implements Spliterator.OfInt { + private final char[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CodePointsSpliterator(char[] array, int acs) { + this(array, 0, array.length, acs); + } + + CodePointsSpliterator(char[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + if (lo >= mid) + return null; + + int midOneLess; + // If the mid-point intersects a surrogate pair + if (Character.isLowSurrogate(array[mid]) && + Character.isHighSurrogate(array[midOneLess = (mid -1)])) { + // If there is only one pair it cannot be split + if (lo >= midOneLess) + return null; + // Shift the mid-point to align with the surrogate pair + return new CodePointsSpliterator(array, lo, index = midOneLess, cs); + } + return new CodePointsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + char[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if ((a = array).length >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { + i = advance(a, i, hi, action); + } while (i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + index = advance(array, index, fence, action); + return true; + } + return false; + } + + // Advance one code point from the index, i, and return the next + // index to advance from + private static int advance(char[] a, int i, int hi, IntConsumer action) { + char c1 = a[i++]; + int cp = c1; + if (Character.isHighSurrogate(c1) && i < hi) { + char c2 = a[i]; + if (Character.isLowSurrogate(c2)) { + i++; + cp = Character.toCodePoint(c1, c2); + } + } + action.accept(cp); + return i; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + /** + * Returns a stream of code point values from this sequence. Any surrogate + * pairs encountered in the sequence are combined as if by {@linkplain + * Character#toCodePoint Character.toCodePoint} and the result is passed + * to the stream. Any other code units, including ordinary BMP characters, + * unpaired surrogates, and undefined code units, are zero-extended to + * {@code int} values which are then passed to the stream. + * + * @return an IntStream of Unicode code points from this sequence + * @since 1.9 + */ + @Override + public IntStream codePoints() { + return StreamSupport.intStream( + new CodePointsSpliterator(value, Spliterator.IMMUTABLE), false); + } + /** * Converts this string to a new character array. * diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/java/lang/System.java --- a/jdk/src/java.base/share/classes/java/lang/System.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/System.java Wed Jul 05 20:17:28 2017 +0200 @@ -376,19 +376,16 @@ * the difference between two such values, obtained within the same * instance of a Java virtual machine, is computed. * - *

    For example, to measure how long some code takes to execute: - *

     {@code
    +     * 

    For example, to measure how long some code takes to execute: + *

     {@code
          * long startTime = System.nanoTime();
          * // ... the code being measured ...
    -     * long estimatedTime = System.nanoTime() - startTime;}
    + * long elapsedNanos = System.nanoTime() - startTime;}
    * - *

    To compare two nanoTime values - *

     {@code
    -     * long t0 = System.nanoTime();
    -     * ...
    -     * long t1 = System.nanoTime();}
    - * - * one should use {@code t1 - t0 < 0}, not {@code t1 < t0}, + *

    To compare elapsed time against a timeout, use

     {@code
    +     * if (System.nanoTime() - startTime >= timeoutNanos) ...}
    + * instead of
     {@code
    +     * if (System.nanoTime() >= startTime + timeoutNanos) ...}
    * because of the possibility of numerical overflow. * * @return the current value of the running Java Virtual Machine's diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/java/util/Spliterator.java --- a/jdk/src/java.base/share/classes/java/util/Spliterator.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Spliterator.java Wed Jul 05 20:17:28 2017 +0200 @@ -553,6 +553,12 @@ * sub-split size is known and additions or removals to the source are not * reflected when traversing. * + *

    A top-level Spliterator should not report both {@code CONCURRENT} and + * {@code IMMUTABLE}, since they are mutually exclusive. Such a Spliterator + * is inconsistent and no guarantees can be made about any computation using + * that Spliterator. Sub-spliterators may report {@code IMMUTABLE} if + * additions or removals to the source are not reflected when traversing. + * * @apiNote Most concurrent collections maintain a consistency policy * guaranteeing accuracy with respect to elements present at the point of * Spliterator construction, but possibly not reflecting subsequent diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/share/classes/sun/misc/Unsafe.java --- a/jdk/src/java.base/share/classes/sun/misc/Unsafe.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/share/classes/sun/misc/Unsafe.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, 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 @@ -845,22 +845,6 @@ public native Object allocateInstance(Class cls) throws InstantiationException; - /** Lock the object. It must get unlocked via {@link #monitorExit}. */ - public native void monitorEnter(Object o); - - /** - * Unlock the object. It must have been locked via {@link - * #monitorEnter}. - */ - public native void monitorExit(Object o); - - /** - * Tries to lock the object. Returns true or false to indicate - * whether the lock succeeded. If it did, the object must be - * unlocked via {@link #monitorExit}. - */ - public native boolean tryMonitorEnter(Object o); - /** Throw the exception without telling the verifier. */ public native void throwException(Throwable ee); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/solaris/native/libnet/solaris_close.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/solaris/native/libnet/solaris_close.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 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 +#include +#include +#include + +/* Support for restartable system calls on Solaris. */ + +#define RESTARTABLE_RETURN_INT(_cmd) do { \ + int _result; \ + if (1) { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ + return _result; \ + } \ +} while(0) + +int NET_Read(int s, void* buf, size_t len) { + RESTARTABLE_RETURN_INT(recv(s, buf, len, 0)); +} + +int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) { + RESTARTABLE_RETURN_INT(recvfrom(s, buf, len, flags, from, fromlen)); +} + +int NET_ReadV(int s, const struct iovec * vector, int count) { + RESTARTABLE_RETURN_INT(readv(s, vector, count)); +} + +int NET_WriteV(int s, const struct iovec * vector, int count) { + RESTARTABLE_RETURN_INT(writev(s, vector, count)); +} + +int NET_Send(int s, void *msg, int len, unsigned int flags) { + RESTARTABLE_RETURN_INT(send(s, msg, len, flags)); +} + +int NET_SendTo(int s, const void *msg, int len, unsigned int flags, + const struct sockaddr *to, int tolen) { + RESTARTABLE_RETURN_INT(sendto(s, msg, len, flags, to, tolen)); +} + +int NET_Connect(int s, struct sockaddr *addr, int addrlen) { + RESTARTABLE_RETURN_INT(connect(s, addr, addrlen)); +} + +int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + RESTARTABLE_RETURN_INT(accept(s, addr, addrlen)); +} + +int NET_SocketClose(int fd) { + return close(fd); +} + +int NET_Dup2(int fd, int fd2) { + return dup2(fd, fd2); +} + +int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + RESTARTABLE_RETURN_INT(poll(ufds, nfds, timeout)); +} + +int NET_Timeout(int s, long timeout) { + int result; + struct timeval t; + long prevtime, newtime; + struct pollfd pfd; + pfd.fd = s; + pfd.events = POLLIN; + + if (timeout > 0) { + gettimeofday(&t, NULL); + prevtime = (t.tv_sec * 1000) + t.tv_usec / 1000; + } + + for(;;) { + result = poll(&pfd, 1, timeout); + if (result < 0 && errno == EINTR) { + if (timeout > 0) { + gettimeofday(&t, NULL); + newtime = (t.tv_sec * 1000) + t.tv_usec /1000; + timeout -= newtime - prevtime; + if (timeout <= 0) + return 0; + prevtime = newtime; + } + } else { + return result; + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java --- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -25,24 +25,156 @@ package java.lang; -import java.io.IOException; +import java.lang.ProcessBuilder.Redirect; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.lang.ProcessBuilder.Redirect; -import java.lang.ProcessBuilder.Redirect; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.security.AccessController; +import static java.security.AccessController.doPrivileged; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; /** - * This class is for the exclusive use of ProcessBuilder.start() to - * create new processes. + * This java.lang.Process subclass in the UNIX environment is for the exclusive use of + * ProcessBuilder.start() to create new processes. * + * @author Mario Wolczko and Ross Knippel. + * @author Konstantin Kladko (ported to Linux and Bsd) * @author Martin Buchholz + * @author Volker Simonis (ported to AIX) * @since 1.5 */ -final class ProcessImpl { +final class ProcessImpl extends Process { private static final sun.misc.JavaIOFileDescriptorAccess fdAccess = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); - private ProcessImpl() {} // Not instantiable + private final int pid; + private int exitcode; + private boolean hasExited; + + private /* final */ OutputStream stdin; + private /* final */ InputStream stdout; + private /* final */ InputStream stderr; + + // only used on Solaris + private /* final */ DeferredCloseInputStream stdout_inner_stream; + + private static enum LaunchMechanism { + // order IS important! + FORK, + POSIX_SPAWN, + VFORK + } + + private static enum Platform { + + LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK), + + BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), + + SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), + + AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); + + final LaunchMechanism defaultLaunchMechanism; + final Set validLaunchMechanisms; + + Platform(LaunchMechanism ... launchMechanisms) { + this.defaultLaunchMechanism = launchMechanisms[0]; + this.validLaunchMechanisms = + EnumSet.copyOf(Arrays.asList(launchMechanisms)); + } + + @SuppressWarnings("fallthrough") + private String helperPath(String javahome, String osArch) { + switch (this) { + case SOLARIS: + if (osArch.equals("x86")) { osArch = "i386"; } + else if (osArch.equals("x86_64")) { osArch = "amd64"; } + // fall through... + case LINUX: + case AIX: + return javahome + "/lib/" + osArch + "/jspawnhelper"; + + case BSD: + return javahome + "/lib/jspawnhelper"; + + default: + throw new AssertionError("Unsupported platform: " + this); + } + } + + String helperPath() { + return AccessController.doPrivileged( + (PrivilegedAction) () -> + helperPath(System.getProperty("java.home"), + System.getProperty("os.arch")) + ); + } + + LaunchMechanism launchMechanism() { + return AccessController.doPrivileged( + (PrivilegedAction) () -> { + String s = System.getProperty( + "jdk.lang.Process.launchMechanism"); + LaunchMechanism lm; + if (s == null) { + lm = defaultLaunchMechanism; + s = lm.name().toLowerCase(Locale.ENGLISH); + } else { + try { + lm = LaunchMechanism.valueOf( + s.toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + lm = null; + } + } + if (lm == null || !validLaunchMechanisms.contains(lm)) { + throw new Error( + s + " is not a supported " + + "process launch mechanism on this platform." + ); + } + return lm; + } + ); + } + + static Platform get() { + String osName = AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty("os.name") + ); + + if (osName.equals("Linux")) { return LINUX; } + if (osName.contains("OS X")) { return BSD; } + if (osName.equals("SunOS")) { return SOLARIS; } + if (osName.equals("AIX")) { return AIX; } + + throw new Error(osName + " is not a supported OS platform."); + } + } + + private static final Platform platform = Platform.get(); + private static final LaunchMechanism launchMechanism = platform.launchMechanism(); + private static final byte[] helperpath = toCString(platform.helperPath()); + + /* this is for the reaping thread */ + private native int waitForProcessExit(int pid); private static byte[] toCString(String s) { if (s == null) @@ -50,8 +182,8 @@ byte[] bytes = s.getBytes(); byte[] result = new byte[bytes.length + 1]; System.arraycopy(bytes, 0, - result, 0, - bytes.length); + result, 0, + bytes.length); result[result.length-1] = (byte)0; return result; } @@ -62,7 +194,7 @@ String dir, ProcessBuilder.Redirect[] redirects, boolean redirectErrorStream) - throws IOException + throws IOException { assert cmdarray != null && cmdarray.length > 0; @@ -112,7 +244,7 @@ std_fds[1] = 1; else { f1 = new FileOutputStream(redirects[1].file(), - redirects[1].append()); + redirects[1].append()); std_fds[1] = fdAccess.get(f1.getFD()); } @@ -122,18 +254,18 @@ std_fds[2] = 2; else { f2 = new FileOutputStream(redirects[2].file(), - redirects[2].append()); + redirects[2].append()); std_fds[2] = fdAccess.get(f2.getFD()); } } - return new UNIXProcess - (toCString(cmdarray[0]), - argBlock, args.length, - envBlock, envc[0], - toCString(dir), - std_fds, - redirectErrorStream); + return new ProcessImpl + (toCString(cmdarray[0]), + argBlock, args.length, + envBlock, envc[0], + toCString(dir), + std_fds, + redirectErrorStream); } finally { // In theory, close() can throw IOException // (although it is rather unlikely to happen here) @@ -144,4 +276,654 @@ } } } + + + /** + * Creates a process. Depending on the {@code mode} flag, this is done by + * one of the following mechanisms: + *

    +     *   1 - fork(2) and exec(2)
    +     *   2 - posix_spawn(3P)
    +     *   3 - vfork(2) and exec(2)
    +     *
    +     *  (4 - clone(2) and exec(2) - obsolete and currently disabled in native code)
    +     * 
    + * @param fds an array of three file descriptors. + * Indexes 0, 1, and 2 correspond to standard input, + * standard output and standard error, respectively. On + * input, a value of -1 means to create a pipe to connect + * child and parent processes. On output, a value which + * is not -1 is the parent pipe fd corresponding to the + * pipe which has been created. An element of this array + * is -1 on input if and only if it is not -1 on + * output. + * @return the pid of the subprocess + */ + private native int forkAndExec(int mode, byte[] helperpath, + byte[] prog, + byte[] argBlock, int argc, + byte[] envBlock, int envc, + byte[] dir, + int[] fds, + boolean redirectErrorStream) + throws IOException; + + /** + * The thread pool of "process reaper" daemon threads. + */ + private static final Executor processReaperExecutor = + doPrivileged((PrivilegedAction) () -> { + + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + while (tg.getParent() != null) tg = tg.getParent(); + ThreadGroup systemThreadGroup = tg; + + ThreadFactory threadFactory = grimReaper -> { + // Our thread stack requirement is quite modest. + Thread t = new Thread(systemThreadGroup, grimReaper, + "process reaper", 32768); + t.setDaemon(true); + // A small attempt (probably futile) to avoid priority inversion + t.setPriority(Thread.MAX_PRIORITY); + return t; + }; + + return Executors.newCachedThreadPool(threadFactory); + }); + + private ProcessImpl(final byte[] prog, + final byte[] argBlock, final int argc, + final byte[] envBlock, final int envc, + final byte[] dir, + final int[] fds, + final boolean redirectErrorStream) + throws IOException { + + pid = forkAndExec(launchMechanism.ordinal() + 1, + helperpath, + prog, + argBlock, argc, + envBlock, envc, + dir, + fds, + redirectErrorStream); + + try { + doPrivileged((PrivilegedExceptionAction) () -> { + initStreams(fds); + return null; + }); + } catch (PrivilegedActionException ex) { + throw (IOException) ex.getException(); + } + } + + static FileDescriptor newFileDescriptor(int fd) { + FileDescriptor fileDescriptor = new FileDescriptor(); + fdAccess.set(fileDescriptor, fd); + return fileDescriptor; + } + + void initStreams(int[] fds) throws IOException { + switch (platform) { + case LINUX: + case BSD: + stdin = (fds[0] == -1) ? + ProcessBuilder.NullOutputStream.INSTANCE : + new ProcessPipeOutputStream(fds[0]); + + stdout = (fds[1] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new ProcessPipeInputStream(fds[1]); + + stderr = (fds[2] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new ProcessPipeInputStream(fds[2]); + + processReaperExecutor.execute(() -> { + int exitcode = waitForProcessExit(pid); + + synchronized (this) { + this.exitcode = exitcode; + this.hasExited = true; + this.notifyAll(); + } + + if (stdout instanceof ProcessPipeInputStream) + ((ProcessPipeInputStream) stdout).processExited(); + + if (stderr instanceof ProcessPipeInputStream) + ((ProcessPipeInputStream) stderr).processExited(); + + if (stdin instanceof ProcessPipeOutputStream) + ((ProcessPipeOutputStream) stdin).processExited(); + }); + break; + + case SOLARIS: + stdin = (fds[0] == -1) ? + ProcessBuilder.NullOutputStream.INSTANCE : + new BufferedOutputStream( + new FileOutputStream(newFileDescriptor(fds[0]))); + + stdout = (fds[1] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new BufferedInputStream( + stdout_inner_stream = + new DeferredCloseInputStream( + newFileDescriptor(fds[1]))); + + stderr = (fds[2] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new DeferredCloseInputStream(newFileDescriptor(fds[2])); + + /* + * For each subprocess forked a corresponding reaper task + * is submitted. That task is the only thread which waits + * for the subprocess to terminate and it doesn't hold any + * locks while doing so. This design allows waitFor() and + * exitStatus() to be safely executed in parallel (and they + * need no native code). + */ + processReaperExecutor.execute(() -> { + int exitcode = waitForProcessExit(pid); + + synchronized (this) { + this.exitcode = exitcode; + this.hasExited = true; + this.notifyAll(); + } + }); + break; + + case AIX: + stdin = (fds[0] == -1) ? + ProcessBuilder.NullOutputStream.INSTANCE : + new ProcessPipeOutputStream(fds[0]); + + stdout = (fds[1] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new DeferredCloseProcessPipeInputStream(fds[1]); + + stderr = (fds[2] == -1) ? + ProcessBuilder.NullInputStream.INSTANCE : + new DeferredCloseProcessPipeInputStream(fds[2]); + + processReaperExecutor.execute(() -> { + int exitcode = waitForProcessExit(pid); + + synchronized (this) { + this.exitcode = exitcode; + this.hasExited = true; + this.notifyAll(); + } + + if (stdout instanceof DeferredCloseProcessPipeInputStream) + ((DeferredCloseProcessPipeInputStream) stdout).processExited(); + + if (stderr instanceof DeferredCloseProcessPipeInputStream) + ((DeferredCloseProcessPipeInputStream) stderr).processExited(); + + if (stdin instanceof ProcessPipeOutputStream) + ((ProcessPipeOutputStream) stdin).processExited(); + }); + break; + + default: throw new AssertionError("Unsupported platform: " + platform); + } + } + + public OutputStream getOutputStream() { + return stdin; + } + + public InputStream getInputStream() { + return stdout; + } + + public InputStream getErrorStream() { + return stderr; + } + + public synchronized int waitFor() throws InterruptedException { + while (!hasExited) { + wait(); + } + return exitcode; + } + + @Override + public synchronized boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + if (hasExited) return true; + if (timeout <= 0) return false; + + long remainingNanos = unit.toNanos(timeout); + long deadline = System.nanoTime() + remainingNanos; + + do { + // Round up to next millisecond + wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L)); + if (hasExited) { + return true; + } + remainingNanos = deadline - System.nanoTime(); + } while (remainingNanos > 0); + return hasExited; + } + + public synchronized int exitValue() { + if (!hasExited) { + throw new IllegalThreadStateException("process hasn't exited"); + } + return exitcode; + } + + private static native void destroyProcess(int pid, boolean force); + + private void destroy(boolean force) { + switch (platform) { + case LINUX: + case BSD: + case AIX: + // There is a risk that pid will be recycled, causing us to + // kill the wrong process! So we only terminate processes + // that appear to still be running. Even with this check, + // there is an unavoidable race condition here, but the window + // is very small, and OSes try hard to not recycle pids too + // soon, so this is quite safe. + synchronized (this) { + if (!hasExited) + destroyProcess(pid, force); + } + try { stdin.close(); } catch (IOException ignored) {} + try { stdout.close(); } catch (IOException ignored) {} + try { stderr.close(); } catch (IOException ignored) {} + break; + + case SOLARIS: + // There is a risk that pid will be recycled, causing us to + // kill the wrong process! So we only terminate processes + // that appear to still be running. Even with this check, + // there is an unavoidable race condition here, but the window + // is very small, and OSes try hard to not recycle pids too + // soon, so this is quite safe. + synchronized (this) { + if (!hasExited) + destroyProcess(pid, force); + try { + stdin.close(); + if (stdout_inner_stream != null) + stdout_inner_stream.closeDeferred(stdout); + if (stderr instanceof DeferredCloseInputStream) + ((DeferredCloseInputStream) stderr) + .closeDeferred(stderr); + } catch (IOException e) { + // ignore + } + } + break; + + default: throw new AssertionError("Unsupported platform: " + platform); + } + } + + public void destroy() { + destroy(false); + } + + @Override + public Process destroyForcibly() { + destroy(true); + return this; + } + + @Override + public long getPid() { + return pid; + } + + @Override + public synchronized boolean isAlive() { + return !hasExited; + } + + private static native void init(); + + static { + init(); + } + + /** + * A buffered input stream for a subprocess pipe file descriptor + * that allows the underlying file descriptor to be reclaimed when + * the process exits, via the processExited hook. + * + * This is tricky because we do not want the user-level InputStream to be + * closed until the user invokes close(), and we need to continue to be + * able to read any buffered data lingering in the OS pipe buffer. + */ + private static class ProcessPipeInputStream extends BufferedInputStream { + private final Object closeLock = new Object(); + + ProcessPipeInputStream(int fd) { + super(new FileInputStream(newFileDescriptor(fd))); + } + private static byte[] drainInputStream(InputStream in) + throws IOException { + int n = 0; + int j; + byte[] a = null; + while ((j = in.available()) > 0) { + a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); + n += in.read(a, n, j); + } + return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); + } + + /** Called by the process reaper thread when the process exits. */ + synchronized void processExited() { + synchronized (closeLock) { + try { + InputStream in = this.in; + // this stream is closed if and only if: in == null + if (in != null) { + byte[] stragglers = drainInputStream(in); + in.close(); + this.in = (stragglers == null) ? + ProcessBuilder.NullInputStream.INSTANCE : + new ByteArrayInputStream(stragglers); + } + } catch (IOException ignored) {} + } + } + + @Override + public void close() throws IOException { + // BufferedInputStream#close() is not synchronized unlike most other + // methods. Synchronizing helps avoid race with processExited(). + synchronized (closeLock) { + super.close(); + } + } + } + + /** + * A buffered output stream for a subprocess pipe file descriptor + * that allows the underlying file descriptor to be reclaimed when + * the process exits, via the processExited hook. + */ + private static class ProcessPipeOutputStream extends BufferedOutputStream { + ProcessPipeOutputStream(int fd) { + super(new FileOutputStream(newFileDescriptor(fd))); + } + + /** Called by the process reaper thread when the process exits. */ + synchronized void processExited() { + OutputStream out = this.out; + if (out != null) { + try { + out.close(); + } catch (IOException ignored) { + // We know of no reason to get an IOException, but if + // we do, there's nothing else to do but carry on. + } + this.out = ProcessBuilder.NullOutputStream.INSTANCE; + } + } + } + + // A FileInputStream that supports the deferment of the actual close + // operation until the last pending I/O operation on the stream has + // finished. This is required on Solaris because we must close the stdin + // and stdout streams in the destroy method in order to reclaim the + // underlying file descriptors. Doing so, however, causes any thread + // currently blocked in a read on one of those streams to receive an + // IOException("Bad file number"), which is incompatible with historical + // behavior. By deferring the close we allow any pending reads to see -1 + // (EOF) as they did before. + // + private static class DeferredCloseInputStream extends FileInputStream + { + DeferredCloseInputStream(FileDescriptor fd) { + super(fd); + } + + private Object lock = new Object(); // For the following fields + private boolean closePending = false; + private int useCount = 0; + private InputStream streamToClose; + + private void raise() { + synchronized (lock) { + useCount++; + } + } + + private void lower() throws IOException { + synchronized (lock) { + useCount--; + if (useCount == 0 && closePending) { + streamToClose.close(); + } + } + } + + // stc is the actual stream to be closed; it might be this object, or + // it might be an upstream object for which this object is downstream. + // + private void closeDeferred(InputStream stc) throws IOException { + synchronized (lock) { + if (useCount == 0) { + stc.close(); + } else { + closePending = true; + streamToClose = stc; + } + } + } + + public void close() throws IOException { + synchronized (lock) { + useCount = 0; + closePending = false; + } + super.close(); + } + + public int read() throws IOException { + raise(); + try { + return super.read(); + } finally { + lower(); + } + } + + public int read(byte[] b) throws IOException { + raise(); + try { + return super.read(b); + } finally { + lower(); + } + } + + public int read(byte[] b, int off, int len) throws IOException { + raise(); + try { + return super.read(b, off, len); + } finally { + lower(); + } + } + + public long skip(long n) throws IOException { + raise(); + try { + return super.skip(n); + } finally { + lower(); + } + } + + public int available() throws IOException { + raise(); + try { + return super.available(); + } finally { + lower(); + } + } + } + + /** + * A buffered input stream for a subprocess pipe file descriptor + * that allows the underlying file descriptor to be reclaimed when + * the process exits, via the processExited hook. + * + * This is tricky because we do not want the user-level InputStream to be + * closed until the user invokes close(), and we need to continue to be + * able to read any buffered data lingering in the OS pipe buffer. + * + * On AIX this is especially tricky, because the 'close()' system call + * will block if another thread is at the same time blocked in a file + * operation (e.g. 'read()') on the same file descriptor. We therefore + * combine 'ProcessPipeInputStream' approach used on Linux and Bsd + * with the DeferredCloseInputStream approach used on Solaris. This means + * that every potentially blocking operation on the file descriptor + * increments a counter before it is executed and decrements it once it + * finishes. The 'close()' operation will only be executed if there are + * no pending operations. Otherwise it is deferred after the last pending + * operation has finished. + * + */ + private static class DeferredCloseProcessPipeInputStream + extends BufferedInputStream { + + private final Object closeLock = new Object(); + private int useCount = 0; + private boolean closePending = false; + + DeferredCloseProcessPipeInputStream(int fd) { + super(new FileInputStream(newFileDescriptor(fd))); + } + + private InputStream drainInputStream(InputStream in) + throws IOException { + int n = 0; + int j; + byte[] a = null; + synchronized (closeLock) { + if (buf == null) // asynchronous close()? + return null; // discard + j = in.available(); + } + while (j > 0) { + a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); + synchronized (closeLock) { + if (buf == null) // asynchronous close()? + return null; // discard + n += in.read(a, n, j); + j = in.available(); + } + } + return (a == null) ? + ProcessBuilder.NullInputStream.INSTANCE : + new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); + } + + /** Called by the process reaper thread when the process exits. */ + synchronized void processExited() { + try { + InputStream in = this.in; + if (in != null) { + InputStream stragglers = drainInputStream(in); + in.close(); + this.in = stragglers; + } + } catch (IOException ignored) { } + } + + private void raise() { + synchronized (closeLock) { + useCount++; + } + } + + private void lower() throws IOException { + synchronized (closeLock) { + useCount--; + if (useCount == 0 && closePending) { + closePending = false; + super.close(); + } + } + } + + @Override + public int read() throws IOException { + raise(); + try { + return super.read(); + } finally { + lower(); + } + } + + @Override + public int read(byte[] b) throws IOException { + raise(); + try { + return super.read(b); + } finally { + lower(); + } + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + raise(); + try { + return super.read(b, off, len); + } finally { + lower(); + } + } + + @Override + public long skip(long n) throws IOException { + raise(); + try { + return super.skip(n); + } finally { + lower(); + } + } + + @Override + public int available() throws IOException { + raise(); + try { + return super.available(); + } finally { + lower(); + } + } + + @Override + public void close() throws IOException { + // BufferedInputStream#close() is not synchronized unlike most other + // methods. Synchronizing helps avoid racing with drainInputStream(). + synchronized (closeLock) { + if (useCount == 0) { + super.close(); + } + else { + closePending = true; + } + } + } + } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/classes/java/lang/UNIXProcess.java --- a/jdk/src/java.base/unix/classes/java/lang/UNIXProcess.java Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,836 +0,0 @@ -/* - * Copyright (c) 1995, 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. - */ - -package java.lang; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.Executors; -import java.util.concurrent.Executor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.security.AccessController; -import static java.security.AccessController.doPrivileged; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; - -/** - * java.lang.Process subclass in the UNIX environment. - * - * @author Mario Wolczko and Ross Knippel. - * @author Konstantin Kladko (ported to Linux and Bsd) - * @author Martin Buchholz - * @author Volker Simonis (ported to AIX) - */ -final class UNIXProcess extends Process { - private static final sun.misc.JavaIOFileDescriptorAccess fdAccess - = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); - - private final int pid; - private int exitcode; - private boolean hasExited; - - private /* final */ OutputStream stdin; - private /* final */ InputStream stdout; - private /* final */ InputStream stderr; - - // only used on Solaris - private /* final */ DeferredCloseInputStream stdout_inner_stream; - - private static enum LaunchMechanism { - // order IS important! - FORK, - POSIX_SPAWN, - VFORK - } - - private static enum Platform { - - LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK), - - BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), - - SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK), - - AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK); - - final LaunchMechanism defaultLaunchMechanism; - final Set validLaunchMechanisms; - - Platform(LaunchMechanism ... launchMechanisms) { - this.defaultLaunchMechanism = launchMechanisms[0]; - this.validLaunchMechanisms = - EnumSet.copyOf(Arrays.asList(launchMechanisms)); - } - - @SuppressWarnings("fallthrough") - private String helperPath(String javahome, String osArch) { - switch (this) { - case SOLARIS: - if (osArch.equals("x86")) { osArch = "i386"; } - else if (osArch.equals("x86_64")) { osArch = "amd64"; } - // fall through... - case LINUX: - case AIX: - return javahome + "/lib/" + osArch + "/jspawnhelper"; - - case BSD: - return javahome + "/lib/jspawnhelper"; - - default: - throw new AssertionError("Unsupported platform: " + this); - } - } - - String helperPath() { - return AccessController.doPrivileged( - (PrivilegedAction) () -> - helperPath(System.getProperty("java.home"), - System.getProperty("os.arch")) - ); - } - - LaunchMechanism launchMechanism() { - return AccessController.doPrivileged( - (PrivilegedAction) () -> { - String s = System.getProperty( - "jdk.lang.Process.launchMechanism"); - LaunchMechanism lm; - if (s == null) { - lm = defaultLaunchMechanism; - s = lm.name().toLowerCase(Locale.ENGLISH); - } else { - try { - lm = LaunchMechanism.valueOf( - s.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - lm = null; - } - } - if (lm == null || !validLaunchMechanisms.contains(lm)) { - throw new Error( - s + " is not a supported " + - "process launch mechanism on this platform." - ); - } - return lm; - } - ); - } - - static Platform get() { - String osName = AccessController.doPrivileged( - (PrivilegedAction) () -> System.getProperty("os.name") - ); - - if (osName.equals("Linux")) { return LINUX; } - if (osName.contains("OS X")) { return BSD; } - if (osName.equals("SunOS")) { return SOLARIS; } - if (osName.equals("AIX")) { return AIX; } - - throw new Error(osName + " is not a supported OS platform."); - } - } - - private static final Platform platform = Platform.get(); - private static final LaunchMechanism launchMechanism = platform.launchMechanism(); - private static final byte[] helperpath = toCString(platform.helperPath()); - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length-1] = (byte)0; - return result; - } - - /* this is for the reaping thread */ - private native int waitForProcessExit(int pid); - - /** - * Creates a process. Depending on the {@code mode} flag, this is done by - * one of the following mechanisms: - *
    -     *   1 - fork(2) and exec(2)
    -     *   2 - posix_spawn(3P)
    -     *   3 - vfork(2) and exec(2)
    -     *
    -     *  (4 - clone(2) and exec(2) - obsolete and currently disabled in native code)
    -     * 
    - * @param fds an array of three file descriptors. - * Indexes 0, 1, and 2 correspond to standard input, - * standard output and standard error, respectively. On - * input, a value of -1 means to create a pipe to connect - * child and parent processes. On output, a value which - * is not -1 is the parent pipe fd corresponding to the - * pipe which has been created. An element of this array - * is -1 on input if and only if it is not -1 on - * output. - * @return the pid of the subprocess - */ - private native int forkAndExec(int mode, byte[] helperpath, - byte[] prog, - byte[] argBlock, int argc, - byte[] envBlock, int envc, - byte[] dir, - int[] fds, - boolean redirectErrorStream) - throws IOException; - - /** - * The thread pool of "process reaper" daemon threads. - */ - private static final Executor processReaperExecutor = - doPrivileged((PrivilegedAction) () -> { - - ThreadGroup tg = Thread.currentThread().getThreadGroup(); - while (tg.getParent() != null) tg = tg.getParent(); - ThreadGroup systemThreadGroup = tg; - - ThreadFactory threadFactory = grimReaper -> { - // Our thread stack requirement is quite modest. - Thread t = new Thread(systemThreadGroup, grimReaper, - "process reaper", 32768); - t.setDaemon(true); - // A small attempt (probably futile) to avoid priority inversion - t.setPriority(Thread.MAX_PRIORITY); - return t; - }; - - return Executors.newCachedThreadPool(threadFactory); - }); - - UNIXProcess(final byte[] prog, - final byte[] argBlock, final int argc, - final byte[] envBlock, final int envc, - final byte[] dir, - final int[] fds, - final boolean redirectErrorStream) - throws IOException { - - pid = forkAndExec(launchMechanism.ordinal() + 1, - helperpath, - prog, - argBlock, argc, - envBlock, envc, - dir, - fds, - redirectErrorStream); - - try { - doPrivileged((PrivilegedExceptionAction) () -> { - initStreams(fds); - return null; - }); - } catch (PrivilegedActionException ex) { - throw (IOException) ex.getException(); - } - } - - static FileDescriptor newFileDescriptor(int fd) { - FileDescriptor fileDescriptor = new FileDescriptor(); - fdAccess.set(fileDescriptor, fd); - return fileDescriptor; - } - - void initStreams(int[] fds) throws IOException { - switch (platform) { - case LINUX: - case BSD: - stdin = (fds[0] == -1) ? - ProcessBuilder.NullOutputStream.INSTANCE : - new ProcessPipeOutputStream(fds[0]); - - stdout = (fds[1] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new ProcessPipeInputStream(fds[1]); - - stderr = (fds[2] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new ProcessPipeInputStream(fds[2]); - - processReaperExecutor.execute(() -> { - int exitcode = waitForProcessExit(pid); - - synchronized (this) { - this.exitcode = exitcode; - this.hasExited = true; - this.notifyAll(); - } - - if (stdout instanceof ProcessPipeInputStream) - ((ProcessPipeInputStream) stdout).processExited(); - - if (stderr instanceof ProcessPipeInputStream) - ((ProcessPipeInputStream) stderr).processExited(); - - if (stdin instanceof ProcessPipeOutputStream) - ((ProcessPipeOutputStream) stdin).processExited(); - }); - break; - - case SOLARIS: - stdin = (fds[0] == -1) ? - ProcessBuilder.NullOutputStream.INSTANCE : - new BufferedOutputStream( - new FileOutputStream(newFileDescriptor(fds[0]))); - - stdout = (fds[1] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new BufferedInputStream( - stdout_inner_stream = - new DeferredCloseInputStream( - newFileDescriptor(fds[1]))); - - stderr = (fds[2] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new DeferredCloseInputStream(newFileDescriptor(fds[2])); - - /* - * For each subprocess forked a corresponding reaper task - * is submitted. That task is the only thread which waits - * for the subprocess to terminate and it doesn't hold any - * locks while doing so. This design allows waitFor() and - * exitStatus() to be safely executed in parallel (and they - * need no native code). - */ - processReaperExecutor.execute(() -> { - int exitcode = waitForProcessExit(pid); - - synchronized (this) { - this.exitcode = exitcode; - this.hasExited = true; - this.notifyAll(); - } - }); - break; - - case AIX: - stdin = (fds[0] == -1) ? - ProcessBuilder.NullOutputStream.INSTANCE : - new ProcessPipeOutputStream(fds[0]); - - stdout = (fds[1] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new DeferredCloseProcessPipeInputStream(fds[1]); - - stderr = (fds[2] == -1) ? - ProcessBuilder.NullInputStream.INSTANCE : - new DeferredCloseProcessPipeInputStream(fds[2]); - - processReaperExecutor.execute(() -> { - int exitcode = waitForProcessExit(pid); - - synchronized (this) { - this.exitcode = exitcode; - this.hasExited = true; - this.notifyAll(); - } - - if (stdout instanceof DeferredCloseProcessPipeInputStream) - ((DeferredCloseProcessPipeInputStream) stdout).processExited(); - - if (stderr instanceof DeferredCloseProcessPipeInputStream) - ((DeferredCloseProcessPipeInputStream) stderr).processExited(); - - if (stdin instanceof ProcessPipeOutputStream) - ((ProcessPipeOutputStream) stdin).processExited(); - }); - break; - - default: throw new AssertionError("Unsupported platform: " + platform); - } - } - - public OutputStream getOutputStream() { - return stdin; - } - - public InputStream getInputStream() { - return stdout; - } - - public InputStream getErrorStream() { - return stderr; - } - - public synchronized int waitFor() throws InterruptedException { - while (!hasExited) { - wait(); - } - return exitcode; - } - - @Override - public synchronized boolean waitFor(long timeout, TimeUnit unit) - throws InterruptedException - { - if (hasExited) return true; - if (timeout <= 0) return false; - - long remainingNanos = unit.toNanos(timeout); - long deadline = System.nanoTime() + remainingNanos; - - do { - // Round up to next millisecond - wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L)); - if (hasExited) { - return true; - } - remainingNanos = deadline - System.nanoTime(); - } while (remainingNanos > 0); - return hasExited; - } - - public synchronized int exitValue() { - if (!hasExited) { - throw new IllegalThreadStateException("process hasn't exited"); - } - return exitcode; - } - - private static native void destroyProcess(int pid, boolean force); - - private void destroy(boolean force) { - switch (platform) { - case LINUX: - case BSD: - case AIX: - // There is a risk that pid will be recycled, causing us to - // kill the wrong process! So we only terminate processes - // that appear to still be running. Even with this check, - // there is an unavoidable race condition here, but the window - // is very small, and OSes try hard to not recycle pids too - // soon, so this is quite safe. - synchronized (this) { - if (!hasExited) - destroyProcess(pid, force); - } - try { stdin.close(); } catch (IOException ignored) {} - try { stdout.close(); } catch (IOException ignored) {} - try { stderr.close(); } catch (IOException ignored) {} - break; - - case SOLARIS: - // There is a risk that pid will be recycled, causing us to - // kill the wrong process! So we only terminate processes - // that appear to still be running. Even with this check, - // there is an unavoidable race condition here, but the window - // is very small, and OSes try hard to not recycle pids too - // soon, so this is quite safe. - synchronized (this) { - if (!hasExited) - destroyProcess(pid, force); - try { - stdin.close(); - if (stdout_inner_stream != null) - stdout_inner_stream.closeDeferred(stdout); - if (stderr instanceof DeferredCloseInputStream) - ((DeferredCloseInputStream) stderr) - .closeDeferred(stderr); - } catch (IOException e) { - // ignore - } - } - break; - - default: throw new AssertionError("Unsupported platform: " + platform); - } - } - - public void destroy() { - destroy(false); - } - - @Override - public Process destroyForcibly() { - destroy(true); - return this; - } - - @Override - public long getPid() { - return pid; - } - - @Override - public synchronized boolean isAlive() { - return !hasExited; - } - - private static native void init(); - - static { - init(); - } - - /** - * A buffered input stream for a subprocess pipe file descriptor - * that allows the underlying file descriptor to be reclaimed when - * the process exits, via the processExited hook. - * - * This is tricky because we do not want the user-level InputStream to be - * closed until the user invokes close(), and we need to continue to be - * able to read any buffered data lingering in the OS pipe buffer. - */ - private static class ProcessPipeInputStream extends BufferedInputStream { - private final Object closeLock = new Object(); - - ProcessPipeInputStream(int fd) { - super(new FileInputStream(newFileDescriptor(fd))); - } - private static byte[] drainInputStream(InputStream in) - throws IOException { - int n = 0; - int j; - byte[] a = null; - while ((j = in.available()) > 0) { - a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); - n += in.read(a, n, j); - } - return (a == null || n == a.length) ? a : Arrays.copyOf(a, n); - } - - /** Called by the process reaper thread when the process exits. */ - synchronized void processExited() { - synchronized (closeLock) { - try { - InputStream in = this.in; - // this stream is closed if and only if: in == null - if (in != null) { - byte[] stragglers = drainInputStream(in); - in.close(); - this.in = (stragglers == null) ? - ProcessBuilder.NullInputStream.INSTANCE : - new ByteArrayInputStream(stragglers); - } - } catch (IOException ignored) {} - } - } - - @Override - public void close() throws IOException { - // BufferedInputStream#close() is not synchronized unlike most other - // methods. Synchronizing helps avoid race with processExited(). - synchronized (closeLock) { - super.close(); - } - } - } - - /** - * A buffered output stream for a subprocess pipe file descriptor - * that allows the underlying file descriptor to be reclaimed when - * the process exits, via the processExited hook. - */ - private static class ProcessPipeOutputStream extends BufferedOutputStream { - ProcessPipeOutputStream(int fd) { - super(new FileOutputStream(newFileDescriptor(fd))); - } - - /** Called by the process reaper thread when the process exits. */ - synchronized void processExited() { - OutputStream out = this.out; - if (out != null) { - try { - out.close(); - } catch (IOException ignored) { - // We know of no reason to get an IOException, but if - // we do, there's nothing else to do but carry on. - } - this.out = ProcessBuilder.NullOutputStream.INSTANCE; - } - } - } - - // A FileInputStream that supports the deferment of the actual close - // operation until the last pending I/O operation on the stream has - // finished. This is required on Solaris because we must close the stdin - // and stdout streams in the destroy method in order to reclaim the - // underlying file descriptors. Doing so, however, causes any thread - // currently blocked in a read on one of those streams to receive an - // IOException("Bad file number"), which is incompatible with historical - // behavior. By deferring the close we allow any pending reads to see -1 - // (EOF) as they did before. - // - private static class DeferredCloseInputStream extends FileInputStream - { - DeferredCloseInputStream(FileDescriptor fd) { - super(fd); - } - - private Object lock = new Object(); // For the following fields - private boolean closePending = false; - private int useCount = 0; - private InputStream streamToClose; - - private void raise() { - synchronized (lock) { - useCount++; - } - } - - private void lower() throws IOException { - synchronized (lock) { - useCount--; - if (useCount == 0 && closePending) { - streamToClose.close(); - } - } - } - - // stc is the actual stream to be closed; it might be this object, or - // it might be an upstream object for which this object is downstream. - // - private void closeDeferred(InputStream stc) throws IOException { - synchronized (lock) { - if (useCount == 0) { - stc.close(); - } else { - closePending = true; - streamToClose = stc; - } - } - } - - public void close() throws IOException { - synchronized (lock) { - useCount = 0; - closePending = false; - } - super.close(); - } - - public int read() throws IOException { - raise(); - try { - return super.read(); - } finally { - lower(); - } - } - - public int read(byte[] b) throws IOException { - raise(); - try { - return super.read(b); - } finally { - lower(); - } - } - - public int read(byte[] b, int off, int len) throws IOException { - raise(); - try { - return super.read(b, off, len); - } finally { - lower(); - } - } - - public long skip(long n) throws IOException { - raise(); - try { - return super.skip(n); - } finally { - lower(); - } - } - - public int available() throws IOException { - raise(); - try { - return super.available(); - } finally { - lower(); - } - } - } - - /** - * A buffered input stream for a subprocess pipe file descriptor - * that allows the underlying file descriptor to be reclaimed when - * the process exits, via the processExited hook. - * - * This is tricky because we do not want the user-level InputStream to be - * closed until the user invokes close(), and we need to continue to be - * able to read any buffered data lingering in the OS pipe buffer. - * - * On AIX this is especially tricky, because the 'close()' system call - * will block if another thread is at the same time blocked in a file - * operation (e.g. 'read()') on the same file descriptor. We therefore - * combine 'ProcessPipeInputStream' approach used on Linux and Bsd - * with the DeferredCloseInputStream approach used on Solaris. This means - * that every potentially blocking operation on the file descriptor - * increments a counter before it is executed and decrements it once it - * finishes. The 'close()' operation will only be executed if there are - * no pending operations. Otherwise it is deferred after the last pending - * operation has finished. - * - */ - private static class DeferredCloseProcessPipeInputStream - extends BufferedInputStream { - - private final Object closeLock = new Object(); - private int useCount = 0; - private boolean closePending = false; - - DeferredCloseProcessPipeInputStream(int fd) { - super(new FileInputStream(newFileDescriptor(fd))); - } - - private InputStream drainInputStream(InputStream in) - throws IOException { - int n = 0; - int j; - byte[] a = null; - synchronized (closeLock) { - if (buf == null) // asynchronous close()? - return null; // discard - j = in.available(); - } - while (j > 0) { - a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j); - synchronized (closeLock) { - if (buf == null) // asynchronous close()? - return null; // discard - n += in.read(a, n, j); - j = in.available(); - } - } - return (a == null) ? - ProcessBuilder.NullInputStream.INSTANCE : - new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n)); - } - - /** Called by the process reaper thread when the process exits. */ - synchronized void processExited() { - try { - InputStream in = this.in; - if (in != null) { - InputStream stragglers = drainInputStream(in); - in.close(); - this.in = stragglers; - } - } catch (IOException ignored) { } - } - - private void raise() { - synchronized (closeLock) { - useCount++; - } - } - - private void lower() throws IOException { - synchronized (closeLock) { - useCount--; - if (useCount == 0 && closePending) { - closePending = false; - super.close(); - } - } - } - - @Override - public int read() throws IOException { - raise(); - try { - return super.read(); - } finally { - lower(); - } - } - - @Override - public int read(byte[] b) throws IOException { - raise(); - try { - return super.read(b); - } finally { - lower(); - } - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - raise(); - try { - return super.read(b, off, len); - } finally { - lower(); - } - } - - @Override - public long skip(long n) throws IOException { - raise(); - try { - return super.skip(n); - } finally { - lower(); - } - } - - @Override - public int available() throws IOException { - raise(); - try { - return super.available(); - } finally { - lower(); - } - } - - @Override - public void close() throws IOException { - // BufferedInputStream#close() is not synchronized unlike most other - // methods. Synchronizing helps avoid racing with drainInputStream(). - synchronized (closeLock) { - if (useCount == 0) { - super.close(); - } - else { - closePending = true; - } - } - } - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,724 @@ +/* + * Copyright (c) 1995, 2015, 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. + */ + +#undef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE 1 + +#include "jni.h" +#include "jvm.h" +#include "jvm_md.h" +#include "jni_util.h" +#include "io_util.h" + +/* + * Platform-specific support for java.lang.Process + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) +#include +#endif + +#include "childproc.h" + +/* + * There are 4 possible strategies we might use to "fork": + * + * - fork(2). Very portable and reliable but subject to + * failure due to overcommit (see the documentation on + * /proc/sys/vm/overcommit_memory in Linux proc(5)). + * This is the ancient problem of spurious failure whenever a large + * process starts a small subprocess. + * + * - vfork(). Using this is scary because all relevant man pages + * contain dire warnings, e.g. Linux vfork(2). But at least it's + * documented in the glibc docs and is standardized by XPG4. + * http://www.opengroup.org/onlinepubs/000095399/functions/vfork.html + * On Linux, one might think that vfork() would be implemented using + * the clone system call with flag CLONE_VFORK, but in fact vfork is + * a separate system call (which is a good sign, suggesting that + * vfork will continue to be supported at least on Linux). + * Another good sign is that glibc implements posix_spawn using + * vfork whenever possible. Note that we cannot use posix_spawn + * ourselves because there's no reliable way to close all inherited + * file descriptors. + * + * - clone() with flags CLONE_VM but not CLONE_THREAD. clone() is + * Linux-specific, but this ought to work - at least the glibc + * sources contain code to handle different combinations of CLONE_VM + * and CLONE_THREAD. However, when this was implemented, it + * appeared to fail on 32-bit i386 (but not 64-bit x86_64) Linux with + * the simple program + * Runtime.getRuntime().exec("/bin/true").waitFor(); + * with: + * # Internal Error (os_linux_x86.cpp:683), pid=19940, tid=2934639536 + * # Error: pthread_getattr_np failed with errno = 3 (ESRCH) + * We believe this is a glibc bug, reported here: + * http://sources.redhat.com/bugzilla/show_bug.cgi?id=10311 + * but the glibc maintainers closed it as WONTFIX. + * + * - posix_spawn(). While posix_spawn() is a fairly elaborate and + * complicated system call, it can't quite do everything that the old + * fork()/exec() combination can do, so the only feasible way to do + * this, is to use posix_spawn to launch a new helper executable + * "jprochelper", which in turn execs the target (after cleaning + * up file-descriptors etc.) The end result is the same as before, + * a child process linked to the parent in the same way, but it + * avoids the problem of duplicating the parent (VM) process + * address space temporarily, before launching the target command. + * + * Based on the above analysis, we are currently using vfork() on + * Linux and spawn() on other Unix systems, but the code to use clone() + * and fork() remains. + */ + + +static void +setSIGCHLDHandler(JNIEnv *env) +{ + /* There is a subtle difference between having the signal handler + * for SIGCHLD be SIG_DFL and SIG_IGN. We cannot obtain process + * termination information for child processes if the signal + * handler is SIG_IGN. It must be SIG_DFL. + * + * We used to set the SIGCHLD handler only on Linux, but it's + * safest to set it unconditionally. + * + * Consider what happens if java's parent process sets the SIGCHLD + * handler to SIG_IGN. Normally signal handlers are inherited by + * children, but SIGCHLD is a controversial case. Solaris appears + * to always reset it to SIG_DFL, but this behavior may be + * non-standard-compliant, and we shouldn't rely on it. + * + * References: + * http://www.opengroup.org/onlinepubs/7908799/xsh/exec.html + * http://www.pasc.org/interps/unofficial/db/p1003.1/pasc-1003.1-132.html + */ + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) < 0) + JNU_ThrowInternalError(env, "Can't set SIGCHLD handler"); +} + +static void* +xmalloc(JNIEnv *env, size_t size) +{ + void *p = malloc(size); + if (p == NULL) + JNU_ThrowOutOfMemoryError(env, NULL); + return p; +} + +#define NEW(type, n) ((type *) xmalloc(env, (n) * sizeof(type))) + +/** + * If PATH is not defined, the OS provides some default value. + * Unfortunately, there's no portable way to get this value. + * Fortunately, it's only needed if the child has PATH while we do not. + */ +static const char* +defaultPath(void) +{ +#ifdef __solaris__ + /* These really are the Solaris defaults! */ + return (geteuid() == 0 || getuid() == 0) ? + "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:/usr/sbin" : + "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:"; +#else + return ":/bin:/usr/bin"; /* glibc */ +#endif +} + +static const char* +effectivePath(void) +{ + const char *s = getenv("PATH"); + return (s != NULL) ? s : defaultPath(); +} + +static int +countOccurrences(const char *s, char c) +{ + int count; + for (count = 0; *s != '\0'; s++) + count += (*s == c); + return count; +} + +static const char * const * +effectivePathv(JNIEnv *env) +{ + char *p; + int i; + const char *path = effectivePath(); + int count = countOccurrences(path, ':') + 1; + size_t pathvsize = sizeof(const char *) * (count+1); + size_t pathsize = strlen(path) + 1; + const char **pathv = (const char **) xmalloc(env, pathvsize + pathsize); + + if (pathv == NULL) + return NULL; + p = (char *) pathv + pathvsize; + memcpy(p, path, pathsize); + /* split PATH by replacing ':' with NULs; empty components => "." */ + for (i = 0; i < count; i++) { + char *q = p + strcspn(p, ":"); + pathv[i] = (p == q) ? "." : p; + *q = '\0'; + p = q + 1; + } + pathv[count] = NULL; + return pathv; +} + +JNIEXPORT void JNICALL +Java_java_lang_ProcessImpl_init(JNIEnv *env, jclass clazz) +{ + parentPathv = effectivePathv(env); + CHECK_NULL(parentPathv); + setSIGCHLDHandler(env); +} + + +#ifndef WIFEXITED +#define WIFEXITED(status) (((status)&0xFF) == 0) +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status)>>8)&0xFF) +#endif + +#ifndef WIFSIGNALED +#define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0) +#endif + +#ifndef WTERMSIG +#define WTERMSIG(status) ((status)&0x7F) +#endif + +/* Block until a child process exits and return its exit code. + Note, can only be called once for any given pid. */ +JNIEXPORT jint JNICALL +Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env, + jobject junk, + jint pid) +{ + /* We used to use waitid() on Solaris, waitpid() on Linux, but + * waitpid() is more standard, so use it on all POSIX platforms. */ + int status; + /* Wait for the child process to exit. This returns immediately if + the child has already exited. */ + while (waitpid(pid, &status, 0) < 0) { + switch (errno) { + case ECHILD: return 0; + case EINTR: break; + default: return -1; + } + } + + if (WIFEXITED(status)) { + /* + * The child exited normally; get its exit code. + */ + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + /* The child exited because of a signal. + * The best value to return is 0x80 + signal number, + * because that is what all Unix shells do, and because + * it allows callers to distinguish between process exit and + * process death by signal. + * Unfortunately, the historical behavior on Solaris is to return + * the signal number, and we preserve this for compatibility. */ +#ifdef __solaris__ + return WTERMSIG(status); +#else + return 0x80 + WTERMSIG(status); +#endif + } else { + /* + * Unknown exit code; pass it through. + */ + return status; + } +} + +static const char * +getBytes(JNIEnv *env, jbyteArray arr) +{ + return arr == NULL ? NULL : + (const char*) (*env)->GetByteArrayElements(env, arr, NULL); +} + +static void +releaseBytes(JNIEnv *env, jbyteArray arr, const char* parr) +{ + if (parr != NULL) + (*env)->ReleaseByteArrayElements(env, arr, (jbyte*) parr, JNI_ABORT); +} + +static void +throwIOException(JNIEnv *env, int errnum, const char *defaultDetail) +{ + static const char * const format = "error=%d, %s"; + const char *detail = defaultDetail; + char *errmsg; + jstring s; + + if (errnum != 0) { + const char *s = strerror(errnum); + if (strcmp(s, "Unknown error") != 0) + detail = s; + } + /* ASCII Decimal representation uses 2.4 times as many bits as binary. */ + errmsg = NEW(char, strlen(format) + strlen(detail) + 3 * sizeof(errnum)); + if (errmsg == NULL) + return; + + sprintf(errmsg, format, errnum, detail); + s = JNU_NewStringPlatform(env, errmsg); + if (s != NULL) { + jobject x = JNU_NewObjectByName(env, "java/io/IOException", + "(Ljava/lang/String;)V", s); + if (x != NULL) + (*env)->Throw(env, x); + } + free(errmsg); +} + +#ifdef DEBUG_PROCESS +/* Debugging process code is difficult; where to write debug output? */ +static void +debugPrint(char *format, ...) +{ + FILE *tty = fopen("/dev/tty", "w"); + va_list ap; + va_start(ap, format); + vfprintf(tty, format, ap); + va_end(ap); + fclose(tty); +} +#endif /* DEBUG_PROCESS */ + +static void +copyPipe(int from[2], int to[2]) +{ + to[0] = from[0]; + to[1] = from[1]; +} + +/* arg is an array of pointers to 0 terminated strings. array is terminated + * by a null element. + * + * *nelems and *nbytes receive the number of elements of array (incl 0) + * and total number of bytes (incl. 0) + * Note. An empty array will have one null element + * But if arg is null, then *nelems set to 0, and *nbytes to 0 + */ +static void arraysize(const char * const *arg, int *nelems, int *nbytes) +{ + int i, bytes, count; + const char * const *a = arg; + char *p; + int *q; + if (arg == 0) { + *nelems = 0; + *nbytes = 0; + return; + } + /* count the array elements and number of bytes */ + for (count=0, bytes=0; *a != 0; count++, a++) { + bytes += strlen(*a)+1; + } + *nbytes = bytes; + *nelems = count+1; +} + +/* copy the strings from arg[] into buf, starting at given offset + * return new offset to next free byte + */ +static int copystrings(char *buf, int offset, const char * const *arg) { + char *p; + const char * const *a; + int count=0; + + if (arg == 0) { + return offset; + } + for (p=buf+offset, a=arg; *a != 0; a++) { + int len = strlen(*a) +1; + memcpy(p, *a, len); + p += len; + count += len; + } + return offset+count; +} + +/** + * We are unusually paranoid; use of clone/vfork is + * especially likely to tickle gcc/glibc bugs. + */ +#ifdef __attribute_noinline__ /* See: sys/cdefs.h */ +__attribute_noinline__ +#endif + +#define START_CHILD_USE_CLONE 0 /* clone() currently disabled; see above. */ + +#ifdef START_CHILD_USE_CLONE +static pid_t +cloneChild(ChildStuff *c) { +#ifdef __linux__ +#define START_CHILD_CLONE_STACK_SIZE (64 * 1024) + /* + * See clone(2). + * Instead of worrying about which direction the stack grows, just + * allocate twice as much and start the stack in the middle. + */ + if ((c->clone_stack = malloc(2 * START_CHILD_CLONE_STACK_SIZE)) == NULL) + /* errno will be set to ENOMEM */ + return -1; + return clone(childProcess, + c->clone_stack + START_CHILD_CLONE_STACK_SIZE, + CLONE_VFORK | CLONE_VM | SIGCHLD, c); +#else +/* not available on Solaris / Mac */ + assert(0); + return -1; +#endif +} +#endif + +static pid_t +vforkChild(ChildStuff *c) { + volatile pid_t resultPid; + + /* + * We separate the call to vfork into a separate function to make + * very sure to keep stack of child from corrupting stack of parent, + * as suggested by the scary gcc warning: + * warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' + */ + resultPid = vfork(); + + if (resultPid == 0) { + childProcess(c); + } + assert(resultPid != 0); /* childProcess never returns */ + return resultPid; +} + +static pid_t +forkChild(ChildStuff *c) { + pid_t resultPid; + + /* + * From Solaris fork(2): In Solaris 10, a call to fork() is + * identical to a call to fork1(); only the calling thread is + * replicated in the child process. This is the POSIX-specified + * behavior for fork(). + */ + resultPid = fork(); + + if (resultPid == 0) { + childProcess(c); + } + assert(resultPid != 0); /* childProcess never returns */ + return resultPid; +} + +#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) +static pid_t +spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { + pid_t resultPid; + jboolean isCopy; + int i, offset, rval, bufsize, magic; + char *buf, buf1[16]; + char *hlpargs[2]; + SpawnInfo sp; + + /* need to tell helper which fd is for receiving the childstuff + * and which fd to send response back on + */ + snprintf(buf1, sizeof(buf1), "%d:%d", c->childenv[0], c->fail[1]); + /* put the fd string as argument to the helper cmd */ + hlpargs[0] = buf1; + hlpargs[1] = 0; + + /* Following items are sent down the pipe to the helper + * after it is spawned. + * All strings are null terminated. All arrays of strings + * have an empty string for termination. + * - the ChildStuff struct + * - the SpawnInfo struct + * - the argv strings array + * - the envv strings array + * - the home directory string + * - the parentPath string + * - the parentPathv array + */ + /* First calculate the sizes */ + arraysize(c->argv, &sp.nargv, &sp.argvBytes); + bufsize = sp.argvBytes; + arraysize(c->envv, &sp.nenvv, &sp.envvBytes); + bufsize += sp.envvBytes; + sp.dirlen = c->pdir == 0 ? 0 : strlen(c->pdir)+1; + bufsize += sp.dirlen; + arraysize(parentPathv, &sp.nparentPathv, &sp.parentPathvBytes); + bufsize += sp.parentPathvBytes; + /* We need to clear FD_CLOEXEC if set in the fds[]. + * Files are created FD_CLOEXEC in Java. + * Otherwise, they will be closed when the target gets exec'd */ + for (i=0; i<3; i++) { + if (c->fds[i] != -1) { + int flags = fcntl(c->fds[i], F_GETFD); + if (flags & FD_CLOEXEC) { + fcntl(c->fds[i], F_SETFD, flags & (~1)); + } + } + } + + rval = posix_spawn(&resultPid, helperpath, 0, 0, (char * const *) hlpargs, environ); + + if (rval != 0) { + return -1; + } + + /* now the lengths are known, copy the data */ + buf = NEW(char, bufsize); + if (buf == 0) { + return -1; + } + offset = copystrings(buf, 0, &c->argv[0]); + offset = copystrings(buf, offset, &c->envv[0]); + memcpy(buf+offset, c->pdir, sp.dirlen); + offset += sp.dirlen; + offset = copystrings(buf, offset, parentPathv); + assert(offset == bufsize); + + magic = magicNumber(); + + /* write the two structs and the data buffer */ + write(c->childenv[1], (char *)&magic, sizeof(magic)); // magic number first + write(c->childenv[1], (char *)c, sizeof(*c)); + write(c->childenv[1], (char *)&sp, sizeof(sp)); + write(c->childenv[1], buf, bufsize); + free(buf); + + /* In this mode an external main() in invoked which calls back into + * childProcess() in this file, rather than directly + * via the statement below */ + return resultPid; +} +#endif + +/* + * Start a child process running function childProcess. + * This function only returns in the parent. + */ +static pid_t +startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { + switch (c->mode) { + case MODE_VFORK: + return vforkChild(c); + case MODE_FORK: + return forkChild(c); +#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) + case MODE_POSIX_SPAWN: + return spawnChild(env, process, c, helperpath); +#endif + default: + return -1; + } +} + +JNIEXPORT jint JNICALL +Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, + jobject process, + jint mode, + jbyteArray helperpath, + jbyteArray prog, + jbyteArray argBlock, jint argc, + jbyteArray envBlock, jint envc, + jbyteArray dir, + jintArray std_fds, + jboolean redirectErrorStream) +{ + int errnum; + int resultPid = -1; + int in[2], out[2], err[2], fail[2], childenv[2]; + jint *fds = NULL; + const char *phelperpath = NULL; + const char *pprog = NULL; + const char *pargBlock = NULL; + const char *penvBlock = NULL; + ChildStuff *c; + + in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1; + childenv[0] = childenv[1] = -1; + + if ((c = NEW(ChildStuff, 1)) == NULL) return -1; + c->argv = NULL; + c->envv = NULL; + c->pdir = NULL; + c->clone_stack = NULL; + + /* Convert prog + argBlock into a char ** argv. + * Add one word room for expansion of argv for use by + * execve_as_traditional_shell_script. + * This word is also used when using spawn mode + */ + assert(prog != NULL && argBlock != NULL); + if ((phelperpath = getBytes(env, helperpath)) == NULL) goto Catch; + if ((pprog = getBytes(env, prog)) == NULL) goto Catch; + if ((pargBlock = getBytes(env, argBlock)) == NULL) goto Catch; + if ((c->argv = NEW(const char *, argc + 3)) == NULL) goto Catch; + c->argv[0] = pprog; + c->argc = argc + 2; + initVectorFromBlock(c->argv+1, pargBlock, argc); + + if (envBlock != NULL) { + /* Convert envBlock into a char ** envv */ + if ((penvBlock = getBytes(env, envBlock)) == NULL) goto Catch; + if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch; + initVectorFromBlock(c->envv, penvBlock, envc); + } + + if (dir != NULL) { + if ((c->pdir = getBytes(env, dir)) == NULL) goto Catch; + } + + assert(std_fds != NULL); + fds = (*env)->GetIntArrayElements(env, std_fds, NULL); + if (fds == NULL) goto Catch; + + if ((fds[0] == -1 && pipe(in) < 0) || + (fds[1] == -1 && pipe(out) < 0) || + (fds[2] == -1 && pipe(err) < 0) || + (pipe(childenv) < 0) || + (pipe(fail) < 0)) { + throwIOException(env, errno, "Bad file descriptor"); + goto Catch; + } + c->fds[0] = fds[0]; + c->fds[1] = fds[1]; + c->fds[2] = fds[2]; + + copyPipe(in, c->in); + copyPipe(out, c->out); + copyPipe(err, c->err); + copyPipe(fail, c->fail); + copyPipe(childenv, c->childenv); + + c->redirectErrorStream = redirectErrorStream; + c->mode = mode; + + resultPid = startChild(env, process, c, phelperpath); + assert(resultPid != 0); + + if (resultPid < 0) { + switch (c->mode) { + case MODE_VFORK: + throwIOException(env, errno, "vfork failed"); + break; + case MODE_FORK: + throwIOException(env, errno, "fork failed"); + break; + case MODE_POSIX_SPAWN: + throwIOException(env, errno, "spawn failed"); + break; + } + goto Catch; + } + close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ + + switch (readFully(fail[0], &errnum, sizeof(errnum))) { + case 0: break; /* Exec succeeded */ + case sizeof(errnum): + waitpid(resultPid, NULL, 0); + throwIOException(env, errnum, "Exec failed"); + goto Catch; + default: + throwIOException(env, errno, "Read failed"); + goto Catch; + } + + fds[0] = (in [1] != -1) ? in [1] : -1; + fds[1] = (out[0] != -1) ? out[0] : -1; + fds[2] = (err[0] != -1) ? err[0] : -1; + + Finally: + free(c->clone_stack); + + /* Always clean up the child's side of the pipes */ + closeSafely(in [0]); + closeSafely(out[1]); + closeSafely(err[1]); + + /* Always clean up fail and childEnv descriptors */ + closeSafely(fail[0]); + closeSafely(fail[1]); + closeSafely(childenv[0]); + closeSafely(childenv[1]); + + releaseBytes(env, helperpath, phelperpath); + releaseBytes(env, prog, pprog); + releaseBytes(env, argBlock, pargBlock); + releaseBytes(env, envBlock, penvBlock); + releaseBytes(env, dir, c->pdir); + + free(c->argv); + free(c->envv); + free(c); + + if (fds != NULL) + (*env)->ReleaseIntArrayElements(env, std_fds, fds, 0); + + return resultPid; + + Catch: + /* Clean up the parent's side of the pipes in case of failure only */ + closeSafely(in [1]); in[1] = -1; + closeSafely(out[0]); out[0] = -1; + closeSafely(err[0]); err[0] = -1; + goto Finally; +} + +JNIEXPORT void JNICALL +Java_java_lang_ProcessImpl_destroyProcess(JNIEnv *env, + jobject junk, + jint pid, + jboolean force) +{ + int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; + kill(pid, sig); +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libjava/UNIXProcess_md.c --- a/jdk/src/java.base/unix/native/libjava/UNIXProcess_md.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,724 +0,0 @@ -/* - * Copyright (c) 1995, 2013, 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. - */ - -#undef _LARGEFILE64_SOURCE -#define _LARGEFILE64_SOURCE 1 - -#include "jni.h" -#include "jvm.h" -#include "jvm_md.h" -#include "jni_util.h" -#include "io_util.h" - -/* - * Platform-specific support for java.lang.Process - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) -#include -#endif - -#include "childproc.h" - -/* - * There are 4 possible strategies we might use to "fork": - * - * - fork(2). Very portable and reliable but subject to - * failure due to overcommit (see the documentation on - * /proc/sys/vm/overcommit_memory in Linux proc(5)). - * This is the ancient problem of spurious failure whenever a large - * process starts a small subprocess. - * - * - vfork(). Using this is scary because all relevant man pages - * contain dire warnings, e.g. Linux vfork(2). But at least it's - * documented in the glibc docs and is standardized by XPG4. - * http://www.opengroup.org/onlinepubs/000095399/functions/vfork.html - * On Linux, one might think that vfork() would be implemented using - * the clone system call with flag CLONE_VFORK, but in fact vfork is - * a separate system call (which is a good sign, suggesting that - * vfork will continue to be supported at least on Linux). - * Another good sign is that glibc implements posix_spawn using - * vfork whenever possible. Note that we cannot use posix_spawn - * ourselves because there's no reliable way to close all inherited - * file descriptors. - * - * - clone() with flags CLONE_VM but not CLONE_THREAD. clone() is - * Linux-specific, but this ought to work - at least the glibc - * sources contain code to handle different combinations of CLONE_VM - * and CLONE_THREAD. However, when this was implemented, it - * appeared to fail on 32-bit i386 (but not 64-bit x86_64) Linux with - * the simple program - * Runtime.getRuntime().exec("/bin/true").waitFor(); - * with: - * # Internal Error (os_linux_x86.cpp:683), pid=19940, tid=2934639536 - * # Error: pthread_getattr_np failed with errno = 3 (ESRCH) - * We believe this is a glibc bug, reported here: - * http://sources.redhat.com/bugzilla/show_bug.cgi?id=10311 - * but the glibc maintainers closed it as WONTFIX. - * - * - posix_spawn(). While posix_spawn() is a fairly elaborate and - * complicated system call, it can't quite do everything that the old - * fork()/exec() combination can do, so the only feasible way to do - * this, is to use posix_spawn to launch a new helper executable - * "jprochelper", which in turn execs the target (after cleaning - * up file-descriptors etc.) The end result is the same as before, - * a child process linked to the parent in the same way, but it - * avoids the problem of duplicating the parent (VM) process - * address space temporarily, before launching the target command. - * - * Based on the above analysis, we are currently using vfork() on - * Linux and spawn() on other Unix systems, but the code to use clone() - * and fork() remains. - */ - - -static void -setSIGCHLDHandler(JNIEnv *env) -{ - /* There is a subtle difference between having the signal handler - * for SIGCHLD be SIG_DFL and SIG_IGN. We cannot obtain process - * termination information for child processes if the signal - * handler is SIG_IGN. It must be SIG_DFL. - * - * We used to set the SIGCHLD handler only on Linux, but it's - * safest to set it unconditionally. - * - * Consider what happens if java's parent process sets the SIGCHLD - * handler to SIG_IGN. Normally signal handlers are inherited by - * children, but SIGCHLD is a controversial case. Solaris appears - * to always reset it to SIG_DFL, but this behavior may be - * non-standard-compliant, and we shouldn't rely on it. - * - * References: - * http://www.opengroup.org/onlinepubs/7908799/xsh/exec.html - * http://www.pasc.org/interps/unofficial/db/p1003.1/pasc-1003.1-132.html - */ - struct sigaction sa; - sa.sa_handler = SIG_DFL; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; - if (sigaction(SIGCHLD, &sa, NULL) < 0) - JNU_ThrowInternalError(env, "Can't set SIGCHLD handler"); -} - -static void* -xmalloc(JNIEnv *env, size_t size) -{ - void *p = malloc(size); - if (p == NULL) - JNU_ThrowOutOfMemoryError(env, NULL); - return p; -} - -#define NEW(type, n) ((type *) xmalloc(env, (n) * sizeof(type))) - -/** - * If PATH is not defined, the OS provides some default value. - * Unfortunately, there's no portable way to get this value. - * Fortunately, it's only needed if the child has PATH while we do not. - */ -static const char* -defaultPath(void) -{ -#ifdef __solaris__ - /* These really are the Solaris defaults! */ - return (geteuid() == 0 || getuid() == 0) ? - "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:/usr/sbin" : - "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin:"; -#else - return ":/bin:/usr/bin"; /* glibc */ -#endif -} - -static const char* -effectivePath(void) -{ - const char *s = getenv("PATH"); - return (s != NULL) ? s : defaultPath(); -} - -static int -countOccurrences(const char *s, char c) -{ - int count; - for (count = 0; *s != '\0'; s++) - count += (*s == c); - return count; -} - -static const char * const * -effectivePathv(JNIEnv *env) -{ - char *p; - int i; - const char *path = effectivePath(); - int count = countOccurrences(path, ':') + 1; - size_t pathvsize = sizeof(const char *) * (count+1); - size_t pathsize = strlen(path) + 1; - const char **pathv = (const char **) xmalloc(env, pathvsize + pathsize); - - if (pathv == NULL) - return NULL; - p = (char *) pathv + pathvsize; - memcpy(p, path, pathsize); - /* split PATH by replacing ':' with NULs; empty components => "." */ - for (i = 0; i < count; i++) { - char *q = p + strcspn(p, ":"); - pathv[i] = (p == q) ? "." : p; - *q = '\0'; - p = q + 1; - } - pathv[count] = NULL; - return pathv; -} - -JNIEXPORT void JNICALL -Java_java_lang_UNIXProcess_init(JNIEnv *env, jclass clazz) -{ - parentPathv = effectivePathv(env); - CHECK_NULL(parentPathv); - setSIGCHLDHandler(env); -} - - -#ifndef WIFEXITED -#define WIFEXITED(status) (((status)&0xFF) == 0) -#endif - -#ifndef WEXITSTATUS -#define WEXITSTATUS(status) (((status)>>8)&0xFF) -#endif - -#ifndef WIFSIGNALED -#define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0) -#endif - -#ifndef WTERMSIG -#define WTERMSIG(status) ((status)&0x7F) -#endif - -/* Block until a child process exits and return its exit code. - Note, can only be called once for any given pid. */ -JNIEXPORT jint JNICALL -Java_java_lang_UNIXProcess_waitForProcessExit(JNIEnv* env, - jobject junk, - jint pid) -{ - /* We used to use waitid() on Solaris, waitpid() on Linux, but - * waitpid() is more standard, so use it on all POSIX platforms. */ - int status; - /* Wait for the child process to exit. This returns immediately if - the child has already exited. */ - while (waitpid(pid, &status, 0) < 0) { - switch (errno) { - case ECHILD: return 0; - case EINTR: break; - default: return -1; - } - } - - if (WIFEXITED(status)) { - /* - * The child exited normally; get its exit code. - */ - return WEXITSTATUS(status); - } else if (WIFSIGNALED(status)) { - /* The child exited because of a signal. - * The best value to return is 0x80 + signal number, - * because that is what all Unix shells do, and because - * it allows callers to distinguish between process exit and - * process death by signal. - * Unfortunately, the historical behavior on Solaris is to return - * the signal number, and we preserve this for compatibility. */ -#ifdef __solaris__ - return WTERMSIG(status); -#else - return 0x80 + WTERMSIG(status); -#endif - } else { - /* - * Unknown exit code; pass it through. - */ - return status; - } -} - -static const char * -getBytes(JNIEnv *env, jbyteArray arr) -{ - return arr == NULL ? NULL : - (const char*) (*env)->GetByteArrayElements(env, arr, NULL); -} - -static void -releaseBytes(JNIEnv *env, jbyteArray arr, const char* parr) -{ - if (parr != NULL) - (*env)->ReleaseByteArrayElements(env, arr, (jbyte*) parr, JNI_ABORT); -} - -static void -throwIOException(JNIEnv *env, int errnum, const char *defaultDetail) -{ - static const char * const format = "error=%d, %s"; - const char *detail = defaultDetail; - char *errmsg; - jstring s; - - if (errnum != 0) { - const char *s = strerror(errnum); - if (strcmp(s, "Unknown error") != 0) - detail = s; - } - /* ASCII Decimal representation uses 2.4 times as many bits as binary. */ - errmsg = NEW(char, strlen(format) + strlen(detail) + 3 * sizeof(errnum)); - if (errmsg == NULL) - return; - - sprintf(errmsg, format, errnum, detail); - s = JNU_NewStringPlatform(env, errmsg); - if (s != NULL) { - jobject x = JNU_NewObjectByName(env, "java/io/IOException", - "(Ljava/lang/String;)V", s); - if (x != NULL) - (*env)->Throw(env, x); - } - free(errmsg); -} - -#ifdef DEBUG_PROCESS -/* Debugging process code is difficult; where to write debug output? */ -static void -debugPrint(char *format, ...) -{ - FILE *tty = fopen("/dev/tty", "w"); - va_list ap; - va_start(ap, format); - vfprintf(tty, format, ap); - va_end(ap); - fclose(tty); -} -#endif /* DEBUG_PROCESS */ - -static void -copyPipe(int from[2], int to[2]) -{ - to[0] = from[0]; - to[1] = from[1]; -} - -/* arg is an array of pointers to 0 terminated strings. array is terminated - * by a null element. - * - * *nelems and *nbytes receive the number of elements of array (incl 0) - * and total number of bytes (incl. 0) - * Note. An empty array will have one null element - * But if arg is null, then *nelems set to 0, and *nbytes to 0 - */ -static void arraysize(const char * const *arg, int *nelems, int *nbytes) -{ - int i, bytes, count; - const char * const *a = arg; - char *p; - int *q; - if (arg == 0) { - *nelems = 0; - *nbytes = 0; - return; - } - /* count the array elements and number of bytes */ - for (count=0, bytes=0; *a != 0; count++, a++) { - bytes += strlen(*a)+1; - } - *nbytes = bytes; - *nelems = count+1; -} - -/* copy the strings from arg[] into buf, starting at given offset - * return new offset to next free byte - */ -static int copystrings(char *buf, int offset, const char * const *arg) { - char *p; - const char * const *a; - int count=0; - - if (arg == 0) { - return offset; - } - for (p=buf+offset, a=arg; *a != 0; a++) { - int len = strlen(*a) +1; - memcpy(p, *a, len); - p += len; - count += len; - } - return offset+count; -} - -/** - * We are unusually paranoid; use of clone/vfork is - * especially likely to tickle gcc/glibc bugs. - */ -#ifdef __attribute_noinline__ /* See: sys/cdefs.h */ -__attribute_noinline__ -#endif - -#define START_CHILD_USE_CLONE 0 /* clone() currently disabled; see above. */ - -#ifdef START_CHILD_USE_CLONE -static pid_t -cloneChild(ChildStuff *c) { -#ifdef __linux__ -#define START_CHILD_CLONE_STACK_SIZE (64 * 1024) - /* - * See clone(2). - * Instead of worrying about which direction the stack grows, just - * allocate twice as much and start the stack in the middle. - */ - if ((c->clone_stack = malloc(2 * START_CHILD_CLONE_STACK_SIZE)) == NULL) - /* errno will be set to ENOMEM */ - return -1; - return clone(childProcess, - c->clone_stack + START_CHILD_CLONE_STACK_SIZE, - CLONE_VFORK | CLONE_VM | SIGCHLD, c); -#else -/* not available on Solaris / Mac */ - assert(0); - return -1; -#endif -} -#endif - -static pid_t -vforkChild(ChildStuff *c) { - volatile pid_t resultPid; - - /* - * We separate the call to vfork into a separate function to make - * very sure to keep stack of child from corrupting stack of parent, - * as suggested by the scary gcc warning: - * warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' - */ - resultPid = vfork(); - - if (resultPid == 0) { - childProcess(c); - } - assert(resultPid != 0); /* childProcess never returns */ - return resultPid; -} - -static pid_t -forkChild(ChildStuff *c) { - pid_t resultPid; - - /* - * From Solaris fork(2): In Solaris 10, a call to fork() is - * identical to a call to fork1(); only the calling thread is - * replicated in the child process. This is the POSIX-specified - * behavior for fork(). - */ - resultPid = fork(); - - if (resultPid == 0) { - childProcess(c); - } - assert(resultPid != 0); /* childProcess never returns */ - return resultPid; -} - -#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) -static pid_t -spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { - pid_t resultPid; - jboolean isCopy; - int i, offset, rval, bufsize, magic; - char *buf, buf1[16]; - char *hlpargs[2]; - SpawnInfo sp; - - /* need to tell helper which fd is for receiving the childstuff - * and which fd to send response back on - */ - snprintf(buf1, sizeof(buf1), "%d:%d", c->childenv[0], c->fail[1]); - /* put the fd string as argument to the helper cmd */ - hlpargs[0] = buf1; - hlpargs[1] = 0; - - /* Following items are sent down the pipe to the helper - * after it is spawned. - * All strings are null terminated. All arrays of strings - * have an empty string for termination. - * - the ChildStuff struct - * - the SpawnInfo struct - * - the argv strings array - * - the envv strings array - * - the home directory string - * - the parentPath string - * - the parentPathv array - */ - /* First calculate the sizes */ - arraysize(c->argv, &sp.nargv, &sp.argvBytes); - bufsize = sp.argvBytes; - arraysize(c->envv, &sp.nenvv, &sp.envvBytes); - bufsize += sp.envvBytes; - sp.dirlen = c->pdir == 0 ? 0 : strlen(c->pdir)+1; - bufsize += sp.dirlen; - arraysize(parentPathv, &sp.nparentPathv, &sp.parentPathvBytes); - bufsize += sp.parentPathvBytes; - /* We need to clear FD_CLOEXEC if set in the fds[]. - * Files are created FD_CLOEXEC in Java. - * Otherwise, they will be closed when the target gets exec'd */ - for (i=0; i<3; i++) { - if (c->fds[i] != -1) { - int flags = fcntl(c->fds[i], F_GETFD); - if (flags & FD_CLOEXEC) { - fcntl(c->fds[i], F_SETFD, flags & (~1)); - } - } - } - - rval = posix_spawn(&resultPid, helperpath, 0, 0, (char * const *) hlpargs, environ); - - if (rval != 0) { - return -1; - } - - /* now the lengths are known, copy the data */ - buf = NEW(char, bufsize); - if (buf == 0) { - return -1; - } - offset = copystrings(buf, 0, &c->argv[0]); - offset = copystrings(buf, offset, &c->envv[0]); - memcpy(buf+offset, c->pdir, sp.dirlen); - offset += sp.dirlen; - offset = copystrings(buf, offset, parentPathv); - assert(offset == bufsize); - - magic = magicNumber(); - - /* write the two structs and the data buffer */ - write(c->childenv[1], (char *)&magic, sizeof(magic)); // magic number first - write(c->childenv[1], (char *)c, sizeof(*c)); - write(c->childenv[1], (char *)&sp, sizeof(sp)); - write(c->childenv[1], buf, bufsize); - free(buf); - - /* In this mode an external main() in invoked which calls back into - * childProcess() in this file, rather than directly - * via the statement below */ - return resultPid; -} -#endif - -/* - * Start a child process running function childProcess. - * This function only returns in the parent. - */ -static pid_t -startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { - switch (c->mode) { - case MODE_VFORK: - return vforkChild(c); - case MODE_FORK: - return forkChild(c); -#if defined(__solaris__) || defined(_ALLBSD_SOURCE) || defined(_AIX) - case MODE_POSIX_SPAWN: - return spawnChild(env, process, c, helperpath); -#endif - default: - return -1; - } -} - -JNIEXPORT jint JNICALL -Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env, - jobject process, - jint mode, - jbyteArray helperpath, - jbyteArray prog, - jbyteArray argBlock, jint argc, - jbyteArray envBlock, jint envc, - jbyteArray dir, - jintArray std_fds, - jboolean redirectErrorStream) -{ - int errnum; - int resultPid = -1; - int in[2], out[2], err[2], fail[2], childenv[2]; - jint *fds = NULL; - const char *phelperpath = NULL; - const char *pprog = NULL; - const char *pargBlock = NULL; - const char *penvBlock = NULL; - ChildStuff *c; - - in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1; - childenv[0] = childenv[1] = -1; - - if ((c = NEW(ChildStuff, 1)) == NULL) return -1; - c->argv = NULL; - c->envv = NULL; - c->pdir = NULL; - c->clone_stack = NULL; - - /* Convert prog + argBlock into a char ** argv. - * Add one word room for expansion of argv for use by - * execve_as_traditional_shell_script. - * This word is also used when using spawn mode - */ - assert(prog != NULL && argBlock != NULL); - if ((phelperpath = getBytes(env, helperpath)) == NULL) goto Catch; - if ((pprog = getBytes(env, prog)) == NULL) goto Catch; - if ((pargBlock = getBytes(env, argBlock)) == NULL) goto Catch; - if ((c->argv = NEW(const char *, argc + 3)) == NULL) goto Catch; - c->argv[0] = pprog; - c->argc = argc + 2; - initVectorFromBlock(c->argv+1, pargBlock, argc); - - if (envBlock != NULL) { - /* Convert envBlock into a char ** envv */ - if ((penvBlock = getBytes(env, envBlock)) == NULL) goto Catch; - if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch; - initVectorFromBlock(c->envv, penvBlock, envc); - } - - if (dir != NULL) { - if ((c->pdir = getBytes(env, dir)) == NULL) goto Catch; - } - - assert(std_fds != NULL); - fds = (*env)->GetIntArrayElements(env, std_fds, NULL); - if (fds == NULL) goto Catch; - - if ((fds[0] == -1 && pipe(in) < 0) || - (fds[1] == -1 && pipe(out) < 0) || - (fds[2] == -1 && pipe(err) < 0) || - (pipe(childenv) < 0) || - (pipe(fail) < 0)) { - throwIOException(env, errno, "Bad file descriptor"); - goto Catch; - } - c->fds[0] = fds[0]; - c->fds[1] = fds[1]; - c->fds[2] = fds[2]; - - copyPipe(in, c->in); - copyPipe(out, c->out); - copyPipe(err, c->err); - copyPipe(fail, c->fail); - copyPipe(childenv, c->childenv); - - c->redirectErrorStream = redirectErrorStream; - c->mode = mode; - - resultPid = startChild(env, process, c, phelperpath); - assert(resultPid != 0); - - if (resultPid < 0) { - switch (c->mode) { - case MODE_VFORK: - throwIOException(env, errno, "vfork failed"); - break; - case MODE_FORK: - throwIOException(env, errno, "fork failed"); - break; - case MODE_POSIX_SPAWN: - throwIOException(env, errno, "spawn failed"); - break; - } - goto Catch; - } - close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ - - switch (readFully(fail[0], &errnum, sizeof(errnum))) { - case 0: break; /* Exec succeeded */ - case sizeof(errnum): - waitpid(resultPid, NULL, 0); - throwIOException(env, errnum, "Exec failed"); - goto Catch; - default: - throwIOException(env, errno, "Read failed"); - goto Catch; - } - - fds[0] = (in [1] != -1) ? in [1] : -1; - fds[1] = (out[0] != -1) ? out[0] : -1; - fds[2] = (err[0] != -1) ? err[0] : -1; - - Finally: - free(c->clone_stack); - - /* Always clean up the child's side of the pipes */ - closeSafely(in [0]); - closeSafely(out[1]); - closeSafely(err[1]); - - /* Always clean up fail and childEnv descriptors */ - closeSafely(fail[0]); - closeSafely(fail[1]); - closeSafely(childenv[0]); - closeSafely(childenv[1]); - - releaseBytes(env, helperpath, phelperpath); - releaseBytes(env, prog, pprog); - releaseBytes(env, argBlock, pargBlock); - releaseBytes(env, envBlock, penvBlock); - releaseBytes(env, dir, c->pdir); - - free(c->argv); - free(c->envv); - free(c); - - if (fds != NULL) - (*env)->ReleaseIntArrayElements(env, std_fds, fds, 0); - - return resultPid; - - Catch: - /* Clean up the parent's side of the pipes in case of failure only */ - closeSafely(in [1]); in[1] = -1; - closeSafely(out[0]); out[0] = -1; - closeSafely(err[0]); err[0] = -1; - goto Finally; -} - -JNIEXPORT void JNICALL -Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env, - jobject junk, - jint pid, - jboolean force) -{ - int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; - kill(pid, sig); -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libjava/childproc.h --- a/jdk/src/java.base/unix/native/libjava/childproc.h Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/unix/native/libjava/childproc.h Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,7 @@ } while((_result == -1) && (errno == EINTR)); \ } while(0) -/* These numbers must be the same as the Enum in UNIXProcess.java +/* These numbers must be the same as the Enum in ProcessImpl.java * Must be a better way of doing this. */ #define MODE_FORK 1 diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libjava/java_props_macosx.c --- a/jdk/src/java.base/unix/native/libjava/java_props_macosx.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,349 +0,0 @@ -/* - * Copyright (c) 1998, 2013, 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 -#include -#include -#include - -#include -#include -#include -#include - -#include "java_props_macosx.h" - - -// need dlopen/dlsym trick to avoid pulling in JavaRuntimeSupport before libjava.dylib is loaded -static void *getJRSFramework() { - static void *jrsFwk = NULL; - if (jrsFwk == NULL) { - jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); - } - return jrsFwk; -} - -char *getPosixLocale(int cat) { - char *lc = setlocale(cat, NULL); - if ((lc == NULL) || (strcmp(lc, "C") == 0)) { - lc = getenv("LANG"); - } - if (lc == NULL) return NULL; - return strdup(lc); -} - -#define LOCALEIDLENGTH 128 -char *getMacOSXLocale(int cat) { - switch (cat) { - case LC_MESSAGES: - { - void *jrsFwk = getJRSFramework(); - if (jrsFwk == NULL) return NULL; - - char *(*JRSCopyPrimaryLanguage)() = dlsym(jrsFwk, "JRSCopyPrimaryLanguage"); - char *primaryLanguage = JRSCopyPrimaryLanguage ? JRSCopyPrimaryLanguage() : NULL; - if (primaryLanguage == NULL) return NULL; - - char *(*JRSCopyCanonicalLanguageForPrimaryLanguage)(char *) = dlsym(jrsFwk, "JRSCopyCanonicalLanguageForPrimaryLanguage"); - char *canonicalLanguage = JRSCopyCanonicalLanguageForPrimaryLanguage ? JRSCopyCanonicalLanguageForPrimaryLanguage(primaryLanguage) : NULL; - free (primaryLanguage); - - return canonicalLanguage; - } - break; - default: - { - char localeString[LOCALEIDLENGTH]; - if (CFStringGetCString(CFLocaleGetIdentifier(CFLocaleCopyCurrent()), - localeString, LOCALEIDLENGTH, CFStringGetSystemEncoding())) { - return strdup(localeString); - } - } - break; - } - - return NULL; -} - -char *setupMacOSXLocale(int cat) { - char * ret = getMacOSXLocale(cat); - - if (cat == LC_MESSAGES && ret != NULL) { - void *jrsFwk = getJRSFramework(); - if (jrsFwk != NULL) { - void (*JRSSetDefaultLocalization)(char *) = dlsym(jrsFwk, "JRSSetDefaultLocalization"); - if (JRSSetDefaultLocalization) JRSSetDefaultLocalization(ret); - } - } - - if (ret == NULL) { - return getPosixLocale(cat); - } else { - return ret; - } -} - -int isInAquaSession() { - // environment variable to bypass the aqua session check - char *ev = getenv("AWT_FORCE_HEADFUL"); - if (ev && (strncasecmp(ev, "true", 4) == 0)) { - // if "true" then tell the caller we're in an Aqua session without actually checking - return 1; - } - // Is the WindowServer available? - SecuritySessionId session_id; - SessionAttributeBits session_info; - OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info); - if (status == noErr) { - if (session_info & sessionHasGraphicAccess) { - return 1; - } - } - return 0; -} - -void setOSNameAndVersion(java_props_t *sprops) { - /* Don't rely on JRSCopyOSName because there's no guarantee the value will - * remain the same, or even if the JRS functions will continue to be part of - * Mac OS X. So hardcode os_name, and fill in os_version if we can. - */ - sprops->os_name = strdup("Mac OS X"); - - void *jrsFwk = getJRSFramework(); - if (jrsFwk != NULL) { - char *(*copyOSVersion)() = dlsym(jrsFwk, "JRSCopyOSVersion"); - if (copyOSVersion != NULL) { - sprops->os_version = copyOSVersion(); - return; - } - } - sprops->os_version = strdup("Unknown"); -} - - -static Boolean getProxyInfoForProtocol(CFDictionaryRef inDict, CFStringRef inEnabledKey, CFStringRef inHostKey, CFStringRef inPortKey, CFStringRef *outProxyHost, int *ioProxyPort) { - /* See if the proxy is enabled. */ - CFNumberRef cf_enabled = CFDictionaryGetValue(inDict, inEnabledKey); - if (cf_enabled == NULL) { - return false; - } - - int isEnabled = false; - if (!CFNumberGetValue(cf_enabled, kCFNumberIntType, &isEnabled)) { - return isEnabled; - } - - if (!isEnabled) return false; - *outProxyHost = CFDictionaryGetValue(inDict, inHostKey); - - // If cf_host is null, that means the checkbox is set, - // but no host was entered. We'll treat that as NOT ENABLED. - // If cf_port is null or cf_port isn't a number, that means - // no port number was entered. Treat this as ENABLED with the - // protocol's default port. - if (*outProxyHost == NULL) { - return false; - } - - if (CFStringGetLength(*outProxyHost) == 0) { - return false; - } - - int newPort = 0; - CFNumberRef cf_port = NULL; - if ((cf_port = CFDictionaryGetValue(inDict, inPortKey)) != NULL && - CFNumberGetValue(cf_port, kCFNumberIntType, &newPort) && - newPort > 0) { - *ioProxyPort = newPort; - } else { - // bad port or no port - leave *ioProxyPort unchanged - } - - return true; -} - -static char *createUTF8CString(const CFStringRef theString) { - if (theString == NULL) return NULL; - - const CFIndex stringLength = CFStringGetLength(theString); - const CFIndex bufSize = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 1; - char *returnVal = (char *)malloc(bufSize); - - if (CFStringGetCString(theString, returnVal, bufSize, kCFStringEncodingUTF8)) { - return returnVal; - } - - free(returnVal); - return NULL; -} - -// Return TRUE if str is a syntactically valid IP address. -// Using inet_pton() instead of inet_aton() for IPv6 support. -// len is only a hint; cstr must still be nul-terminated -static int looksLikeIPAddress(char *cstr, size_t len) { - if (len == 0 || (len == 1 && cstr[0] == '.')) return FALSE; - - char dst[16]; // big enough for INET6 - return (1 == inet_pton(AF_INET, cstr, dst) || - 1 == inet_pton(AF_INET6, cstr, dst)); -} - - - -// Convert Mac OS X proxy exception entry to Java syntax. -// See Radar #3441134 for details. -// Returns NULL if this exception should be ignored by Java. -// May generate a string with multiple exceptions separated by '|'. -static char * createConvertedException(CFStringRef cf_original) { - // This is done with char* instead of CFString because inet_pton() - // needs a C string. - char *c_exception = createUTF8CString(cf_original); - if (!c_exception) return NULL; - - int c_len = strlen(c_exception); - - // 1. sanitize exception prefix - if (c_len >= 1 && 0 == strncmp(c_exception, ".", 1)) { - memmove(c_exception, c_exception+1, c_len); - c_len -= 1; - } else if (c_len >= 2 && 0 == strncmp(c_exception, "*.", 2)) { - memmove(c_exception, c_exception+2, c_len-1); - c_len -= 2; - } - - // 2. pre-reject other exception wildcards - if (strchr(c_exception, '*')) { - free(c_exception); - return NULL; - } - - // 3. no IP wildcarding - if (looksLikeIPAddress(c_exception, c_len)) { - return c_exception; - } - - // 4. allow domain suffixes - // c_exception is now "str\0" - change to "str|*.str\0" - c_exception = reallocf(c_exception, c_len+3+c_len+1); - if (!c_exception) return NULL; - - strncpy(c_exception+c_len, "|*.", 3); - strncpy(c_exception+c_len+3, c_exception, c_len); - c_exception[c_len+3+c_len] = '\0'; - return c_exception; -} - -/* - * Method for fetching the user.home path and storing it in the property list. - * For signed .apps running in the Mac App Sandbox, user.home is set to the - * app's sandbox container. - */ -void setUserHome(java_props_t *sprops) { - if (sprops == NULL) { return; } - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - sprops->user_home = createUTF8CString((CFStringRef)NSHomeDirectory()); - [pool drain]; -} - -/* - * Method for fetching proxy info and storing it in the property list. - */ -void setProxyProperties(java_props_t *sProps) { - if (sProps == NULL) return; - - char buf[16]; /* Used for %d of an int - 16 is plenty */ - CFStringRef - cf_httpHost = NULL, - cf_httpsHost = NULL, - cf_ftpHost = NULL, - cf_socksHost = NULL, - cf_gopherHost = NULL; - int - httpPort = 80, // Default proxy port values - httpsPort = 443, - ftpPort = 21, - socksPort = 1080, - gopherPort = 70; - - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); - if (dict == NULL) return; - - /* Read the proxy exceptions list */ - CFArrayRef cf_list = CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList); - - CFMutableStringRef cf_exceptionList = NULL; - if (cf_list != NULL) { - CFIndex len = CFArrayGetCount(cf_list), idx; - - cf_exceptionList = CFStringCreateMutable(NULL, 0); - for (idx = (CFIndex)0; idx < len; idx++) { - CFStringRef cf_ehost; - if ((cf_ehost = CFArrayGetValueAtIndex(cf_list, idx))) { - /* Convert this exception from Mac OS X syntax to Java syntax. - See Radar #3441134 for details. This may generate a string - with multiple Java exceptions separated by '|'. */ - char *c_exception = createConvertedException(cf_ehost); - if (c_exception) { - /* Append the host to the list of exclusions. */ - if (CFStringGetLength(cf_exceptionList) > 0) { - CFStringAppendCString(cf_exceptionList, "|", kCFStringEncodingMacRoman); - } - CFStringAppendCString(cf_exceptionList, c_exception, kCFStringEncodingMacRoman); - free(c_exception); - } - } - } - } - - if (cf_exceptionList != NULL) { - if (CFStringGetLength(cf_exceptionList) > 0) { - sProps->exceptionList = createUTF8CString(cf_exceptionList); - } - CFRelease(cf_exceptionList); - } - -#define CHECK_PROXY(protocol, PROTOCOL) \ - sProps->protocol##ProxyEnabled = \ - getProxyInfoForProtocol(dict, kSCPropNetProxies##PROTOCOL##Enable, \ - kSCPropNetProxies##PROTOCOL##Proxy, \ - kSCPropNetProxies##PROTOCOL##Port, \ - &cf_##protocol##Host, &protocol##Port); \ - if (sProps->protocol##ProxyEnabled) { \ - sProps->protocol##Host = createUTF8CString(cf_##protocol##Host); \ - snprintf(buf, sizeof(buf), "%d", protocol##Port); \ - sProps->protocol##Port = malloc(strlen(buf) + 1); \ - strcpy(sProps->protocol##Port, buf); \ - } - - CHECK_PROXY(http, HTTP); - CHECK_PROXY(https, HTTPS); - CHECK_PROXY(ftp, FTP); - CHECK_PROXY(socks, SOCKS); - CHECK_PROXY(gopher, Gopher); - -#undef CHECK_PROXY - - CFRelease(dict); -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libjava/java_props_macosx.h --- a/jdk/src/java.base/unix/native/libjava/java_props_macosx.h Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 1998, 2013, 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 "java_props.h" - -char *setupMacOSXLocale(int cat); -void setOSNameAndVersion(java_props_t *sprops); -void setUserHome(java_props_t *sprops); -void setProxyProperties(java_props_t *sProps); -int isInAquaSession(); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libnet/bsd_close.c --- a/jdk/src/java.base/unix/native/libnet/bsd_close.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,421 +0,0 @@ -/* - * Copyright (c) 2001, 2012, 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Stack allocated by thread when doing blocking operation - */ -typedef struct threadEntry { - pthread_t thr; /* this thread */ - struct threadEntry *next; /* next thread */ - int intr; /* interrupted */ -} threadEntry_t; - -/* - * Heap allocated during initialized - one entry per fd - */ -typedef struct { - pthread_mutex_t lock; /* fd lock */ - threadEntry_t *threads; /* threads blocked on fd */ -} fdEntry_t; - -/* - * Signal to unblock thread - */ -static int sigWakeup = SIGIO; - -/* - * The fd table and the number of file descriptors - */ -static fdEntry_t *fdTable; -static int fdCount; - -/* - * This limit applies if getlimit() returns unlimited. - * Unfortunately, this means if someone wants a higher limit - * then they have to set an explicit limit, higher than this, - * which is probably counter-intuitive. - */ -#define MAX_FD_COUNT 4096 - -/* - * Null signal handler - */ -static void sig_wakeup(int sig) { -} - -/* - * Initialization routine (executed when library is loaded) - * Allocate fd tables and sets up signal handler. - */ -static void __attribute((constructor)) init() { - struct rlimit nbr_files; - sigset_t sigset; - struct sigaction sa; - int i; - - /* - * Allocate table based on the maximum number of - * file descriptors. - */ - getrlimit(RLIMIT_NOFILE, &nbr_files); - if (nbr_files.rlim_max == RLIM_INFINITY) { - fdCount = MAX_FD_COUNT; - } else { - fdCount = nbr_files.rlim_max; - } - fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); - if (fdTable == NULL) { - fprintf(stderr, "library initialization failed - " - "unable to allocate file descriptor table - out of memory"); - abort(); - } - for (i=0; i= fdCount) { - return NULL; - } - return &fdTable[fd]; -} - -/* - * Start a blocking operation :- - * Insert thread onto thread list for the fd. - */ -static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) -{ - self->thr = pthread_self(); - self->intr = 0; - - pthread_mutex_lock(&(fdEntry->lock)); - { - self->next = fdEntry->threads; - fdEntry->threads = self; - } - pthread_mutex_unlock(&(fdEntry->lock)); -} - -/* - * End a blocking operation :- - * Remove thread from thread list for the fd - * If fd has been interrupted then set errno to EBADF - */ -static inline void endOp - (fdEntry_t *fdEntry, threadEntry_t *self) -{ - int orig_errno = errno; - pthread_mutex_lock(&(fdEntry->lock)); - { - threadEntry_t *curr, *prev=NULL; - curr = fdEntry->threads; - while (curr != NULL) { - if (curr == self) { - if (curr->intr) { - orig_errno = EBADF; - } - if (prev == NULL) { - fdEntry->threads = curr->next; - } else { - prev->next = curr->next; - } - break; - } - prev = curr; - curr = curr->next; - } - } - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; -} - -/* - * Close or dup2 a file descriptor ensuring that all threads blocked on - * the file descriptor are notified via a wakeup signal. - * - * fd1 < 0 => close(fd2) - * fd1 >= 0 => dup2(fd1, fd2) - * - * Returns -1 with errno set if operation fails. - */ -static int closefd(int fd1, int fd2) { - int rv, orig_errno; - fdEntry_t *fdEntry = getFdEntry(fd2); - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Lock the fd to hold-off additional I/O on this fd. - */ - pthread_mutex_lock(&(fdEntry->lock)); - - { - /* - * Send a wakeup signal to all threads blocked on this - * file descriptor. - */ - threadEntry_t *curr = fdEntry->threads; - while (curr != NULL) { - curr->intr = 1; - pthread_kill( curr->thr, sigWakeup ); - curr = curr->next; - } - - /* - * And close/dup the file descriptor - * (restart if interrupted by signal) - */ - do { - if (fd1 < 0) { - rv = close(fd2); - } else { - rv = dup2(fd1, fd2); - } - } while (rv == -1 && errno == EINTR); - - } - - /* - * Unlock without destroying errno - */ - orig_errno = errno; - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; - - return rv; -} - -/* - * Wrapper for dup2 - same semantics as dup2 system call except - * that any threads blocked in an I/O system call on fd2 will be - * preempted and return -1/EBADF; - */ -int NET_Dup2(int fd, int fd2) { - if (fd < 0) { - errno = EBADF; - return -1; - } - return closefd(fd, fd2); -} - -/* - * Wrapper for close - same semantics as close system call - * except that any threads blocked in an I/O on fd will be - * preempted and the I/O system call will return -1/EBADF. - */ -int NET_SocketClose(int fd) { - return closefd(-1, fd); -} - -/************** Basic I/O operations here ***************/ - -/* - * Macro to perform a blocking IO operation. Restarts - * automatically if interrupted by signal (other than - * our wakeup signal) - */ -#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ - int ret; \ - threadEntry_t self; \ - fdEntry_t *fdEntry = getFdEntry(FD); \ - if (fdEntry == NULL) { \ - errno = EBADF; \ - return -1; \ - } \ - do { \ - startOp(fdEntry, &self); \ - ret = FUNC; \ - endOp(fdEntry, &self); \ - } while (ret == -1 && errno == EINTR); \ - return ret; \ -} - -int NET_Read(int s, void* buf, size_t len) { - BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); -} - -int NET_ReadV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); -} - -int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen) { - BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) ); -} - -int NET_Send(int s, void *msg, int len, unsigned int flags) { - BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); -} - -int NET_WriteV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); -} - -int NET_SendTo(int s, const void *msg, int len, unsigned int - flags, const struct sockaddr *to, int tolen) { - BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); -} - -int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) ); -} - -int NET_Connect(int s, struct sockaddr *addr, int addrlen) { - BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); -} - -int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { - BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); -} - -/* - * Wrapper for select(s, timeout). We are using select() on Mac OS due to Bug 7131399. - * Auto restarts with adjusted timeout if interrupted by - * signal other than our wakeup signal. - */ -int NET_Timeout(int s, long timeout) { - long prevtime = 0, newtime; - struct timeval t, *tp = &t; - fd_set fds; - fd_set* fdsp = NULL; - int allocated = 0; - threadEntry_t self; - fdEntry_t *fdEntry = getFdEntry(s); - - /* - * Check that fd hasn't been closed. - */ - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Pick up current time as may need to adjust timeout - */ - if (timeout > 0) { - /* Timed */ - struct timeval now; - gettimeofday(&now, NULL); - prevtime = now.tv_sec * 1000 + now.tv_usec / 1000; - t.tv_sec = timeout / 1000; - t.tv_usec = (timeout % 1000) * 1000; - } else if (timeout < 0) { - /* Blocking */ - tp = 0; - } else { - /* Poll */ - t.tv_sec = 0; - t.tv_usec = 0; - } - - if (s < FD_SETSIZE) { - fdsp = &fds; - FD_ZERO(fdsp); - } else { - int length = (howmany(s+1, NFDBITS)) * sizeof(int); - fdsp = (fd_set *) calloc(1, length); - if (fdsp == NULL) { - return -1; // errno will be set to ENOMEM - } - allocated = 1; - } - FD_SET(s, fdsp); - - for(;;) { - int rv; - - /* - * call select on the fd. If interrupted by our wakeup signal - * errno will be set to EBADF. - */ - - startOp(fdEntry, &self); - rv = select(s+1, fdsp, 0, 0, tp); - endOp(fdEntry, &self); - - /* - * If interrupted then adjust timeout. If timeout - * has expired return 0 (indicating timeout expired). - */ - if (rv < 0 && errno == EINTR) { - if (timeout > 0) { - struct timeval now; - gettimeofday(&now, NULL); - newtime = now.tv_sec * 1000 + now.tv_usec / 1000; - timeout -= newtime - prevtime; - if (timeout <= 0) { - if (allocated != 0) - free(fdsp); - return 0; - } - prevtime = newtime; - t.tv_sec = timeout / 1000; - t.tv_usec = (timeout % 1000) * 1000; - } - } else { - if (allocated != 0) - free(fdsp); - return rv; - } - - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libnet/linux_close.c --- a/jdk/src/java.base/unix/native/libnet/linux_close.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2001, 2013, 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Stack allocated by thread when doing blocking operation - */ -typedef struct threadEntry { - pthread_t thr; /* this thread */ - struct threadEntry *next; /* next thread */ - int intr; /* interrupted */ -} threadEntry_t; - -/* - * Heap allocated during initialized - one entry per fd - */ -typedef struct { - pthread_mutex_t lock; /* fd lock */ - threadEntry_t *threads; /* threads blocked on fd */ -} fdEntry_t; - -/* - * Signal to unblock thread - */ -static int sigWakeup = (__SIGRTMAX - 2); - -/* - * The fd table and the number of file descriptors - */ -static fdEntry_t *fdTable; -static int fdCount; - -/* - * Null signal handler - */ -static void sig_wakeup(int sig) { -} - -/* - * Initialization routine (executed when library is loaded) - * Allocate fd tables and sets up signal handler. - */ -static void __attribute((constructor)) init() { - struct rlimit nbr_files; - sigset_t sigset; - struct sigaction sa; - - /* - * Allocate table based on the maximum number of - * file descriptors. - */ - getrlimit(RLIMIT_NOFILE, &nbr_files); - fdCount = nbr_files.rlim_max; - fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); - if (fdTable == NULL) { - fprintf(stderr, "library initialization failed - " - "unable to allocate file descriptor table - out of memory"); - abort(); - } - - /* - * Setup the signal handler - */ - sa.sa_handler = sig_wakeup; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(sigWakeup, &sa, NULL); - - sigemptyset(&sigset); - sigaddset(&sigset, sigWakeup); - sigprocmask(SIG_UNBLOCK, &sigset, NULL); -} - -/* - * Return the fd table for this fd or NULL is fd out - * of range. - */ -static inline fdEntry_t *getFdEntry(int fd) -{ - if (fd < 0 || fd >= fdCount) { - return NULL; - } - return &fdTable[fd]; -} - -/* - * Start a blocking operation :- - * Insert thread onto thread list for the fd. - */ -static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) -{ - self->thr = pthread_self(); - self->intr = 0; - - pthread_mutex_lock(&(fdEntry->lock)); - { - self->next = fdEntry->threads; - fdEntry->threads = self; - } - pthread_mutex_unlock(&(fdEntry->lock)); -} - -/* - * End a blocking operation :- - * Remove thread from thread list for the fd - * If fd has been interrupted then set errno to EBADF - */ -static inline void endOp - (fdEntry_t *fdEntry, threadEntry_t *self) -{ - int orig_errno = errno; - pthread_mutex_lock(&(fdEntry->lock)); - { - threadEntry_t *curr, *prev=NULL; - curr = fdEntry->threads; - while (curr != NULL) { - if (curr == self) { - if (curr->intr) { - orig_errno = EBADF; - } - if (prev == NULL) { - fdEntry->threads = curr->next; - } else { - prev->next = curr->next; - } - break; - } - prev = curr; - curr = curr->next; - } - } - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; -} - -/* - * Close or dup2 a file descriptor ensuring that all threads blocked on - * the file descriptor are notified via a wakeup signal. - * - * fd1 < 0 => close(fd2) - * fd1 >= 0 => dup2(fd1, fd2) - * - * Returns -1 with errno set if operation fails. - */ -static int closefd(int fd1, int fd2) { - int rv, orig_errno; - fdEntry_t *fdEntry = getFdEntry(fd2); - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Lock the fd to hold-off additional I/O on this fd. - */ - pthread_mutex_lock(&(fdEntry->lock)); - - { - /* - * And close/dup the file descriptor - * (restart if interrupted by signal) - */ - do { - if (fd1 < 0) { - rv = close(fd2); - } else { - rv = dup2(fd1, fd2); - } - } while (rv == -1 && errno == EINTR); - - /* - * Send a wakeup signal to all threads blocked on this - * file descriptor. - */ - threadEntry_t *curr = fdEntry->threads; - while (curr != NULL) { - curr->intr = 1; - pthread_kill( curr->thr, sigWakeup ); - curr = curr->next; - } - } - - /* - * Unlock without destroying errno - */ - orig_errno = errno; - pthread_mutex_unlock(&(fdEntry->lock)); - errno = orig_errno; - - return rv; -} - -/* - * Wrapper for dup2 - same semantics as dup2 system call except - * that any threads blocked in an I/O system call on fd2 will be - * preempted and return -1/EBADF; - */ -int NET_Dup2(int fd, int fd2) { - if (fd < 0) { - errno = EBADF; - return -1; - } - return closefd(fd, fd2); -} - -/* - * Wrapper for close - same semantics as close system call - * except that any threads blocked in an I/O on fd will be - * preempted and the I/O system call will return -1/EBADF. - */ -int NET_SocketClose(int fd) { - return closefd(-1, fd); -} - -/************** Basic I/O operations here ***************/ - -/* - * Macro to perform a blocking IO operation. Restarts - * automatically if interrupted by signal (other than - * our wakeup signal) - */ -#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ - int ret; \ - threadEntry_t self; \ - fdEntry_t *fdEntry = getFdEntry(FD); \ - if (fdEntry == NULL) { \ - errno = EBADF; \ - return -1; \ - } \ - do { \ - startOp(fdEntry, &self); \ - ret = FUNC; \ - endOp(fdEntry, &self); \ - } while (ret == -1 && errno == EINTR); \ - return ret; \ -} - -int NET_Read(int s, void* buf, size_t len) { - BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); -} - -int NET_ReadV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); -} - -int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen) { - BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) ); -} - -int NET_Send(int s, void *msg, int len, unsigned int flags) { - BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); -} - -int NET_WriteV(int s, const struct iovec * vector, int count) { - BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); -} - -int NET_SendTo(int s, const void *msg, int len, unsigned int - flags, const struct sockaddr *to, int tolen) { - BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); -} - -int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) ); -} - -int NET_Connect(int s, struct sockaddr *addr, int addrlen) { - BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); -} - -int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { - BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); -} - -/* - * Wrapper for poll(s, timeout). - * Auto restarts with adjusted timeout if interrupted by - * signal other than our wakeup signal. - */ -int NET_Timeout(int s, long timeout) { - long prevtime = 0, newtime; - struct timeval t; - fdEntry_t *fdEntry = getFdEntry(s); - - /* - * Check that fd hasn't been closed. - */ - if (fdEntry == NULL) { - errno = EBADF; - return -1; - } - - /* - * Pick up current time as may need to adjust timeout - */ - if (timeout > 0) { - gettimeofday(&t, NULL); - prevtime = t.tv_sec * 1000 + t.tv_usec / 1000; - } - - for(;;) { - struct pollfd pfd; - int rv; - threadEntry_t self; - - /* - * Poll the fd. If interrupted by our wakeup signal - * errno will be set to EBADF. - */ - pfd.fd = s; - pfd.events = POLLIN | POLLERR; - - startOp(fdEntry, &self); - rv = poll(&pfd, 1, timeout); - endOp(fdEntry, &self); - - /* - * If interrupted then adjust timeout. If timeout - * has expired return 0 (indicating timeout expired). - */ - if (rv < 0 && errno == EINTR) { - if (timeout > 0) { - gettimeofday(&t, NULL); - newtime = t.tv_sec * 1000 + t.tv_usec / 1000; - timeout -= newtime - prevtime; - if (timeout <= 0) { - return 0; - } - prevtime = newtime; - } - } else { - return rv; - } - - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/unix/native/libnet/solaris_close.c --- a/jdk/src/java.base/unix/native/libnet/solaris_close.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * Copyright (c) 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 -#include -#include -#include - -/* Support for restartable system calls on Solaris. */ - -#define RESTARTABLE_RETURN_INT(_cmd) do { \ - int _result; \ - if (1) { \ - do { \ - _result = _cmd; \ - } while((_result == -1) && (errno == EINTR)); \ - return _result; \ - } \ -} while(0) - -int NET_Read(int s, void* buf, size_t len) { - RESTARTABLE_RETURN_INT(recv(s, buf, len, 0)); -} - -int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen) { - RESTARTABLE_RETURN_INT(recvfrom(s, buf, len, flags, from, fromlen)); -} - -int NET_ReadV(int s, const struct iovec * vector, int count) { - RESTARTABLE_RETURN_INT(readv(s, vector, count)); -} - -int NET_WriteV(int s, const struct iovec * vector, int count) { - RESTARTABLE_RETURN_INT(writev(s, vector, count)); -} - -int NET_Send(int s, void *msg, int len, unsigned int flags) { - RESTARTABLE_RETURN_INT(send(s, msg, len, flags)); -} - -int NET_SendTo(int s, const void *msg, int len, unsigned int flags, - const struct sockaddr *to, int tolen) { - RESTARTABLE_RETURN_INT(sendto(s, msg, len, flags, to, tolen)); -} - -int NET_Connect(int s, struct sockaddr *addr, int addrlen) { - RESTARTABLE_RETURN_INT(connect(s, addr, addrlen)); -} - -int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { - RESTARTABLE_RETURN_INT(accept(s, addr, addrlen)); -} - -int NET_SocketClose(int fd) { - return close(fd); -} - -int NET_Dup2(int fd, int fd2) { - return dup2(fd, fd2); -} - -int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { - RESTARTABLE_RETURN_INT(poll(ufds, nfds, timeout)); -} - -int NET_Timeout(int s, long timeout) { - int result; - struct timeval t; - long prevtime, newtime; - struct pollfd pfd; - pfd.fd = s; - pfd.events = POLLIN; - - if (timeout > 0) { - gettimeofday(&t, NULL); - prevtime = (t.tv_sec * 1000) + t.tv_usec / 1000; - } - - for(;;) { - result = poll(&pfd, 1, timeout); - if (result < 0 && errno == EINTR) { - if (timeout > 0) { - gettimeofday(&t, NULL); - newtime = (t.tv_sec * 1000) + t.tv_usec /1000; - timeout -= newtime - prevtime; - if (timeout <= 0) - return 0; - prevtime = newtime; - } - } else { - return result; - } - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c --- a/jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/windows/native/libnet/DualStackPlainSocketImpl.c Wed Jul 05 20:17:28 2017 +0200 @@ -48,6 +48,8 @@ isa_ctorID = (*env)->GetMethodID(env, cls, "", "(Ljava/net/InetAddress;I)V"); + initInetAddressIDs(env); + // implement read timeout with select. isRcvTimeoutSupported = 0; } @@ -294,6 +296,8 @@ return -1; } + SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0); + ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); (*env)->SetObjectArrayElement(env, isaa, 0, isa); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c --- a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c Wed Jul 05 20:17:28 2017 +0200 @@ -699,6 +699,7 @@ } return; } + SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, 0); (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, fd); if (him.him.sa_family == AF_INET) { diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.base/windows/native/libnio/ch/ServerSocketChannelImpl.c --- a/jdk/src/java.base/windows/native/libnio/ch/ServerSocketChannelImpl.c Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.base/windows/native/libnio/ch/ServerSocketChannelImpl.c Wed Jul 05 20:17:28 2017 +0200 @@ -105,6 +105,7 @@ return IOS_THROWN; } + SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0); (*env)->SetIntField(env, newfdo, fd_fdID, newfd); remote_ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, (int *)&remote_port); CHECK_NULL_RETURN(remote_ia, IOS_THROWN); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.security.jgss/share/classes/sun/security/krb5/Config.java --- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/Config.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/Config.java Wed Jul 05 20:17:28 2017 +0200 @@ -913,9 +913,9 @@ private static String unquote(String s) { s = s.trim(); - if (s.isEmpty()) return s; - if (s.charAt(0) == '"' && s.charAt(s.length()-1) == '"' || - s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\'') { + if (s.length() >= 2 && + ((s.charAt(0) == '"' && s.charAt(s.length()-1) == '"') || + (s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\''))) { s = s.substring(1, s.length()-1).trim(); } return s; diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java --- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbKdcRep.java Wed Jul 05 20:17:28 2017 +0200 @@ -62,7 +62,8 @@ throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); } - for (int i = 1; i < 6; i++) { + // We allow KDC to return a non-forwardable ticket if request has -f + for (int i = 2; i < 6; i++) { if (req.reqBody.kdcOptions.get(i) != rep.encKDCRepPart.flags.get(i)) { if (Krb5.DEBUG) { diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java --- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java Wed Jul 05 20:17:28 2017 +0200 @@ -149,19 +149,11 @@ ctime = KerberosTime.now(); // check if they are valid arguments. The optional fields - // should be consistent with settings in KDCOptions. - - // TODO: Is this necessary? If the TGT is not FORWARDABLE, - // you can still request for a FORWARDABLE ticket, just the - // KDC will give you a non-FORWARDABLE one. Even if you - // cannot use the ticket expected, it still contains info. - // This means there will be problem later. We already have - // flags check in KrbTgsRep. Of course, sometimes the KDC - // will not issue the ticket at all. + // should be consistent with settings in KDCOptions. if (options.get(KDCOptions.FORWARDABLE) && (!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) { - throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + options.set(KDCOptions.FORWARDABLE, false); } if (options.get(KDCOptions.FORWARDED)) { if (!(asCreds.flags.get(KDCOptions.FORWARDABLE))) diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java --- a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java Wed Jul 05 20:17:28 2017 +0200 @@ -58,6 +58,9 @@ // TODO: we do not support kerberos referral now throw new KrbException("Cross realm impersonation not supported"); } + if (!ccreds.isForwardable()) { + throw new KrbException("S4U2self needs a FORWARDABLE ticket"); + } KrbTgsReq req = new KrbTgsReq( ccreds, ccreds.getClient(), @@ -68,6 +71,9 @@ if (!creds.getClient().equals(client)) { throw new KrbException("S4U2self request not honored by KDC"); } + if (!creds.isForwardable()) { + throw new KrbException("S4U2self ticket must be FORWARDABLE"); + } return creds; } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/jdk.sctp/unix/native/libsctp/SctpNet.c --- a/jdk/src/jdk.sctp/unix/native/libsctp/SctpNet.c Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/src/jdk.sctp/unix/native/libsctp/SctpNet.c Wed Jul 05 20:17:28 2017 +0200 @@ -316,11 +316,12 @@ if (isaCls == 0) { jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress"); CHECK_NULL(c); + isaCtrID = (*env)->GetMethodID(env, c, "", + "(Ljava/net/InetAddress;I)V"); + CHECK_NULL(isaCtrID); isaCls = (*env)->NewGlobalRef(env, c); CHECK_NULL(isaCls); (*env)->DeleteLocalRef(env, c); - isaCtrID = (*env)->GetMethodID(env, isaCls, "", - "(Ljava/net/InetAddress;I)V"); } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/jdk.security.auth/solaris/native/libjaas/Solaris.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.security.auth/solaris/native/libjaas/Solaris.c Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2000, 2013, 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 +#include "com_sun_security_auth_module_SolarisSystem.h" +#include +#include +#include +#include +#include +#include + +static void throwIllegalArgumentException(JNIEnv *env, const char *msg) { + jclass clazz = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); + if (clazz != NULL) + (*env)->ThrowNew(env, clazz, msg); +} + +JNIEXPORT void JNICALL +Java_com_sun_security_auth_module_SolarisSystem_getSolarisInfo + (JNIEnv *env, jobject obj) { + + int i; + char pwd_buf[1024]; + struct passwd pwd; + jsize numSuppGroups = getgroups(0, NULL); + jfieldID fid; + jstring jstr; + jlongArray jgroups; + jlong *jgroupsAsArray; + gid_t *groups; + jclass cls; + + groups = (gid_t *)calloc(numSuppGroups, sizeof(gid_t)); + + if (groups == NULL) { + jclass cls = (*env)->FindClass(env,"java/lang/OutOfMemoryError"); + if (cls != NULL) + (*env)->ThrowNew(env, cls, NULL); + return; + } + + cls = (*env)->GetObjectClass(env, obj); + + memset(pwd_buf, 0, sizeof(pwd_buf)); + if (getpwuid_r(getuid(), &pwd, pwd_buf, sizeof(pwd_buf)) != NULL && + getgroups(numSuppGroups, groups) != -1) { + + /* + * set username + */ + fid = (*env)->GetFieldID(env, cls, "username", "Ljava/lang/String;"); + if (fid == 0) { + (*env)->ExceptionClear(env); + throwIllegalArgumentException(env, "invalid field: username"); + goto cleanupAndReturn; + } + jstr = (*env)->NewStringUTF(env, pwd.pw_name); + if (jstr == NULL) { + goto cleanupAndReturn; + } + (*env)->SetObjectField(env, obj, fid, jstr); + + /* + * set uid + */ + fid = (*env)->GetFieldID(env, cls, "uid", "J"); + if (fid == 0) { + (*env)->ExceptionClear(env); + throwIllegalArgumentException(env, "invalid field: uid"); + goto cleanupAndReturn; + } + (*env)->SetLongField(env, obj, fid, pwd.pw_uid); + + /* + * set gid + */ + fid = (*env)->GetFieldID(env, cls, "gid", "J"); + if (fid == 0) { + (*env)->ExceptionClear(env); + throwIllegalArgumentException(env, "invalid field: gid"); + goto cleanupAndReturn; + } + (*env)->SetLongField(env, obj, fid, pwd.pw_gid); + + /* + * set supplementary groups + */ + fid = (*env)->GetFieldID(env, cls, "groups", "[J"); + if (fid == 0) { + (*env)->ExceptionClear(env); + throwIllegalArgumentException(env, "invalid field: groups"); + goto cleanupAndReturn; + } + + jgroups = (*env)->NewLongArray(env, numSuppGroups); + if (jgroups == NULL) { + goto cleanupAndReturn; + } + jgroupsAsArray = (*env)->GetLongArrayElements(env, jgroups, 0); + if (jgroupsAsArray == NULL) { + goto cleanupAndReturn; + } + for (i = 0; i < numSuppGroups; i++) + jgroupsAsArray[i] = groups[i]; + (*env)->ReleaseLongArrayElements(env, jgroups, jgroupsAsArray, 0); + (*env)->SetObjectField(env, obj, fid, jgroups); + } +cleanupAndReturn: + free(groups); + + return; +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/src/jdk.security.auth/unix/native/libjaas/Solaris.c --- a/jdk/src/jdk.security.auth/unix/native/libjaas/Solaris.c Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2000, 2013, 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 -#include "com_sun_security_auth_module_SolarisSystem.h" -#include -#include -#include -#include -#include -#include - -static void throwIllegalArgumentException(JNIEnv *env, const char *msg) { - jclass clazz = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); - if (clazz != NULL) - (*env)->ThrowNew(env, clazz, msg); -} - -JNIEXPORT void JNICALL -Java_com_sun_security_auth_module_SolarisSystem_getSolarisInfo - (JNIEnv *env, jobject obj) { - - int i; - char pwd_buf[1024]; - struct passwd pwd; - jsize numSuppGroups = getgroups(0, NULL); - jfieldID fid; - jstring jstr; - jlongArray jgroups; - jlong *jgroupsAsArray; - gid_t *groups; - jclass cls; - - groups = (gid_t *)calloc(numSuppGroups, sizeof(gid_t)); - - if (groups == NULL) { - jclass cls = (*env)->FindClass(env,"java/lang/OutOfMemoryError"); - if (cls != NULL) - (*env)->ThrowNew(env, cls, NULL); - return; - } - - cls = (*env)->GetObjectClass(env, obj); - - memset(pwd_buf, 0, sizeof(pwd_buf)); - if (getpwuid_r(getuid(), &pwd, pwd_buf, sizeof(pwd_buf)) != NULL && - getgroups(numSuppGroups, groups) != -1) { - - /* - * set username - */ - fid = (*env)->GetFieldID(env, cls, "username", "Ljava/lang/String;"); - if (fid == 0) { - (*env)->ExceptionClear(env); - throwIllegalArgumentException(env, "invalid field: username"); - goto cleanupAndReturn; - } - jstr = (*env)->NewStringUTF(env, pwd.pw_name); - if (jstr == NULL) { - goto cleanupAndReturn; - } - (*env)->SetObjectField(env, obj, fid, jstr); - - /* - * set uid - */ - fid = (*env)->GetFieldID(env, cls, "uid", "J"); - if (fid == 0) { - (*env)->ExceptionClear(env); - throwIllegalArgumentException(env, "invalid field: uid"); - goto cleanupAndReturn; - } - (*env)->SetLongField(env, obj, fid, pwd.pw_uid); - - /* - * set gid - */ - fid = (*env)->GetFieldID(env, cls, "gid", "J"); - if (fid == 0) { - (*env)->ExceptionClear(env); - throwIllegalArgumentException(env, "invalid field: gid"); - goto cleanupAndReturn; - } - (*env)->SetLongField(env, obj, fid, pwd.pw_gid); - - /* - * set supplementary groups - */ - fid = (*env)->GetFieldID(env, cls, "groups", "[J"); - if (fid == 0) { - (*env)->ExceptionClear(env); - throwIllegalArgumentException(env, "invalid field: groups"); - goto cleanupAndReturn; - } - - jgroups = (*env)->NewLongArray(env, numSuppGroups); - if (jgroups == NULL) { - goto cleanupAndReturn; - } - jgroupsAsArray = (*env)->GetLongArrayElements(env, jgroups, 0); - if (jgroupsAsArray == NULL) { - goto cleanupAndReturn; - } - for (i = 0; i < numSuppGroups; i++) - jgroupsAsArray[i] = groups[i]; - (*env)->ReleaseLongArrayElements(env, jgroups, jgroupsAsArray, 0); - (*env)->SetObjectField(env, obj, fid, jgroups); - } -cleanupAndReturn: - free(groups); - - return; -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/ProblemList.txt --- a/jdk/test/ProblemList.txt Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/ProblemList.txt Wed Jul 05 20:17:28 2017 +0200 @@ -128,9 +128,6 @@ # jdk_instrument -# 8058536 -java/lang/instrument/NativeMethodPrefixAgent.java generic-all - # 8061177 java/lang/instrument/RedefineBigClass.sh generic-all java/lang/instrument/RetransformBigClass.sh generic-all @@ -246,6 +243,9 @@ # 8062758 java/security/Security/ClassLoaderDeadlock/Deadlock2.sh generic-all +# 8026393 +sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java generic-all + ############################################################################ # jdk_sound diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/TEST.groups --- a/jdk/test/TEST.groups Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/TEST.groups Wed Jul 05 20:17:28 2017 +0200 @@ -628,7 +628,6 @@ sun/net/www/protocol/http \ java/io/BufferedReader/Lines.java \ java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java \ - java/lang/CharSequence/DefaultTest.java \ java/lang/IntegralPrimitiveToString.java \ java/lang/PrimitiveSumMinMaxTest.java \ java/lang/String/StringJoinTest.java \ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/io/Serializable/subclassGC/SubclassGC.java --- a/jdk/test/java/io/Serializable/subclassGC/SubclassGC.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/io/Serializable/subclassGC/SubclassGC.java Wed Jul 05 20:17:28 2017 +0200 @@ -50,8 +50,9 @@ } ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); - ClassLoader loader = new URLClassLoader(((URLClassLoader) systemLoader).getURLs(), - systemLoader.getParent()); + URL testClassesURL = new File(System.getProperty("test.classes")).toURI().toURL(); + ClassLoader loader = new URLClassLoader(new URL[] { testClassesURL } , + systemLoader.getParent()); Class cl = Class.forName(SubclassOfOOS.class.getName(), false, loader).asSubclass(ObjectOutputStream.class); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/io/Serializable/subclassGC/security.policy --- a/jdk/test/java/io/Serializable/subclassGC/security.policy Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/io/Serializable/subclassGC/security.policy Wed Jul 05 20:17:28 2017 +0200 @@ -2,5 +2,7 @@ grant { permission java.lang.RuntimePermission "createClassLoader"; permission java.lang.RuntimePermission "getClassLoader"; + permission java.util.PropertyPermission "test.classes", "read"; + permission java.io.FilePermission "<>", "read"; }; diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/lang/CharSequence/DefaultTest.java --- a/jdk/test/java/lang/CharSequence/DefaultTest.java Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2012, 2013, 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.util.Arrays; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.PrimitiveIterator; -import java.util.Spliterator; -import java.util.stream.Collectors; - -import org.testng.annotations.Test; - -import static org.testng.Assert.*; - -/* - * @test - * @summary Unit test for CharSequence default methods - * @bug 8012665 8025002 - * @run testng DefaultTest - */ - -@Test(groups = "lib") -public class DefaultTest { - - @Test(expectedExceptions = NoSuchElementException.class) - public void testEmptyChars() { - PrimitiveIterator.OfInt s = "".chars().iterator(); - assertFalse(s.hasNext()); - int ch = s.nextInt(); - } - - public void testSimpleChars() { - List list = "abc".chars().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList((int) 'a', (int) 'b', (int) 'c')); - } - - public void testCodePointsCharacteristics() { - Spliterator.OfInt s = "".codePoints().spliterator(); - assertFalse(s.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)); - assertTrue(s.hasCharacteristics(Spliterator.ORDERED)); - } - - @Test(expectedExceptions = NoSuchElementException.class) - public void testEmptyCodePoints() { - PrimitiveIterator.OfInt s = "".codePoints().iterator(); - assertFalse(s.hasNext()); - int cp = s.nextInt(); - } - - public void testSimpleCodePoints() { - List list = "abc".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList((int)'a', (int)'b', (int)'c')); - } - - public void testUndefCodePoints() { - List list = "X\ufffeY".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList((int)'X', 0xFFFE, (int)'Y')); - } - - public void testSurrogatePairing() { - // U+1D11E = MUSICAL SYMBOL G CLEF - // equivalent to surrogate pair U+D834 U+DD1E - List list; - final int GCLEF = 0x1d11e; - - list = "\ud834\udd1e".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList(GCLEF)); - list = "A\ud834\udd1e".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList((int)'A', GCLEF)); - list = "\ud834\udd1eB".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList(GCLEF, (int)'B')); - list = "X\ud834\udd1eY".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList((int)'X', GCLEF, (int)'Y')); - } - - public void testUndefUnpaired() { - List list = "W\udd1eX\ud834Y\ufffeZ".codePoints().boxed().collect(Collectors.toList()); - assertEquals(list, Arrays.asList( - (int)'W', 0xdd1e, (int)'X', 0xd834, (int)'Y', 0xfffe, (int)'Z')); - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/lang/ProcessBuilder/Basic.java --- a/jdk/test/java/lang/ProcessBuilder/Basic.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/lang/ProcessBuilder/Basic.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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,7 @@ * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 - * 4947220 7018606 7034570 4244896 5049299 8003488 + * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 * @summary Basic tests for Process and Environment Variable code * @run main/othervm/timeout=300 Basic * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic @@ -2042,7 +2042,7 @@ final Object deferred; Class c = s.getClass(); if (c.getName().equals( - "java.lang.UNIXProcess$DeferredCloseInputStream")) + "java.lang.ProcessImpl$DeferredCloseInputStream")) { deferred = s; } else { @@ -2059,13 +2059,11 @@ Thread.yield(); } } else if (s instanceof BufferedInputStream) { - Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - Unsafe unsafe = (Unsafe)f.get(null); - - while (unsafe.tryMonitorEnter(s)) { - unsafe.monitorExit(s); - Thread.sleep(1); + // Wait until after the s.read occurs in "thread" by + // checking when the input stream monitor is acquired + // (BufferedInputStream.read is synchronized) + while (!isLocked(s, 10)) { + Thread.sleep(100); } } p.destroy(); @@ -2565,4 +2563,21 @@ catch (Throwable t) { if (k.isAssignableFrom(t.getClass())) pass(); else unexpected(t);}} + + static boolean isLocked(final Object monitor, final long millis) throws InterruptedException { + return new Thread() { + volatile boolean unlocked; + + @Override + public void run() { + synchronized (monitor) { unlocked = true; } + } + + boolean isLocked() throws InterruptedException { + start(); + join(millis); + return !unlocked; + } + }.isLocked(); + } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/lang/ref/OOMEInReferenceHandler.java --- a/jdk/test/java/lang/ref/OOMEInReferenceHandler.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/lang/ref/OOMEInReferenceHandler.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -25,7 +25,7 @@ * @test * @bug 7038914 8016341 * @summary Verify that the reference handler does not die after an OOME allocating the InterruptedException object - * @run main/othervm -Xmx24M -XX:-UseTLAB OOMEInReferenceHandler + * @run main/othervm -XX:-UseGCOverheadLimit -Xmx24M -XX:-UseTLAB OOMEInReferenceHandler * @author peter.levart@gmail.com */ diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/net/ServerSocket/AcceptInheritHandle.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/ServerSocket/AcceptInheritHandle.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015, 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 8067105 + * @summary Socket returned by ServerSocket.accept() is inherited by child process on Windows + * @author Chris Hegarty + */ + +import java.io.*; +import java.net.*; +import java.nio.channels.ServerSocketChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public class AcceptInheritHandle { + + enum ServerSocketProducer { + JAVA_NET(() -> { + try { + return new ServerSocket(); } + catch(IOException x) { + throw new UncheckedIOException(x); + } + }), + NIO_CHANNELS(() -> { + try { + return ServerSocketChannel.open().socket(); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + + final Supplier supplier; + ServerSocketProducer(Supplier supplier) { + this.supplier = supplier; + } + Supplier supplier () { return supplier; } + } + + static final String JAVA = System.getProperty("java.home") + + File.separator + "bin" + File.separator + "java"; + + static final String CLASSPATH = System.getProperty("java.class.path"); + + public static void main(String[] args) throws Exception { + if (args.length == 1) + server(ServerSocketProducer.valueOf(args[0])); + else + mainEntry(); + } + + static void mainEntry() throws Exception { + testJavaNetServerSocket(); + testNioServerSocketChannel(); + } + + static void testJavaNetServerSocket() throws Exception { + test(ServerSocketProducer.JAVA_NET); + test(ServerSocketProducer.JAVA_NET, "-Djava.net.preferIPv4Stack=true"); + } + static void testNioServerSocketChannel() throws Exception { + test(ServerSocketProducer.NIO_CHANNELS); + } + + static void test(ServerSocketProducer ssp, String... sysProps) throws Exception { + System.out.println("\nStarting test for " + ssp.name()); + + List commands = new ArrayList<>(); + commands.add(JAVA); + for (String prop : sysProps) + commands.add(prop); + commands.add("-cp"); + commands.add(CLASSPATH); + commands.add("AcceptInheritHandle"); + commands.add(ssp.name()); + + System.out.println("Executing: "+ commands); + ProcessBuilder pb = new ProcessBuilder(commands); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + Process serverProcess = pb.start(); + DataInputStream dis = new DataInputStream(serverProcess.getInputStream()); + + int port = dis.readInt(); + System.out.println("Server process listening on " + port + ", connecting..."); + + Socket socket = new Socket("localhost", port); + String s = dis.readUTF(); + System.out.println("Server process said " + s); + + serverProcess.destroy(); + serverProcess.waitFor(30, TimeUnit.SECONDS); + System.out.println("serverProcess exitCode:" + serverProcess.exitValue()); + + try { + socket.setSoTimeout(10 * 1000); + socket.getInputStream().read(); + } catch (SocketTimeoutException x) { + // failed + throw new RuntimeException("Failed: should get reset, not " + x); + } catch (SocketException x) { + System.out.println("Expected:" + x); + } + } + + static void server(ServerSocketProducer producer) throws Exception { + try (ServerSocket ss = producer.supplier().get()) { + ss.bind(new InetSocketAddress(0)); + int port = ss.getLocalPort(); + DataOutputStream dos = new DataOutputStream(System.out); + dos.writeInt(port); + dos.flush(); + + ss.accept(); // do not close + + Runtime.getRuntime().exec("sleep 20"); + Thread.sleep(3 * 1000); + + dos.writeUTF("kill me!"); + dos.flush(); + Thread.sleep(30 * 1000); + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/net/Socket/GetLocalAddress.java --- a/jdk/test/java/net/Socket/GetLocalAddress.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/net/Socket/GetLocalAddress.java Wed Jul 05 20:17:28 2017 +0200 @@ -23,7 +23,7 @@ /* * @test - * @bug 4106601 8026245 + * @bug 4106601 8026245 8071424 * @run main/othervm GetLocalAddress * @run main/othervm -Djava.net.preferIPv4Stack=true GetLocalAddress * @run main/othervm -Djava.net.preferIPv6Addresses=true GetLocalAddress @@ -39,6 +39,8 @@ static int port; public static void main(String args[]) throws Exception { + testBindNull(); + boolean error = true; int linger = 65546; int value = 0; @@ -66,4 +68,18 @@ } } + static void testBindNull() throws Exception { + try (Socket soc = new Socket()) { + soc.bind(null); + if (!soc.isBound()) + throw new RuntimeException( + "should be bound after bind(null)"); + if (soc.getLocalPort() <= 0) + throw new RuntimeException( + "bind(null) failed, local port: " + soc.getLocalPort()); + if (soc.getLocalAddress() == null) + throw new RuntimeException( + "bind(null) failed, local address is null"); + } + } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/util/ResourceBundle/Bug6287579.java --- a/jdk/test/java/util/ResourceBundle/Bug6287579.java Thu Jan 29 15:36:21 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +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 6287579 - * @summary Make sure that getContents() of ListResourceBundle subclasses is 'protected' - * and returns a different Object[]][] instance in each invocation. - */ - -import java.lang.reflect.*; -import java.util.*; - -public class Bug6287579 { - static final Locale ROOT = new Locale(""); - - static final String[] baseNames = { - "sun.text.resources.BreakIteratorInfo", - "sun.text.resources.FormatData", - "sun.text.resources.CollationData", - "sun.util.resources.LocaleNames", - "sun.util.resources.TimeZoneNames", - - // Make sure the properties-to-class conversion tool generates - // the proper getContents(). - "sun.awt.resources.awt", - }; - - public static void main(String[] args) throws Exception { - int errors = 0; - - List locales = new ArrayList(); - locales.addAll(Arrays.asList(Locale.getAvailableLocales())); - locales.add(ROOT); - - for (Locale locale : locales) { - for (String base : baseNames) { - String className = getResourceName(base, locale); - errors += checkGetContents(className); - } - } - if (errors > 0) { - throw new RuntimeException(errors + " errors found"); - } - } - - static int checkGetContents(String className) throws Exception { - int err = 0; - try { - Class clazz = Class.forName(className); - Method getContentsMethod = clazz.getDeclaredMethod("getContents", - (Class[]) null); - if (!Modifier.isProtected(getContentsMethod.getModifiers())) { - System.err.println(className + ": not protected"); - err++; - } - getContentsMethod.setAccessible(true); - Object bundle = clazz.newInstance(); - Object o1 = getContentsMethod.invoke(bundle, (Object[]) null); - Object o2 = getContentsMethod.invoke(bundle, (Object[]) null); - if (o1 == o2) { - System.err.println(className + ": same instance returned"); - err++; - } - } catch (ClassNotFoundException ce) { - // Skip nonexistent classes - } catch (NoSuchMethodException me) { - System.out.println(className + ": no declared getContents()"); - } - return err; - } - - static String getResourceName(String base, Locale locale) { - if (locale.equals(ROOT)) { - return base; - } - StringBuilder sb = new StringBuilder(base); - sb.append('_').append(locale.getLanguage()); - if (locale.getCountry().length() > 0 - || locale.getVariant().length() > 0) { - sb.append('_').append(locale.getCountry()); - } - if (locale.getVariant().length() > 0) { - sb.append('_').append(locale.getVariant()); - } - return sb.toString(); - } -} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java --- a/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -23,7 +23,7 @@ /** * @test - * @bug 8020156 8020009 8022326 8012913 8024405 8024408 + * @bug 8020156 8020009 8022326 8012913 8024405 8024408 8071477 * @run testng SpliteratorCharacteristics */ @@ -59,6 +59,57 @@ @Test public class SpliteratorCharacteristics { + public void testSpliteratorFromCharSequence() { + class CharSequenceImpl implements CharSequence { + final String s; + + public CharSequenceImpl(String s) { + this.s = s; + } + + @Override + public int length() { + return s.length(); + } + + @Override + public char charAt(int index) { + return s.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return s.subSequence(start, end); + } + + @Override + public String toString() { + return s; + } + } + + CharSequence cs = "A"; + Spliterator.OfInt s = cs.chars().spliterator(); + assertCharacteristics(s, Spliterator.IMMUTABLE | Spliterator.ORDERED | + Spliterator.SIZED | Spliterator.SUBSIZED); + assertHasNotCharacteristics(s, Spliterator.CONCURRENT); + s = cs.codePoints().spliterator(); + assertCharacteristics(s, Spliterator.IMMUTABLE | Spliterator.ORDERED); + assertHasNotCharacteristics(s, Spliterator.CONCURRENT); + + for (CharSequence c : Arrays.asList(new CharSequenceImpl("A"), + new StringBuilder("A"), + new StringBuffer("A"))) { + s = cs.chars().spliterator(); + assertCharacteristics(s, Spliterator.ORDERED | + Spliterator.SIZED | Spliterator.SUBSIZED); + assertHasNotCharacteristics(s, Spliterator.CONCURRENT); + s = cs.codePoints().spliterator(); + assertCharacteristics(s, Spliterator.ORDERED); + assertHasNotCharacteristics(s, Spliterator.CONCURRENT); + } + } + public void testSpliteratorFromCollection() { List l = Arrays.asList(1, 2, 3, 4); diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java --- a/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -25,7 +25,7 @@ * @test * @summary Spliterator traversing and splitting tests * @run testng SpliteratorTraversingAndSplittingTest - * @bug 8020016 + * @bug 8020016 8071477 */ import org.testng.annotations.DataProvider; @@ -85,7 +85,38 @@ @Test public class SpliteratorTraversingAndSplittingTest { - private static List SIZES = Arrays.asList(0, 1, 10, 100, 1000); + private static final List SIZES = Arrays.asList(0, 1, 10, 100, 1000); + + private static final String LOW = new String(new char[] {Character.MIN_LOW_SURROGATE}); + private static final String HIGH = new String(new char[] {Character.MIN_HIGH_SURROGATE}); + private static final String HIGH_LOW = HIGH + LOW; + private static final String CHAR_HIGH_LOW = "A" + HIGH_LOW; + private static final String HIGH_LOW_CHAR = HIGH_LOW + "A"; + private static final String CHAR_HIGH_LOW_CHAR = "A" + HIGH_LOW + "A"; + + private static final List STRINGS = generateTestStrings(); + + private static List generateTestStrings() { + List strings = new ArrayList<>(); + for (int n : Arrays.asList(1, 2, 3, 16, 17)) { + strings.add(generate("A", n)); + strings.add(generate(LOW, n)); + strings.add(generate(HIGH, n)); + strings.add(generate(HIGH_LOW, n)); + strings.add(generate(CHAR_HIGH_LOW, n)); + strings.add(generate(HIGH_LOW_CHAR, n)); + strings.add(generate(CHAR_HIGH_LOW_CHAR, n)); + } + return strings; + } + + private static String generate(String s, int n) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append(s); + } + return sb.toString(); + } private static class SpliteratorDataBuilder { List data; @@ -564,6 +595,60 @@ } } + private static class SpliteratorOfIntCharDataBuilder { + List data; + + String s; + + List expChars; + + List expCodePoints; + + SpliteratorOfIntCharDataBuilder(List data, String s) { + this.data = data; + this.s = s; + this.expChars = transform(s, false); + this.expCodePoints = transform(s, true); + } + + static List transform(String s, boolean toCodePoints) { + List l = new ArrayList<>(); + + if (!toCodePoints) { + for (int i = 0; i < s.length(); i++) { + l.add((int) s.charAt(i)); + } + } + else { + for (int i = 0; i < s.length();) { + char c1 = s.charAt(i++); + int cp = c1; + if (Character.isHighSurrogate(c1) && i < s.length()) { + char c2 = s.charAt(i); + if (Character.isLowSurrogate(c2)) { + i++; + cp = Character.toCodePoint(c1, c2); + } + } + l.add(cp); + } + } + return l; + } + + void add(String description, Function f) { + description = description.replace("%s", s); + { + Supplier supplier = () -> f.apply(s).chars().spliterator(); + data.add(new Object[]{description + ".chars().spliterator()", expChars, supplier}); + } + { + Supplier supplier = () -> f.apply(s).codePoints().spliterator(); + data.add(new Object[]{description + ".codePoints().spliterator()", expCodePoints, supplier}); + } + } + } + static Object[][] spliteratorOfIntDataProvider; @DataProvider(name = "Spliterator.OfInt") @@ -615,6 +700,43 @@ () -> new IntSpliteratorFromArray(exp)); } + // Class for testing default methods + class CharSequenceImpl implements CharSequence { + final String s; + + public CharSequenceImpl(String s) { + this.s = s; + } + + @Override + public int length() { + return s.length(); + } + + @Override + public char charAt(int index) { + return s.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return s.subSequence(start, end); + } + + @Override + public String toString() { + return s; + } + } + + for (String string : STRINGS) { + SpliteratorOfIntCharDataBuilder cdb = new SpliteratorOfIntCharDataBuilder(data, string); + cdb.add("\"%s\"", s -> s); + cdb.add("new CharSequenceImpl(\"%s\")", CharSequenceImpl::new); + cdb.add("new StringBuilder(\"%s\")", StringBuilder::new); + cdb.add("new StringBuffer(\"%s\")", StringBuffer::new); + } + return spliteratorOfIntDataProvider = data.toArray(new Object[0][]); } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/javax/xml/jaxp/transform/8062923/XslSubstringTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/jaxp/transform/8062923/XslSubstringTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, 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 8062923 8062924 + * @run testng XslSubstringTest + * @summary Test xsl substring function with negative, Inf and + * NaN length and few other use cases + */ + +import java.io.StringReader; +import java.io.StringWriter; +import javax.xml.transform.Source; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import static org.testng.Assert.assertEquals; +import org.testng.annotations.Test; + +public class XslSubstringTest { + + final String xml = ""; + final String xslPre = "" + + "" + + ""; + final String xslPost = ""; + + private String testTransform(String xsl) throws Exception { + //Prepare sources for transormation + Source src = new StreamSource(new StringReader(xml)); + Source xslsrc = new StreamSource(new StringReader(xslPre + xsl + xslPost)); + //Create factory, template and transformer + TransformerFactory tf = TransformerFactory.newInstance(); + Templates tmpl = tf.newTemplates(xslsrc); + Transformer t = tmpl.newTransformer(); + //Prepare output stream + StringWriter xmlResultString = new StringWriter(); + StreamResult xmlResultStream = new StreamResult(xmlResultString); + //Transform + t.transform(src, xmlResultStream); + return xmlResultString.toString().trim(); + } + + @Test + public void test8062923() throws Exception { + assertEquals(testTransform("||"), + "||"); + } + + @Test + public void test8062924() throws Exception { + assertEquals(testTransform("||"), + "||"); + } + + @Test + public void testGeneral1() throws Exception { + assertEquals(testTransform("||"), + "|s|"); + } + + @Test + public void testGeneral2() throws Exception { + assertEquals(testTransform("||"), + "|sdf|"); + } + + @Test + public void testGeneral3() throws Exception { + assertEquals(testTransform("||"), + "||"); + } + + @Test + public void testGeneral4() throws Exception { + assertEquals(testTransform("||"), + "||"); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/javax/xml/ws/8046817/GenerateEnumSchema.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/ws/8046817/GenerateEnumSchema.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 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. + * + * 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 8046817 + * @summary schemagen fails to generate xsd for enum types + * @run main/othervm GenerateEnumSchema + */ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Paths; +import java.util.Scanner; + +public class GenerateEnumSchema { + + private static final String SCHEMA_OUTPUT_FILENAME = "schema1.xsd"; + private static final File schemaOutputFile = new File(SCHEMA_OUTPUT_FILENAME); + + public static void main(String[] args) throws Exception, IOException { + //Check schema generation for class type + runSchemaGen("TestClassType.java"); + checkIfSchemaGenerated(); + checkSchemaContent(""); + checkSchemaContent(""); + schemaOutputFile.delete(); + //Check schema generation for enum type + runSchemaGen("TestEnumType.java"); + checkIfSchemaGenerated(); + checkSchemaContent(""); + checkSchemaContent(""); + checkSchemaContent(""); + checkSchemaContent(""); + schemaOutputFile.delete(); + } + + private static void checkIfSchemaGenerated() { + if (!schemaOutputFile.exists()) { + throw new RuntimeException("FAIL:" + SCHEMA_OUTPUT_FILENAME + " was not generated by schemagen tool"); + } + } + + private static void checkSchemaContent(String exp_token) throws FileNotFoundException { + System.out.print("Check if generated schema contains '" + exp_token + "' string: "); + try (Scanner scanner = new Scanner(schemaOutputFile)) { + if (scanner.findWithinHorizon(exp_token, 0) != null) { + System.out.println("OK"); + return; + } + } + System.out.println("FAIL"); + throw new RuntimeException("The '" + exp_token + "' is not found in generated schema"); + + } + + private static String getClassFilePath(String filename) { + String testSrc = System.getProperty("test.src"); + if (testSrc == null) { + testSrc = "."; + } + return Paths.get(testSrc).resolve(filename).toString(); + } + + private static String getSchemagen() { + String javaHome = System.getProperty("java.home"); + if (javaHome.endsWith("jre")) { + javaHome = new File(javaHome).getParent(); + } + String schemagen = javaHome + File.separator + "bin" + File.separator + "schemagen"; + if (System.getProperty("os.name").startsWith("Windows")) { + schemagen = schemagen.concat(".exe"); + } + return schemagen; + } + + private static void logOutput(Process p) throws IOException { + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); + String s = r.readLine(); + while (s != null) { + System.out.println(s.trim()); + s = r.readLine(); + } + } + + private static void runSchemaGen(String classFile) { + String schemagen = getSchemagen(); + + try { + System.out.println("Call to schemagen: " + schemagen + " " + classFile); + String[] schemagen_args = { + schemagen, + getClassFilePath(classFile) + }; + + ProcessBuilder pb = new ProcessBuilder(schemagen_args); + pb.redirectErrorStream(true); + Process p = pb.start(); + logOutput(p); + int result = p.waitFor(); + p.destroy(); + + if (result != 0) { + throw new RuntimeException("schemagen failed"); + } + } catch (IOException | InterruptedException e) { + System.err.println("Can't run schemagen tool. Exception:"); + e.printStackTrace(System.err); + throw new RuntimeException("Error launching schemagen tool"); + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/javax/xml/ws/8046817/TestClassType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/ws/8046817/TestClassType.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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. + * + * 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 javax.xml.bind.annotation.XmlType; + +@XmlType +public class TestClassType { + public int a; +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/javax/xml/ws/8046817/TestEnumType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/ws/8046817/TestEnumType.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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. + * + * 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 javax.xml.bind.annotation.XmlEnum; + +@XmlEnum(String.class) +public enum TestEnumType { + ONE, TWO, THREE +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/lib/testlibrary/jdk/testlibrary/JarUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/lib/testlibrary/jdk/testlibrary/JarUtils.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.testlibrary; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +/** + * Common library for various test jar file utility functions. + */ +public final class JarUtils { + + /** + * Create jar file with specified files. + */ + public static void createJar(String dest, String... files) + throws IOException { + try (JarOutputStream jos = new JarOutputStream( + new FileOutputStream(dest), new Manifest())) { + for (String file : files) { + System.out.println(String.format("Adding %s to %s", + file, dest)); + + // add an archive entry, and write a file + jos.putNextEntry(new JarEntry(file)); + try (FileInputStream fis = new FileInputStream(file)) { + fis.transferTo(jos); + } + } + } + System.out.println(); + } + + /** + * Add specified files to existing jar file. + */ + public static void updateJar(String src, String dest, String... files) + throws IOException { + try (JarOutputStream jos = new JarOutputStream( + new FileOutputStream(dest))) { + + // copy each old entry into destination unless the entry name + // is in the updated list + List updatedFiles = new ArrayList<>(); + try (JarFile srcJarFile = new JarFile(src)) { + Enumeration entries = srcJarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = (JarEntry) entries.nextElement(); + String name = entry.getName(); + boolean found = false; + for (String file : files) { + if (name.equals(file)) { + updatedFiles.add(file); + found = true; + break; + } + } + + if (found) { + System.out.println(String.format("Updating %s with %s", + dest, name)); + jos.putNextEntry(new JarEntry(name)); + try (FileInputStream fis = new FileInputStream(name)) { + fis.transferTo(jos); + } + } else { + System.out.println(String.format("Copying %s to %s", + name, dest)); + jos.putNextEntry(entry); + srcJarFile.getInputStream(entry).transferTo(jos); + } + } + } + + // append new files + for (String file : files) { + if (!updatedFiles.contains(file)) { + System.out.println(String.format("Adding %s with %s", + dest, file)); + jos.putNextEntry(new JarEntry(file)); + try (FileInputStream fis = new FileInputStream(file)) { + fis.transferTo(jos); + } + } + } + } + System.out.println(); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java --- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -30,6 +30,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.Map; @@ -378,4 +379,40 @@ } return cmd.toString().trim(); } + + /** + * Executes a process, waits for it to finish, prints the process output + * to stdout, and returns the process output. + * + * The process will have exited before this method returns. + * + * @param cmds The command line to execute. + * @return The {@linkplain OutputAnalyzer} instance wrapping the process. + */ + public static OutputAnalyzer executeCommand(String... cmds) + throws Throwable { + String cmdLine = Arrays.stream(cmds).collect(Collectors.joining(" ")); + System.out.println("Command line: [" + cmdLine + "]"); + OutputAnalyzer analyzer = ProcessTools.executeProcess(cmds); + System.out.println(analyzer.getOutput()); + return analyzer; + } + + /** + * Executes a process, waits for it to finish, prints the process output + * to stdout and returns the process output. + * + * The process will have exited before this method returns. + * + * @param pb The ProcessBuilder to execute. + * @return The {@linkplain OutputAnalyzer} instance wrapping the process. + */ + public static OutputAnalyzer executeCommand(ProcessBuilder pb) + throws Throwable { + String cmdLine = pb.command().stream().collect(Collectors.joining(" ")); + System.out.println("Command line: [" + cmdLine + "]"); + OutputAnalyzer analyzer = ProcessTools.executeProcess(pb); + System.out.println(analyzer.getOutput()); + return analyzer; + } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/krb5/auto/Context.java --- a/jdk/test/sun/security/krb5/auto/Context.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/sun/security/krb5/auto/Context.java Wed Jul 05 20:17:28 2017 +0200 @@ -23,6 +23,7 @@ import com.sun.security.auth.module.Krb5LoginModule; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -584,7 +585,12 @@ out.name = name + " as " + out.cred.getName().toString(); return out; } catch (PrivilegedActionException pae) { - throw pae.getException(); + Exception e = pae.getException(); + if (e instanceof InvocationTargetException) { + throw (Exception)((InvocationTargetException) e).getTargetException(); + } else { + throw e; + } } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/krb5/auto/ForwardableCheck.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/ForwardableCheck.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 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. + * + * 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 8022582 + * @summary Relax response flags checking in sun.security.krb5.KrbKdcRep.check. + * @compile -XDignore.symbol.file ForwardableCheck.java + * @run main/othervm ForwardableCheck + */ + +import org.ietf.jgss.GSSException; +import sun.security.jgss.GSSUtil; + +import java.util.Arrays; + +public class ForwardableCheck { + + public static void main(String[] args) throws Exception { + OneKDC kdc = new OneKDC(null); + kdc.writeJAASConf(); + + // USER can impersonate someone else + kdc.setOption(KDC.Option.ALLOW_S4U2SELF, + Arrays.asList(OneKDC.USER + "@" + OneKDC.REALM)); + // USER2 is sensitive + kdc.setOption(KDC.Option.SENSITIVE_ACCOUNTS, + Arrays.asList(OneKDC.USER2 + "@" + OneKDC.REALM)); + + Context c; + + // USER2 is sensitive but it's still able to get a normal ticket + c = Context.fromUserPass(OneKDC.USER2, OneKDC.PASS2, false); + + // ... and connect to another account + c.startAsClient(OneKDC.USER, GSSUtil.GSS_KRB5_MECH_OID); + c.x().requestCredDeleg(true); + c.x().requestMutualAuth(false); + + c.take(new byte[0]); + + if (!c.x().isEstablished()) { + throw new Exception("Context should have been established"); + } + + // ... but will not be able to delegate itself + if (c.x().getCredDelegState()) { + throw new Exception("Impossible"); + } + + // Although USER is allowed to impersonate other people, + // it cannot impersonate USER2 coz it's sensitive. + c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + try { + c.impersonate(OneKDC.USER2); + throw new Exception("Should fail"); + } catch (GSSException e) { + e.printStackTrace(); + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/krb5/auto/KDC.java --- a/jdk/test/sun/security/krb5/auto/KDC.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/sun/security/krb5/auto/KDC.java Wed Jul 05 20:17:28 2017 +0200 @@ -198,6 +198,10 @@ * Krb5.KDC_ERR_POLICY will be send for S4U2proxy request. */ ALLOW_S4U2PROXY, + /** + * Sensitive accounts can never be delegated. + */ + SENSITIVE_ACCOUNTS, }; static { @@ -643,7 +647,7 @@ try { System.out.println(realm + "> " + tgsReq.reqBody.cname + " sends TGS-REQ for " + - service); + service + ", " + tgsReq.reqBody.kdcOptions); KDCReqBody body = tgsReq.reqBody; int[] eTypes = KDCReqBodyDotEType(body); int e2 = eTypes[0]; // etype for outgoing session key @@ -719,7 +723,13 @@ boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1]; if (body.kdcOptions.get(KDCOptions.FORWARDABLE) && allowForwardable) { - bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true; + List sensitives = (List) + options.get(Option.SENSITIVE_ACCOUNTS); + if (sensitives != null && sensitives.contains(cname.toString())) { + // Cannot make FORWARDABLE + } else { + bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true; + } } if (body.kdcOptions.get(KDCOptions.FORWARDED) || etp.flags.get(Krb5.TKT_OPTS_FORWARDED)) { @@ -824,7 +834,8 @@ t, edata); System.out.println(" Return " + tgsRep.cname - + " ticket for " + tgsRep.ticket.sname); + + " ticket for " + tgsRep.ticket.sname + ", flags " + + tFlags); DerOutputStream out = new DerOutputStream(); out.write(DerValue.createTag(DerValue.TAG_APPLICATION, @@ -869,7 +880,7 @@ try { System.out.println(realm + "> " + asReq.reqBody.cname + " sends AS-REQ for " + - service); + service + ", " + asReq.reqBody.kdcOptions); KDCReqBody body = asReq.reqBody; @@ -926,7 +937,13 @@ //body.from boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1]; if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) { - bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true; + List sensitives = (List) + options.get(Option.SENSITIVE_ACCOUNTS); + if (sensitives != null && sensitives.contains(body.cname.toString())) { + // Cannot make FORWARDABLE + } else { + bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true; + } } if (body.kdcOptions.get(KDCOptions.RENEWABLE)) { bFlags[Krb5.TKT_OPTS_RENEWABLE] = true; @@ -1102,7 +1119,8 @@ edata); System.out.println(" Return " + asRep.cname - + " ticket for " + asRep.ticket.sname); + + " ticket for " + asRep.ticket.sname + ", flags " + + tFlags); DerOutputStream out = new DerOutputStream(); out.write(DerValue.createTag(DerValue.TAG_APPLICATION, diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/krb5/config/ParseConfig.java --- a/jdk/test/sun/security/krb5/config/ParseConfig.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/sun/security/krb5/config/ParseConfig.java Wed Jul 05 20:17:28 2017 +0200 @@ -22,7 +22,7 @@ */ /* * @test - * @bug 6319046 + * @bug 6319046 8055045 * @compile -XDignore.symbol.file ParseConfig.java * @run main/othervm ParseConfig * @summary Problem with parsing krb5.conf @@ -32,7 +32,8 @@ public class ParseConfig { public static void main(String[] args) throws Exception { - System.setProperty("java.security.krb5.conf", System.getProperty("test.src", ".") +"/krb5.conf"); + System.setProperty("java.security.krb5.conf", + System.getProperty("test.src", ".") + "/krb5.conf"); Config config = Config.getInstance(); config.listTable(); @@ -44,5 +45,11 @@ expected + "\""); } } + + // JDK-8055045: IOOBE when reading an empty value + config.get("empty1", "NOVAL.COM"); + config.get("empty2", "NOVAL.COM"); + config.get("quote1", "NOVAL.COM"); + config.get("quote2", "NOVAL.COM"); } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/krb5/config/krb5.conf --- a/jdk/test/sun/security/krb5/config/krb5.conf Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/sun/security/krb5/config/krb5.conf Wed Jul 05 20:17:28 2017 +0200 @@ -27,3 +27,9 @@ } } + NOVAL.COM = { + empty1 = + empty2 =. + quote1 = " + quote2 = ' + } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/TimestampCheck.java --- a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Thu Jan 29 15:36:21 2015 -0800 +++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Wed Jul 05 20:17:28 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -58,7 +58,12 @@ static final String defaultPolicyId = "2.3.4.5"; - static class Handler implements HttpHandler { + static class Handler implements HttpHandler, AutoCloseable { + + private final HttpServer httpServer; + private final String keystore; + + @Override public void handle(HttpExchange t) throws IOException { int len = 0; for (String h: t.getRequestHeaders().keySet()) { @@ -136,7 +141,9 @@ // Write TSResponse System.err.println("\nResponse\n==================="); KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new FileInputStream(TSKS), "changeit".toCharArray()); + try (FileInputStream fis = new FileInputStream(keystore)) { + ks.load(fis, "changeit".toCharArray()); + } String alias = "ts"; if (path == 6) alias = "tsbad1"; @@ -240,33 +247,74 @@ return out.toByteArray(); } + + private Handler(HttpServer httpServer, String keystore) { + this.httpServer = httpServer; + this.keystore = keystore; + } + + /** + * Initialize TSA instance. + * + * Extended Key Info extension of certificate that is used for + * signing TSA responses should contain timeStamping value. + */ + static Handler init(int port, String keystore) throws IOException { + HttpServer httpServer = HttpServer.create( + new InetSocketAddress(port), 0); + Handler tsa = new Handler(httpServer, keystore); + httpServer.createContext("/", tsa); + return tsa; + } + + /** + * Start TSA service. + */ + void start() { + httpServer.start(); + } + + /** + * Stop TSA service. + */ + void stop() { + httpServer.stop(0); + } + + /** + * Return server port number. + */ + int getPort() { + return httpServer.getAddress().getPort(); + } + + @Override + public void close() throws Exception { + stop(); + } } public static void main(String[] args) throws Exception { - - Handler h = new Handler(); - HttpServer server = HttpServer.create(new InetSocketAddress(0), 0); - int port = server.getAddress().getPort(); - HttpContext ctx = server.createContext("/", h); - server.start(); + try (Handler tsa = Handler.init(0, TSKS);) { + tsa.start(); + int port = tsa.getPort(); - String cmd = null; - // Use -J-Djava.security.egd=file:/dev/./urandom to speed up - // nonce generation in timestamping request. Not avaibale on - // Windows and defaults to thread seed generator, not too bad. - if (System.getProperty("java.home").endsWith("jre")) { - cmd = System.getProperty("java.home") + "/../bin/jarsigner"; - } else { - cmd = System.getProperty("java.home") + "/bin/jarsigner"; - } + String cmd; + // Use -J-Djava.security.egd=file:/dev/./urandom to speed up + // nonce generation in timestamping request. Not avaibale on + // Windows and defaults to thread seed generator, not too bad. + if (System.getProperty("java.home").endsWith("jre")) { + cmd = System.getProperty("java.home") + "/../bin/jarsigner"; + } else { + cmd = System.getProperty("java.home") + "/bin/jarsigner"; + } - cmd += " " + System.getProperty("test.tool.vm.opts") + - " -J-Djava.security.egd=file:/dev/./urandom" + - " -debug -keystore " + TSKS + " -storepass changeit" + - " -tsa http://localhost:" + port + "/%d" + - " -signedjar new_%d.jar " + JAR + " old"; + cmd += " " + System.getProperty("test.tool.vm.opts") + + " -J-Djava.security.egd=file:/dev/./urandom" + + " -debug -keystore " + TSKS + " -storepass changeit" + + " -tsa http://localhost:" + port + "/%d" + + " -signedjar new_%d.jar " + JAR + " old"; - try { if (args.length == 0) { // Run this test jarsigner(cmd, 0, true); // Success, normal call jarsigner(cmd, 1, false); // These 4 should fail @@ -287,8 +335,6 @@ System.err.println("Press Enter to quit server"); System.in.read(); } - } finally { - server.stop(0); } } diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/TsacertOptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/TsacertOptionTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary The test signs and verifies a jar file with -tsacert option + * @library /lib/testlibrary + * @run main TsacertOptionTest + */ +public class TsacertOptionTest { + + private static final String FS = System.getProperty("file.separator"); + private static final String JAVA_HOME = System.getProperty("java.home"); + private static final String KEYTOOL = JAVA_HOME + FS + "bin" + FS + + "keytool"; + private static final String JARSIGNER = JAVA_HOME + FS + "bin" + FS + + "jarsigner"; + private static final String UNSIGNED_JARFILE = "unsigned.jar"; + private static final String SIGNED_JARFILE = "signed.jar"; + private static final String FILENAME = TsacertOptionTest.class.getName() + + ".txt"; + private static final String PASSWORD = "changeit"; + private static final String KEYSTORE = "ks.jks"; + private static final String SIGNING_KEY_ALIAS = "sign_alias"; + private static final String TSA_KEY_ALIAS = "ts"; + private static final String KEY_ALG = "RSA"; + private static final int KEY_SIZE = 2048; + private static final int VALIDITY = 365; + private static final String WARNING = "Warning:"; + private static final String JAR_SIGNED = "jar signed."; + private static final String JAR_VERIFIED = "jar verified."; + + /** + * The test signs and verifies a jar file with -tsacert option, + * and checks that no warning was shown. + * A certificate that is addressed in -tsacert option contains URL to TSA + * in Subject Information Access extension. + */ + public static void main(String[] args) throws Throwable { + TsacertOptionTest test = new TsacertOptionTest(); + test.start(); + } + + void start() throws Throwable { + // create a jar file that contains one file + Utils.createFiles(FILENAME); + JarUtils.createJar(UNSIGNED_JARFILE, FILENAME); + + // look for free network port for TSA service + int port = jdk.testlibrary.Utils.getFreePort(); + String host = jdk.testlibrary.Utils.getHostname(); + String tsaUrl = "http://" + host + ":" + port; + + // create key pair for jar signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", SIGNING_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // create key pair for TSA service + // SubjectInfoAccess extension contains URL to TSA service + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-v", + "-alias", TSA_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=TSA", + "-ext", "ExtendedkeyUsage:critical=timeStamping", + "-ext", "SubjectInfoAccess=timeStamping:URI:" + tsaUrl, + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + try (TimestampCheck.Handler tsa = TimestampCheck.Handler.init(port, + KEYSTORE);) { + + // start TSA + tsa.start(); + + // sign jar file + // specify -tsadigestalg option because + // TSA server uses SHA-1 digest algorithm + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + "-tsacert", TSA_KEY_ALIAS, + "-tsadigestalg", "SHA-1", + UNSIGNED_JARFILE, + SIGNING_KEY_ALIAS); + + analyzer.shouldHaveExitValue(0); + analyzer.stdoutShouldNotContain(WARNING); + analyzer.shouldContain(JAR_SIGNED); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + SIGNED_JARFILE); + + analyzer.shouldHaveExitValue(0); + analyzer.stdoutShouldNotContain(WARNING); + analyzer.shouldContain(JAR_VERIFIED); + } + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/Utils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/Utils.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013, 2015, 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.File; +import java.io.IOException; + +/** + * Helper class. + */ +public class Utils { + + static void createFiles(String... filenames) throws IOException { + for (String filename : filenames) { + new File(filename).createNewFile(); + } + } + +} + diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for aliasNotInStore warning + * @library /lib/testlibrary ../ + * @run main AliasNotInStoreTest + */ +public class AliasNotInStoreTest extends Test { + + /** + * The test signs and verifies a jar that contains signed entries + * that are not signed by any alias in keystore (aliasNotInStore). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + AliasNotInStoreTest test = new AliasNotInStoreTest(); + test.start(); + } + + private void start() throws Throwable { + Utils.createFiles(FIRST_FILE, SECOND_FILE); + System.out.println(String.format("Create a %s that contains %s", + new Object[]{UNSIGNED_JARFILE, FIRST_FILE})); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create first key pair for signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", FIRST_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", BOTH_KEYS_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=First", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // create second key pair for signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", SECOND_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", BOTH_KEYS_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Second", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar with first key + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", BOTH_KEYS_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + FIRST_KEY_ALIAS); + + checkSigning(analyzer); + + System.out.println(String.format("Copy %s to %s, and add %s", + new Object[] {SIGNED_JARFILE, UPDATED_SIGNED_JARFILE, + SECOND_FILE})); + + JarUtils.updateJar(SIGNED_JARFILE, UPDATED_SIGNED_JARFILE, SECOND_FILE); + + // sign jar with second key + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", BOTH_KEYS_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE, + SECOND_KEY_ALIAS); + + checkSigning(analyzer); + + // create keystore that contains only first key + ProcessTools.executeCommand(KEYTOOL, + "-importkeystore", + "-srckeystore", BOTH_KEYS_KEYSTORE, + "-srcalias", FIRST_KEY_ALIAS, + "-srcstorepass", PASSWORD, + "-srckeypass", PASSWORD, + "-destkeystore", FIRST_KEY_KEYSTORE, + "-destalias", FIRST_KEY_ALIAS, + "-deststorepass", PASSWORD, + "-destkeypass", PASSWORD).shouldHaveExitValue(0); + + // verify jar with keystore that contains only first key in strict mode, + // so there is signed entry (FirstClass.class) that is not signed + // by any alias in the keystore + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", FIRST_KEY_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE); + + checkVerifying(analyzer, 0, CHAIN_NOT_VALIDATED_VERIFYING_WARNING, + ALIAS_NOT_IN_STORE_VERIFYING_WARNING); + + // verify jar with keystore that contains only first key in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", FIRST_KEY_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE); + + int expectedExitCode = ALIAS_NOT_IN_STORE_EXIT_CODE + + CHAIN_NOT_VALIDATED_EXIT_CODE; + checkVerifying(analyzer, expectedExitCode, + CHAIN_NOT_VALIDATED_VERIFYING_WARNING, + ALIAS_NOT_IN_STORE_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for badExtendedKeyUsage warning + * @library /lib/testlibrary ../ + * @run main BadExtendedKeyUsageTest + */ +public class BadExtendedKeyUsageTest extends Test { + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate's ExtendedKeyUsage extension + * doesn't allow code signing (badExtendedKeyUsage). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + BadExtendedKeyUsageTest test = new BadExtendedKeyUsageTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create a certificate whose signer certificate's + // ExtendedKeyUsage extension doesn't allow code signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-ext", "ExtendedkeyUsage=serverAuth", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, BAD_EXTENDED_KEY_USAGE_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, 0, BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING); + + // verity signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, BAD_EXTENDED_KEY_USAGE_EXIT_CODE, + BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for badKeyUsage warning + * @library /lib/testlibrary ../ + * @ignore until 8026393 is fixed + * @run main BadKeyUsageTest + */ +public class BadKeyUsageTest extends Test { + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate's KeyUsage extension + * doesn't allow code signing (badKeyUsage). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + BadKeyUsageTest test = new BadKeyUsageTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create a certificate whose signer certificate's KeyUsage extension + // doesn't allow code signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-ext", "KeyUsage=keyAgreement", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, BAD_KEY_USAGE_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, 0, BAD_KEY_USAGE_VERIFYING_WARNING); + + // verify signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, BAD_KEY_USAGE_EXIT_CODE, + BAD_KEY_USAGE_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for badNetscapeCertType warning + * @library /lib/testlibrary ../ + * @run main BadNetscapeCertTypeTest + */ +public class BadNetscapeCertTypeTest extends Test { + + private static final String NETSCAPE_KEYSTORE_BASE64 = TEST_SOURCES + FS + + "bad_netscape_cert_type.jks.base64"; + + private static final String NETSCAPE_KEYSTORE + = "bad_netscape_cert_type.jks"; + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate's NetscapeCertType extension + * doesn't allow code signing (badNetscapeCertType). + * Warning message is expected. + * Run bad_netscape_cert_type.sh script to create bad_netscape_cert_type.jks + */ + public static void main(String[] args) throws Throwable { + + Files.write(Paths.get(NETSCAPE_KEYSTORE), + Base64.getMimeDecoder().decode( + Files.readAllBytes(Paths.get(NETSCAPE_KEYSTORE_BASE64)))); + + BadNetscapeCertTypeTest test = new BadNetscapeCertTypeTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-keystore", NETSCAPE_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, BAD_NETSCAPE_CERT_TYPE_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", NETSCAPE_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, 0, BAD_NETSCAPE_CERT_TYPE_VERIFYING_WARNING); + + // verify signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", NETSCAPE_KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, BAD_NETSCAPE_CERT_TYPE_EXIT_CODE, + BAD_NETSCAPE_CERT_TYPE_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013, 2015, 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.File; +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for chainNotValidated warning + * @library /lib/testlibrary ../ + * @run main ChainNotValidatedTest + */ +public class ChainNotValidatedTest extends Test { + + private static final String CHAIN = "chain"; + + /** + * The test signs and verifies a jar that contains entries + * whose cert chain can't be correctly validated (chainNotValidated). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + ChainNotValidatedTest test = new ChainNotValidatedTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create self-signed certificate whose BasicConstraints extension + // is set to false, so the certificate may not be used + // as a parent certificate (certpath validation should fail) + ProcessTools.executeCommand(KEYTOOL, + "-genkeypair", + "-alias", CA_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=CA", + "-ext", "BasicConstraints:critical=ca:false", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // create a certificate that is signed by self-signed certificate + // despite of it may not be used as a parent certificate + // (certpath validation should fail) + ProcessTools.executeCommand(KEYTOOL, + "-genkeypair", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-ext", "BasicConstraints:critical=ca:false", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + ProcessTools.executeCommand(KEYTOOL, + "-certreq", + "-alias", KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-file", CERT_REQUEST_FILENAME).shouldHaveExitValue(0); + + ProcessTools.executeCommand(KEYTOOL, + "-gencert", + "-alias", CA_KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-infile", CERT_REQUEST_FILENAME, + "-validity", Integer.toString(VALIDITY), + "-outfile", CERT_FILENAME).shouldHaveExitValue(0); + + ProcessTools.executeCommand(KEYTOOL, + "-importcert", + "-alias", KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-file", CERT_FILENAME).shouldHaveExitValue(0); + + ProcessBuilder pb = new ProcessBuilder(KEYTOOL, + "-export", + "-rfc", + "-alias", KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD); + pb.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(CHAIN))); + ProcessTools.executeCommand(pb).shouldHaveExitValue(0); + + pb = new ProcessBuilder(KEYTOOL, + "-export", + "-rfc", + "-alias", CA_KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD); + pb.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(CHAIN))); + ProcessTools.executeCommand(pb).shouldHaveExitValue(0); + + // remove CA certificate + ProcessTools.executeCommand(KEYTOOL, + "-delete", + "-alias", CA_KEY_ALIAS, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD).shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-certchain", CHAIN, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, CHAIN_NOT_VALIDATED_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-certchain", CHAIN, + SIGNED_JARFILE); + + checkVerifying(analyzer, 0, CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + + // verify signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-certchain", CHAIN, + SIGNED_JARFILE); + + checkVerifying(analyzer, CHAIN_NOT_VALIDATED_EXIT_CODE, + CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for hasExpiredCert warning + * @library /lib/testlibrary ../ + * @run main HasExpiredCertTest + */ +public class HasExpiredCertTest extends Test { + + static final int SHORT_VALIDITY = 365; + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate has expired (hasExpiredCert). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + HasExpiredCertTest test = new HasExpiredCertTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create key pair for jar signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-startdate", "-" + SHORT_VALIDITY * 2 + "d", + "-validity", Integer.toString(SHORT_VALIDITY)) + .shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, HAS_EXPIRED_CERT_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, 0, HAS_EXPIRED_CERT_VERIFYING_WARNING); + + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE); + + checkVerifying(analyzer, HAS_EXPIRED_CERT_EXIT_CODE, + HAS_EXPIRED_CERT_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for hasExpiringCert warning + * @library /lib/testlibrary ../ + * @run main HasExpiringCertTest + */ +public class HasExpiringCertTest extends Test { + + static final int SHORT_VALIDITY = 90; // less than 6 month + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate will expire within six months (hasExpiringCert). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + HasExpiringCertTest test = new HasExpiringCertTest(); + test.start(); + } + + private void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create key pair for jar signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-validity", Integer.toString(SHORT_VALIDITY)) + .shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-verbose", + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, HAS_EXPIRING_CERT_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + checkVerifying(analyzer, 0, HAS_EXPIRING_CERT_VERIFYING_WARNING); + + // verify signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + checkVerifying(analyzer, 0, HAS_EXPIRING_CERT_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for hasUnsignedEntry warning + * @library /lib/testlibrary ../ + * @run main HasUnsignedEntryTest + */ +public class HasUnsignedEntryTest extends Test { + + /** + * The test signs and verifies a jar that contains unsigned entries + * which have not been integrity-checked (hasUnsignedEntry). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + HasUnsignedEntryTest test = new HasUnsignedEntryTest(); + test.start(); + } + + private void start() throws Throwable { + System.out.println(String.format("Create a %s that contains %s", + UNSIGNED_JARFILE, FIRST_FILE)); + Utils.createFiles(FIRST_FILE, SECOND_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create key pair for signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer); + + System.out.println(String.format("Copy %s to %s, and add %s.class, " + + "so it contains unsigned entry", + new Object[]{SIGNED_JARFILE, UPDATED_SIGNED_JARFILE, + SECOND_FILE})); + + JarUtils.updateJar(SIGNED_JARFILE, UPDATED_SIGNED_JARFILE, SECOND_FILE); + + // verify jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE); + + checkVerifying(analyzer, 0, HAS_UNSIGNED_ENTRY_VERIFYING_WARNING); + + // verify jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE); + + checkVerifying(analyzer, HAS_UNSIGNED_ENTRY_EXIT_CODE, + HAS_UNSIGNED_ENTRY_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Checks if jarsigner prints appropriate warnings + * @library /lib/testlibrary ../ + * @run main MultipleWarningsTest + */ +public class MultipleWarningsTest extends Test { + + /** + * The test signs and verifies a jar that: + * - contains entries whose signer certificate has expired + * - contains entries whose signer certificate's ExtendedKeyUsage + * extension doesn't allow code signing + * - contains unsigned entries which have not been integrity-checked + * - contains signed entries which are not signed by the specified alias + * Warning messages are expected. + */ + public static void main(String[] args) throws Throwable { + MultipleWarningsTest test = new MultipleWarningsTest(); + test.start(); + } + + private void start() throws Throwable { + Utils.createFiles(FIRST_FILE, SECOND_FILE); + + // create a jar file that contains one class file + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create first expired certificate + // whose ExtendedKeyUsage extension does not allow code signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", FIRST_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=First", + "-ext", "ExtendedkeyUsage=serverAuth", + "-startdate", "-" + VALIDITY * 2 + "d", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // create second expired certificate + // whose KeyUsage extension does not allow code signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", SECOND_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Second", + "-ext", "ExtendedkeyUsage=serverAuth", + "-startdate", "-" + VALIDITY * 2 + "d", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar with first key + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + FIRST_KEY_ALIAS); + + checkSigning(analyzer, HAS_EXPIRED_CERT_SIGNING_WARNING, + BAD_EXTENDED_KEY_USAGE_SIGNING_WARNING); + + // add a second class to created jar, so it contains unsigned entry + JarUtils.updateJar(SIGNED_JARFILE, UPDATED_SIGNED_JARFILE, SECOND_FILE); + + // verify jar with second key + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE, + SECOND_KEY_ALIAS); + + checkVerifying(analyzer, 0, BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING, + HAS_EXPIRED_CERT_VERIFYING_WARNING, + HAS_UNSIGNED_ENTRY_VERIFYING_WARNING, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with second key in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE, + SECOND_KEY_ALIAS); + + int expectedExitCode = HAS_EXPIRED_CERT_EXIT_CODE + + BAD_EXTENDED_KEY_USAGE_EXIT_CODE + + HAS_UNSIGNED_ENTRY_EXIT_CODE + + NOT_SIGNED_BY_ALIAS_EXIT_CODE; + checkVerifying(analyzer, expectedExitCode, + BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING, + HAS_EXPIRED_CERT_VERIFYING_WARNING, + HAS_UNSIGNED_ENTRY_VERIFYING_WARNING, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with non-exisiting alias + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE, + "bogus"); + + checkVerifying(analyzer, 0, BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING, + HAS_EXPIRED_CERT_VERIFYING_WARNING, + HAS_UNSIGNED_ENTRY_VERIFYING_WARNING, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with non-exisiting alias in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + UPDATED_SIGNED_JARFILE, + "bogus"); + + checkVerifying(analyzer, expectedExitCode, + BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING, + HAS_EXPIRED_CERT_VERIFYING_WARNING, + HAS_UNSIGNED_ENTRY_VERIFYING_WARNING, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, 2015, 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.util.Date; +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Checks warnings if -tsa and -tsacert options are not specified + * @library /lib/testlibrary ../ + * @run main NoTimestampTest + */ +public class NoTimestampTest extends Test { + + /** + * The test signs and verifies a jar file without -tsa and -tsacert options, + * and checks that proper warnings are shown. + */ + public static void main(String[] args) throws Throwable { + NoTimestampTest test = new NoTimestampTest(); + test.start(); + } + + private void start() throws Throwable { + String timezone = System.getProperty("user.timezone"); + System.out.println(String.format("Timezone = %s", timezone)); + + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // calculate certificate expiration date + Date expirationDate = new Date(System.currentTimeMillis() + VALIDITY + * 24 * 60 * 60 * 1000L); + + // create key pair + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-validity", Integer.toString(VALIDITY)); + + // sign jar file + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-J-Duser.timezone=" + timezone, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + String warning = String.format(NO_TIMESTAMP_SIGNING_WARN_TEMPLATE, + expirationDate); + checkSigning(analyzer, warning); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-J-Duser.timezone=" + timezone, + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + warning = String.format(NO_TIMESTAMP_VERIFYING_WARN_TEMPLATE, expirationDate); + checkVerifying(analyzer, 0, warning); + + // verify signed jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-J-Duser.timezone=" + timezone, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + checkVerifying(analyzer, 0, warning); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for notSignedByAlias warning + * @library /lib/testlibrary ../ + * @run main NotSignedByAliasTest + */ +public class NotSignedByAliasTest extends Test { + + /** + * The test signs and verifies a jar that contains signed entries + * which are not signed by the specified alias(es) (notSignedByAlias). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + NotSignedByAliasTest test = new NotSignedByAliasTest(); + test.start(); + } + + protected void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create first key pair for signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", FIRST_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=First", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // create first key pair for signing + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", SECOND_KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Second", + "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + + // sign jar with first key + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + FIRST_KEY_ALIAS); + + checkSigning(analyzer); + + // verify jar with second key + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + SECOND_KEY_ALIAS); + + checkVerifying(analyzer, 0, NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with second key in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + SECOND_KEY_ALIAS); + + checkVerifying(analyzer, NOT_SIGNED_BY_ALIAS_EXIT_CODE, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with non-existing alias + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + "bogus"); + + checkVerifying(analyzer, 0, NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + // verify jar with non-existing alias in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + "bogus"); + + checkVerifying(analyzer, NOT_SIGNED_BY_ALIAS_EXIT_CODE, + NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JarUtils; + +/** + * @test + * @bug 8024302 8026037 + * @summary Test for notYetValidCert warning + * @library /lib/testlibrary ../ + * @run main NotYetValidCertTest + */ +public class NotYetValidCertTest extends Test { + + /** + * The test signs and verifies a jar that contains entries + * whose signer certificate is not yet valid (notYetValidCert). + * Warning message is expected. + */ + public static void main(String[] args) throws Throwable { + NotYetValidCertTest test = new NotYetValidCertTest(); + test.start(); + } + + protected void start() throws Throwable { + // create a jar file that contains one class file + Utils.createFiles(FIRST_FILE); + JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + + // create certificate that will be valid only tomorrow + ProcessTools.executeCommand(KEYTOOL, + "-genkey", + "-alias", KEY_ALIAS, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=Test", + "-startdate", "+1d", + "-validity", Integer.toString(VALIDITY)); + + // sign jar + OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-signedjar", SIGNED_JARFILE, + UNSIGNED_JARFILE, + KEY_ALIAS); + + checkSigning(analyzer, NOT_YET_VALID_CERT_SIGNING_WARNING); + + // verify signed jar + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + checkVerifying(analyzer, 0, NOT_YET_VALID_CERT_VERIFYING_WARNING); + + // verify jar in strict mode + analyzer = ProcessTools.executeCommand(JARSIGNER, + "-verify", + "-verbose", + "-strict", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + SIGNED_JARFILE, + KEY_ALIAS); + + checkVerifying(analyzer, HAS_EXPIRED_CERT_EXIT_CODE, + NOT_YET_VALID_CERT_VERIFYING_WARNING); + + System.out.println("Test passed"); + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/Test.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.OutputAnalyzer; + +/** + * Base class. + */ +public abstract class Test { + + static final String TEST_SOURCES = System.getProperty("test.src", "."); + static final String TEST_CLASSES = System.getProperty("test.classes"); + static final String FS = System.getProperty("file.separator"); + static final String JAVA_HOME = System.getProperty("java.home"); + static final String KEYTOOL = JAVA_HOME + FS + "bin" + FS + "keytool"; + static final String JARSIGNER = JAVA_HOME + FS + "bin" + FS + "jarsigner"; + static final String UNSIGNED_JARFILE = "unsigned.jar"; + static final String SIGNED_JARFILE = "signed.jar"; + static final String UPDATED_SIGNED_JARFILE = "updated_signed.jar"; + static final String FIRST_FILE = "first.txt"; + static final String SECOND_FILE = "second.txt"; + static final String PASSWORD = "password"; + static final String BOTH_KEYS_KEYSTORE = "both_keys.jks"; + static final String FIRST_KEY_KEYSTORE = "first_key.jks"; + static final String KEYSTORE = "keystore.jks"; + static final String FIRST_KEY_ALIAS = "first"; + static final String SECOND_KEY_ALIAS = "second"; + static final String KEY_ALG = "RSA"; + static final String KEY_ALIAS = "alias"; + static final String CERT_REQUEST_FILENAME = "test.req"; + static final String CERT_FILENAME = "test.crt"; + static final String CA_KEY_ALIAS = "ca"; + static final int KEY_SIZE = 2048; + static final int TIMEOUT = 6 * 60 * 1000; // in millis + static final int VALIDITY = 365; + + static final String WARNING = "Warning:"; + + static final String CHAIN_NOT_VALIDATED_VERIFYING_WARNING + = "This jar contains entries " + + "whose certificate chain is not validated."; + + static final String ALIAS_NOT_IN_STORE_VERIFYING_WARNING + = "This jar contains signed entries " + + "that are not signed by alias in this keystore."; + + static final String BAD_EXTENDED_KEY_USAGE_SIGNING_WARNING + = "The signer certificate's ExtendedKeyUsage extension " + + "doesn't allow code signing."; + + static final String BAD_EXTENDED_KEY_USAGE_VERIFYING_WARNING + = "This jar contains entries whose signer certificate's " + + "ExtendedKeyUsage extension doesn't allow code signing."; + + static final String BAD_KEY_USAGE_SIGNING_WARNING + = "The signer certificate's KeyUsage extension " + + "doesn't allow code signing."; + + static final String BAD_KEY_USAGE_VERIFYING_WARNING + = "This jar contains entries whose signer certificate's KeyUsage " + + "extension doesn't allow code signing."; + + static final String BAD_NETSCAPE_CERT_TYPE_SIGNING_WARNING + = "The signer certificate's NetscapeCertType extension " + + "doesn't allow code signing."; + + static final String BAD_NETSCAPE_CERT_TYPE_VERIFYING_WARNING + = "This jar contains entries " + + "whose signer certificate's NetscapeCertType extension " + + "doesn't allow code signing."; + + static final String CHAIN_NOT_VALIDATED_SIGNING_WARNING + = "The signer's certificate chain is not validated."; + + static final String HAS_EXPIRING_CERT_SIGNING_WARNING + = "The signer certificate will expire within six months."; + + static final String HAS_EXPIRING_CERT_VERIFYING_WARNING + = "This jar contains entries " + + "whose signer certificate will expire within six months."; + + static final String HAS_EXPIRED_CERT_SIGNING_WARNING + = "The signer certificate has expired."; + + static final String HAS_EXPIRED_CERT_VERIFYING_WARNING + = "This jar contains entries whose signer certificate has expired."; + + static final String HAS_UNSIGNED_ENTRY_VERIFYING_WARNING + = "This jar contains unsigned entries " + + "which have not been integrity-checked."; + + static final String NOT_SIGNED_BY_ALIAS_VERIFYING_WARNING + = "This jar contains signed entries " + + "which are not signed by the specified alias(es)."; + + static final String NO_TIMESTAMP_SIGNING_WARN_TEMPLATE + = "No -tsa or -tsacert is provided " + + "and this jar is not timestamped. " + + "Without a timestamp, users may not be able to validate this jar " + + "after the signer certificate's expiration date " + + "(%1$tY-%1$tm-%1$td) or after any future revocation date."; + + static final String NO_TIMESTAMP_VERIFYING_WARN_TEMPLATE + = "This jar contains signatures that does not include a timestamp. " + + "Without a timestamp, users may not be able to validate this jar " + + "after the signer certificate's expiration date " + + "(%1$tY-%1$tm-%1$td) or after any future revocation date."; + + static final String NOT_YET_VALID_CERT_SIGNING_WARNING + = "The signer certificate is not yet valid."; + + static final String NOT_YET_VALID_CERT_VERIFYING_WARNING + = "This jar contains entries " + + "whose signer certificate is not yet valid."; + + static final String JAR_SIGNED = "jar signed."; + + static final String JAR_VERIFIED = "jar verified."; + + static final String JAR_VERIFIED_WITH_SIGNER_ERRORS + = "jar verified, with signer errors."; + + static final int CHAIN_NOT_VALIDATED_EXIT_CODE = 4; + static final int HAS_EXPIRED_CERT_EXIT_CODE = 4; + static final int BAD_KEY_USAGE_EXIT_CODE = 8; + static final int BAD_EXTENDED_KEY_USAGE_EXIT_CODE = 8; + static final int BAD_NETSCAPE_CERT_TYPE_EXIT_CODE = 8; + static final int HAS_UNSIGNED_ENTRY_EXIT_CODE = 16; + static final int ALIAS_NOT_IN_STORE_EXIT_CODE = 32; + static final int NOT_SIGNED_BY_ALIAS_EXIT_CODE = 32; + + protected void checkVerifying(OutputAnalyzer analyzer, int expectedExitCode, + String... warnings) { + analyzer.shouldHaveExitValue(expectedExitCode); + for (String warning : warnings) { + analyzer.shouldContain(warning); + } + if (warnings.length > 0) { + analyzer.shouldContain(WARNING); + } + if (expectedExitCode == 0) { + analyzer.shouldContain(JAR_VERIFIED); + } else { + analyzer.shouldContain(JAR_VERIFIED_WITH_SIGNER_ERRORS); + } + } + + protected void checkSigning(OutputAnalyzer analyzer, String... warnings) { + analyzer.shouldHaveExitValue(0); + for (String warning : warnings) { + analyzer.shouldContain(warning); + } + if (warnings.length > 0) { + analyzer.shouldContain(WARNING); + } + analyzer.shouldContain(JAR_SIGNED); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,26 @@ +/u3+7QAAAAIAAAABAAAAAQAFYWxpYXMAAAFBpkwW0gAAAr0wggK5MA4GCisGAQQB +KgIRAQEFAASCAqWkGJ3PPjYmWNKrV23Y1u413RMAkrRZ+1OLWYRcQt4jtxtIyEH5 +Ho5b9dy9XN9FBKlTOD4c2Pc1T43BLKXeuLu3uLLeIxgXFt0z9CLyGwdYZZ751kXr +DQ99qY6aNQUO6SeE4Wdty0KPAqid6ZJ8bF7T6wsTZSvNhaBRzyFydEfG7bbUYjOl +mWC44nlsu6VEU3o9RQpcm1gIMwradOaIVT/HoB2bKmAv8gHqI6kreiEZwTdZkSAI +IRi2vt1RPllXt5hgjDxUfZe8XOYYweR4Vt2/jVuKLJ80DNTu/9SeUD88zQAz53k4 +r3nRhv6TRcPm6tV/Fh92XLHiskL+TAzTfm+bUAudPCCVxN+yRtxvAgA+UhdV/SuM +Zn5F6nrmP+YJG1hmprgCJIJJaCEXa9RXYC+vIVpO0WVNRuGlGm+/1afnOuQC8Wss +ShXwjkaqTwAhqBFq7eYmmP8BK3gflYrt2zDLXvhl4ndVvMhMthFJ3ZvLh2LWpqLI +/n8EMCf8US3lIEFk9DTHBZjffiHkqK2e7+FXEpG3xrgE6ZYLMdbd5Pb3YjZfhQx+ +ZTtiEFzYSaEGhacek/m7dRq1qmwgFsytng2OdWZe2ln8LJY0odr1dGUfJHfgafvi +tlfbkg/rgjONtwliChDggbkUwnerrj/D/zrdEufUvfyltSshhHXRNDD3fH6spmEk +hHKgxEc4yvxqJxzdMGtuib355aSfNegyl+GsnsKzXQCVEK2h3BLTQObzaD+8NZ12 +LQHvbrCiaS34vxJ3rEC+a+SW7itZp0aCdXMWdMJNkRKqyLBD3vG3zN05sN3XrhEM +8BRT020TWY00tbVFbbBFheYLQRgTjrQtr0Yt6UHWBZc4N20crDLcSH5gqcCOVpla +1Y2uqFEn8yqrGRwn/kgfNgAAAAEABVguNTA5AAABtTCCAbEwggEaoAMCAQICCQDH +cEuVvzCuqzANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTEzMTAx +MTA2NTUwNloXDTIzMTAwOTA2NTUwNlowDzENMAsGA1UEAwwEVGVzdDCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEA8hOfp2Dcnvt//ZZQAja9TRiwKqXVS+TiYE3S +gngCBjIi+YYdo0DsUeO5MBfE6uvCWOr5lwAR/u1iaJOhIoGJDiGoPasZlt+yIgtR +LzA7j2q+1q6kcwiVxfikI3aUgHV/QsybTriT4Bf7TQNKtJG23MQa4sD7+PjtCWD7 +p3cHTfkCAwEAAaMVMBMwEQYJYIZIAYb4QgEBBAQDAgeAMA0GCSqGSIb3DQEBBQUA +A4GBAKoDlTJ8wLRA7G8XdGm4gv733n1cSQzlkcsjfOO6/mA5Jvu8tyFNq9HTf9AT +VXbrbGcUYJjhzSSY3w5apXK1kXyqTB1LUNEJ45WnmciqSSecVTpJz9TuegyoX0Zf +HScSgqfDmjqoiiFiNCgn3ZEJ85ykGvoFYGH+php+BVi3S0bj5E/jRpyV3vNnii/S +wJDSAXF6bYU= diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,48 @@ +# +# Copyright (c) 2013, 2015, 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. +# + +#!/bin/sh + +# This script creates JKS keystore with a certificate +# that contains Netscape Certificate Type extension +# that does not allow code signing +# The keystore is used by BadNetscapeCertTypeTest.java test + +rm -rf keystore.jks +echo "nsCertType = client" > ext.cfg + +openssl req -new -out cert.req -keyout key.pem -days 3650 \ + -passin pass:password -passout pass:password -subj "/CN=Test" +openssl x509 -in cert.req -out cert.pem -req -signkey key.pem -days 3650 \ + -passin pass:password -extfile ext.cfg +openssl pkcs12 -export -in cert.pem -inkey key.pem -out keystore.p12 \ + -passin pass:password -passout pass:password -name alias + +${JAVA_HOME}/bin/keytool -importkeystore \ + -srckeystore keystore.p12 -srcstoretype pkcs12 \ + -srcstorepass password -alias alias \ + -destkeystore bad_netscape_cert_type.jks -deststoretype jks \ + -deststorepass password -destalias alias \ + +openssl base64 < bad_netscape_cert_type.jks > bad_netscape_cert_type.jks.base64 +rm -rf cert.req key.pem cert.pem keystore.p12 ext.cfg bad_netscape_cert_type.jks diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015, 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 8042397 + * @summary Unit test for jmap utility test heap configuration reader + * @library /lib/testlibrary + * @build jdk.testlibrary.* + * @build JMapHeapConfigTest LingeredApp TmtoolTestScenario + * @run main JMapHeapConfigTest + */ +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jdk.testlibrary.Utils; + +public class JMapHeapConfigTest { + + static final String expectedJMapValues[] = { + "MinHeapFreeRatio", + "MaxHeapFreeRatio", + "MaxHeapSize", + "NewSize", + "MaxNewSize", + "OldSize", + "NewRatio", + "SurvivorRatio", + "MetaspaceSize", + "CompressedClassSpaceSize", + "G1HeapRegionSize"}; + + // ignoring MaxMetaspaceSize + + private static Map parseJMapOutput(List jmapOutput) { + Map heapConfigMap = new HashMap(); + boolean shouldParse = false; + + for (String line : jmapOutput) { + line = line.trim(); + + if (line.startsWith("Heap Configuration:")) { + shouldParse = true; + continue; + } + + if (line.startsWith("Heap Usage:")) { + shouldParse = false; + continue; + } + + if (shouldParse && !line.equals("")) { + String[] lv = line.split("\\s+"); + try { + heapConfigMap.put(lv[0], lv[2]); + } catch (ArrayIndexOutOfBoundsException ex) { + // Ignore mailformed lines + } + } + } + return heapConfigMap; + } + + // Compare stored values + private static void compareValues(Map parsedJMapOutput, Map parsedVmOutput) { + for (String key : expectedJMapValues) { + try { + String jmapVal = parsedJMapOutput.get(key); + if (jmapVal == null) { + throw new RuntimeException("Key '" + key + "' doesn't exists in jmap output"); + } + + String vmVal = parsedVmOutput.get(key); + if (vmVal == null) { + throw new RuntimeException("Key '" + key + "' doesn't exists in vm output"); + } + + if (new BigDecimal(jmapVal).compareTo(new BigDecimal(vmVal)) != 0) { + throw new RuntimeException(String.format("Key %s doesn't match %s vs %s", key, vmVal, jmapVal)); + } + } catch (NumberFormatException ex) { + throw new RuntimeException("Unexpected key '" + key + "' value", ex); + } + } + } + + public static void main(String[] args) { + System.out.println("Starting JMapHeapConfigTest"); + + // Forward vm options to LingeredApp + ArrayList cmd = new ArrayList(); + cmd.addAll(Utils.getVmOptions()); + cmd.add("-XX:+PrintFlagsFinal"); + + TmtoolTestScenario tmt = TmtoolTestScenario.create("jmap", "-heap"); + int exitcode = tmt.launch(cmd); + if (exitcode != 0) { + throw new RuntimeException("Test FAILED jmap exits with non zero exit code " + exitcode); + } + + Map parsedJmapOutput = parseJMapOutput(tmt.getToolOutput()); + Map parsedVMOutput = tmt.parseFlagsFinal(); + + compareValues(parsedJmapOutput, parsedVMOutput); + + // If test fails it throws RuntimeException + System.out.println("Test PASSED"); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2015, 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.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * This is a framework to launch an app that could be synchronized with caller + * to make further attach actions reliable across supported platforms + + * Caller example: + * SmartTestApp a = SmartTestApp.startApp(cmd); + * // do something + * a.stopApp(); + * + * or fine grained control + * + * a = new SmartTestApp("MyLock.lck"); + * a.createLock(); + * a.runApp(); + * a.waitAppReady(); + * // do something + * a.deleteLock(); + * a.waitAppTerminate(); + * + * Then you can work with app output and process object + * + * output = a.getAppOutput(); + * process = a.getProcess(); + * + */ +public class LingeredApp { + + private static final long spinDelay = 1000; + + private final String lockFileName; + private long lockCreationTime; + private Process appProcess; + private final ArrayList storedAppOutput; + + /* + * Drain child process output, store it into string array + */ + class InputGobbler extends Thread { + + InputStream is; + List astr; + + InputGobbler(InputStream is, List astr) { + this.is = is; + this.astr = astr; + } + + public void run() { + try { + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line = null; + while ((line = br.readLine()) != null) { + astr.add(line); + } + } catch (IOException ex) { + // pass + } + } + } + + /** + * Create LingeredApp object on caller side. Lock file have be a valid filename + * at writable location + * + * @param lockFileName - the name of lock file + */ + public LingeredApp(String lockFileName) { + this.lockFileName = lockFileName; + this.storedAppOutput = new ArrayList(); + } + + /** + * + * @return name of lock file + */ + public String getLockFileName() { + return this.lockFileName; + } + + /** + * + * @return name of testapp + */ + public String getAppName() { + return this.getClass().getName(); + } + + /** + * + * @return pid of java process running testapp + */ + public long getPid() { + if (appProcess == null) { + throw new RuntimeException("Process is not alive"); + } + return appProcess.getPid(); + } + + /** + * + * @return process object + */ + public Process getProcess() { + return appProcess; + } + + /** + * + * @return application output as string array. Empty array if application produced no output + */ + List getAppOutput() { + if (appProcess.isAlive()) { + throw new RuntimeException("Process is still alive. Can't get its output."); + } + return storedAppOutput; + } + + /* Make sure all part of the app use the same method to get dates, + as different methods could produce different results + */ + private static long epoch() { + return new Date().getTime(); + } + + private static long lastModified(String fileName) throws IOException { + Path path = Paths.get(fileName); + BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); + return attr.lastModifiedTime().toMillis(); + } + + private static void setLastModified(String fileName, long newTime) throws IOException { + Path path = Paths.get(fileName); + FileTime fileTime = FileTime.fromMillis(newTime); + Files.setLastModifiedTime(path, fileTime); + } + + /** + * create lock + * + * @throws IOException + */ + public void createLock() throws IOException { + Path path = Paths.get(lockFileName); + // Files.deleteIfExists(path); + Files.createFile(path); + lockCreationTime = lastModified(lockFileName); + } + + /** + * Delete lock + * + * @throws IOException + */ + public void deleteLock() throws IOException { + try { + Path path = Paths.get(lockFileName); + Files.delete(path); + } catch (NoSuchFileException ex) { + // Lock already deleted. Ignore error + } + } + + public void waitAppTerminate() { + while (true) { + try { + appProcess.waitFor(); + break; + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * The app touches the lock file when it's started + * wait while it happens. Caller have to delete lock on wait error. + * + * @param timeout + * @throws java.io.IOException + */ + public void waitAppReady(long timeout) throws IOException { + long here = epoch(); + while (true) { + long epoch = epoch(); + if (epoch - here > (timeout * 1000)) { + throw new IOException("App waiting timeout"); + } + + // Live process should touch lock file every second + long lm = lastModified(lockFileName); + if (lm > lockCreationTime) { + break; + } + + // Make sure process didn't already exit + if (!appProcess.isAlive()) { + throw new IOException("App exited unexpectedly with " + appProcess.exitValue()); + } + + try { + Thread.sleep(spinDelay); + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * Run the app + * + * @param vmArguments + * @throws IOException + */ + public void runApp(List vmArguments) + throws IOException { + + // We should always use testjava or throw an exception, + // so we can't use JDKToolFinder.getJDKTool("java"); + // that falls back to compile java on error + String jdkPath = System.getProperty("test.jdk"); + if (jdkPath == null) { + // we are not under jtreg, try env + Map env = System.getenv(); + jdkPath = env.get("TESTJAVA"); + } + + if (jdkPath == null) { + throw new RuntimeException("Can't determine jdk path neither test.jdk property no TESTJAVA env are set"); + } + + String osname = System.getProperty("os.name"); + String javapath = jdkPath + ((osname.startsWith("window")) ? "/bin/java.exe" : "/bin/java"); + + List cmd = new ArrayList(); + cmd.add(javapath); + + + if (vmArguments == null) { + // Propagate test.vm.options to LingeredApp, filter out possible empty options + String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); + for (String s : testVmOpts) { + if (!s.equals("")) { + cmd.add(s); + } + } + } + else{ + // Lets user manage LingerApp options + cmd.addAll(vmArguments); + } + + // Make sure we set correct classpath to run the app + cmd.add("-cp"); + String classpath = System.getProperty("test.class.path"); + cmd.add((classpath == null) ? "." : classpath); + + cmd.add(this.getAppName()); + cmd.add(lockFileName); + + // Reporting + StringBuilder cmdLine = new StringBuilder(); + for (String strCmd : cmd) { + cmdLine.append("'").append(strCmd).append("' "); + } + + // A bit of verbosity + System.out.println("Command line: [" + cmdLine.toString() + "]"); + + ProcessBuilder pb = new ProcessBuilder(cmd); + // we don't expect any error output but make sure we are not stuck on pipe + // pb.redirectErrorStream(false); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + + appProcess = pb.start(); + + // Create pipe reader for process, and read stdin and stderr to array of strings + InputGobbler gb = new InputGobbler(appProcess.getInputStream(), storedAppOutput); + gb.start(); + } + + /** + * High level interface for test writers + */ + /** + * Factory method that creates SmartAppTest object with ready to use application + * lock name is autogenerated, wait timeout is hardcoded + * @param cmd - vm options, could be null to auto add testvm.options + * @return LingeredApp object + * @throws IOException + */ + public static LingeredApp startApp(List cmd) throws IOException { + final String lockName = UUID.randomUUID().toString() + ".lck"; + final int waitTime = 10; + + LingeredApp a = new LingeredApp(lockName); + a.createLock(); + try { + a.runApp(cmd); + a.waitAppReady(waitTime); + } catch (Exception ex) { + a.deleteLock(); + throw ex; + } + + return a; + } + + public static LingeredApp startApp() throws IOException { + return startApp(null); + } + + /** + * Delete lock file that signal app to terminate, then + * waits until app is actually terminated. + * @throws IOException + */ + public void stopApp() throws IOException { + deleteLock(); + waitAppTerminate(); + int exitcode = appProcess.exitValue(); + if (exitcode != 0) { + throw new IOException("LingeredApp terminated with non-zero exit code " + exitcode); + } + } + + /** + * This part is the application it self + */ + public static void main(String args[]) { + + if (args.length != 1) { + System.err.println("Lock file name is not specified"); + System.exit(7); + } + + String theLockFileName = args[0]; + + try { + Path path = Paths.get(theLockFileName); + + while (Files.exists(path)) { + long lm = lastModified(theLockFileName); + long now = epoch(); + + // A bit of paranoja, don't allow test app to run more than an hour + if (now - lm > 3600) { + throw new IOException("Lock is too old. Aborting"); + } + + // Touch lock to indicate our rediness + setLastModified(theLockFileName, now); + Thread.sleep(spinDelay); + } + + } catch (Exception ex) { + System.err.println("LingeredApp ERROR: " + ex); + // Leave exit_code = 1 to Java launcher + System.exit(3); + } + + System.exit(0); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/tools/jmap/heapconfig/LingeredAppTest.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Unit test for LingeredApp + * @compile LingeredAppTest.java + * @compile LingeredApp.java + * @run main LingeredAppTest + */ +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LingeredAppTest { + + public static void main(String[] args) { + try { + System.out.println("Starting LingeredApp with default parameters"); + + ArrayList cmd = new ArrayList(); + + // Propagate test.vm.options to LingeredApp, filter out possible empty options + String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); + for (String s : testVmOpts) { + if (!s.equals("")) { + cmd.add(s); + } + } + + cmd.add("-XX:+PrintFlagsFinal"); + + LingeredApp a = LingeredApp.startApp(cmd); + System.out.printf("App pid: %d\n", a.getPid()); + a.stopApp(); + + System.out.println("App output:"); + int count = 0; + for (String line : a.getAppOutput()) { + count += 1; + } + System.out.println("Found " + count + " lines in VM output"); + System.out.println("Test PASSED"); + } catch (IOException ex) { + ex.printStackTrace(); + System.out.println("Test ERROR"); + System.exit(3); + } + } +} diff -r ac59fe6cd98f -r 4746a63313f6 jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/tools/jmap/heapconfig/TmtoolTestScenario.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, 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.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import jdk.testlibrary.JDKToolLauncher; +import jdk.testlibrary.Utils; + +public class TmtoolTestScenario { + + private final ArrayList toolOutput = new ArrayList(); + private LingeredApp theApp = null; + private final String toolName; + private final String[] toolArgs; + + /** + * @param toolName - name of tool to test + * @param toolArgs - tool arguments + * @return the object + */ + public static TmtoolTestScenario create(String toolName, String... toolArgs) { + return new TmtoolTestScenario(toolName, toolArgs); + } + + /** + * @return STDOUT of tool + */ + public List getToolOutput() { + return toolOutput; + } + + /** + * + * @return STDOUT of test app + */ + public List getAppOutput() { + return theApp.getAppOutput(); + } + + /** + * @return Value of the app output with -XX:+PrintFlagsFinal as a map. + */ + public Map parseFlagsFinal() { + List astr = theApp.getAppOutput(); + Map vmMap = new HashMap(); + + for (String line : astr) { + String[] lv = line.trim().split("\\s+"); + try { + vmMap.put(lv[1], lv[3]); + } catch (ArrayIndexOutOfBoundsException ex) { + // ignore mailformed lines + } + } + return vmMap; + } + + /** + * + * @param vmArgs - vm and java arguments to launch test app + * @return exit code of tool + */ + public int launch(List vmArgs) { + System.out.println("Starting LingeredApp"); + try { + try { + theApp = LingeredApp.startApp(vmArgs); + + System.out.println("Starting " + toolName + " against " + theApp.getPid()); + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK(toolName); + + for (String cmd : toolArgs) { + launcher.addToolArg(cmd); + } + launcher.addToolArg(Long.toString(theApp.getPid())); + + ProcessBuilder processBuilder = new ProcessBuilder(launcher.getCommand()); + processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); + Process toolProcess = processBuilder.start(); + + // By default child process output stream redirected to pipe, so we are reading it in foreground. + BufferedReader reader = new BufferedReader(new InputStreamReader(toolProcess.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) { + toolOutput.add(line.trim()); + } + + toolProcess.waitFor(); + + return toolProcess.exitValue(); + } finally { + theApp.stopApp(); + } + } catch (IOException | InterruptedException ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } + } + + public void launch(String... appArgs) throws IOException { + launch(Arrays.asList(appArgs)); + } + + private TmtoolTestScenario(String toolName, String[] toolArgs) { + this.toolName = toolName; + this.toolArgs = toolArgs; + } + +} diff -r ac59fe6cd98f -r 4746a63313f6 make/Images.gmk --- a/make/Images.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/Images.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -111,14 +111,16 @@ # Use this file inside the image as target for make rule JIMAGE_TARGET_FILE := bin/java$(EXE_SUFFIX) -$(JDK_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) +$(JDK_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) \ + $(call DependOnVariable, JDK_MODULES_LIST) $(ECHO) Creating jdk jimage $(RM) -r $(JDK_IMAGE_DIR) $(JDK_SORTED_MODULES) $(JIMAGE_TOOL) --mods $(JDK_MODULES_LIST) --output $(JDK_IMAGE_DIR) \ $(MODULES_XML) > $(JDK_SORTED_MODULES) $(TOUCH) $@ -$(JRE_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) +$(JRE_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) \ + $(call DependOnVariable, JRE_MODULES_LIST) $(ECHO) Creating jre jimage $(RM) -r $(JRE_IMAGE_DIR) $(JRE_SORTED_MODULES) $(JIMAGE_TOOL) --mods $(JRE_MODULES_LIST) --output $(JRE_IMAGE_DIR) \ @@ -131,7 +133,8 @@ COMPACT_EXTRA_MODULES := jdk.localedata jdk.crypto.pkcs11 jdk.crypto.ec -$(JRE_COMPACT1_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) +$(JRE_COMPACT1_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) \ + $(call DependOnVariable, JRE_COMPACT1_MODULES_LIST) $(ECHO) Creating jre compact1 jimage $(RM) -r $(JRE_COMPACT1_IMAGE_DIR) $(JRE_COMPACT1_SORTED_MODULES) $(JIMAGE_TOOL) \ @@ -140,7 +143,8 @@ $(MODULES_XML) > $(JRE_COMPACT1_SORTED_MODULES) $(TOUCH) $@ -$(JRE_COMPACT2_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) +$(JRE_COMPACT2_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) \ + $(call DependOnVariable, JRE_COMPACT2_MODULES_LIST) $(ECHO) Creating jre compact2 jimage $(RM) -r $(JRE_COMPACT2_IMAGE_DIR) $(JRE_COMPACT2_SORTED_MODULES) $(JIMAGE_TOOL) \ @@ -149,7 +153,8 @@ $(MODULES_XML) > $(JRE_COMPACT2_SORTED_MODULES) $(TOUCH) $@ -$(JRE_COMPACT3_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) +$(JRE_COMPACT3_IMAGE_DIR)/$(JIMAGE_TARGET_FILE): $(DEPENDENCIES) \ + $(call DependOnVariable, JRE_COMPACT3_MODULES_LIST) $(ECHO) Creating jre compact3 jimage $(RM) -r $(JRE_COMPACT3_IMAGE_DIR) $(JRE_COMPACT3_SORTED_MODULES) $(JIMAGE_TOOL) \ @@ -368,45 +373,59 @@ # Common way to emit a line into the release or info file define info-file-item # name value - $(PRINTF) '%s="%s"\n' $1 $2 >> $@ + $(PRINTF) '%s="%s"\n' $1 $2 >> $@ endef # Param 1 - The file containing the MODULES list define create-info-file - $(ECHO) $(LOG_INFO) Generating $(patsubst $(OUTPUT_ROOT)/%,%,$@) - $(MKDIR) -p $(@D) - $(RM) $@ - $(call info-file-item, "JAVA_VERSION", "$(JDK_VERSION)") - $(call info-file-item, "OS_NAME", "$(REQUIRED_OS_NAME)") - $(call info-file-item, "OS_VERSION", "$(REQUIRED_OS_VERSION)") - $(call info-file-item, "OS_ARCH", "$(OPENJDK_TARGET_CPU_LEGACY)") - $(if $(JDK_ARCH_ABI_PROP_NAME), \ - $(call info-file-item, "SUN_ARCH_ABI", "$(JDK_ARCH_ABI_PROP_NAME)")) - $(call info-file-item, "SOURCE", "$(ALL_SOURCE_TIPS)") - $(call info-file-item, "MODULES", "`$(CAT) $1`") + $(call info-file-item, "JAVA_VERSION", "$(JDK_VERSION)") + $(call info-file-item, "OS_NAME", "$(REQUIRED_OS_NAME)") + $(call info-file-item, "OS_VERSION", "$(REQUIRED_OS_VERSION)") + $(call info-file-item, "OS_ARCH", "$(OPENJDK_TARGET_CPU_LEGACY)") + $(if $(JDK_ARCH_ABI_PROP_NAME), \ + $(call info-file-item, "SUN_ARCH_ABI", "$(JDK_ARCH_ABI_PROP_NAME)")) + $(call info-file-item, "SOURCE", "$(ALL_SOURCE_TIPS)") + $(call info-file-item, "MODULES", "`$(CAT) $1`") endef +# Param 1 - The file containing the MODULES list +define prepare-info-file + $(ECHO) $(LOG_INFO) Generating $(patsubst $(OUTPUT_ROOT)/%,%,$@) + $(MKDIR) -p $(@D) + $(RM) $@ +endef + +define info-file + $(call prepare-info-file, $1) + $(call create-info-file, $1) +endef + +# Create a variable dependency file common for all release info files. The +# sorted module list will only change if the image is regenerated, which will +# trigger a rebuild of these files anyway. +INFO_FILE_VARDEPS := $(call DependOnVariable, create-info-file) + ALL_SOURCE_TIPS = $(shell \ if [ -f $(SUPPORT_OUTPUTDIR)/source_tips ] ; then \ $(CAT) $(SUPPORT_OUTPUTDIR)/source_tips ; \ fi) -$(JRE_INFO_FILE): $(OUTPUT_ROOT)/spec.gmk $(SUPPORT_OUTPUTDIR)/source_tips - $(call create-info-file, $(JRE_SORTED_MODULES)) +$(JRE_INFO_FILE): $(INFO_FILE_VARDEPS) $(SUPPORT_OUTPUTDIR)/source_tips + $(call info-file, $(JRE_SORTED_MODULES)) -$(JDK_INFO_FILE): $(OUTPUT_ROOT)/spec.gmk $(SUPPORT_OUTPUTDIR)/source_tips - $(call create-info-file, $(JDK_SORTED_MODULES)) +$(JDK_INFO_FILE): $(INFO_FILE_VARDEPS) $(SUPPORT_OUTPUTDIR)/source_tips + $(call info-file, $(JDK_SORTED_MODULES)) -$(JRE_COMPACT1_INFO_FILE): $(OUTPUT_ROOT)/spec.gmk $(SUPPORT_OUTPUTDIR)/source_tips - $(call create-info-file, $(JRE_COMPACT1_SORTED_MODULES)) +$(JRE_COMPACT1_INFO_FILE): $(INFO_FILE_VARDEPS) $(SUPPORT_OUTPUTDIR)/source_tips + $(call info-file, $(JRE_COMPACT1_SORTED_MODULES)) $(call info-file-item, "JAVA_PROFILE", "compact1") -$(JRE_COMPACT2_INFO_FILE): $(OUTPUT_ROOT)/spec.gmk $(SUPPORT_OUTPUTDIR)/source_tips - $(call create-info-file, $(JRE_COMPACT2_SORTED_MODULES)) +$(JRE_COMPACT2_INFO_FILE): $(INFO_FILE_VARDEPS) $(SUPPORT_OUTPUTDIR)/source_tips + $(call info-file, $(JRE_COMPACT2_SORTED_MODULES)) $(call info-file-item, "JAVA_PROFILE", "compact2") -$(JRE_COMPACT3_INFO_FILE): $(OUTPUT_ROOT)/spec.gmk $(SUPPORT_OUTPUTDIR)/source_tips - $(call create-info-file, $(JRE_COMPACT3_SORTED_MODULES)) +$(JRE_COMPACT3_INFO_FILE): $(INFO_FILE_VARDEPS) $(SUPPORT_OUTPUTDIR)/source_tips + $(call info-file, $(JRE_COMPACT3_SORTED_MODULES)) $(call info-file-item, "JAVA_PROFILE", "compact3") JRE_TARGETS += $(JRE_INFO_FILE) diff -r ac59fe6cd98f -r 4746a63313f6 make/Main.gmk --- a/make/Main.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/Main.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -402,6 +402,8 @@ verify-modules: exploded-image + test-make: clean-test-make + endif ################################################################################ @@ -446,7 +448,7 @@ # alias for ease of use. jdk: exploded-image -images: test-image jimages demos samples zip-security +images: test-image jimages demos samples zip-security verify-modules ifeq ($(OPENJDK_TARGET_OS), macosx) images: mac-bundles @@ -477,7 +479,7 @@ # file. CLEAN_DIRS += hotspot jdk bootcycle-build test buildtools support \ - images make-support + images make-support test-make CLEAN_DIR_TARGETS := $(addprefix clean-, $(CLEAN_DIRS)) CLEAN_PHASES := gensrc java native include CLEAN_PHASE_TARGETS := $(addprefix clean-, $(CLEAN_PHASES)) diff -r ac59fe6cd98f -r 4746a63313f6 make/ZipSource.gmk --- a/make/ZipSource.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/ZipSource.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -71,7 +71,7 @@ $(JDK_TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_API_DIR)/native/libjli/java_md*))) # This dir needs to exist before macro is evaluated to avoid warning from find. -$(eval $(call MakeDir, $(SUPPORT_OUTPUTDIR)/src)) +$(call MakeDir, $(SUPPORT_OUTPUTDIR)/src) $(eval $(call SetupZipArchive,BUILD_SRC_ZIP, \ SRC := $(SRC_ZIP_SRCS) $(SUPPORT_OUTPUTDIR)/src, \ INCLUDES := $(SRC_ZIP_INCLUDES) launcher, \ diff -r ac59fe6cd98f -r 4746a63313f6 make/common/IdlCompilation.gmk --- a/make/common/IdlCompilation.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/common/IdlCompilation.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -99,7 +99,7 @@ $(if $(16),$(error Internal makefile error: Too many arguments to SetupIdlCompilation, please update IdlCompilation.gmk)) # Find all existing java files and existing class files. - $$(eval $$(call MakeDir,$$($1_BIN))) + $$(call MakeDir,$$($1_BIN)) $1_SRCS := $$(shell find $$($1_SRC) -name "*.idl") $1_BINS := $$(shell find $$($1_BIN) -name "*.java") # Prepend the source/bin path to the filter expressions. diff -r ac59fe6cd98f -r 4746a63313f6 make/common/JavaCompilation.gmk --- a/make/common/JavaCompilation.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/common/JavaCompilation.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -126,17 +126,20 @@ $1_FIND_PATTERNS:=$(FALSE_FIND_PATTERN) $$(patsubst %,$(SPACE)-o$(SPACE)-name$(SPACE)$(DQUOTE)*%$(DQUOTE),$$($1_SUFFIXES)) # On windows, a lot of includes/excludes risk making the command line too long, so # writing the grep patterns to files. + # Grep returns 1 if nothing is matched. Do not fail the build for this. ifneq (,$$($1_INCLUDES)) $1_GREP_INCLUDE_PATTERNS:=$$(call EscapeDollar, \ $$(foreach src,$$($1_SRCS), $$(addprefix $$(src)/,$$($1_INCLUDES)))) # If there are a lot of include patterns, output to file to shorten command lines ifeq ($$(word 20,$$($1_GREP_INCLUDE_PATTERNS)),) - $1_GREP_INCLUDES:=| $(GREP) $$(patsubst %,$(SPACE)-e$(SPACE)$(DQUOTE)%$(DQUOTE),$$($1_GREP_INCLUDE_PATTERNS)) + $1_GREP_INCLUDES:=| ( $(GREP) $$(patsubst %,$(SPACE)-e$(SPACE)$(DQUOTE)%$(DQUOTE),$$($1_GREP_INCLUDE_PATTERNS)) \ + || test "$$$$?" = "1" ) else $1_GREP_INCLUDE_OUTPUT:=$(RM) $$($1_BIN)/_the.$$($1_JARNAME)_include $$(NEWLINE) \ $$(call ListPathsSafely,$1_GREP_INCLUDE_PATTERNS,\n, \ >> $$($1_BIN)/_the.$$($1_JARNAME)_include) - $1_GREP_INCLUDES:=| $(GREP) -f $$($1_BIN)/_the.$$($1_JARNAME)_include + $1_GREP_INCLUDES:=| ( $(GREP) -f $$($1_BIN)/_the.$$($1_JARNAME)_include \ + || test "$$$$?" = "1" ) endif endif ifneq (,$$($1_EXCLUDES)$$($1_EXCLUDE_FILES)) @@ -145,12 +148,14 @@ $$($1_EXCLUDES) $$($1_EXCLUDE_FILES)))) # If there are a lot of include patterns, output to file to shorten command lines ifeq ($$(word 20,$$($1_GREP_EXCLUDE_PATTERNS)),) - $1_GREP_EXCLUDES:=| $(GREP) -v $$(patsubst %,$(SPACE)-e$(SPACE)$(DQUOTE)%$(DQUOTE),$$($1_GREP_EXCLUDE_PATTERNS)) + $1_GREP_EXCLUDES:=| ( $(GREP) -v $$(patsubst %,$(SPACE)-e$(SPACE)$(DQUOTE)%$(DQUOTE),$$($1_GREP_EXCLUDE_PATTERNS)) \ + || test "$$$$?" = "1" ) else $1_GREP_EXCLUDE_OUTPUT=$(RM) $$($1_BIN)/_the.$$($1_JARNAME)_exclude $$(NEWLINE) \ $$(call ListPathsSafely,$1_GREP_EXCLUDE_PATTERNS,\n, \ >> $$($1_BIN)/_the.$$($1_JARNAME)_exclude) - $1_GREP_EXCLUDES:=| $(GREP) -v -f $$($1_BIN)/_the.$$($1_JARNAME)_exclude + $1_GREP_EXCLUDES:=| ( $(GREP) -v -f $$($1_BIN)/_the.$$($1_JARNAME)_exclude \ + || test "$$$$?" = "1" ) endif endif @@ -222,9 +227,11 @@ $$($1_CAPTURE_EXTRA_FILES) # The capture metainf macro finds all files below the META-INF directory that are newer than the jar-file. + # Find returns non zero if the META-INF dir does not exist, ignore this. ifeq (,$$($1_SKIP_METAINF)) $1_CAPTURE_METAINF =$$(foreach src,$$($1_SRCS), \ - ( $(FIND) $$(src)/META-INF -type f -a -newer $$@ 2> /dev/null | $(SED) 's|$$(src)/|-C $$(src) |g' >> \ + ( ( $(FIND) $$(src)/META-INF -type f -a -newer $$@ 2> /dev/null || true ) \ + | $(SED) 's|$$(src)/|-C $$(src) |g' >> \ $$($1_BIN)/_the.$$($1_JARNAME)_contents ) $$(NEWLINE) ) endif # The capture deletes macro finds all deleted files and concatenates them. The resulting file @@ -248,9 +255,11 @@ >> $$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE)) \ $$($1_CAPTURE_EXTRA_FILES) + # Find returns non zero if the META-INF dir does not exist, ignore this. ifeq (,$$($1_SKIP_METAINF)) $1_SCAPTURE_METAINF=$$(foreach src,$$($1_SRCS), \ - ( $(FIND) $$(src)/META-INF -type f 2> /dev/null | $(SED) 's|$$(src)/|-C $$(src) |g' >> \ + ( ( $(FIND) $$(src)/META-INF -type f 2> /dev/null || true ) \ + | $(SED) 's|$$(src)/|-C $$(src) |g' >> \ $$($1_BIN)/_the.$$($1_JARNAME)_contents) $$(NEWLINE) ) endif $1_SUPDATE_CONTENTS=$(JAR) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE) @@ -270,19 +279,37 @@ $1_JAR_UPDATE_OPTIONS := uf endif + # Include all variables of significance in the vardeps file + $1_VARDEPS := $(JAR) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) $(RELEASE) $(COMPANY_NAME) \ + $$($1_JARMAIN) $$($1_EXTRA_MANIFEST_ATTR) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$(dir $$($1_JAR))_the.$$($1_JARNAME).vardeps) + # Here is the rule that creates/updates the jar file. - $$($1_JAR) : $$($1_DEPS) + $$($1_JAR) : $$($1_DEPS) $$($1_MANIFEST) $$($1_VARDEPS_FILE) $(MKDIR) -p $$($1_BIN) $$($1_GREP_INCLUDE_OUTPUT) $$($1_GREP_EXCLUDE_OUTPUT) - $$(if $$($1_MANIFEST), \ - $(SED) -e "s#@@RELEASE@@#$(RELEASE)#" \ - -e "s#@@COMPANY_NAME@@#$(COMPANY_NAME)#" $$($1_MANIFEST) > $$($1_MANIFEST_FILE) \ + # If the vardeps file is part of the newer prereq list, it means that + # either the jar file does not exist, or we need to recreate it from + # from scratch anyway since a simple update will not catch all the + # potential changes. + $$(if $$(filter $$($1_VARDEPS_FILE) $$($1_MANIFEST), $$?), \ + $$(if $$($1_MANIFEST), \ + $(SED) -e "s#@@RELEASE@@#$(RELEASE)#" \ + -e "s#@@COMPANY_NAME@@#$(COMPANY_NAME)#" $$($1_MANIFEST) > $$($1_MANIFEST_FILE) $$(NEWLINE) \ + , \ + $(RM) $$($1_MANIFEST_FILE) && $(TOUCH) $$($1_MANIFEST_FILE) $$(NEWLINE)) \ + $$(if $$($1_JARMAIN), \ + $(ECHO) "Main-Class: $$(strip $$($1_JARMAIN))" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \ + $$(if $$($1_EXTRA_MANIFEST_ATTR), \ + $(PRINTF) "$$($1_EXTRA_MANIFEST_ATTR)\n" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \ + $(ECHO) Creating $$($1_NAME) $$(NEWLINE) \ + $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $$($1_SCAPTURE_CONTENTS) \ + $$($1_SCAPTURE_METAINF) \ + $$($1_SUPDATE_CONTENTS) \ + $$($1_JARINDEX) && true \ , \ - $(RM) $$($1_MANIFEST_FILE) && $(TOUCH) $$($1_MANIFEST_FILE)) - $$(if $$($1_JARMAIN),$(ECHO) "Main-Class: $$(strip $$($1_JARMAIN))" >> $$($1_MANIFEST_FILE)) - $$(if $$($1_EXTRA_MANIFEST_ATTR),$(PRINTF) "$$($1_EXTRA_MANIFEST_ATTR)\n" >> $$($1_MANIFEST_FILE)) - $$(if $$(wildcard $$@), \ $(ECHO) Modifying $$($1_NAME) $$(NEWLINE) \ $$($1_CAPTURE_CONTENTS) \ $$($1_CAPTURE_METAINF) \ @@ -294,12 +321,6 @@ $(ZIP) -q -d $$@ `$(CAT) $$($1_DELETESS_FILE)` ; \ fi $$(NEWLINE) \ $$($1_UPDATE_CONTENTS) true $$(NEWLINE) \ - $$($1_JARINDEX) && true \ - , \ - $(ECHO) Creating $$($1_NAME) && $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ - $$($1_SCAPTURE_CONTENTS) \ - $$($1_SCAPTURE_METAINF) \ - $$($1_SUPDATE_CONTENTS) \ $$($1_JARINDEX) && true ) # Add jar to target list @@ -431,7 +452,7 @@ $1_SRC:=$$(call ADD_SRCS,$$($1_SRC)) # Make sure the dirs exist. $$(foreach d,$$($1_SRC), $$(if $$(wildcard $$d),,$$(error SRC specified to SetupJavaCompilation $1 contains missing directory $$d))) - $$(eval $$(call MakeDir,$$($1_BIN))) + $$(call MakeDir,$$($1_BIN)) # Add all source roots to the find cache since we are likely going to run find # on these more than once. The cache will only be updated if necessary. $$(eval $$(call FillCacheFind,$$($1_SRC))) @@ -475,23 +496,23 @@ $1_ALL_COPIES += $$(filter $$(addprefix %,$$($1_COPY)),$$($1_ALL_SRCS)) # Copy these explicitly $1_ALL_COPIES += $$($1_COPY_FILES) - # Copy must also respect filters. - ifneq (,$$($1_INCLUDES)) - $1_ALL_COPIES := $$(filter $$($1_SRC_INCLUDES),$$($1_ALL_COPIES)) - endif - ifneq (,$$($1_EXCLUDES)) - $1_ALL_COPIES := $$(filter-out $$($1_SRC_EXCLUDES),$$($1_ALL_COPIES)) - endif - ifneq (,$$($1_EXCLUDE_FILES)) - $1_ALL_COPIES := $$(filter-out $$($1_EXCLUDE_FILES_PATTERN),$$($1_ALL_COPIES)) - endif + endif + # Copy must also respect filters. + ifneq (,$$($1_INCLUDES)) + $1_ALL_COPIES := $$(filter $$($1_SRC_INCLUDES),$$($1_ALL_COPIES)) + endif + ifneq (,$$($1_EXCLUDES)) + $1_ALL_COPIES := $$(filter-out $$($1_SRC_EXCLUDES),$$($1_ALL_COPIES)) endif - ifneq (,$$($1_ALL_COPIES)) - # Yep, there are files to be copied! - $1_ALL_COPY_TARGETS:= - $$(foreach i,$$($1_ALL_COPIES),$$(eval $$(call add_file_to_copy,$1,$$i))) - # Now we can depend on $$($1_ALL_COPY_TARGETS) to copy all files! - endif + ifneq (,$$($1_EXCLUDE_FILES)) + $1_ALL_COPIES := $$(filter-out $$($1_EXCLUDE_FILES_PATTERN),$$($1_ALL_COPIES)) + endif + ifneq (,$$($1_ALL_COPIES)) + # Yep, there are files to be copied! + $1_ALL_COPY_TARGETS:= + $$(foreach i,$$($1_ALL_COPIES),$$(eval $$(call add_file_to_copy,$1,$$i))) + # Now we can depend on $$($1_ALL_COPY_TARGETS) to copy all files! + endif # Find all property files to be copied and cleaned from source to bin. ifneq (,$$($1_CLEAN)$$($1_CLEAN_FILES)) @@ -535,9 +556,14 @@ $1_SJAVAC:=$$(subst com.sun.tools.javac.Main,com.sun.tools.sjavac.Main,$$($1_JAVAC)) # Set the $1_REMOTE to spawn a background javac server. - $1_REMOTE:=--server:portfile=$$($1_SJAVAC_PORTFILE),id=$1,sjavac=$$(subst $$(SPACE),%20,$$(subst $$(COMMA),%2C,$$(strip $$($1_SERVER_JVM) $$($1_SJAVAC)))) + $1_REMOTE:=--server:portfile=$$($1_SJAVAC_PORTFILE),id=$1,sjavac=$$(subst \ + $$(SPACE),%20,$$(subst $$(COMMA),%2C,$$(strip $$($1_SERVER_JVM) $$($1_SJAVAC)))) - $$($1_BIN)/_the.$1_batch: $$($1_SRCS) $$($1_DEPENDS) + $1_VARDEPS := $$($1_JVM) $$($1_SJAVAC) $$($1_SJAVAC_ARGS) $$($1_FLAGS) \ + $$($1_HEADERS_ARG) $$($1_BIN) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$($1_BIN)/_the.$1.vardeps) + + $$($1_BIN)/_the.$1_batch: $$($1_SRCS) $$($1_DEPENDS) $$($1_VARDEPS_FILE) $(MKDIR) -p $$(@D) $$(dir $$($1_SJAVAC_PORTFILE)) # As a workaround for sjavac not tracking api changed from the classpath, force full # recompile if an external dependency, which is something other than a source @@ -592,8 +618,11 @@ $1_HEADER_TARGETS := $$($1_HEADERS)/_the.$1_headers endif + $1_VARDEPS := $$($1_JVM) $$($1_JAVAC) $$($1_FLAGS) $$($1_BIN) $$($1_HEADERS_ARG) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$($1_BIN)/_the.$1.vardeps) + # When not using sjavac, pass along all sources to javac using an @file. - $$($1_BIN)/_the.$1_batch: $$($1_SRCS) $$($1_DEPENDS) + $$($1_BIN)/_the.$1_batch: $$($1_SRCS) $$($1_DEPENDS) $$($1_VARDEPS_FILE) $(MKDIR) -p $$(@D) $(RM) $$($1_BIN)/_the.$1_batch $$($1_BIN)/_the.$1_batch.tmp $$(call ListPathsSafely,$1_SRCS,\n, >> $$($1_BIN)/_the.$1_batch.tmp) @@ -659,4 +688,5 @@ $(if $(findstring yes, $(ENABLE_SJAVAC)), $(strip $2)/_the.$(strip $1)_pubapi, \ $(strip $2)/_the.$(strip $1)_batch) endef -endif + +endif # _JAVA_COMPILATION_GMK diff -r ac59fe6cd98f -r 4746a63313f6 make/common/MakeBase.gmk --- a/make/common/MakeBase.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/common/MakeBase.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -349,7 +349,7 @@ # (and causing a crash on Cygwin). # Default shell seems to always be /bin/sh. Must override with bash to get this to work on Solaris. # Only use time if it's GNU time which supports format and output file. - WRAPPER_SHELL:=$$(BASH) $$(SRC_ROOT)/common/bin/shell-tracer.sh $$(if $$(findstring yes,$$(IS_GNU_TIME)),$$(TIME),-) $$(OUTPUT_ROOT)/build-trace-time.log $$(BASH) + WRAPPER_SHELL:=$$(BASH) $$(SRC_ROOT)/common/bin/shell-tracer.sh $$(if $$(findstring yes,$$(IS_GNU_TIME)),$$(TIME),-) $$(OUTPUT_ROOT)/build-trace-time.log $$(SHELL) SHELL=$$(warning $$(if $$@,Building $$@,Running shell command) $$(if $$<, (from $$<))$$(if $$?, ($$(wordlist 1, 20, $$?) $$(if $$(wordlist 21, 22, $$?), ... [in total $$(words $$?) files]) newer)))$$(WRAPPER_SHELL) endif # Never remove warning messages; this is just for completeness @@ -392,11 +392,9 @@ endef # Make directory without forking mkdir if not needed -define MakeDir - ifneq ($$(wildcard $1 $2 $3 $4 $5 $6 $7 $8 $9),$$(strip $1 $2 $3 $4 $5 $6 $7 $8 $9)) - $$(shell $(MKDIR) -p $1 $2 $3 $4 $5 $6 $7 $8 $9) - endif -endef +MakeDir = \ + $(strip $(if $(subst $(wildcard $1 $2 $3 $4 $5 $6 $7 $8 $9),,$(strip $1 $2 $3 $4 $5 $6 $7 $8 $9)),\ + $(shell $(MKDIR) -p $1 $2 $3 $4 $5 $6 $7 $8 $9))) ifeq ($(OPENJDK_TARGET_OS),solaris) # On Solaris, if the target is a symlink and exists, cp won't overwrite. @@ -446,6 +444,11 @@ # Filter out duplicate sub strings while preserving order. Keeps the first occurance. uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1))) +# String equals +equals = \ + $(and $(findstring $(strip $1),$(strip $2)),\ + $(findstring $(strip $2),$(strip $1))) + ifneq ($(DISABLE_CACHE_FIND), true) ################################################################################ # In Cygwin, finds are very costly, both because of expensive forks and because @@ -543,6 +546,80 @@ endef ################################################################################ +# ShellQuote +# +# Quotes a string with single quotes and replaces single quotes with '\'' so +# that the contents survives being given to the shell. + +ShellQuote = \ + $(SQUOTE)$(subst $(SQUOTE),$(SQUOTE)\$(SQUOTE)$(SQUOTE),$(strip $1))$(SQUOTE) + +################################################################################ +# Write to and read from file + +# Param 1 - File to read +ReadFile = \ + $(shell $(CAT) $1) + +# Param 1 - Text to write +# Param 2 - File to write to +# Use printf to get consistent behavior on all platforms. +WriteFile = \ + $(shell $(PRINTF) "%s" $(call ShellQuote, $1) > $2) + +################################################################################ +# DependOnVariable +# +# This macro takes a variable name and puts the value in a file only if the +# value has changed since last. The name of the file is returned. This can be +# used to create rule dependencies on make variable values. The following +# example would get rebuilt if the value of SOME_VAR was changed: +# +# path/to/some-file: $(call DependOnVariable, SOME_VAR) +# echo $(SOME_VAR) > $@ +# +# Note that leading and trailing white space in the value is ignored. +# + +# Defines the sub directory structure to store variable value file in +DependOnVariableDirName = \ + $(strip $(subst $(SRC_ROOT)/,,\ + $(if $(filter /%, $(firstword $(MAKEFILE_LIST))), \ + $(firstword $(MAKEFILE_LIST)), \ + $(CURDIR)/$(firstword $(MAKEFILE_LIST))))) + +# Defines the name of the file to store variable value in. Generates a name +# unless parameter 2 is given. +# Param 1 - Name of variable +# Param 2 - (optional) name of file to store value in +DependOnVariableFileName = \ + $(strip $(if $(strip $2), $2, \ + $(MAKESUPPORT_OUTPUTDIR)/vardeps/$(DependOnVariableDirName)/$(strip $1).vardeps)) + +# Does the actual work with parameters stripped. +# If the file exists AND the contents is the same as the variable, do nothing +# else print a new file. +# Always returns the name of the file where the value was printed. +# Param 1 - Name of variable +# Param 2 - (optional) name of file to store value in +DependOnVariableHelper = \ + $(strip $(if $(and $(wildcard $(call DependOnVariableFileName, $1, $2)),\ + $(call equals, $(strip $($1)), \ + $(call ReadFile, $(call DependOnVariableFileName, $1, $2)))),,\ + $(call MakeDir, $(dir $(call DependOnVariableFileName, $1, $2))) \ + $(if $(findstring $(LOG_LEVEL), trace), \ + $(info Variable $1: >$(strip $($1))<) \ + $(info File: >$(call ReadFile, $(call DependOnVariableFileName, $1, $2))<)) \ + $(call WriteFile, $($1), $(call DependOnVariableFileName, $1, $2))) \ + $(call DependOnVariableFileName, $1, $2)) + +# Main macro +# Param 1 - Name of variable +# Param 2 - (optional) name of file to store value in +DependOnVariable = \ + $(call DependOnVariableHelper,$(strip $1),$(strip $2)) + +################################################################################ # Hook to include the corresponding custom file, if present. $(eval $(call IncludeCustomExtension, , common/MakeBase.gmk)) diff -r ac59fe6cd98f -r 4746a63313f6 make/common/NativeCompilation.gmk --- a/make/common/NativeCompilation.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/common/NativeCompilation.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -114,7 +114,7 @@ endif endif - $$($1_$2_OBJ) : $2 | $$($1_BUILD_INFO) + $$($1_$2_OBJ) : $2 $$($1_COMPILE_VARDEPS_FILE) | $$($1_BUILD_INFO) $(ECHO) $(LOG_INFO) "Compiling $$(notdir $2) (for $$(notdir $$($1_TARGET)))" ifneq ($(TOOLCHAIN_TYPE), microsoft) # The Solaris studio compiler doesn't output the full path to the object file in the @@ -133,7 +133,8 @@ ($$($1_$2_COMP) $$($1_$2_FLAGS) -showIncludes $$($1_$2_DEBUG_OUT_FLAGS) \ $(CC_OUT_OPTION)$$($1_$2_OBJ) $2 ; echo $$$$? > $$($1_$2_DEP).exitvalue) \ | $(TEE) $$($1_$2_DEP).raw | $(GREP) -v -e "^Note: including file:" \ - -e "^$(notdir $2)$$$$" ; exit `cat $$($1_$2_DEP).exitvalue` + -e "^$(notdir $2)$$$$" || test "$$$$?" = "1" ; \ + exit `cat $$($1_$2_DEP).exitvalue` $(RM) $$($1_$2_DEP).exitvalue ($(ECHO) $$@: \\ \ && $(SED) $(WINDOWS_SHOWINCLUDE_SED_PATTERN) $$($1_$2_DEP).raw) > $$($1_$2_DEP) @@ -306,7 +307,7 @@ endif # Make sure the dirs exist. - $$(eval $$(call MakeDir,$$($1_OBJECT_DIR) $$($1_OUTPUT_DIR))) + $$(call MakeDir,$$($1_OBJECT_DIR) $$($1_OUTPUT_DIR)) $$(foreach d,$$($1_SRC), $$(if $$(wildcard $$d),,$$(error SRC specified to SetupNativeCompilation $1 contains missing directory $$d))) # Find all files in the source trees. Sort to remove duplicates. @@ -426,15 +427,16 @@ $1_BUILD_INFO := $$($1_OBJECT_DIR)/_build-info.marker - # Setup rule for printing progress info when compiling source files. - # This is a rough heuristic and may not always print accurate information. - $$($1_BUILD_INFO): $$($1_SRCS) - ifeq ($$(wildcard $$($1_TARGET)),) - $(ECHO) 'Creating $$($1_BASENAME) from $$(words $$?) file(s)' - else - $(ECHO) 'Updating $$($1_BASENAME) from $$(words $$?) file(s)' - endif - $(TOUCH) $$@ + # Track variable changes for all variables that affect the compilation command + # lines for all object files in this setup. This includes at least all the + # variables used in the call to add_native_source below. + $1_COMPILE_VARDEPS := $$($1_CFLAGS) $$($1_EXTRA_CFLAGS) $(SYSROOT_CFLAGS) \ + $$($1_CXXFLAGS) $$($1_EXTRA_CXXFLAGS) \ + $$($1_CC) $$($1_CXX) $$($1_OBJC) $$($1_ASFLAGS) \ + $$(foreach s, $$($1_SRCS), \ + $$($1_$$(notdir $$s)_CFLAGS) $$($1_$$(notdir $$s)_CXXFLAGS)) + $1_COMPILE_VARDEPS_FILE := $$(call DependOnVariable, $1_COMPILE_VARDEPS, \ + $$($1_OBJECT_DIR)/$$($1_NOSUFFIX).comp.vardeps) # Now call add_native_source for each source file we are going to compile. $$(foreach p,$$($1_SRCS), \ @@ -444,13 +446,28 @@ $$($1_CXXFLAGS) $$($1_EXTRA_CXXFLAGS) $(SYSROOT_CFLAGS), \ $$($1_CXX),$$($1_OBJC),$$($1_ASFLAGS)))) + # Setup rule for printing progress info when compiling source files. + # This is a rough heuristic and may not always print accurate information. + $$($1_BUILD_INFO): $$($1_SRCS) $$($1_COMPILE_VARDEPS_FILE) + ifeq ($$(wildcard $$($1_TARGET)),) + $(ECHO) 'Creating $$($1_BASENAME) from $$(words $$(filter-out %.vardeps, $$?)) file(s)' + else + $(ECHO) 'Updating $$($1_BASENAME) from $$(words $$(filter-out %.vardeps, $$?)) file(s)' + endif + $(TOUCH) $$@ + # On windows we need to create a resource file ifeq ($(OPENJDK_TARGET_OS), windows) ifneq (,$$($1_VERSIONINFO_RESOURCE)) $1_RES:=$$($1_OBJECT_DIR)/$$($1_BASENAME).res $1_RES_DEP:=$$($1_RES).d -include $$($1_RES_DEP) - $$($1_RES): $$($1_VERSIONINFO_RESOURCE) + + $1_RES_VARDEPS := $(RC) $$($1_RC_FLAGS) + $1_RES_VARDEPS_FILE := $$(call DependOnVariable, $1_RES_VARDEPS, \ + $$($1_RES).vardeps) + + $$($1_RES): $$($1_VERSIONINFO_RESOURCE) $$($1_RES_VARDEPS_FILE) $(ECHO) $(LOG_INFO) "Compiling resource $$(notdir $$($1_VERSIONINFO_RESOURCE)) (for $$(notdir $$($1_TARGET)))" $(RC) $$($1_RC_FLAGS) $(CC_OUT_OPTION)$$@ $$($1_VERSIONINFO_RESOURCE) # Windows RC compiler does not support -showIncludes, so we mis-use CL for this. @@ -462,7 +479,9 @@ ifneq (,$$($1_MANIFEST)) $1_GEN_MANIFEST:=$$($1_OBJECT_DIR)/$$($1_PROGRAM).manifest IMVERSIONVALUE:=$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION).$(JDK_UPDATE_VERSION).$(COOKED_BUILD_NUMBER) - $$($1_GEN_MANIFEST): $$($1_MANIFEST) + $1_MANIFEST_VARDEPS_FILE := $$(call DependOnVariable, IMVERSIONVALUE, \ + $$($1_GEN_MANIFEST).vardeps) + $$($1_GEN_MANIFEST): $$($1_MANIFEST) $$($1_MANIFEST_VARDEPS_FILE) $(SED) 's%IMVERSION%$$(IMVERSIONVALUE)%g;s%PROGRAM%$$($1_PROGRAM)%g' $$< > $$@ endif endif @@ -575,8 +594,13 @@ $1_EXTRA_LDFLAGS_SUFFIX += $(GLOBAL_LDFLAGS_SUFFIX) + $1_VARDEPS := $$($1_LD) $(SYSROOT_LDFLAGS) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) \ + $$($1_LDFLAGS_SUFFIX) $$($1_EXTRA_LDFLAGS_SUFFIX) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, \ + $$($1_OBJECT_DIR)/$$($1_NOSUFFIX).vardeps) + $$($1_TARGET): $$($1_EXPECTED_OBJS) $$($1_RES) $$($1_REAL_MAPFILE) \ - $$($1_DEBUGINFO_EXTRA_DEPS) + $$($1_DEBUGINFO_EXTRA_DEPS) $$($1_VARDEPS_FILE) $(ECHO) $(LOG_INFO) "Linking $$($1_BASENAME)" $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $(SYSROOT_LDFLAGS) \ $(LD_OUT_OPTION)$$@ \ @@ -592,8 +616,12 @@ endif ifneq (,$$($1_STATIC_LIBRARY)) + $1_VARDEPS := $(AR) $$($1_ARFLAGS) $$($1_LDFLAGS_SUFFIX) $$($1_EXTRA_LDFLAGS_SUFFIX) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, \ + $$($1_OBJECT_DIR)/$$($1_NOSUFFIX).vardeps) + # Generating a static library, ie object file archive. - $$($1_TARGET): $$($1_EXPECTED_OBJS) $$($1_RES) + $$($1_TARGET): $$($1_EXPECTED_OBJS) $$($1_RES) $$($1_VARDEPS_FILE) $(ECHO) $(LOG_INFO) "Archiving $$($1_STATIC_LIBRARY)" $(AR) $$($1_ARFLAGS) $(AR_OUT_OPTION)$$($1_TARGET) $$($1_EXPECTED_OBJS) \ $$($1_RES) $$($1_LDFLAGS_SUFFIX) $$($1_EXTRA_LDFLAGS_SUFFIX) @@ -603,8 +631,13 @@ # A executable binary has been specified, setup the target for it. $1_EXTRA_LDFLAGS_SUFFIX += $(GLOBAL_LDFLAGS_SUFFIX) + $1_VARDEPS := $$($1_LDEXE) $(SYSROOT_LDFLAGS) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) \ + $$($1_LDFLAGS_SUFFIX) $$($1_EXTRA_LDFLAGS_SUFFIX) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, \ + $$($1_OBJECT_DIR)/$$($1_NOSUFFIX).vardeps) + $$($1_TARGET): $$($1_EXPECTED_OBJS) $$($1_RES) $$($1_GEN_MANIFEST) \ - $$($1_DEBUGINFO_EXTRA_DEPS) + $$($1_DEBUGINFO_EXTRA_DEPS) $$($1_VARDEPS_FILE) $(ECHO) $(LOG_INFO) "Linking executable $$($1_BASENAME)" $$($1_LDEXE) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $(SYSROOT_LDFLAGS) \ $(EXE_OUT_OPTION)$$($1_TARGET) \ diff -r ac59fe6cd98f -r 4746a63313f6 make/common/TextFileProcessing.gmk --- a/make/common/TextFileProcessing.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/make/common/TextFileProcessing.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -34,7 +34,7 @@ # param 3 = the target base directory # param 4 = the target file name (possibly with a partial path) define SetupSingleTextFileForProcessing - $(strip $3)/$(strip $4): $2 + $(strip $3)/$(strip $4): $2 $$($1_VARDEPS_FILE) $(ECHO) $(LOG_INFO) "Processing $(strip $4)" $(MKDIR) -p '$$(@D)' $(RM) '$$@' '$$@.includes.tmp' '$$@.replacements.tmp' @@ -193,6 +193,9 @@ $1_INCLUDES_COMMAND_LINE := $(CAT) endif + $1_VARDEPS := $$($1_INCLUDES_COMMAND_LINE) $$($1_REPLACEMENTS_COMMAND_LINE) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS) + # Reset target list before populating it $1 := diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/.hgtags --- a/nashorn/.hgtags Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/.hgtags Wed Jul 05 20:17:28 2017 +0200 @@ -281,3 +281,4 @@ 3c2bbeda038aef7061455fec604db7d8a342fac5 jdk9-b45 2ecf0a617f0f9af1ffd278a0c70e76f1946ce773 jdk9-b46 29046d42a95e5b9f105ab086a628bbd7f81c915d jdk9-b47 +f08660f30051ba0b38ad00e692979b37d107c9c4 jdk9-b48 diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Jul 05 20:17:28 2017 +0200 @@ -926,9 +926,7 @@ @Override public Node leaveTryNode(final TryNode tryNode) { tryNode.setException(exceptionSymbol()); - if (tryNode.getFinallyBody() != null) { - tryNode.setFinallyCatchAll(exceptionSymbol()); - } + assert tryNode.getFinallyBody() == null; end(tryNode); diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jul 05 20:17:28 2017 +0200 @@ -85,7 +85,6 @@ import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; -import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; @@ -102,6 +101,7 @@ import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.JumpStatement; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; @@ -1110,7 +1110,14 @@ @Override public boolean enterBlock(final Block block) { - method.label(block.getEntryLabel()); + final Label entryLabel = block.getEntryLabel(); + if (entryLabel.isBreakTarget()) { + // Entry label is a break target only for an inlined finally block. + assert !method.isReachable(); + method.breakLabel(entryLabel, lc.getUsedSlotCount()); + } else { + method.label(entryLabel); + } if(!method.isReachable()) { return false; } @@ -1240,6 +1247,11 @@ return enterJumpStatement(breakNode); } + @Override + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + return enterJumpStatement(jumpToInlinedFinally); + } + private boolean enterJumpStatement(final JumpStatement jump) { if(!method.isReachable()) { return false; @@ -1247,9 +1259,8 @@ enterStatement(jump); method.beforeJoinPoint(jump); - final BreakableNode target = jump.getTarget(lc); - popScopesUntil(target); - final Label targetLabel = jump.getTargetLabel(target); + popScopesUntil(jump.getPopScopeLimit(lc)); + final Label targetLabel = jump.getTargetLabel(lc); targetLabel.markAsBreakTarget(); method._goto(targetLabel); @@ -3053,6 +3064,14 @@ if (method.isReachable()) { method._goto(skip); } + + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { + TryNode.getLabelledInlinedFinallyBlock(inlinedFinally).accept(this); + // All inlined finallies end with a jump or a return + assert !method.isReachable(); + } + + method._catch(recovery); method.store(vmException, EXCEPTION_TYPE); @@ -3112,15 +3131,14 @@ catchBody.accept(this); leaveBlock(catchBlock); lc.pop(catchBlock); - if(method.isReachable()) { - method._goto(afterCatch); - } if(nextCatch != null) { + if(method.isReachable()) { + method._goto(afterCatch); + } method.breakLabel(nextCatch, lc.getUsedSlotCount()); } } - assert !method.isReachable(); // afterCatch could be the same as skip, except that we need to establish that the vmException is dead. method.label(afterCatch); if(method.isReachable()) { @@ -3129,6 +3147,8 @@ method.label(skip); // Finally body is always inlined elsewhere so it doesn't need to be emitted + assert tryNode.getFinallyBody() == null; + return false; } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Jul 05 20:17:28 2017 +0200 @@ -74,7 +74,7 @@ /** size of next free slot vector */ private int nextFreeSlotsSize; - private boolean isWithBoundary(final LexicalContextNode node) { + private boolean isWithBoundary(final Object node) { return node instanceof Block && !isEmpty() && peek() instanceof WithNode; } @@ -102,7 +102,7 @@ } @Override - public T pop(final T node) { + public T pop(final T node) { final T popped = super.pop(node); if (isWithBoundary(node)) { dynamicScopeCount--; diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Jul 05 20:17:28 2017 +0200 @@ -62,6 +62,7 @@ import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.JumpStatement; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; @@ -529,8 +530,7 @@ return false; } assertTypeStackIsEmpty(); - final BreakableNode target = jump.getTarget(lc); - jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); + jumpToLabel(jump, jump.getTargetLabel(lc), getBreakTargetTypes(jump.getPopScopeLimit(lc))); doesNotContinueSequentially(); return false; } @@ -784,6 +784,11 @@ } @Override + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + return enterJumpStatement(jumpToInlinedFinally); + } + + @Override public boolean enterLiteralNode(final LiteralNode literalNode) { if (literalNode instanceof ArrayLiteralNode) { final List expressions = ((ArrayLiteralNode)literalNode).getElementExpressions(); @@ -1042,6 +1047,17 @@ } doesNotContinueSequentially(); + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { + final Block finallyBody = TryNode.getLabelledInlinedFinallyBlock(inlinedFinally); + joinOnLabel(finallyBody.getEntryLabel()); + // NOTE: the jump to inlined finally can end up in dead code, so it is not necessarily reachable. + if (reachable) { + finallyBody.accept(this); + // All inlined finallies end with a jump or a return + assert !reachable; + } + } + joinOnLabel(catchLabel); for(final CatchNode catchNode: tryNode.getCatches()) { final IdentNode exception = catchNode.getException(); @@ -1125,7 +1141,7 @@ return false; }; - private Map getBreakTargetTypes(final BreakableNode target) { + private Map getBreakTargetTypes(final LexicalContextNode target) { // Remove symbols defined in the the blocks that are being broken out of. Map types = localVariableTypes; for(final Iterator it = lc.getAllNodes(); it.hasNext();) { @@ -1380,7 +1396,11 @@ if(node instanceof JoinPredecessor) { final JoinPredecessor original = joinPredecessors.pop(); assert original.getClass() == node.getClass() : original.getClass().getName() + "!=" + node.getClass().getName(); - return (Node)setLocalVariableConversion(original, (JoinPredecessor)node); + final JoinPredecessor newNode = setLocalVariableConversion(original, (JoinPredecessor)node); + if (newNode instanceof LexicalContextNode) { + lc.replace((LexicalContextNode)node, (LexicalContextNode)newNode); + } + return (Node)newNode; } return node; } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Wed Jul 05 20:17:28 2017 +0200 @@ -56,9 +56,11 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JumpStatement; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ReturnNode; @@ -115,7 +117,7 @@ for (final Statement statement : statements) { if (!terminated) { newStatements.add(statement); - if (statement.isTerminal() || statement instanceof BreakNode || statement instanceof ContinueNode) { //TODO hasGoto? But some Loops are hasGoto too - why? + if (statement.isTerminal() || statement instanceof JumpStatement) { //TODO hasGoto? But some Loops are hasGoto too - why? terminated = true; } } else { @@ -183,6 +185,12 @@ } @Override + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + addStatement(jumpToInlinedFinally); + return false; + } + + @Override public boolean enterEmptyNode(final EmptyNode emptyNode) { return false; } @@ -318,8 +326,8 @@ return addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor } - private static Node ensureUniqueNamesIn(final Node node) { - return node.accept(new NodeVisitor(new LexicalContext()) { + private static T ensureUniqueNamesIn(final T node) { + return (T)node.accept(new NodeVisitor(new LexicalContext()) { @Override public Node leaveFunctionNode(final FunctionNode functionNode) { final String name = functionNode.getName(); @@ -333,15 +341,15 @@ }); } - private static List copyFinally(final Block finallyBody) { + private static Block createFinallyBlock(final Block finallyBody) { final List newStatements = new ArrayList<>(); for (final Statement statement : finallyBody.getStatements()) { - newStatements.add((Statement)ensureUniqueNamesIn(statement)); + newStatements.add(statement); if (statement.hasTerminalFlags()) { - return newStatements; + break; } } - return newStatements; + return finallyBody.setStatements(null, newStatements); } private Block catchAllBlock(final TryNode tryNode) { @@ -367,28 +375,24 @@ return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); } - private static boolean isTerminal(final List statements) { - return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags(); + private static boolean isTerminalFinally(final Block finallyBlock) { + return finallyBlock.getLastStatement().hasTerminalFlags(); } /** * Splice finally code into all endpoints of a trynode * @param tryNode the try node - * @param rethrows list of rethrowing throw nodes from synthetic catch blocks + * @param rethrow the rethrowing throw nodes from the synthetic catch block * @param finallyBody the code in the original finally block * @return new try node after splicing finally code (same if nop) */ - private Node spliceFinally(final TryNode tryNode, final List rethrows, final Block finallyBody) { + private TryNode spliceFinally(final TryNode tryNode, final ThrowNode rethrow, final Block finallyBody) { assert tryNode.getFinallyBody() == null; + final Block finallyBlock = createFinallyBlock(finallyBody); + final ArrayList inlinedFinallies = new ArrayList<>(); + final FunctionNode fn = lc.getCurrentFunction(); final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor(new LexicalContext()) { - final List insideTry = new ArrayList<>(); - - @Override - public boolean enterDefault(final Node node) { - insideTry.add(node); - return true; - } @Override public boolean enterFunctionNode(final FunctionNode functionNode) { @@ -398,12 +402,8 @@ @Override public Node leaveThrowNode(final ThrowNode throwNode) { - if (rethrows.contains(throwNode)) { - final List newStatements = copyFinally(finallyBody); - if (!isTerminal(newStatements)) { - newStatements.add(throwNode); - } - return BlockStatement.createReplacement(throwNode, newStatements); + if (rethrow == throwNode) { + return new BlockStatement(prependFinally(finallyBlock, throwNode)); } return throwNode; } @@ -419,58 +419,94 @@ } private Node leaveJumpStatement(final JumpStatement jump) { - return copy(jump, (Node)jump.getTarget(Lower.this.lc)); + // NOTE: leaveJumpToInlinedFinally deliberately does not delegate to this method, only break and + // continue are edited. JTIF nodes should not be changed, rather the surroundings of + // break/continue/return that were moved into the inlined finally block itself will be changed. + + // If this visitor's lc doesn't find the target of the jump, it means it's external to the try block. + if (jump.getTarget(lc) == null) { + return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, jump)); + } + return jump; } @Override public Node leaveReturnNode(final ReturnNode returnNode) { - final Expression expr = returnNode.getExpression(); - final List newStatements = new ArrayList<>(); - - final Expression resultNode; - if (expr != null) { - //we need to evaluate the result of the return in case it is complex while - //still in the try block, store it in a result value and return it afterwards - resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); - newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); + final Expression expr = returnNode.getExpression(); + if (isTerminalFinally(finallyBlock)) { + if (expr == null) { + // Terminal finally; no return expression. + return createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock)); + } + // Terminal finally; has a return expression. + final List newStatements = new ArrayList<>(2); + final int retLineNumber = returnNode.getLineNumber(); + final long retToken = returnNode.getToken(); + // Expression is evaluated for side effects. + newStatements.add(new ExpressionStatement(retLineNumber, retToken, returnNode.getFinish(), expr)); + newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, ensureUniqueNamesIn(finallyBlock))); + return new BlockStatement(retLineNumber, new Block(retToken, finallyBlock.getFinish(), newStatements)); + } else if (expr == null || expr instanceof PrimitiveLiteralNode || (expr instanceof IdentNode && RETURN.symbolName().equals(((IdentNode)expr).getName()))) { + // Nonterminal finally; no return expression, or returns a primitive literal, or returns :return. + // Just move the return expression into the finally block. + return createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode)); } else { - resultNode = null; + // We need to evaluate the result of the return in case it is complex while still in the try block, + // store it in :return, and return it afterwards. + final List newStatements = new ArrayList<>(); + final int retLineNumber = returnNode.getLineNumber(); + final long retToken = returnNode.getToken(); + final int retFinish = returnNode.getFinish(); + final Expression resultNode = new IdentNode(expr.getToken(), expr.getFinish(), RETURN.symbolName()); + // ":return = ;" + newStatements.add(new ExpressionStatement(retLineNumber, retToken, retFinish, new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); + // inline finally and end it with "return :return;" + newStatements.add(createJumpToInlinedFinally(fn, inlinedFinallies, prependFinally(finallyBlock, returnNode.setExpression(resultNode)))); + return new BlockStatement(retLineNumber, new Block(retToken, retFinish, newStatements)); } - - newStatements.addAll(copyFinally(finallyBody)); - if (!isTerminal(newStatements)) { - newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); - } - - return BlockStatement.createReplacement(returnNode, lc.getCurrentBlock().getFinish(), newStatements); - } - - private Node copy(final Statement endpoint, final Node targetNode) { - if (!insideTry.contains(targetNode)) { - final List newStatements = copyFinally(finallyBody); - if (!isTerminal(newStatements)) { - newStatements.add(endpoint); - } - return BlockStatement.createReplacement(endpoint, tryNode.getFinish(), newStatements); - } - return endpoint; } }); - - addStatement(newTryNode); - for (final Node statement : finallyBody.getStatements()) { - addStatement((Statement)statement); - } + addStatement(inlinedFinallies.isEmpty() ? newTryNode : newTryNode.setInlinedFinallies(lc, inlinedFinallies)); + // TODO: if finallyStatement is terminal, we could just have sites of inlined finallies jump here. + addStatement(new BlockStatement(finallyBlock)); return newTryNode; } + private static JumpToInlinedFinally createJumpToInlinedFinally(final FunctionNode fn, final List inlinedFinallies, final Block finallyBlock) { + final String labelName = fn.uniqueName(":finally"); + final long token = finallyBlock.getToken(); + final int finish = finallyBlock.getFinish(); + inlinedFinallies.add(new Block(token, finish, new LabelNode(finallyBlock.getFirstStatementLineNumber(), + token, finish, labelName, finallyBlock))); + return new JumpToInlinedFinally(labelName); + } + + private static Block prependFinally(final Block finallyBlock, final Statement statement) { + final Block inlinedFinally = ensureUniqueNamesIn(finallyBlock); + if (isTerminalFinally(finallyBlock)) { + return inlinedFinally; + } + final List stmts = inlinedFinally.getStatements(); + final List newStmts = new ArrayList<>(stmts.size() + 1); + newStmts.addAll(stmts); + newStmts.add(statement); + return new Block(inlinedFinally.getToken(), statement.getFinish(), newStmts); + } + @Override public Node leaveTryNode(final TryNode tryNode) { final Block finallyBody = tryNode.getFinallyBody(); + TryNode newTryNode = tryNode.setFinallyBody(lc, null); - if (finallyBody == null) { - return addStatement(ensureUnconditionalCatch(tryNode)); + // No finally or empty finally + if (finallyBody == null || finallyBody.getStatementCount() == 0) { + final List catches = newTryNode.getCatches(); + if (catches == null || catches.isEmpty()) { + // A completely degenerate try block: empty finally, no catches. Replace it with try body. + return addStatement(new BlockStatement(tryNode.getBody())); + } + return addStatement(ensureUnconditionalCatch(newTryNode)); } /* @@ -496,11 +532,9 @@ * now splice in finally code wherever needed * */ - TryNode newTryNode; - final Block catchAll = catchAllBlock(tryNode); - final List rethrows = new ArrayList<>(); + final List rethrows = new ArrayList<>(1); catchAll.accept(new NodeVisitor(new LexicalContext()) { @Override public boolean enterThrowNode(final ThrowNode throwNode) { @@ -510,20 +544,18 @@ }); assert rethrows.size() == 1; - if (tryNode.getCatchBlocks().isEmpty()) { - newTryNode = tryNode.setFinallyBody(null); - } else { - final Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), ensureUnconditionalCatch(tryNode.setFinallyBody(null))); - newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); + if (!tryNode.getCatchBlocks().isEmpty()) { + final Block outerBody = new Block(newTryNode.getToken(), newTryNode.getFinish(), ensureUnconditionalCatch(newTryNode)); + newTryNode = newTryNode.setBody(lc, outerBody).setCatchBlocks(lc, null); } - newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null); + newTryNode = newTryNode.setCatchBlocks(lc, Arrays.asList(catchAll)); /* * Now that the transform is done, we have to go into the try and splice * the finally block in front of any statement that is outside the try */ - return spliceFinally(newTryNode, rethrows, finallyBody); + return (TryNode)lc.replace(tryNode, spliceFinally(newTryNode, rethrows.get(0), finallyBody)); } private TryNode ensureUnconditionalCatch(final TryNode tryNode) { @@ -535,7 +567,7 @@ final List newCatchBlocks = new ArrayList<>(tryNode.getCatchBlocks()); newCatchBlocks.add(catchAllBlock(tryNode)); - return tryNode.setCatchBlocks(newCatchBlocks); + return tryNode.setCatchBlocks(lc, newCatchBlocks); } @Override diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Wed Jul 05 20:17:28 2017 +0200 @@ -52,6 +52,7 @@ import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.JumpStatement; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ReturnNode; @@ -359,6 +360,11 @@ return leaveJumpNode(continueNode); } + @Override + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + return leaveJumpNode(jumpToInlinedFinally); + } + private JumpStatement leaveJumpNode(final JumpStatement jump) { if (inSplitNode()) { final SplitState splitState = getCurrentSplitState(); diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/WeighNodes.java Wed Jul 05 20:17:28 2017 +0200 @@ -40,6 +40,7 @@ import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; @@ -197,6 +198,12 @@ return indexNode; } + @Override + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + weight += BREAK_WEIGHT; + return jumpToInlinedFinally; + } + @SuppressWarnings("rawtypes") @Override public boolean enterLiteralNode(final LiteralNode literalNode) { diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Wed Jul 05 20:17:28 2017 +0200 @@ -322,6 +322,14 @@ } /** + * Returns the last statement in the block. + * @return the last statement in the block, or null if the block has no statements. + */ + public Statement getLastStatement() { + return statements.isEmpty() ? null : statements.get(statements.size() - 1); + } + + /** * Reset the statement list for this block * * @param lc lexical context diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockLexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed Jul 05 20:17:28 2017 +0200 @@ -74,7 +74,7 @@ @SuppressWarnings("unchecked") @Override - public T pop(final T node) { + public T pop(final T node) { T expected = node; if (node instanceof Block) { final List newStatements = popStatements(); diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockStatement.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockStatement.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockStatement.java Wed Jul 05 20:17:28 2017 +0200 @@ -40,6 +40,15 @@ /** * Constructor * + * @param block the block to execute + */ + public BlockStatement(final Block block) { + this(block.getFirstStatementLineNumber(), block); + } + + /** + * Constructor + * * @param lineNumber line number * @param block the block to execute */ diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakNode.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakNode.java Wed Jul 05 20:17:28 2017 +0200 @@ -77,7 +77,7 @@ } @Override - public Label getTargetLabel(final BreakableNode target) { + Label getTargetLabel(final BreakableNode target) { return target.getBreakLabel(); } } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ContinueNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ContinueNode.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ContinueNode.java Wed Jul 05 20:17:28 2017 +0200 @@ -78,7 +78,7 @@ } @Override - public Label getTargetLabel(final BreakableNode target) { + Label getTargetLabel(final BreakableNode target) { return ((LoopNode)target).getContinueLabel(); } } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpStatement.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpStatement.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpStatement.java Wed Jul 05 20:17:28 2017 +0200 @@ -101,7 +101,26 @@ * @throws ClassCastException if invoked on the kind of breakable node that this jump statement is not prepared to * handle. */ - public abstract Label getTargetLabel(final BreakableNode target); + abstract Label getTargetLabel(final BreakableNode target); + + /** + * Returns the label this jump statement targets. + * @param lc the lexical context + * @return the label this jump statement targets. + */ + public Label getTargetLabel(final LexicalContext lc) { + return getTargetLabel(getTarget(lc)); + } + + /** + * Returns the limit node for popping scopes when this jump statement is effected. + * @param lc the current lexical context + * @return the limit node for popping scopes when this jump statement is effected. + */ + public LexicalContextNode getPopScopeLimit(final LexicalContext lc) { + // In most cases (break and continue) this is equal to the target. + return getTarget(lc); + } @Override public JumpStatement setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) { diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpToInlinedFinally.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpToInlinedFinally.java Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, 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 jdk.nashorn.internal.ir; + +import java.util.Objects; +import jdk.nashorn.internal.codegen.Label; +import jdk.nashorn.internal.ir.annotations.Immutable; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * IR representation for synthetic jump into an inlined finally statement. + */ +@Immutable +public final class JumpToInlinedFinally extends JumpStatement { + private static final long serialVersionUID = 1L; + + /** + * Constructor + * + * @param labelName label name for inlined finally block + */ + public JumpToInlinedFinally(final String labelName) { + super(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, Objects.requireNonNull(labelName)); + } + + private JumpToInlinedFinally(final JumpToInlinedFinally breakNode, final LocalVariableConversion conversion) { + super(breakNode, conversion); + } + + @Override + public Node accept(final NodeVisitor visitor) { + if (visitor.enterJumpToInlinedFinally(this)) { + return visitor.leaveJumpToInlinedFinally(this); + } + + return this; + } + + @Override + JumpStatement createNewJumpStatement(final LocalVariableConversion conversion) { + return new JumpToInlinedFinally(this, conversion); + } + + @Override + String getStatementName() { + return ":jumpToInlinedFinally"; + } + + @Override + public Block getTarget(final LexicalContext lc) { + return lc.getInlinedFinally(getLabelName()); + } + + @Override + public TryNode getPopScopeLimit(final LexicalContext lc) { + // Returns the try node to which this jump's target belongs. This will make scope popping also pop the scope + // for the body of the try block, if it needs scope. + return lc.getTryNodeForInlinedFinally(getLabelName()); + } + + @Override + Label getTargetLabel(final BreakableNode target) { + assert target != null; + // We're jumping to the entry of the inlined finally block + return ((Block)target).getEntryLabel(); + } +} diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Wed Jul 05 20:17:28 2017 +0200 @@ -190,7 +190,7 @@ * @return the node that was popped */ @SuppressWarnings("unchecked") - public T pop(final T node) { + public T pop(final T node) { --sp; final LexicalContextNode popped = stack[sp]; stack[sp] = null; @@ -469,7 +469,7 @@ * scopes that need to be explicitly popped in order to perform a break or continue jump within the current bytecode * method. For this reason, the method returns 0 if it encounters a {@code SplitNode} between the current location * and the break/continue target. - * @param until node to stop counting at. Must be within the current function + * @param until node to stop counting at. Must be within the current function * @return number of with scopes encountered in the context */ public int getScopeNestingLevelTo(final LexicalContextNode until) { @@ -565,11 +565,41 @@ } /** + * Find the inlined finally block node corresponding to this label. + * @param labelName label name to search for. Must not be null. + * @return closest inlined finally block with the given label + */ + public Block getInlinedFinally(final String labelName) { + for (final NodeIterator iter = new NodeIterator<>(TryNode.class); iter.hasNext(); ) { + final Block inlinedFinally = iter.next().getInlinedFinally(labelName); + if (inlinedFinally != null) { + return inlinedFinally; + } + } + return null; + } + + /** + * Find the try node for an inlined finally block corresponding to this label. + * @param labelName label name to search for. Must not be null. + * @return the try node to which the labelled inlined finally block belongs. + */ + public TryNode getTryNodeForInlinedFinally(final String labelName) { + for (final NodeIterator iter = new NodeIterator<>(TryNode.class); iter.hasNext(); ) { + final TryNode tryNode = iter.next(); + if (tryNode.getInlinedFinally(labelName) != null) { + return tryNode; + } + } + return null; + } + + /** * Check the lexical context for a given label node by name * @param name name of the label * @return LabelNode if found, null otherwise */ - public LabelNode findLabel(final String name) { + private LabelNode findLabel(final String name) { for (final Iterator iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) { final LabelNode next = iter.next(); if (next.getLabelName().equals(name)) { @@ -592,6 +622,12 @@ return true; } else if (next == target) { return false; + } else if (next instanceof TryNode) { + for(final Block inlinedFinally: ((TryNode)next).getInlinedFinallies()) { + if (TryNode.getLabelledInlinedFinallyBlock(inlinedFinally) == target) { + return false; + } + } } } throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't"); diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextNode.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextNode.java Wed Jul 05 20:17:28 2017 +0200 @@ -54,8 +54,8 @@ static Node accept(final LexicalContextNode node, final NodeVisitor visitor) { final LexicalContext lc = visitor.getLexicalContext(); lc.push(node); - final LexicalContextNode newNode = (LexicalContextNode)node.accept(lc, visitor); - return (Node)lc.pop(newNode); + final Node newNode = node.accept(lc, visitor); + return lc.pop(newNode); } } } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/OptimisticLexicalContext.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/OptimisticLexicalContext.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/OptimisticLexicalContext.java Wed Jul 05 20:17:28 2017 +0200 @@ -115,7 +115,7 @@ } @Override - public T pop(final T node) { + public T pop(final T node) { final T popped = super.pop(node); if (isEnabled) { if(node instanceof FunctionNode) { diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Wed Jul 05 20:17:28 2017 +0200 @@ -27,7 +27,9 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -35,7 +37,7 @@ * IR representation of a TRY statement. */ @Immutable -public final class TryNode extends Statement implements JoinPredecessor { +public final class TryNode extends LexicalContextStatement implements JoinPredecessor { private static final long serialVersionUID = 1L; /** Try statements. */ @@ -47,12 +49,24 @@ /** Finally clause. */ private final Block finallyBody; + /** + * List of inlined finally blocks. The structure of every inlined finally is: + * Block(LabelNode(label, Block(finally-statements, (JumpStatement|ReturnNode)?))). + * That is, the block has a single LabelNode statement with the label and a block containing the + * statements of the inlined finally block with the jump or return statement appended (if the finally + * block was not terminal; the original jump/return is simply ignored if the finally block itself + * terminates). The reason for this somewhat strange arrangement is that we didn't want to create a + * separate class for the (label, BlockStatement pair) but rather reused the already available LabelNode. + * However, if we simply used List without wrapping the label nodes in an additional Block, + * that would've thrown off visitors relying on BlockLexicalContext -- same reason why we never use + * Statement as the type of bodies of e.g. IfNode, WhileNode etc. but rather blockify them even when they're + * single statements. + */ + private final List inlinedFinallies; + /** Exception symbol. */ private Symbol exception; - /** Catchall exception for finally expansion, where applicable */ - private Symbol finallyCatchAll; - private final LocalVariableConversion conversion; /** @@ -71,21 +85,23 @@ this.catchBlocks = catchBlocks; this.finallyBody = finallyBody; this.conversion = null; + this.inlinedFinallies = Collections.emptyList(); } - private TryNode(final TryNode tryNode, final Block body, final List catchBlocks, final Block finallyBody, final LocalVariableConversion conversion) { + private TryNode(final TryNode tryNode, final Block body, final List catchBlocks, final Block finallyBody, final LocalVariableConversion conversion, final List inlinedFinallies) { super(tryNode); this.body = body; this.catchBlocks = catchBlocks; this.finallyBody = finallyBody; this.conversion = conversion; + this.inlinedFinallies = inlinedFinallies; this.exception = tryNode.exception; } @Override public Node ensureUniqueLabels(final LexicalContext lc) { //try nodes are never in lex context - return new TryNode(this, body, catchBlocks, finallyBody, conversion); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); } @Override @@ -106,16 +122,16 @@ * @param visitor IR navigating visitor. */ @Override - public Node accept(final NodeVisitor visitor) { + public Node accept(final LexicalContext lc, NodeVisitor visitor) { if (visitor.enterTryNode(this)) { // Need to do finallybody first for termination analysis. TODO still necessary? final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor); final Block newBody = (Block)body.accept(visitor); return visitor.leaveTryNode( - setBody(newBody). - setFinallyBody(newFinallyBody). - setCatchBlocks(Node.accept(visitor, catchBlocks)). - setFinallyCatchAll(finallyCatchAll)); + setBody(lc, newBody). + setFinallyBody(lc, newFinallyBody). + setCatchBlocks(lc, Node.accept(visitor, catchBlocks)). + setInlinedFinallies(lc, Node.accept(visitor, inlinedFinallies))); } return this; @@ -136,14 +152,15 @@ /** * Reset the body of this try block + * @param lc current lexical context * @param body new body * @return new TryNode or same if unchanged */ - public TryNode setBody(final Block body) { + public TryNode setBody(final LexicalContext lc, final Block body) { if (this.body == body) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); } /** @@ -172,14 +189,15 @@ /** * Set the catch blocks of this try + * @param lc current lexical context * @param catchBlocks list of catch blocks * @return new TryNode or same if unchanged */ - public TryNode setCatchBlocks(final List catchBlocks) { + public TryNode setCatchBlocks(final LexicalContext lc, final List catchBlocks) { if (this.catchBlocks == catchBlocks) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); } /** @@ -200,27 +218,6 @@ } /** - * Get the catch all symbol for this try block - * @return catch all symbol - */ - public Symbol getFinallyCatchAll() { - return this.finallyCatchAll; - } - - /** - * If a finally block exists, the synthetic catchall needs another symbol to - * store its throwable - * @param finallyCatchAll a symbol for the finally catch all exception - * @return new TryNode or same if unchanged - * - * TODO can this still be stateful? - */ - public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) { - this.finallyCatchAll = finallyCatchAll; - return this; - } - - /** * Get the body of the finally clause for this try * @return finally body, or null if no finally */ @@ -229,15 +226,87 @@ } /** + * Get the inlined finally block with the given label name. This returns the actual finally block in the + * {@link LabelNode}, not the outer wrapper block for the {@link LabelNode}. + * @param labelName the name of the inlined finally's label + * @return the requested finally block, or null if no finally block's label matches the name. + */ + public Block getInlinedFinally(final String labelName) { + for(final Block inlinedFinally: inlinedFinallies) { + final LabelNode labelNode = getInlinedFinallyLabelNode(inlinedFinally); + if (labelNode.getLabelName().equals(labelName)) { + return labelNode.getBody(); + } + } + return null; + } + + private static LabelNode getInlinedFinallyLabelNode(final Block inlinedFinally) { + return (LabelNode)inlinedFinally.getStatements().get(0); + } + + /** + * Given an outer wrapper block for the {@link LabelNode} as returned by {@link #getInlinedFinallies()}, + * returns its actual inlined finally block. + * @param inlinedFinally the outer block for inlined finally, as returned as an element of + * {@link #getInlinedFinallies()}. + * @return the block contained in the {@link LabelNode} contained in the passed block. + */ + public static Block getLabelledInlinedFinallyBlock(final Block inlinedFinally) { + return getInlinedFinallyLabelNode(inlinedFinally).getBody(); + } + + /** + * Returns a list of inlined finally blocks. Note that this returns a list of {@link Block}s such that each one of + * them has a single {@link LabelNode}, which in turn contains the label name for the finally block and the + * actual finally block. To safely extract the actual finally block, use + * {@link #getLabelledInlinedFinallyBlock(Block)}. + * @return a list of inlined finally blocks. + */ + public List getInlinedFinallies() { + return Collections.unmodifiableList(inlinedFinallies); + } + + /** * Set the finally body of this try + * @param lc current lexical context * @param finallyBody new finally body * @return new TryNode or same if unchanged */ - public TryNode setFinallyBody(final Block finallyBody) { + public TryNode setFinallyBody(final LexicalContext lc, final Block finallyBody) { if (this.finallyBody == finallyBody) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + } + + /** + * Set the inlined finally blocks of this try. Each element should be a block with a single statement that is a + * {@link LabelNode} with a unique label, and the block within the label node should contain the actual inlined + * finally block. + * @param lc current lexical context + * @param inlinedFinallies list of inlined finally blocks + * @return new TryNode or same if unchanged + */ + public TryNode setInlinedFinallies(final LexicalContext lc, final List inlinedFinallies) { + if (this.inlinedFinallies == inlinedFinallies) { + return this; + } + assert checkInlinedFinallies(inlinedFinallies); + return Node.replaceInLexicalContext(lc, this, new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies)); + } + + private static boolean checkInlinedFinallies(final List inlinedFinallies) { + if (!inlinedFinallies.isEmpty()) { + final Set labels = new HashSet<>(); + for (final Block inlinedFinally : inlinedFinallies) { + final List stmts = inlinedFinally.getStatements(); + assert stmts.size() == 1; + final LabelNode ln = getInlinedFinallyLabelNode(inlinedFinally); + assert labels.add(ln.getLabelName()); // unique label + } + } + return true; } @Override @@ -245,7 +314,7 @@ if(this.conversion == conversion) { return this; } - return new TryNode(this, body, catchBlocks, finallyBody, conversion); + return new TryNode(this, body, catchBlocks, finallyBody, conversion, inlinedFinallies); } @Override diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/PrintVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/PrintVisitor.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed Jul 05 20:17:28 2017 +0200 @@ -391,6 +391,9 @@ finallyBody.accept(this); } + for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { + inlinedFinally.accept(this); + } return false; } diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu Jan 29 15:36:21 2015 -0800 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Wed Jul 05 20:17:28 2017 +0200 @@ -43,6 +43,7 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; +import jdk.nashorn.internal.ir.JumpToInlinedFinally; import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; @@ -473,6 +474,26 @@ } /** + * Callback for entering a JumpToInlinedFinally + * + * @param jumpToInlinedFinally the node + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + return enterDefault(jumpToInlinedFinally); + } + + /** + * Callback for leaving a JumpToInlinedFinally + * + * @param jumpToInlinedFinally the node + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { + return leaveDefault(jumpToInlinedFinally); + } + + /** * Callback for entering a LabelNode * * @param labelNode the node diff -r ac59fe6cd98f -r 4746a63313f6 nashorn/test/script/basic/JDK-8067139.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8067139.js Wed Jul 05 20:17:28 2017 +0200 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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. + * + * 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-8067139: Finally blocks inlined incorrectly + * + * @test + * @run + */ + +// Test case for JDK-8067139 +// as well as for JDK-8030198 which is a duplicate. +(function(){ + var catchCount = 0; + try { + (function (){ + try { + return; + } catch(x) { + ++catchCount; + } finally { + throw 0; + } + })(); + Assert.fail(); // must throw + } catch(e) { + Assert.assertEquals(e, 0); // threw 0 + Assert.assertEquals(catchCount, 0); // inner catch never executed + } +})(); + +// Test case for JDK-8048862 which is a duplicate of this bug +var ret = (function(o) { + try{ + with(o) { + return x; + } + } finally { + try { + return x; + } catch(e) { + Assert.assertTrue(e instanceof ReferenceError); + return 2; + } + } +})({x: 1}); +Assert.assertEquals(ret, 2); // executed the catch block + +// Test cases for JDK-8066231 that is a duplicate of this bug +// Case 1 +(function (){ try { Object; } catch(x if x >>>=0) { throw x2; } finally { } })(); +// Case 2 +try { + (function (){ try { return; } catch(x) { return x ^= 0; } finally { throw 0; } })(); + Assert.fail(); +} catch(e) { + Assert.assertEquals(e, 0); // threw 0 +} +// Case 3 +try { + (function (){ try { return; } catch(x) { return x ^= Object; } finally { throw Object; } })(); + Assert.fail(); +} catch(e) { + Assert.assertEquals(e, Object); // threw Object +} +// Case from comment +try { + (function () { try { Object } catch(x) { (x=y); return; } finally { throw Object; } })(); + Assert.fail(); +} catch(e) { + Assert.assertEquals(e, Object); // threw Object +} diff -r ac59fe6cd98f -r 4746a63313f6 test/make/TestJavaCompilation.gmk --- a/test/make/TestJavaCompilation.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/test/make/TestJavaCompilation.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -50,6 +50,9 @@ clean-jar1: $(RM) -r $(OUTPUT_DIR)/_jar1* $(OUTPUT_DIR)/jar1* +$(JAR1_MANIFEST): | $(OUTPUT_DIR)/_jar1_created + $(ECHO) "Test-Attribute: value" > $(JAR1_MANIFEST) + $(OUTPUT_DIR)/_jar1_created: $(DEPS) $(RM) -r $(JAR1_SRC_ROOT) $(RM) $(JAR1_FILE) @@ -61,7 +64,6 @@ $(TOUCH) $(JAR1_SRC_ROOT)/dir1/file1.class $(TOUCH) $(JAR1_SRC_ROOT)/dir2/file2.class $(TOUCH) $(JAR1_SRC_ROOT)/META-INF/metafile - $(ECHO) "Test-Attribute: value" > $(JAR1_MANIFEST) $(TOUCH) $@ $(eval $(call SetupArchive,BUILD_JAR1, \ @@ -77,7 +79,7 @@ $(DIFF) -r $(JAR1_SRC_ROOT)/dir1 $(JAR1_UNZIP)/dir1 $(DIFF) -r $(JAR1_SRC_ROOT)/dir2 $(JAR1_UNZIP)/dir2 $(DIFF) -r $(JAR1_SRC_ROOT)/META-INF/metafile $(JAR1_UNZIP)/META-INF/metafile - if [ "`$(GREP) 'Test-Attribute: value' $(JAR1_MANIFEST)`" = "" ]; then \ + if [ "`$(GREP) 'Test-Attribute: value' $(JAR1_UNZIP)/META-INF/MANIFEST.MF`" = "" ]; then \ $(ECHO) "Could not find Test-Attribute in manifest of $(JAR1_FILE)"; \ exit 1; \ fi @@ -88,7 +90,7 @@ # Change a source file and call this makefile again to force the jar to be # updated. -$(OUTPUT_DIR)_jar1_updated: $(OUTPUT_DIR)/_jar1_verified +$(OUTPUT_DIR)/_jar1_updated: $(OUTPUT_DIR)/_jar1_verified $(ECHO) updated > $(JAR1_SRC_ROOT)/dir1/file1.class $(ECHO) updated > $(JAR1_SRC_ROOT)/META-INF/metafile $(TOUCH) $(OUTPUT_DIR)/_jar1_created @@ -96,9 +98,26 @@ $(TOUCH) $@ update-jar1: $(OUTPUT_DIR)_jar1_updated -TEST_TARGETS += $(OUTPUT_DIR)_jar1_updated -.PHONY: clean-jar1 create-jar1 update-jar1 +# Change the manifest file and call this makefile again to force the jar +# to be updated +$(OUTPUT_DIR)/_jar1_updated_manifest: $(OUTPUT_DIR)/_jar1_updated + $(ECHO) "Test-Attribute: foobar" > $(JAR1_MANIFEST) + +$(MAKE) -f $(THIS_FILE) $(BUILD_JAR1) + $(RM) -r $(JAR1_UNZIP) + $(MKDIR) -p $(JAR1_UNZIP) + $(CD) $(JAR1_UNZIP) && $(UNZIP) $(JAR1_FILE) $(LOG_DEBUG) + if [ "`$(GREP) 'Test-Attribute: foobar' $(JAR1_UNZIP)/META-INF/MANIFEST.MF`" = "" ]; then \ + $(ECHO) "Could not find Test-Attribute in manifest of $(JAR1_FILE)"; \ + exit 1; \ + fi + $(TOUCH) $@ + +update-jar1-manifest: $(OUTPUT_DIR)/_jar1_updated_manifest + +TEST_TARGETS += $(OUTPUT_DIR)/_jar1_updated $(OUTPUT_DIR)/_jar1_updated_manifest + +.PHONY: clean-jar1 create-jar1 update-jar1 update-jar1-manifest ################################################################################ # Test: jar2 @@ -139,14 +158,14 @@ create-jar2: $(OUTPUT_DIR)/_jar2_verified TEST_TARGETS += $(OUTPUT_DIR)/_jar2_verified -$(OUTPUT_DIR)_jar2_updated: $(OUTPUT_DIR)/_jar2_verified +$(OUTPUT_DIR)/_jar2_updated: $(OUTPUT_DIR)/_jar2_verified $(ECHO) updated > $(JAR2_SRC_ROOT1)/dir1/file1.class $(TOUCH) $(OUTPUT_DIR)/_jar2_created +$(MAKE) -f $(THIS_FILE) $(OUTPUT_DIR)/_jar2_verified $(TOUCH) $@ -update-jar2: $(OUTPUT_DIR)_jar2_updated -TEST_TARGETS += $(OUTPUT_DIR)_jar2_updated +update-jar2: $(OUTPUT_DIR)/_jar2_updated +TEST_TARGETS += $(OUTPUT_DIR)/_jar2_updated .PHONY: clean-jar2 create-jar2 update-jar2 @@ -200,14 +219,14 @@ create-jar3: $(OUTPUT_DIR)/_jar3_verified TEST_TARGETS += $(OUTPUT_DIR)/_jar3_verified -$(OUTPUT_DIR)_jar3_updated: $(OUTPUT_DIR)/_jar3_verified +$(OUTPUT_DIR)/_jar3_updated: $(OUTPUT_DIR)/_jar3_verified $(ECHO) updated > $(JAR3_SRC_ROOT2)/extra-file $(TOUCH) $(OUTPUT_DIR)/_jar3_created +$(MAKE) -f $(THIS_FILE) $(OUTPUT_DIR)/_jar3_verified $(TOUCH) $@ -update-jar3: $(OUTPUT_DIR)_jar3_updated -TEST_TARGETS += $(OUTPUT_DIR)_jar3_updated +update-jar3: $(OUTPUT_DIR)/_jar3_updated +TEST_TARGETS += $(OUTPUT_DIR)/_jar3_updated .PHONY: clean-jar3 create-jar3 update-jar3 diff -r ac59fe6cd98f -r 4746a63313f6 test/make/TestMakeBase.gmk --- a/test/make/TestMakeBase.gmk Thu Jan 29 15:36:21 2015 -0800 +++ b/test/make/TestMakeBase.gmk Wed Jul 05 20:17:28 2017 +0200 @@ -33,7 +33,14 @@ $(SRC_ROOT)/make/common/MakeBase.gmk \ # +# On macosx, file system timestamps only have 1 second resultion so must add +# sleeps to properly test dependencies. +ifeq ($(OPENJDK_BUILD_OS), macosx) + SLEEP_ON_MAC := sleep 1 +endif + OUTPUT_DIR := $(TESTMAKE_OUTPUTDIR)/make-base +$(call MakeDir, $(OUTPUT_DIR)) ################################################################################ # Escape $ @@ -56,5 +63,124 @@ TEST_TARGETS += $(ESCAPE_DOLLAR_DIR)/_escape_dollar ################################################################################ +# Test Equals + +EQUALS_VALUE1 := value1$(SPACE) +EQUALS_VALUE2 := value2 + +ifneq ($(call equals, $(EQUALS_VALUE1), $(EQUALS_VALUE2)), ) + $(error The strings >$(EQUALS_VALUE1)< and >$(EQUALS_VALUE2)< are equal) +endif + +ifeq ($(call equals, $(EQUALS_VALUE1), $(EQUALS_VALUE1)), ) + $(error The strings >$(EQUALS_VALUE1)< and >$(EQUALS_VALUE1)< are not equal) +endif + +################################################################################ +# Test ShellQuote + +SHELL_QUOTE_VALUE := foo '""' "''" bar +SHELL_QUOTE_RESULT := $(shell $(ECHO) $(call ShellQuote, \ + $(SHELL_QUOTE_VALUE))) + +ifneq ($(SHELL_QUOTE_VALUE), $(SHELL_QUOTE_RESULT)) + $(error Expected: >$(SHELL_QUOTE_VALUE)< - Result: >$(SHELL_QUOTE_RESULT)<) +endif + +################################################################################ +# Test read and write to file + +READ_WRITE_FILE := $(OUTPUT_DIR)/read-write +READ_WRITE_VALUE := foo '""' "''" \t\n\\ bar +$(call WriteFile, $(READ_WRITE_VALUE), $(READ_WRITE_FILE)) +READ_WRITE_RESULT := $(call ReadFile, $(READ_WRITE_FILE)) + +ifneq ($(READ_WRITE_VALUE), $(READ_WRITE_RESULT)) + $(error Expected: >$(READ_WRITE_VALUE)< - Result: >$(READ_WRITE_RESULT)<) +endif + +################################################################################ +# Test creating dependencies on make variables + +VARDEP_DIR := $(OUTPUT_DIR)/vardep +VARDEP_SRC_FILE := $(VARDEP_DIR)/src-file +VARDEP_TARGET_FILE := $(VARDEP_DIR)/target-file +VARDEP_FLAG_FILE := $(VARDEP_DIR)/flag-file + +$(VARDEP_DIR)/src-file: + $(MKDIR) -p $(@D) + $(ECHO) "some string XXX" > $@ + +$(VARDEP_TARGET_FILE): $(VARDEP_DIR)/src-file \ + $(call DependOnVariable, VARDEP_TEST_VAR) + $(MKDIR) -p $(@D) + $(SED) -e 's/XXX/$(VARDEP_TEST_VAR)/g' $< > $@ + $(TOUCH) $(VARDEP_FLAG_FILE) + +test-vardep: + $(RM) $(VARDEP_SRC_FILE) $(VARDEP_TARGET_FILE) $(VARDEP_FLAG_FILE) + # + # Simply create the target file and verify that it has the correct value + # + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR=value1 $(VARDEP_TARGET_FILE) + $(PRINTF) "Expecting value1: %s\n" "`$(CAT) $(VARDEP_DIR)/target-file`" + test "some string value1" = "`$(CAT) $(VARDEP_DIR)/target-file`" + test -e $(VARDEP_FLAG_FILE) + # + # Make the target file again and verify that the value is updated with + # the new value + # + $(SLEEP_ON_MAC) + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR=value2 $(VARDEP_TARGET_FILE) + $(PRINTF) "Expecting value2: %s\n" "`$(CAT) $(VARDEP_DIR)/target-file`" + test "some string value2" = "`$(CAT) $(VARDEP_DIR)/target-file`" + test -e $(VARDEP_FLAG_FILE) + # + # Make the target again with the same value and verify that the recipe + # was never run by checking that the flag file was not recreated + # + $(SLEEP_ON_MAC) + $(RM) $(VARDEP_FLAG_FILE) + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR=value2 $(VARDEP_TARGET_FILE) + $(PRINTF) "Expecting value2: %s\n" "`$(CAT) $(VARDEP_DIR)/target-file`" + test "some string value2" = "`$(CAT) $(VARDEP_DIR)/target-file`" + test ! -e $(VARDEP_FLAG_FILE) + # + # Test running with spaces at the end and the middle of the value + # and verify that the file isn't rewritten the second time + # + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR="value3 foo " $(VARDEP_TARGET_FILE) + $(RM) $(VARDEP_FLAG_FILE) + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR="value3 foo" $(VARDEP_TARGET_FILE) + test ! -e $(VARDEP_FLAG_FILE) + $(MAKE) -f $(THIS_FILE) VARDEP_TEST_VAR=" value3 foo" $(VARDEP_TARGET_FILE) + test ! -e $(VARDEP_FLAG_FILE) + +# Test specifying a specific value file to store variable in +VARDEP_VALUE_FILE := $(VARDEP_DIR)/value-file +VARDEP_TEST_VAR2 := value3 + +VARDEP_RETURN_VALUE := $(call DependOnVariable, VARDEP_TEST_VAR2, $(VARDEP_VALUE_FILE)) +ifneq ($(VARDEP_VALUE_FILE), $(VARDEP_RETURN_VALUE)) + $(error Expected: $(VARDEP_VALUE_FILE) - DependOnVariable: $(VARDEP_RETURN_VALUE)) +endif +VARDEP_FILE_CONTENTS := $(shell $(CAT) $(VARDEP_VALUE_FILE)) +ifneq ($(VARDEP_TEST_VAR2), $(VARDEP_FILE_CONTENTS)) + $(error Expected: $(VARDEP_TEST_VAR2) - DependOnVariable file contained: \ + $(VARDEP_FILE_CONTENTS)) +endif + +# Test with a variable value containing some problematic characters +VARDEP_TEST_VAR3 := foo '""' "''" bar +VARDEP_VALUE_FILE := $(call DependOnVariable, VARDEP_TEST_VAR3) +VARDEP_FILE_CONTENTS := $(shell $(CAT) $(VARDEP_VALUE_FILE)) +ifneq ($(VARDEP_TEST_VAR3), $(VARDEP_FILE_CONTENTS)) + $(error Expected: >$(VARDEP_TEST_VAR3)< - DependOnVariable file contained: \ + >$(VARDEP_FILE_CONTENTS)<) +endif + +TEST_TARGETS += test-vardep + +################################################################################ all: $(TEST_TARGETS)
  • * If the expression contains a variable reference, its value will be found through the {@link XPathVariableResolver}. * An {@link XPathExpressionException} is raised if the variable resolver is undefined or - * the resolver returns null for the variable. + * the resolver returns {@code null} for the variable. * The value of a variable must be immutable through the course of any single evaluation.

    *
    * If the expression contains a function reference, the function will be found through the {@link XPathFunctionResolver}. * An {@link XPathExpressionException} is raised if the function resolver is undefined or - * the function resolver returns null for the function.

    + * the function resolver returns {@code null} for the function.

    *