diff --git a/.hgtags b/.hgtags index 462b081a399..9ac115a08b5 100644 --- a/.hgtags +++ b/.hgtags @@ -345,3 +345,4 @@ f9bcdce2df26678c3fe468130b535c0342c69b89 jdk-9+99 4379223f8806626852c46c52d4e7a27a584b406e jdk-9+100 80f67512daa15cf37b4825c1c62a675d524d7c49 jdk-9+101 2dc4c11fe48831854916d53c3913bdb7d49023ea jdk-9+102 +4a652e4ca9523422149958673033e0ac740d5e1e jdk-9+103 diff --git a/.hgtags-top-repo b/.hgtags-top-repo index ae78a92b7f7..9de50aa4824 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -345,3 +345,4 @@ cf1dc4c035fb84693d4ae5ad818785cb4d1465d1 jdk9-b90 c1f30ac14db0eaff398429c04cd9fab92e1b4b2a jdk-9+100 c4d72a1620835b5d657b7b6792c2879367d0154f jdk-9+101 6406ecf5d39482623225bb1b3098c2cac6f7d450 jdk-9+102 +47d6462e514b2097663305a57d9c844c15d5b609 jdk-9+103 diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index c295b3bf838..0fdc4605014 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -787,8 +787,6 @@ CPP ac_ct_CXX CXXFLAGS CXX -ac_ct_PROPER_COMPILER_CXX -PROPER_COMPILER_CXX TOOLCHAIN_PATH_CXX POTENTIAL_CXX OBJEXT @@ -798,8 +796,6 @@ CPPFLAGS LDFLAGS CFLAGS CC -ac_ct_PROPER_COMPILER_CC -PROPER_COMPILER_CC TOOLCHAIN_PATH_CC POTENTIAL_CC TOOLCHAIN_VERSION @@ -4119,16 +4115,6 @@ pkgadd_help() { - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false - # Fastdebug builds with this setting will essentially be slowdebug - # in hotspot. - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false # # Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -4849,7 +4835,7 @@ VS_SDK_PLATFORM_NAME_2013= #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1453385294 +DATE_WHEN_GENERATED=1454146111 ############################################################################### # @@ -32022,12 +32008,10 @@ $as_echo "$as_me: Rewriting CC to \"$new_complete\"" >&6;} fi TEST_COMPILER="$CC" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5 $as_echo_n "checking resolved symbolic links for CC... " >&6; } + SYMLINK_ORIGINAL="$TEST_COMPILER" if test "x$OPENJDK_BUILD_OS" != xwindows; then # Follow a chain of symbolic links. Use readlink @@ -32046,13 +32030,13 @@ $as_echo_n "checking resolved symbolic links for CC... " >&6; } fi if test "x$READLINK" != x; then - TEST_COMPILER=`$READLINK -f $TEST_COMPILER` + SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL` else # Save the current directory for restoring afterwards STARTDIR=$PWD COUNTER=0 - sym_link_dir=`$DIRNAME $TEST_COMPILER` - sym_link_file=`$BASENAME $TEST_COMPILER` + sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL` + sym_link_file=`$BASENAME $SYMLINK_ORIGINAL` cd $sym_link_dir # Use -P flag to resolve symlinks in directories. cd `$THEPWDCMD -P` @@ -32072,474 +32056,25 @@ $as_echo_n "checking resolved symbolic links for CC... " >&6; } let COUNTER=COUNTER+1 done cd $STARTDIR - TEST_COMPILER=$sym_link_dir/$sym_link_file + SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file fi fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5 -$as_echo "$TEST_COMPILER" >&6; } - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CC is disguised ccache" >&5 -$as_echo_n "checking if CC is disguised ccache... " >&6; } - - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5 -$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; } - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. - - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME $CC` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - if test -n "$ac_tool_prefix"; then - for ac_prog in $TOOLCHAIN_CC_BINARY - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PROPER_COMPILER_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PROPER_COMPILER_CC"; then - ac_cv_prog_PROPER_COMPILER_CC="$PROPER_COMPILER_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PROPER_COMPILER_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PROPER_COMPILER_CC=$ac_cv_prog_PROPER_COMPILER_CC -if test -n "$PROPER_COMPILER_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5 -$as_echo "$PROPER_COMPILER_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$PROPER_COMPILER_CC" && break - done -fi -if test -z "$PROPER_COMPILER_CC"; then - ac_ct_PROPER_COMPILER_CC=$PROPER_COMPILER_CC - for ac_prog in $TOOLCHAIN_CC_BINARY -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_PROPER_COMPILER_CC"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_ct_PROPER_COMPILER_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_PROPER_COMPILER_CC=$ac_cv_prog_ac_ct_PROPER_COMPILER_CC -if test -n "$ac_ct_PROPER_COMPILER_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CC" >&5 -$as_echo "$ac_ct_PROPER_COMPILER_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_PROPER_COMPILER_CC" && break -done - - if test "x$ac_ct_PROPER_COMPILER_CC" = x; then - PROPER_COMPILER_CC="" + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5 +$as_echo "no symlink" >&6; } else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PROPER_COMPILER_CC=$ac_ct_PROPER_COMPILER_CC - fi -fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5 +$as_echo "$SYMLINK_ORIGINAL" >&6; } - - # Only process if variable expands to non-empty - - if test "x$PROPER_COMPILER_CC" != x; then - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path=`$CYGPATH -u "$path"` - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path=`$CYGPATH -u "$path"` - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5 +$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;} + as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5 fi - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Cygwin tries to hide some aspects of the Windows file system, such that binaries are - # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered - # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then - # "foo.exe" is OK but "foo" is an error. - # - # This test is therefore slightly more accurate than "test -f" to check for file presence. - # It is also a way to make sure we got the proper file name for the real test later on. - test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` - if test "x$test_shortpath" = x; then - # Short path failed, file does not exist as specified. - # Try adding .exe or .cmd - if test -f "${new_path}.exe"; then - input_to_shortpath="${new_path}.exe" - elif test -f "${new_path}.cmd"; then - input_to_shortpath="${new_path}.cmd" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5 -$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;} - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - else - input_to_shortpath="$new_path" - fi - - # Call helper function which possibly converts this using DOS-style short mode. - # If so, the updated path is stored in $new_path. - new_path="$input_to_shortpath" - - input_path="$input_to_shortpath" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - shortmode_path=`$CYGPATH -s -m -a "$input_path"` - path_after_shortmode=`$CYGPATH -u "$shortmode_path"` - if test "x$path_after_shortmode" != "x$input_to_shortpath"; then - # Going to short mode and back again did indeed matter. Since short mode is - # case insensitive, let's make it lowercase to improve readability. - shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Now convert it back to Unix-style (cygpath) - input_path=`$CYGPATH -u "$shortmode_path"` - new_path="$input_path" - fi - fi - - test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` - if test "x$test_cygdrive_prefix" = x; then - # As a simple fix, exclude /usr/bin since it's not a real path. - if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then - # The path is in a Cygwin special directory (e.g. /home). We need this converted to - # a path prefixed by /cygdrive for fixpath to work. - new_path="$CYGWIN_ROOT_PATH$input_path" - fi - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in MSYS causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - fi - - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Now new_path has a complete unix path to the binary - if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then - # Keep paths in /bin as-is, but remove trailing .exe if any - new_path="${new_path/%.exe/}" - # Do not save /bin paths to all_fixpath_prefixes! - else - # Not in mixed or Windows style, start by that. - new_path=`cmd //c echo $new_path` - - input_path="$new_path" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - fi - - # Output is in $new_path - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - # Save the first 10 bytes of this path to the storage, so fixpath can work. - all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") - fi - - else - # We're on a unix platform. Hooray! :) - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CC" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Cannot rely on the command "which" here since it doesn't always work. - is_absolute_path=`$ECHO "$path" | $GREP ^/` - if test -z "$is_absolute_path"; then - # Path to executable is not absolute. Find it. - IFS_save="$IFS" - IFS=: - for p in $PATH; do - if test -f "$p/$path" && test -x "$p/$path"; then - new_path="$p/$path" - break - fi - done - IFS="$IFS_save" - else - # This is an absolute path, we can use it without further modifications. - new_path="$path" - fi - - if test "x$new_path" = x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5 -$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5 - fi - fi - - # Now join together the path and the arguments once again - if test "x$arguments" != xEOL; then - new_complete="$new_path ${arguments% *}" - else - new_complete="$new_path" - fi - - if test "x$complete" != "x$new_complete"; then - PROPER_COMPILER_CC="$new_complete" - { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&5 -$as_echo "$as_me: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&6;} - fi - fi - - PATH="$RETRY_COMPILER_SAVED_PATH" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CC" >&5 -$as_echo_n "checking for resolved symbolic links for CC... " >&6; } - - if test "x$OPENJDK_BUILD_OS" != xwindows; then - # Follow a chain of symbolic links. Use readlink - # where it exists, else fall back to horribly - # complicated shell code. - if test "x$READLINK_TESTED" != yes; then - # On MacOSX there is a readlink tool with a different - # purpose than the GNU readlink tool. Check the found readlink. - ISGNU=`$READLINK --version 2>&1 | $GREP GNU` - if test "x$ISGNU" = x; then - # A readlink that we do not know how to use. - # Are there other non-GNU readlinks out there? - READLINK_TESTED=yes - READLINK= - fi - fi - - if test "x$READLINK" != x; then - PROPER_COMPILER_CC=`$READLINK -f $PROPER_COMPILER_CC` - else - # Save the current directory for restoring afterwards - STARTDIR=$PWD - COUNTER=0 - sym_link_dir=`$DIRNAME $PROPER_COMPILER_CC` - sym_link_file=`$BASENAME $PROPER_COMPILER_CC` - cd $sym_link_dir - # Use -P flag to resolve symlinks in directories. - cd `$THEPWDCMD -P` - sym_link_dir=`$THEPWDCMD -P` - # Resolve file symlinks - while test $COUNTER -lt 20; do - ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'` - if test "x$ISLINK" == x; then - # This is not a symbolic link! We are done! - break - fi - # Again resolve directory symlinks since the target of the just found - # link could be in a different directory - cd `$DIRNAME $ISLINK` - sym_link_dir=`$THEPWDCMD -P` - sym_link_file=`$BASENAME $ISLINK` - let COUNTER=COUNTER+1 - done - cd $STARTDIR - PROPER_COMPILER_CC=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5 -$as_echo "$PROPER_COMPILER_CC" >&6; } - CC="$PROPER_COMPILER_CC" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CC" >&5 -$as_echo "no, keeping CC" >&6; } fi @@ -33770,12 +33305,10 @@ $as_echo "$as_me: Rewriting CXX to \"$new_complete\"" >&6;} fi TEST_COMPILER="$CXX" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5 $as_echo_n "checking resolved symbolic links for CXX... " >&6; } + SYMLINK_ORIGINAL="$TEST_COMPILER" if test "x$OPENJDK_BUILD_OS" != xwindows; then # Follow a chain of symbolic links. Use readlink @@ -33794,13 +33327,13 @@ $as_echo_n "checking resolved symbolic links for CXX... " >&6; } fi if test "x$READLINK" != x; then - TEST_COMPILER=`$READLINK -f $TEST_COMPILER` + SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL` else # Save the current directory for restoring afterwards STARTDIR=$PWD COUNTER=0 - sym_link_dir=`$DIRNAME $TEST_COMPILER` - sym_link_file=`$BASENAME $TEST_COMPILER` + sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL` + sym_link_file=`$BASENAME $SYMLINK_ORIGINAL` cd $sym_link_dir # Use -P flag to resolve symlinks in directories. cd `$THEPWDCMD -P` @@ -33820,474 +33353,25 @@ $as_echo_n "checking resolved symbolic links for CXX... " >&6; } let COUNTER=COUNTER+1 done cd $STARTDIR - TEST_COMPILER=$sym_link_dir/$sym_link_file + SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file fi fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5 -$as_echo "$TEST_COMPILER" >&6; } - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CXX is disguised ccache" >&5 -$as_echo_n "checking if CXX is disguised ccache... " >&6; } - - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5 -$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; } - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. - - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME $CXX` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - if test -n "$ac_tool_prefix"; then - for ac_prog in $TOOLCHAIN_CXX_BINARY - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PROPER_COMPILER_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PROPER_COMPILER_CXX"; then - ac_cv_prog_PROPER_COMPILER_CXX="$PROPER_COMPILER_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PROPER_COMPILER_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PROPER_COMPILER_CXX=$ac_cv_prog_PROPER_COMPILER_CXX -if test -n "$PROPER_COMPILER_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5 -$as_echo "$PROPER_COMPILER_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$PROPER_COMPILER_CXX" && break - done -fi -if test -z "$PROPER_COMPILER_CXX"; then - ac_ct_PROPER_COMPILER_CXX=$PROPER_COMPILER_CXX - for ac_prog in $TOOLCHAIN_CXX_BINARY -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_PROPER_COMPILER_CXX"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_ct_PROPER_COMPILER_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_PROPER_COMPILER_CXX=$ac_cv_prog_ac_ct_PROPER_COMPILER_CXX -if test -n "$ac_ct_PROPER_COMPILER_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CXX" >&5 -$as_echo "$ac_ct_PROPER_COMPILER_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_PROPER_COMPILER_CXX" && break -done - - if test "x$ac_ct_PROPER_COMPILER_CXX" = x; then - PROPER_COMPILER_CXX="" + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5 +$as_echo "no symlink" >&6; } else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PROPER_COMPILER_CXX=$ac_ct_PROPER_COMPILER_CXX - fi -fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5 +$as_echo "$SYMLINK_ORIGINAL" >&6; } - - # Only process if variable expands to non-empty - - if test "x$PROPER_COMPILER_CXX" != x; then - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path=`$CYGPATH -u "$path"` - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` - fi - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path=`$CYGPATH -u "$path"` - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in cygwin causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path=`$CYGPATH -u "$path"` + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5 +$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;} + as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5 fi - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Cygwin tries to hide some aspects of the Windows file system, such that binaries are - # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered - # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then - # "foo.exe" is OK but "foo" is an error. - # - # This test is therefore slightly more accurate than "test -f" to check for file presence. - # It is also a way to make sure we got the proper file name for the real test later on. - test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` - if test "x$test_shortpath" = x; then - # Short path failed, file does not exist as specified. - # Try adding .exe or .cmd - if test -f "${new_path}.exe"; then - input_to_shortpath="${new_path}.exe" - elif test -f "${new_path}.cmd"; then - input_to_shortpath="${new_path}.cmd" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5 -$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;} - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - else - input_to_shortpath="$new_path" - fi - - # Call helper function which possibly converts this using DOS-style short mode. - # If so, the updated path is stored in $new_path. - new_path="$input_to_shortpath" - - input_path="$input_to_shortpath" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - shortmode_path=`$CYGPATH -s -m -a "$input_path"` - path_after_shortmode=`$CYGPATH -u "$shortmode_path"` - if test "x$path_after_shortmode" != "x$input_to_shortpath"; then - # Going to short mode and back again did indeed matter. Since short mode is - # case insensitive, let's make it lowercase to improve readability. - shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Now convert it back to Unix-style (cygpath) - input_path=`$CYGPATH -u "$shortmode_path"` - new_path="$input_path" - fi - fi - - test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` - if test "x$test_cygdrive_prefix" = x; then - # As a simple fix, exclude /usr/bin since it's not a real path. - if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then - # The path is in a Cygwin special directory (e.g. /home). We need this converted to - # a path prefixed by /cygdrive for fixpath to work. - new_path="$CYGWIN_ROOT_PATH$input_path" - fi - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Input might be given as Windows format, start by converting to - # unix format. - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - # Now try to locate executable using which - new_path=`$WHICH "$new_path" 2> /dev/null` - - if test "x$new_path" = x; then - # Oops. Which didn't find the executable. - # The splitting of arguments from the executable at a space might have been incorrect, - # since paths with space are more likely in Windows. Give it another try with the whole - # argument. - path="$complete" - arguments="EOL" - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - - new_path=`$WHICH "$new_path" 2> /dev/null` - # bat and cmd files are not always considered executable in MSYS causing which - # to not find them - if test "x$new_path" = x \ - && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ - && test "x`$LS \"$path\" 2>/dev/null`" != x; then - new_path="$path" - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - fi - - if test "x$new_path" = x; then - # It's still not found. Now this is an unrecoverable error. - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5 -$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Now new_path has a complete unix path to the binary - if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then - # Keep paths in /bin as-is, but remove trailing .exe if any - new_path="${new_path/%.exe/}" - # Do not save /bin paths to all_fixpath_prefixes! - else - # Not in mixed or Windows style, start by that. - new_path=`cmd //c echo $new_path` - - input_path="$new_path" - # Check if we need to convert this using DOS-style short mode. If the path - # contains just simple characters, use it. Otherwise (spaces, weird characters), - # take no chances and rewrite it. - # Note: m4 eats our [], so we need to use [ and ] instead. - has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` - if test "x$has_forbidden_chars" != x; then - # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) - new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - fi - - # Output is in $new_path - - windows_path="$new_path" - if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then - unix_path=`$CYGPATH -u "$windows_path"` - new_path="$unix_path" - elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then - unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` - new_path="$unix_path" - fi - - # remove trailing .exe if any - new_path="${new_path/%.exe/}" - - # Save the first 10 bytes of this path to the storage, so fixpath can work. - all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") - fi - - else - # We're on a unix platform. Hooray! :) - # First separate the path from the arguments. This will split at the first - # space. - complete="$PROPER_COMPILER_CXX" - path="${complete%% *}" - tmp="$complete EOL" - arguments="${tmp#* }" - - # Cannot rely on the command "which" here since it doesn't always work. - is_absolute_path=`$ECHO "$path" | $GREP ^/` - if test -z "$is_absolute_path"; then - # Path to executable is not absolute. Find it. - IFS_save="$IFS" - IFS=: - for p in $PATH; do - if test -f "$p/$path" && test -x "$p/$path"; then - new_path="$p/$path" - break - fi - done - IFS="$IFS_save" - else - # This is an absolute path, we can use it without further modifications. - new_path="$path" - fi - - if test "x$new_path" = x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5 -$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;} - has_space=`$ECHO "$complete" | $GREP " "` - if test "x$has_space" != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5 -$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;} - fi - as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5 - fi - fi - - # Now join together the path and the arguments once again - if test "x$arguments" != xEOL; then - new_complete="$new_path ${arguments% *}" - else - new_complete="$new_path" - fi - - if test "x$complete" != "x$new_complete"; then - PROPER_COMPILER_CXX="$new_complete" - { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&5 -$as_echo "$as_me: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&6;} - fi - fi - - PATH="$RETRY_COMPILER_SAVED_PATH" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CXX" >&5 -$as_echo_n "checking for resolved symbolic links for CXX... " >&6; } - - if test "x$OPENJDK_BUILD_OS" != xwindows; then - # Follow a chain of symbolic links. Use readlink - # where it exists, else fall back to horribly - # complicated shell code. - if test "x$READLINK_TESTED" != yes; then - # On MacOSX there is a readlink tool with a different - # purpose than the GNU readlink tool. Check the found readlink. - ISGNU=`$READLINK --version 2>&1 | $GREP GNU` - if test "x$ISGNU" = x; then - # A readlink that we do not know how to use. - # Are there other non-GNU readlinks out there? - READLINK_TESTED=yes - READLINK= - fi - fi - - if test "x$READLINK" != x; then - PROPER_COMPILER_CXX=`$READLINK -f $PROPER_COMPILER_CXX` - else - # Save the current directory for restoring afterwards - STARTDIR=$PWD - COUNTER=0 - sym_link_dir=`$DIRNAME $PROPER_COMPILER_CXX` - sym_link_file=`$BASENAME $PROPER_COMPILER_CXX` - cd $sym_link_dir - # Use -P flag to resolve symlinks in directories. - cd `$THEPWDCMD -P` - sym_link_dir=`$THEPWDCMD -P` - # Resolve file symlinks - while test $COUNTER -lt 20; do - ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'` - if test "x$ISLINK" == x; then - # This is not a symbolic link! We are done! - break - fi - # Again resolve directory symlinks since the target of the just found - # link could be in a different directory - cd `$DIRNAME $ISLINK` - sym_link_dir=`$THEPWDCMD -P` - sym_link_file=`$BASENAME $ISLINK` - let COUNTER=COUNTER+1 - done - cd $STARTDIR - PROPER_COMPILER_CXX=$sym_link_dir/$sym_link_file - fi - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5 -$as_echo "$PROPER_COMPILER_CXX" >&6; } - CXX="$PROPER_COMPILER_CXX" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CXX" >&5 -$as_echo "no, keeping CXX" >&6; } fi @@ -58558,6 +57642,10 @@ fi fi done + # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not + # always properly detect -ltinfo + LLVM_LIBS="${LLVM_LIBS} -ltinfo" + diff --git a/common/autoconf/hotspot.m4 b/common/autoconf/hotspot.m4 index e6849af3af4..ec357f99572 100644 --- a/common/autoconf/hotspot.m4 +++ b/common/autoconf/hotspot.m4 @@ -266,14 +266,3 @@ AC_DEFUN_ONCE([HOTSPOT_SETUP_BUILD_TWEAKS], HOTSPOT_MAKE_ARGS="$HOTSPOT_TARGET" AC_SUBST(HOTSPOT_MAKE_ARGS) ]) - - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false - # Fastdebug builds with this setting will essentially be slowdebug - # in hotspot. - # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles - # will basically do slowdebug builds when DEBUG_BINARIES is set for - # fastdebug builds - DEBUG_BINARIES=false \ No newline at end of file diff --git a/common/autoconf/libraries.m4 b/common/autoconf/libraries.m4 index cb3cddd7f92..7bae405555e 100644 --- a/common/autoconf/libraries.m4 +++ b/common/autoconf/libraries.m4 @@ -144,6 +144,10 @@ AC_DEFUN_ONCE([LIB_SETUP_LLVM], fi done + # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not + # always properly detect -ltinfo + LLVM_LIBS="${LLVM_LIBS} -ltinfo" + AC_SUBST(LLVM_CFLAGS) AC_SUBST(LLVM_LDFLAGS) AC_SUBST(LLVM_LIBS) diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in index e2a73d20ccf..a78b30ba5ad 100644 --- a/common/autoconf/spec.gmk.in +++ b/common/autoconf/spec.gmk.in @@ -500,7 +500,7 @@ JAVAC_FLAGS?=@JAVAC_FLAGS@ INTERIM_LANGTOOLS_JAR = $(BUILDTOOLS_OUTPUTDIR)/interim_langtools.jar INTERIM_LANGTOOLS_ARGS = "-Xbootclasspath/p:$(INTERIM_LANGTOOLS_JAR)" -cp $(INTERIM_LANGTOOLS_JAR) NEW_JAVAC = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javac.Main -NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javadoc.Main +NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) jdk.javadoc.internal.tool.Main # Base flags for RC # Guarding this against resetting value. Legacy make files include spec multiple diff --git a/common/autoconf/toolchain.m4 b/common/autoconf/toolchain.m4 index 4254d86a4fb..5741294f309 100644 --- a/common/autoconf/toolchain.m4 +++ b/common/autoconf/toolchain.m4 @@ -433,39 +433,22 @@ AC_DEFUN([TOOLCHAIN_FIND_COMPILER], # Now we have a compiler binary in $1. Make sure it's okay. BASIC_FIXUP_EXECUTABLE($1) TEST_COMPILER="[$]$1" - # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links - # to 'xlc' but it is crucial that we invoke the compiler with the right name! - if test "x$OPENJDK_BUILD_OS" != xaix; then - # FIXME: This test should not be needed anymore; we don't do that for any platform. - AC_MSG_CHECKING([resolved symbolic links for $1]) - BASIC_REMOVE_SYMBOLIC_LINKS(TEST_COMPILER) - AC_MSG_RESULT([$TEST_COMPILER]) - fi - AC_MSG_CHECKING([if $1 is disguised ccache]) - COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"` - if test "x$COMPILER_BASENAME" = "xccache"; then - AC_MSG_RESULT([yes, trying to find proper $COMPILER_NAME compiler]) - # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache. - # We want to control ccache invocation ourselves, so ignore this cc and try - # searching again. - - # Remove the path to the fake ccache cc from the PATH - RETRY_COMPILER_SAVED_PATH="$PATH" - COMPILER_DIRNAME=`$DIRNAME [$]$1` - PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`" - - # Try again looking for our compiler - AC_CHECK_TOOLS(PROPER_COMPILER_$1, $3) - BASIC_FIXUP_EXECUTABLE(PROPER_COMPILER_$1) - PATH="$RETRY_COMPILER_SAVED_PATH" - - AC_MSG_CHECKING([for resolved symbolic links for $1]) - BASIC_REMOVE_SYMBOLIC_LINKS(PROPER_COMPILER_$1) - AC_MSG_RESULT([$PROPER_COMPILER_$1]) - $1="$PROPER_COMPILER_$1" + AC_MSG_CHECKING([resolved symbolic links for $1]) + SYMLINK_ORIGINAL="$TEST_COMPILER" + BASIC_REMOVE_SYMBOLIC_LINKS(SYMLINK_ORIGINAL) + if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then + AC_MSG_RESULT([no symlink]) else - AC_MSG_RESULT([no, keeping $1]) + AC_MSG_RESULT([$SYMLINK_ORIGINAL]) + + # We can't handle ccache by gcc wrappers, since we need to know if we're + # using ccache. Instead ccache usage must be controlled by a configure option. + COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"` + if test "x$COMPILER_BASENAME" = "xccache"; then + AC_MSG_NOTICE([Please use --enable-ccache instead of providing a wrapped compiler.]) + AC_MSG_ERROR([$TEST_COMPILER is a symbolic link to ccache. This is not supported.]) + fi fi TOOLCHAIN_CHECK_COMPILER_VERSION([$1], [$COMPILER_NAME]) diff --git a/common/bin/compare.sh b/common/bin/compare.sh index 9e680f08b0a..b417e8ed819 100644 --- a/common/bin/compare.sh +++ b/common/bin/compare.sh @@ -306,7 +306,7 @@ compare_general_files() { ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \ ! -name "*.obj" ! -name "*.o" ! -name "JavaControlPanelHelper" \ ! -name "JavaUpdater" ! -name "JavaWSApplicationStub" \ - ! -name "jspawnhelper" \ + ! -name "jspawnhelper" ! -name "*.a" \ | $GREP -v "./bin/" | $SORT | $FILTER) echo Other files with binary differences... @@ -939,7 +939,7 @@ compare_all_libs() { WORK_DIR=$3 LIBS=$(cd $THIS_DIR && $FIND . -type f \( -name 'lib*.so' -o -name '*.dylib' \ - -o -name '*.dll' -o -name '*.obj' -o -name '*.o' \ + -o -name '*.dll' -o -name '*.obj' -o -name '*.o' -o -name '*.a' \ -o -name '*.cpl' \) | $SORT | $FILTER) if [ -n "$LIBS" ]; then diff --git a/corba/.hgtags b/corba/.hgtags index d486a0a69bb..ba693c7dd28 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -345,3 +345,4 @@ ea285530245cf4e0edf0479121a41347d3030eba jdk-9+98 791d0d3ac0138faeb6110bd840a4545bc1950df2 jdk-9+100 30dfb3bd3d06b4bb80a087babc0d1841edba187b jdk-9+101 9c4662334d933d299928d1f599d02ff50777cbf8 jdk-9+102 +0680fb7dae4da1ee6cf783c4b74184e3e08d3179 jdk-9+103 diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 4731b6143c4..fbc7ac926fb 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -505,3 +505,4 @@ f008e8cc10d5b3212fb22d58c96fa01d38654f19 jdk-9+99 bdb0acafc63c42e84d9d8195bf2e2b25ee9c3306 jdk-9+100 9f45d3d57d6948cf526fbc2e2891a9a74ac6941a jdk-9+101 d5239fc1b69749ae50793c61b899fcdacf3df857 jdk-9+102 +c5f55130b1b69510d9a6f4a3105b58e21cd7ffe1 jdk-9+103 diff --git a/hotspot/.mx.jvmci/mx_jvmci.py b/hotspot/.mx.jvmci/mx_jvmci.py index d1069e6284c..1db4fbb5dcd 100644 --- a/hotspot/.mx.jvmci/mx_jvmci.py +++ b/hotspot/.mx.jvmci/mx_jvmci.py @@ -40,6 +40,8 @@ from mx_unittest import unittest _suite = mx.suite('jvmci') +JVMCI_VERSION = 9 + """ Top level directory of the JDK source workspace. """ @@ -153,11 +155,17 @@ class JvmciJDKDeployedDist(object): def deploy(self, jdkDir): mx.nyi('deploy', self) + def post_parse_cmd_line(self): + self.set_archiveparticipant() + + def set_archiveparticipant(self): + dist = self.dist() + dist.set_archiveparticipant(JVMCIArchiveParticipant(dist)) + class ExtJDKDeployedDist(JvmciJDKDeployedDist): def __init__(self, name): JvmciJDKDeployedDist.__init__(self, name) - """ The monolithic JVMCI distribution is deployed through use of -Xbootclasspath/p so that it's not necessary to run JDK make after editing JVMCI sources. @@ -186,7 +194,7 @@ To build hotspot and import it into the JDK: "mx make hotspot import-hotspot" # JDK9 must be bootstrapped with a JDK8 compliance = mx.JavaCompliance('8') jdk8 = mx.get_jdk(compliance.exactMatch, versionDescription=compliance.value) - cmd = ['sh', 'configure', '--with-debug-level=' + _vm.debugLevel, '--disable-debug-symbols', '--disable-precompiled-headers', + cmd = ['sh', 'configure', '--with-debug-level=' + _vm.debugLevel, '--with-native-debug-symbols=none', '--disable-precompiled-headers', '--with-jvm-variants=' + _vm.jvmVariant, '--disable-warnings-as-errors', '--with-boot-jdk=' + jdk8.home] mx.run(cmd, cwd=_jdkSourceRoot) cmd = [mx.gmake_cmd(), 'CONF=' + _vm.debugLevel] @@ -205,7 +213,15 @@ To build hotspot and import it into the JDK: "mx make hotspot import-hotspot" mx.run(cmd, cwd=_jdkSourceRoot) if 'images' in cmd: - _create_jdk_bundle(jdkBuildDir) + jdkImageDir = join(jdkBuildDir, 'images', 'jdk') + + # The OpenJDK build creates an empty cacerts file so copy one from + # the default JDK (which is assumed to be an OracleJDK) + srcCerts = join(mx.get_jdk(tag='default').home, 'jre', 'lib', 'security', 'cacerts') + dstCerts = join(jdkImageDir, 'lib', 'security', 'cacerts') + shutil.copyfile(srcCerts, dstCerts) + + _create_jdk_bundle(jdkBuildDir, _vm.debugLevel, jdkImageDir) def _get_jdk_bundle_arches(): """ @@ -220,15 +236,14 @@ def _get_jdk_bundle_arches(): return ['sparcv9'] mx.abort('Unsupported JDK bundle arch: ' + cpu) -def _create_jdk_bundle(jdkBuildDir): +def _create_jdk_bundle(jdkBuildDir, debugLevel, jdkImageDir): """ Creates a tar.gz JDK archive, an accompanying tar.gz.sha1 file with its SHA1 signature plus symlinks to the archive for non-canonical architecture names. """ - jdkImageDir = join(jdkBuildDir, 'images', 'jdk') arches = _get_jdk_bundle_arches() - jdkTgzPath = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arches[0])) + jdkTgzPath = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}-{}.tar.gz'.format(debugLevel, _get_openjdk_os(), arches[0])) with mx.Archiver(jdkTgzPath, kind='tgz') as arc: mx.log('Creating ' + jdkTgzPath) for root, _, filenames in os.walk(jdkImageDir): @@ -236,10 +251,6 @@ def _create_jdk_bundle(jdkBuildDir): f = join(root, name) arcname = 'jdk1.9.0/' + os.path.relpath(f, jdkImageDir) arc.zf.add(name=f, arcname=arcname, recursive=False) - # The OpenJDK build creates an empty cacerts file so grab one from - # the default JDK which is assumed to be an OracleJDK - cacerts = join(mx.get_jdk(tag='default').home, 'jre', 'lib', 'security', 'cacerts') - arc.zf.add(name=cacerts, arcname='jdk1.9.0/lib/security/cacerts') with open(jdkTgzPath + '.sha1', 'w') as fp: mx.log('Creating ' + jdkTgzPath + '.sha1') @@ -252,7 +263,7 @@ def _create_jdk_bundle(jdkBuildDir): os.symlink(source, link_name) for arch in arches[1:]: - link_name = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arch)) + link_name = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}-{}.tar.gz'.format(debugLevel, _get_openjdk_os(), arch)) jdkTgzName = os.path.basename(jdkTgzPath) _create_link(jdkTgzName, link_name) _create_link(jdkTgzName + '.sha1', link_name + '.sha1') @@ -668,15 +679,10 @@ class JVMCIArchiveParticipant: def __opened__(self, arc, srcArc, services): self.services = services + self.jvmciServices = services self.arc = arc def __add__(self, arcname, contents): - if arcname.startswith('META-INF/jvmci.providers/'): - provider = arcname[len('META-INF/jvmci.providers/'):] - for service in contents.strip().split(os.linesep): - assert service - self.services.setdefault(service, []).append(provider) - return True return False def __addsrc__(self, arcname, contents): @@ -757,6 +763,14 @@ class JVMCI9JDKConfig(mx.JDKConfig): args = ['-Xbootclasspath/p:' + dep.classpath_repr() for dep in _jvmci_bootclasspath_prepends] + args + # Remove JVMCI jars from class path. They are only necessary when + # compiling with a javac from JDK8 or earlier. + cpIndex, cp = mx.find_classpath_arg(args) + if cp: + excluded = frozenset([dist.path for dist in _suite.dists]) + cp = os.pathsep.join([e for e in cp.split(os.pathsep) if e not in excluded]) + args[cpIndex] = cp + jvmciModeArgs = _jvmciModes[_vm.jvmciMode] if jvmciModeArgs: bcpDeps = [jdkDist.dist() for jdkDist in jdkDeployedDists] @@ -812,7 +826,7 @@ def get_jvmci_jdk(debugLevel=None): _jvmci_jdks[debugLevel] = jdk return jdk -class JVMCIJDKFactory(mx.JDKFactory): +class JVMCI9JDKFactory(mx.JDKFactory): def getJDKConfig(self): jdk = get_jvmci_jdk(_vm.debugLevel) return jdk @@ -836,8 +850,9 @@ mx.add_argument('--jdk-jvm-variant', '--vm', action='store', choices=_jdkJvmVari mx.add_argument('--jdk-debug-level', '--vmbuild', action='store', choices=_jdkDebugLevels + sorted(_legacyVmbuilds.viewkeys()), help='the JDK debug level to build/run (default: ' + _vm.debugLevel + ')') mx.add_argument('-I', '--use-jdk-image', action='store_true', help='build/run JDK image instead of exploded JDK') +mx.addJDKFactory(_JVMCI_JDK_TAG, mx.JavaCompliance('9'), JVMCI9JDKFactory()) + def mx_post_parse_cmd_line(opts): - mx.addJDKFactory(_JVMCI_JDK_TAG, mx.JavaCompliance('9'), JVMCIJDKFactory()) mx.set_java_command_default_jdk_tag(_JVMCI_JDK_TAG) jdkTag = mx.get_jdk_option().tag @@ -864,6 +879,39 @@ def mx_post_parse_cmd_line(opts): _vm.update(jvmVariant, debugLevel, jvmciMode) for jdkDist in jdkDeployedDists: - dist = jdkDist.dist() - if isinstance(jdkDist, JvmciJDKDeployedDist): - dist.set_archiveparticipant(JVMCIArchiveParticipant(dist)) + jdkDist.post_parse_cmd_line() + +def _update_JDK9_STUBS_library(): + """ + Sets the "path" and "sha1" attributes of the "JDK9_STUBS" library. + """ + jdk9InternalLib = _suite.suiteDict['libraries']['JDK9_STUBS'] + jarInputDir = join(_suite.get_output_root(), 'jdk9-stubs') + jarPath = join(_suite.get_output_root(), 'jdk9-stubs.jar') + + stubs = [ + ('jdk.internal.misc', 'VM', """package jdk.internal.misc; +public class VM { + public static String getSavedProperty(String key) { + throw new InternalError("should not reach here"); + } +} +""") + ] + + if not exists(jarPath): + sourceFiles = [] + for (package, className, source) in stubs: + sourceFile = join(jarInputDir, package.replace('.', os.sep), className + '.java') + mx.ensure_dir_exists(os.path.dirname(sourceFile)) + with open(sourceFile, 'w') as fp: + fp.write(source) + sourceFiles.append(sourceFile) + jdk = mx.get_jdk(tag='default') + mx.run([jdk.javac, '-d', jarInputDir] + sourceFiles) + mx.run([jdk.jar, 'cf', jarPath, '.'], cwd=jarInputDir) + + jdk9InternalLib['path'] = jarPath + jdk9InternalLib['sha1'] = mx.sha1OfFile(jarPath) + +_update_JDK9_STUBS_library() diff --git a/hotspot/.mx.jvmci/suite.py b/hotspot/.mx.jvmci/suite.py index 05d0027a8bb..28c00818dd7 100644 --- a/hotspot/.mx.jvmci/suite.py +++ b/hotspot/.mx.jvmci/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion" : "5.5.12", + "mxversion" : "5.6.11", "name" : "jvmci", "url" : "http://openjdk.java.net/projects/graal", "developer" : { @@ -24,7 +24,7 @@ suite = { "defaultLicense" : "GPLv2-CPE", - # This puts mx/ as a sibiling of the JDK build configuration directories + # This puts mx/ as a sibling of the JDK build configuration directories # (e.g., macosx-x86_64-normal-server-release). "outputRoot" : "../build/mx/hotspot", @@ -32,8 +32,6 @@ suite = { "libraries" : { - # ------------- Libraries ------------- - "HCFDIS" : { "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/hcfdis-3.jar"], "sha1" : "a71247c6ddb90aad4abf7c77e501acc60674ef57", @@ -53,34 +51,32 @@ suite = { "sha1" : "122b87ca88e41a415cf8b523fd3d03b4325134a3", "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/batik-all-1.7.jar"], }, + + # Stubs for classes introduced in JDK9 that allow compilation with a JDK8 javac and Eclipse. + # The "path" and "sha1" attributes are added when mx_jvmci is loaded + # (see mx_jvmci._update_JDK9_STUBS_library()). + "JDK9_STUBS" : { + "license" : "GPLv2-CPE", + }, }, "projects" : { # ------------- JVMCI:Service ------------- - "jdk.vm.ci.service" : { + "jdk.vm.ci.services" : { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, - "jdk.vm.ci.service.processor" : { - "subDir" : "src/jdk.vm.ci/share/classes", - "sourceDirs" : ["src"], - "dependencies" : ["jdk.vm.ci.service"], - "checkstyle" : "jdk.vm.ci.service", - "javaCompliance" : "1.8", - "workingSets" : "JVMCI,Codegen,HotSpot", - }, - # ------------- JVMCI:API ------------- "jdk.vm.ci.common" : { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, @@ -88,7 +84,7 @@ suite = { "jdk.vm.ci.meta" : { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, @@ -97,7 +93,7 @@ suite = { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], "dependencies" : ["jdk.vm.ci.meta"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, @@ -108,7 +104,7 @@ suite = { "dependencies" : [ "jdk.vm.ci.code", ], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, @@ -121,7 +117,7 @@ suite = { "jdk.vm.ci.common", "jdk.vm.ci.runtime", ], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "API,JVMCI", }, @@ -129,7 +125,7 @@ suite = { "jdk.vm.ci.inittimer" : { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI", }, @@ -140,7 +136,7 @@ suite = { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], "dependencies" : ["jdk.vm.ci.code"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,AArch64", }, @@ -149,7 +145,7 @@ suite = { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], "dependencies" : ["jdk.vm.ci.code"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,AMD64", }, @@ -158,7 +154,7 @@ suite = { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], "dependencies" : ["jdk.vm.ci.code"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,SPARC", }, @@ -171,9 +167,10 @@ suite = { "jdk.vm.ci.common", "jdk.vm.ci.inittimer", "jdk.vm.ci.runtime", - "jdk.vm.ci.service", + "jdk.vm.ci.services", + "JDK9_STUBS", ], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI", }, @@ -181,7 +178,7 @@ suite = { "jdk.vm.ci.hotspotvmconfig" : { "subDir" : "src/jdk.vm.ci/share/classes", "sourceDirs" : ["src"], - "checkstyle" : "jdk.vm.ci.service", + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,HotSpot", }, @@ -193,10 +190,7 @@ suite = { "jdk.vm.ci.aarch64", "jdk.vm.ci.hotspot", ], - "checkstyle" : "jdk.vm.ci.service", - "annotationProcessors" : [ - "JVMCI_SERVICE_PROCESSOR", - ], + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,HotSpot,AArch64", }, @@ -208,10 +202,7 @@ suite = { "jdk.vm.ci.amd64", "jdk.vm.ci.hotspot", ], - "checkstyle" : "jdk.vm.ci.service", - "annotationProcessors" : [ - "JVMCI_SERVICE_PROCESSOR", - ], + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,HotSpot,AMD64", }, @@ -223,10 +214,7 @@ suite = { "jdk.vm.ci.sparc", "jdk.vm.ci.hotspot", ], - "checkstyle" : "jdk.vm.ci.service", - "annotationProcessors" : [ - "JVMCI_SERVICE_PROCESSOR", - ], + "checkstyle" : "jdk.vm.ci.services", "javaCompliance" : "1.8", "workingSets" : "JVMCI,HotSpot,SPARC", }, @@ -241,9 +229,9 @@ suite = { # ------------- Distributions ------------- - "JVMCI_SERVICE" : { + "JVMCI_SERVICES" : { "subDir" : "src/jdk.vm.ci/share/classes", - "dependencies" : ["jdk.vm.ci.service"], + "dependencies" : ["jdk.vm.ci.services"], }, "JVMCI_API" : { @@ -257,7 +245,7 @@ suite = { "jdk.vm.ci.sparc", ], "distDependencies" : [ - "JVMCI_SERVICE", + "JVMCI_SERVICES", ], }, @@ -277,7 +265,7 @@ suite = { ], "distDependencies" : [ "JVMCI_HOTSPOTVMCONFIG", - "JVMCI_SERVICE", + "JVMCI_SERVICES", "JVMCI_API", ], }, @@ -293,28 +281,18 @@ suite = { "exclude" : ["mx:JUNIT"], }, - - "JVMCI_SERVICE_PROCESSOR" : { - "subDir" : "src/jdk.vm.ci/share/classes", - "dependencies" : ["jdk.vm.ci.service.processor"], - "distDependencies" : [ - "JVMCI_SERVICE", - ], - }, - # This exists to have a monolithic jvmci.jar file which simplifies # using the -Xoverride option in JDK9. "JVMCI" : { "subDir" : "src/jdk.vm.ci/share/classes", "overlaps" : [ "JVMCI_API", - "JVMCI_SERVICE", + "JVMCI_SERVICES", "JVMCI_HOTSPOT", "JVMCI_HOTSPOTVMCONFIG", - "JVMCI_SERVICE_PROCESSOR", ], "dependencies" : [ - "jdk.vm.ci.service", + "jdk.vm.ci.services", "jdk.vm.ci.inittimer", "jdk.vm.ci.runtime", "jdk.vm.ci.common", @@ -325,8 +303,8 @@ suite = { "jdk.vm.ci.hotspot.aarch64", "jdk.vm.ci.hotspot.amd64", "jdk.vm.ci.hotspot.sparc", - "jdk.vm.ci.service.processor" ], + "exclude" : ["JDK9_STUBS"] }, }, } diff --git a/hotspot/make/aix/Makefile b/hotspot/make/aix/Makefile index 77e54d08dc0..951a98502fa 100644 --- a/hotspot/make/aix/Makefile +++ b/hotspot/make/aix/Makefile @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2015 SAP AG. All rights reserved. +# Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/buildtree.make b/hotspot/make/aix/makefiles/buildtree.make index 1f2b556a4fe..94b71eb760f 100644 --- a/hotspot/make/aix/makefiles/buildtree.make +++ b/hotspot/make/aix/makefiles/buildtree.make @@ -1,6 +1,6 @@ # # Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/compiler2.make b/hotspot/make/aix/makefiles/compiler2.make index 73f544f118d..13986308edf 100644 --- a/hotspot/make/aix/makefiles/compiler2.make +++ b/hotspot/make/aix/makefiles/compiler2.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/debug.make b/hotspot/make/aix/makefiles/debug.make index 5e2cacd365d..1a1c4ebbd7d 100644 --- a/hotspot/make/aix/makefiles/debug.make +++ b/hotspot/make/aix/makefiles/debug.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/defs.make b/hotspot/make/aix/makefiles/defs.make index cca6803c9d5..b9c54e36149 100644 --- a/hotspot/make/aix/makefiles/defs.make +++ b/hotspot/make/aix/makefiles/defs.make @@ -1,6 +1,6 @@ # # Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/fastdebug.make b/hotspot/make/aix/makefiles/fastdebug.make index d7feb025617..c0d50079d16 100644 --- a/hotspot/make/aix/makefiles/fastdebug.make +++ b/hotspot/make/aix/makefiles/fastdebug.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/jsig.make b/hotspot/make/aix/makefiles/jsig.make index 1d8a62184bd..86c4bea5ae1 100644 --- a/hotspot/make/aix/makefiles/jsig.make +++ b/hotspot/make/aix/makefiles/jsig.make @@ -1,6 +1,6 @@ # # Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/jvmti.make b/hotspot/make/aix/makefiles/jvmti.make index 105fd8e109d..cff6a741da6 100644 --- a/hotspot/make/aix/makefiles/jvmti.make +++ b/hotspot/make/aix/makefiles/jvmti.make @@ -1,6 +1,6 @@ # # Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/ppc64.make b/hotspot/make/aix/makefiles/ppc64.make index 2634d8adcc3..d7ad9510e11 100644 --- a/hotspot/make/aix/makefiles/ppc64.make +++ b/hotspot/make/aix/makefiles/ppc64.make @@ -1,6 +1,6 @@ # # Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2015 SAP AG. All rights reserved. +# Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/product.make b/hotspot/make/aix/makefiles/product.make index f1ef85a8192..f36ef69b32e 100644 --- a/hotspot/make/aix/makefiles/product.make +++ b/hotspot/make/aix/makefiles/product.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/tiered.make b/hotspot/make/aix/makefiles/tiered.make index 992d59d5241..f04496bdb60 100644 --- a/hotspot/make/aix/makefiles/tiered.make +++ b/hotspot/make/aix/makefiles/tiered.make @@ -1,6 +1,6 @@ # # Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2015 SAP AG. All rights reserved. +# Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/vm.make b/hotspot/make/aix/makefiles/vm.make index 5de8a2dc997..808bb124892 100644 --- a/hotspot/make/aix/makefiles/vm.make +++ b/hotspot/make/aix/makefiles/vm.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/aix/makefiles/xlc.make b/hotspot/make/aix/makefiles/xlc.make index 676ba5c6a6d..c44d306359e 100644 --- a/hotspot/make/aix/makefiles/xlc.make +++ b/hotspot/make/aix/makefiles/xlc.make @@ -1,6 +1,6 @@ # # Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2012, 2015 SAP. All rights reserved. +# Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/make/gensrc/Gensrc-jdk.vm.ci.gmk b/hotspot/make/gensrc/Gensrc-jdk.vm.ci.gmk deleted file mode 100644 index cfcf9329a3d..00000000000 --- a/hotspot/make/gensrc/Gensrc-jdk.vm.ci.gmk +++ /dev/null @@ -1,105 +0,0 @@ -# -# 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. -# - -default: all - -include $(SPEC) -include MakeBase.gmk -include JavaCompilation.gmk -include SetupJavaCompilers.gmk - -GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/jdk.vm.ci -SRC_DIR := $(HOTSPOT_TOPDIR)/src/jdk.vm.ci/share/classes - -################################################################################ -# Compile the annotation processor - -$(eval $(call SetupJavaCompilation, BUILD_JVMCI_SERVICE, \ - SETUP := GENERATE_OLDBYTECODE, \ - SRC := $(SRC_DIR)/jdk.vm.ci.service/src \ - $(SRC_DIR)/jdk.vm.ci.service.processor/src, \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/jvmci_service, \ - JAR := $(BUILDTOOLS_OUTPUTDIR)/jdk.vm.ci.service.jar, \ -)) - -################################################################################ - -PROC_SRC_SUBDIRS := \ - jdk.vm.ci.hotspot \ - jdk.vm.ci.hotspot.aarch64 \ - jdk.vm.ci.hotspot.amd64 \ - jdk.vm.ci.hotspot.sparc \ - jdk.vm.ci.runtime \ - # - -PROC_SRC_DIRS := $(patsubst %, $(SRC_DIR)/%/src, $(PROC_SRC_SUBDIRS)) - -PROC_SRCS := $(filter %.java, $(call CacheFind, $(PROC_SRC_DIRS))) - -ALL_SRC_DIRS := $(wildcard $(SRC_DIR)/*/src) -SOURCEPATH := $(call PathList, $(ALL_SRC_DIRS)) -PROCESSOR_PATH := $(call PathList, \ - $(BUILDTOOLS_OUTPUTDIR)/jdk.vm.ci.service.jar) - -$(GENSRC_DIR)/_gensrc_proc_done: $(PROC_SRCS) \ - $(BUILD_JVMCI_SERVICE) - $(MKDIR) -p $(@D) - $(eval $(call ListPathsSafely,PROC_SRCS,$(@D)/_gensrc_proc_files)) - $(JAVA_SMALL) $(NEW_JAVAC) \ - -XDignore.symbol.file \ - -bootclasspath $(JDK_OUTPUTDIR)/modules/java.base \ - -sourcepath $(SOURCEPATH) \ - -implicit:none \ - -proc:only \ - -processorpath $(PROCESSOR_PATH) \ - -d $(GENSRC_DIR) \ - -s $(GENSRC_DIR) \ - @$(@D)/_gensrc_proc_files - $(TOUCH) $@ - -TARGETS += $(GENSRC_DIR)/_gensrc_proc_done - -################################################################################ - -$(GENSRC_DIR)/_providers_converted: $(GENSRC_DIR)/_gensrc_proc_done - $(MKDIR) -p $(GENSRC_DIR)/META-INF/services - ($(CD) $(GENSRC_DIR)/META-INF/jvmci.providers && \ - for i in $$($(LS)); do \ - c=$$($(CAT) $$i | $(TR) -d '\n\r'); \ - $(ECHO) $$i >> $(GENSRC_DIR)/META-INF/services/$$c.tmp; \ - done) - ($(CD) $(GENSRC_DIR)/META-INF/services && \ - for i in $$($(LS) *.tmp); do \ - $(MV) $$i $${i%.tmp}; \ - done) - $(TOUCH) $@ - -TARGETS += $(GENSRC_DIR)/_providers_converted - -################################################################################ - -all: $(TARGETS) - -.PHONY: default all diff --git a/hotspot/make/linux/makefiles/ppc64.make b/hotspot/make/linux/makefiles/ppc64.make index f3392de49a6..acb51ab2b7a 100644 --- a/hotspot/make/linux/makefiles/ppc64.make +++ b/hotspot/make/linux/makefiles/ppc64.make @@ -1,6 +1,6 @@ # # Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. All rights reserved. +# Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/make/share/makefiles/mapfile-vers b/hotspot/make/share/makefiles/mapfile-vers index 2085a98b459..28338d046f6 100644 --- a/hotspot/make/share/makefiles/mapfile-vers +++ b/hotspot/make/share/makefiles/mapfile-vers @@ -13,6 +13,7 @@ JVM_Clone; JVM_ConstantPoolGetClassAt; JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetClassRefIndexAt; JVM_ConstantPoolGetDoubleAt; JVM_ConstantPoolGetFieldAt; JVM_ConstantPoolGetFieldAtIfLoaded; @@ -22,8 +23,11 @@ JVM_ConstantPoolGetMethodAt; JVM_ConstantPoolGetMethodAtIfLoaded; JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetNameAndTypeRefInfoAt; + JVM_ConstantPoolGetNameAndTypeRefIndexAt; JVM_ConstantPoolGetSize; JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetTagAt; JVM_ConstantPoolGetUTF8At; JVM_CountStackFrames; JVM_CurrentClassLoader; diff --git a/hotspot/src/cpu/aarch64/vm/aarch64.ad b/hotspot/src/cpu/aarch64/vm/aarch64.ad index f19eb0e71c7..2b5d3d8838d 100644 --- a/hotspot/src/cpu/aarch64/vm/aarch64.ad +++ b/hotspot/src/cpu/aarch64/vm/aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, Red Hat Inc. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // @@ -577,7 +577,7 @@ reg_class no_special_reg32_with_fp( R26 /* R27, */ // heapbase /* R28, */ // thread - R29, // fp + /* R29, */ // fp /* R30, */ // lr /* R31 */ // sp ); @@ -646,7 +646,7 @@ reg_class no_special_reg_with_fp( R26, R26_H, /* R27, R27_H, */ // heapbase /* R28, R28_H, */ // thread - R29, R29_H, // fp + /* R29, R29_H, */ // fp /* R30, R30_H, */ // lr /* R31, R31_H */ // sp ); @@ -4442,11 +4442,7 @@ encode %{ enc_class aarch64_enc_mov_byte_map_base(iRegP dst, immByteMapBase src) %{ MacroAssembler _masm(&cbuf); - address page = (address)$src$$constant; - Register dst_reg = as_Register($dst$$reg); - unsigned long off; - __ adrp(dst_reg, ExternalAddress(page), off); - assert(off == 0, "assumed offset == 0"); + __ load_byte_map_base($dst$$Register); %} enc_class aarch64_enc_mov_n(iRegN dst, immN src) %{ @@ -6673,6 +6669,14 @@ opclass iRegIorL2I(iRegI, iRegL2I); //----------PIPELINE----------------------------------------------------------- // Rules which define the behavior of the target architectures pipeline. + +// For specific pipelines, eg A53, define the stages of that pipeline +//pipe_desc(ISS, EX1, EX2, WR); +#define ISS S0 +#define EX1 S1 +#define EX2 S2 +#define WR S3 + // Integer ALU reg operation pipeline %{ @@ -6707,12 +6711,499 @@ resources( INS0, INS1, INS01 = INS0 | INS1, //----------PIPELINE DESCRIPTION----------------------------------------------- // Pipeline Description specifies the stages in the machine's pipeline -pipe_desc(ISS, EX1, EX2, WR); +// Define the pipeline as a generic 6 stage pipeline +pipe_desc(S0, S1, S2, S3, S4, S5); //----------PIPELINE CLASSES--------------------------------------------------- // Pipeline Classes describe the stages in which input and output are // referenced by the hardware pipeline. +pipe_class fp_dop_reg_reg_s(vRegF dst, vRegF src1, vRegF src2) +%{ + single_instruction; + src1 : S1(read); + src2 : S2(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_dop_reg_reg_d(vRegD dst, vRegD src1, vRegD src2) +%{ + single_instruction; + src1 : S1(read); + src2 : S2(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_uop_s(vRegF dst, vRegF src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_uop_d(vRegD dst, vRegD src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_d2f(vRegF dst, vRegD src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_f2d(vRegD dst, vRegF src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_f2i(iRegINoSp dst, vRegF src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_f2l(iRegLNoSp dst, vRegF src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_i2f(vRegF dst, iRegIorL2I src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_l2f(vRegF dst, iRegL src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_d2i(iRegINoSp dst, vRegD src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_d2l(iRegLNoSp dst, vRegD src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_i2d(vRegD dst, iRegIorL2I src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_l2d(vRegD dst, iRegIorL2I src) +%{ + single_instruction; + src : S1(read); + dst : S5(write); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_div_s(vRegF dst, vRegF src1, vRegF src2) +%{ + single_instruction; + src1 : S1(read); + src2 : S2(read); + dst : S5(write); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_div_d(vRegD dst, vRegD src1, vRegD src2) +%{ + single_instruction; + src1 : S1(read); + src2 : S2(read); + dst : S5(write); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class fp_cond_reg_reg_s(vRegF dst, vRegF src1, vRegF src2, rFlagsReg cr) +%{ + single_instruction; + cr : S1(read); + src1 : S1(read); + src2 : S1(read); + dst : S3(write); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class fp_cond_reg_reg_d(vRegD dst, vRegD src1, vRegD src2, rFlagsReg cr) +%{ + single_instruction; + cr : S1(read); + src1 : S1(read); + src2 : S1(read); + dst : S3(write); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class fp_imm_s(vRegF dst) +%{ + single_instruction; + dst : S3(write); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class fp_imm_d(vRegD dst) +%{ + single_instruction; + dst : S3(write); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class fp_load_constant_s(vRegF dst) +%{ + single_instruction; + dst : S4(write); + INS01 : ISS; + NEON_FP : S4; +%} + +pipe_class fp_load_constant_d(vRegD dst) +%{ + single_instruction; + dst : S4(write); + INS01 : ISS; + NEON_FP : S4; +%} + +pipe_class vmul64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class vmul128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vmla64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + dst : S1(read); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class vmla128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + dst : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vdop64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S4(write); + src1 : S2(read); + src2 : S2(read); + INS01 : ISS; + NEON_FP : S4; +%} + +pipe_class vdop128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S4(write); + src1 : S2(read); + src2 : S2(read); + INS0 : ISS; + NEON_FP : S4; +%} + +pipe_class vlogical64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S3(write); + src1 : S2(read); + src2 : S2(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vlogical128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S3(write); + src1 : S2(read); + src2 : S2(read); + INS0 : ISS; + NEON_FP : S3; +%} + +pipe_class vshift64(vecD dst, vecD src, vecX shift) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + shift : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vshift128(vecX dst, vecX src, vecX shift) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + shift : S1(read); + INS0 : ISS; + NEON_FP : S3; +%} + +pipe_class vshift64_imm(vecD dst, vecD src, immI shift) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vshift128_imm(vecX dst, vecX src, immI shift) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS0 : ISS; + NEON_FP : S3; +%} + +pipe_class vdop_fp64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class vdop_fp128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vmuldiv_fp64(vecD dst, vecD src1, vecD src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vmuldiv_fp128(vecX dst, vecX src1, vecX src2) +%{ + single_instruction; + dst : S5(write); + src1 : S1(read); + src2 : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vsqrt_fp128(vecX dst, vecX src) +%{ + single_instruction; + dst : S5(write); + src : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vunop_fp64(vecD dst, vecD src) +%{ + single_instruction; + dst : S5(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S5; +%} + +pipe_class vunop_fp128(vecX dst, vecX src) +%{ + single_instruction; + dst : S5(write); + src : S1(read); + INS0 : ISS; + NEON_FP : S5; +%} + +pipe_class vdup_reg_reg64(vecD dst, iRegI src) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vdup_reg_reg128(vecX dst, iRegI src) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vdup_reg_freg64(vecD dst, vRegF src) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vdup_reg_freg128(vecX dst, vRegF src) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vdup_reg_dreg128(vecX dst, vRegD src) +%{ + single_instruction; + dst : S3(write); + src : S1(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vmovi_reg_imm64(vecD dst) +%{ + single_instruction; + dst : S3(write); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vmovi_reg_imm128(vecX dst) +%{ + single_instruction; + dst : S3(write); + INS0 : ISS; + NEON_FP : S3; +%} + +pipe_class vload_reg_mem64(vecD dst, vmem mem) +%{ + single_instruction; + dst : S5(write); + mem : ISS(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vload_reg_mem128(vecX dst, vmem mem) +%{ + single_instruction; + dst : S5(write); + mem : ISS(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vstore_reg_mem64(vecD src, vmem mem) +%{ + single_instruction; + mem : ISS(read); + src : S2(read); + INS01 : ISS; + NEON_FP : S3; +%} + +pipe_class vstore_reg_mem128(vecD src, vmem mem) +%{ + single_instruction; + mem : ISS(read); + src : S2(read); + INS01 : ISS; + NEON_FP : S3; +%} + //------- Integer ALU operations -------------------------- // Integer ALU reg-reg operation @@ -7559,7 +8050,7 @@ instruct loadConF_packed(vRegF dst, immFPacked con) %{ __ fmovs(as_FloatRegister($dst$$reg), (double)$con$$constant); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_imm_s); %} // Load Float Constant @@ -7577,7 +8068,7 @@ instruct loadConF(vRegF dst, immF con) %{ __ ldrs(as_FloatRegister($dst$$reg), $constantaddress($con)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_load_constant_s); %} // Load Packed Double Constant @@ -7590,7 +8081,7 @@ instruct loadConD_packed(vRegD dst, immDPacked con) %{ __ fmovd(as_FloatRegister($dst$$reg), $con$$constant); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_imm_d); %} // Load Double Constant @@ -7607,7 +8098,7 @@ instruct loadConD(vRegD dst, immD con) %{ __ ldrd(as_FloatRegister($dst$$reg), $constantaddress($con)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_load_constant_d); %} // Store Instructions @@ -9615,7 +10106,7 @@ instruct cmovF_reg(cmpOp cmp, rFlagsReg cr, vRegF dst, vRegF src1, vRegF src2) cond); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_cond_reg_reg_s); %} instruct cmovUF_reg(cmpOpU cmp, rFlagsRegU cr, vRegF dst, vRegF src1, vRegF src2) @@ -9633,7 +10124,7 @@ instruct cmovUF_reg(cmpOpU cmp, rFlagsRegU cr, vRegF dst, vRegF src1, vRegF src cond); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_cond_reg_reg_s); %} instruct cmovD_reg(cmpOp cmp, rFlagsReg cr, vRegD dst, vRegD src1, vRegD src2) @@ -9651,7 +10142,7 @@ instruct cmovD_reg(cmpOp cmp, rFlagsReg cr, vRegD dst, vRegD src1, vRegD src2) cond); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_cond_reg_reg_d); %} instruct cmovUD_reg(cmpOpU cmp, rFlagsRegU cr, vRegD dst, vRegD src1, vRegD src2) @@ -9669,7 +10160,7 @@ instruct cmovUD_reg(cmpOpU cmp, rFlagsRegU cr, vRegD dst, vRegD src1, vRegD src cond); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_cond_reg_reg_d); %} // ============================================================================ @@ -12033,7 +12524,7 @@ instruct addF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_s); %} instruct addD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ @@ -12048,7 +12539,7 @@ instruct addD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_d); %} instruct subF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ @@ -12063,7 +12554,7 @@ instruct subF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_s); %} instruct subD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ @@ -12078,7 +12569,7 @@ instruct subD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_d); %} instruct mulF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ @@ -12093,7 +12584,7 @@ instruct mulF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_s); %} instruct mulD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ @@ -12108,7 +12599,7 @@ instruct mulD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_dop_reg_reg_d); %} // We cannot use these fused mul w add/sub ops because they don't @@ -12256,7 +12747,7 @@ instruct divF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_div_s); %} instruct divD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ @@ -12271,7 +12762,7 @@ instruct divD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_div_d); %} instruct negF_reg_reg(vRegF dst, vRegF src) %{ @@ -12285,7 +12776,7 @@ instruct negF_reg_reg(vRegF dst, vRegF src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_uop_s); %} instruct negD_reg_reg(vRegD dst, vRegD src) %{ @@ -12299,7 +12790,7 @@ instruct negD_reg_reg(vRegD dst, vRegD src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_uop_d); %} instruct absF_reg(vRegF dst, vRegF src) %{ @@ -12312,7 +12803,7 @@ instruct absF_reg(vRegF dst, vRegF src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_uop_s); %} instruct absD_reg(vRegD dst, vRegD src) %{ @@ -12325,7 +12816,7 @@ instruct absD_reg(vRegD dst, vRegD src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_uop_d); %} instruct sqrtD_reg(vRegD dst, vRegD src) %{ @@ -12338,7 +12829,7 @@ instruct sqrtD_reg(vRegD dst, vRegD src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_div_s); %} instruct sqrtF_reg(vRegF dst, vRegF src) %{ @@ -12351,7 +12842,7 @@ instruct sqrtF_reg(vRegF dst, vRegF src) %{ as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_div_d); %} // ============================================================================ @@ -12638,7 +13129,7 @@ instruct convD2F_reg(vRegF dst, vRegD src) %{ __ fcvtd(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_d2f); %} instruct convF2D_reg(vRegD dst, vRegF src) %{ @@ -12651,7 +13142,7 @@ instruct convF2D_reg(vRegD dst, vRegF src) %{ __ fcvts(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_f2d); %} instruct convF2I_reg_reg(iRegINoSp dst, vRegF src) %{ @@ -12664,7 +13155,7 @@ instruct convF2I_reg_reg(iRegINoSp dst, vRegF src) %{ __ fcvtzsw(as_Register($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_f2i); %} instruct convF2L_reg_reg(iRegLNoSp dst, vRegF src) %{ @@ -12677,7 +13168,7 @@ instruct convF2L_reg_reg(iRegLNoSp dst, vRegF src) %{ __ fcvtzs(as_Register($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_f2l); %} instruct convI2F_reg_reg(vRegF dst, iRegIorL2I src) %{ @@ -12690,7 +13181,7 @@ instruct convI2F_reg_reg(vRegF dst, iRegIorL2I src) %{ __ scvtfws(as_FloatRegister($dst$$reg), as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_i2f); %} instruct convL2F_reg_reg(vRegF dst, iRegL src) %{ @@ -12703,7 +13194,7 @@ instruct convL2F_reg_reg(vRegF dst, iRegL src) %{ __ scvtfs(as_FloatRegister($dst$$reg), as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_l2f); %} instruct convD2I_reg_reg(iRegINoSp dst, vRegD src) %{ @@ -12716,7 +13207,7 @@ instruct convD2I_reg_reg(iRegINoSp dst, vRegD src) %{ __ fcvtzdw(as_Register($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_d2i); %} instruct convD2L_reg_reg(iRegLNoSp dst, vRegD src) %{ @@ -12729,7 +13220,7 @@ instruct convD2L_reg_reg(iRegLNoSp dst, vRegD src) %{ __ fcvtzd(as_Register($dst$$reg), as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_d2l); %} instruct convI2D_reg_reg(vRegD dst, iRegIorL2I src) %{ @@ -12742,7 +13233,7 @@ instruct convI2D_reg_reg(vRegD dst, iRegIorL2I src) %{ __ scvtfwd(as_FloatRegister($dst$$reg), as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_i2d); %} instruct convL2D_reg_reg(vRegD dst, iRegL src) %{ @@ -12755,7 +13246,7 @@ instruct convL2D_reg_reg(vRegD dst, iRegL src) %{ __ scvtfd(as_FloatRegister($dst$$reg), as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(fp_l2d); %} // stack <-> reg and reg <-> reg shuffles with no conversion @@ -14500,7 +14991,7 @@ instruct loadV4(vecD dst, vmem mem) ins_cost(4 * INSN_COST); format %{ "ldrs $dst,$mem\t# vector (32 bits)" %} ins_encode( aarch64_enc_ldrvS(dst, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vload_reg_mem64); %} // Load vector (64 bits) @@ -14511,7 +15002,7 @@ instruct loadV8(vecD dst, vmem mem) ins_cost(4 * INSN_COST); format %{ "ldrd $dst,$mem\t# vector (64 bits)" %} ins_encode( aarch64_enc_ldrvD(dst, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vload_reg_mem64); %} // Load Vector (128 bits) @@ -14522,7 +15013,7 @@ instruct loadV16(vecX dst, vmem mem) ins_cost(4 * INSN_COST); format %{ "ldrq $dst,$mem\t# vector (128 bits)" %} ins_encode( aarch64_enc_ldrvQ(dst, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vload_reg_mem128); %} // Store Vector (32 bits) @@ -14533,7 +15024,7 @@ instruct storeV4(vecD src, vmem mem) ins_cost(4 * INSN_COST); format %{ "strs $mem,$src\t# vector (32 bits)" %} ins_encode( aarch64_enc_strvS(src, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vstore_reg_mem64); %} // Store Vector (64 bits) @@ -14544,7 +15035,7 @@ instruct storeV8(vecD src, vmem mem) ins_cost(4 * INSN_COST); format %{ "strd $mem,$src\t# vector (64 bits)" %} ins_encode( aarch64_enc_strvD(src, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vstore_reg_mem64); %} // Store Vector (128 bits) @@ -14555,7 +15046,7 @@ instruct storeV16(vecX src, vmem mem) ins_cost(4 * INSN_COST); format %{ "strq $mem,$src\t# vector (128 bits)" %} ins_encode( aarch64_enc_strvQ(src, mem) ); - ins_pipe(pipe_class_memory); + ins_pipe(vstore_reg_mem128); %} instruct replicate8B(vecD dst, iRegIorL2I src) @@ -14568,7 +15059,7 @@ instruct replicate8B(vecD dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T8B, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg64); %} instruct replicate16B(vecX dst, iRegIorL2I src) @@ -14580,7 +15071,7 @@ instruct replicate16B(vecX dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T16B, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} instruct replicate8B_imm(vecD dst, immI con) @@ -14593,7 +15084,7 @@ instruct replicate8B_imm(vecD dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T8B, $con$$constant & 0xff); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm64); %} instruct replicate16B_imm(vecX dst, immI con) @@ -14605,7 +15096,7 @@ instruct replicate16B_imm(vecX dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T16B, $con$$constant & 0xff); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm128); %} instruct replicate4S(vecD dst, iRegIorL2I src) @@ -14618,7 +15109,7 @@ instruct replicate4S(vecD dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T4H, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg64); %} instruct replicate8S(vecX dst, iRegIorL2I src) @@ -14630,7 +15121,7 @@ instruct replicate8S(vecX dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T8H, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} instruct replicate4S_imm(vecD dst, immI con) @@ -14643,7 +15134,7 @@ instruct replicate4S_imm(vecD dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T4H, $con$$constant & 0xffff); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm64); %} instruct replicate8S_imm(vecX dst, immI con) @@ -14655,7 +15146,7 @@ instruct replicate8S_imm(vecX dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T8H, $con$$constant & 0xffff); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm128); %} instruct replicate2I(vecD dst, iRegIorL2I src) @@ -14667,7 +15158,7 @@ instruct replicate2I(vecD dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T2S, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg64); %} instruct replicate4I(vecX dst, iRegIorL2I src) @@ -14679,7 +15170,7 @@ instruct replicate4I(vecX dst, iRegIorL2I src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T4S, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} instruct replicate2I_imm(vecD dst, immI con) @@ -14691,7 +15182,7 @@ instruct replicate2I_imm(vecD dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T2S, $con$$constant); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm64); %} instruct replicate4I_imm(vecX dst, immI con) @@ -14703,7 +15194,7 @@ instruct replicate4I_imm(vecX dst, immI con) ins_encode %{ __ mov(as_FloatRegister($dst$$reg), __ T4S, $con$$constant); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm128); %} instruct replicate2L(vecX dst, iRegL src) @@ -14715,7 +15206,7 @@ instruct replicate2L(vecX dst, iRegL src) ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T2D, as_Register($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} instruct replicate2L_zero(vecX dst, immI0 zero) @@ -14729,7 +15220,7 @@ instruct replicate2L_zero(vecX dst, immI0 zero) as_FloatRegister($dst$$reg), as_FloatRegister($dst$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmovi_reg_imm128); %} instruct replicate2F(vecD dst, vRegF src) @@ -14742,7 +15233,7 @@ instruct replicate2F(vecD dst, vRegF src) __ dup(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_freg64); %} instruct replicate4F(vecX dst, vRegF src) @@ -14755,7 +15246,7 @@ instruct replicate4F(vecX dst, vRegF src) __ dup(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_freg128); %} instruct replicate2D(vecX dst, vRegD src) @@ -14768,7 +15259,7 @@ instruct replicate2D(vecX dst, vRegD src) __ dup(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_dreg128); %} // ====================REDUCTION ARITHMETIC==================================== @@ -15014,7 +15505,7 @@ instruct vadd8B(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vadd16B(vecX dst, vecX src1, vecX src2) @@ -15028,7 +15519,7 @@ instruct vadd16B(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vadd4S(vecD dst, vecD src1, vecD src2) @@ -15043,7 +15534,7 @@ instruct vadd4S(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vadd8S(vecX dst, vecX src1, vecX src2) @@ -15057,7 +15548,7 @@ instruct vadd8S(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vadd2I(vecD dst, vecD src1, vecD src2) @@ -15071,7 +15562,7 @@ instruct vadd2I(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vadd4I(vecX dst, vecX src1, vecX src2) @@ -15085,7 +15576,7 @@ instruct vadd4I(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vadd2L(vecX dst, vecX src1, vecX src2) @@ -15099,7 +15590,7 @@ instruct vadd2L(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vadd2F(vecD dst, vecD src1, vecD src2) @@ -15113,7 +15604,7 @@ instruct vadd2F(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp64); %} instruct vadd4F(vecX dst, vecX src1, vecX src2) @@ -15127,7 +15618,7 @@ instruct vadd4F(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp128); %} instruct vadd2D(vecX dst, vecX src1, vecX src2) @@ -15140,7 +15631,7 @@ instruct vadd2D(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp128); %} // --------------------------------- SUB -------------------------------------- @@ -15157,7 +15648,7 @@ instruct vsub8B(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vsub16B(vecX dst, vecX src1, vecX src2) @@ -15171,7 +15662,7 @@ instruct vsub16B(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vsub4S(vecD dst, vecD src1, vecD src2) @@ -15186,7 +15677,7 @@ instruct vsub4S(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vsub8S(vecX dst, vecX src1, vecX src2) @@ -15200,7 +15691,7 @@ instruct vsub8S(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vsub2I(vecD dst, vecD src1, vecD src2) @@ -15214,7 +15705,7 @@ instruct vsub2I(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop64); %} instruct vsub4I(vecX dst, vecX src1, vecX src2) @@ -15228,7 +15719,7 @@ instruct vsub4I(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vsub2L(vecX dst, vecX src1, vecX src2) @@ -15242,7 +15733,7 @@ instruct vsub2L(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop128); %} instruct vsub2F(vecD dst, vecD src1, vecD src2) @@ -15256,7 +15747,7 @@ instruct vsub2F(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp64); %} instruct vsub4F(vecX dst, vecX src1, vecX src2) @@ -15270,7 +15761,7 @@ instruct vsub4F(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp128); %} instruct vsub2D(vecX dst, vecX src1, vecX src2) @@ -15284,7 +15775,7 @@ instruct vsub2D(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdop_fp128); %} // --------------------------------- MUL -------------------------------------- @@ -15301,7 +15792,7 @@ instruct vmul4S(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmul64); %} instruct vmul8S(vecX dst, vecX src1, vecX src2) @@ -15315,7 +15806,7 @@ instruct vmul8S(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmul128); %} instruct vmul2I(vecD dst, vecD src1, vecD src2) @@ -15329,7 +15820,7 @@ instruct vmul2I(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmul64); %} instruct vmul4I(vecX dst, vecX src1, vecX src2) @@ -15343,7 +15834,7 @@ instruct vmul4I(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmul128); %} instruct vmul2F(vecD dst, vecD src1, vecD src2) @@ -15357,7 +15848,7 @@ instruct vmul2F(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp64); %} instruct vmul4F(vecX dst, vecX src1, vecX src2) @@ -15371,7 +15862,7 @@ instruct vmul4F(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp128); %} instruct vmul2D(vecX dst, vecX src1, vecX src2) @@ -15385,7 +15876,7 @@ instruct vmul2D(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp128); %} // --------------------------------- MLA -------------------------------------- @@ -15402,7 +15893,7 @@ instruct vmla4S(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla64); %} instruct vmla8S(vecX dst, vecX src1, vecX src2) @@ -15416,7 +15907,7 @@ instruct vmla8S(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla128); %} instruct vmla2I(vecD dst, vecD src1, vecD src2) @@ -15430,7 +15921,7 @@ instruct vmla2I(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla64); %} instruct vmla4I(vecX dst, vecX src1, vecX src2) @@ -15444,7 +15935,7 @@ instruct vmla4I(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla128); %} // --------------------------------- MLS -------------------------------------- @@ -15461,7 +15952,7 @@ instruct vmls4S(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla64); %} instruct vmls8S(vecX dst, vecX src1, vecX src2) @@ -15475,7 +15966,7 @@ instruct vmls8S(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla128); %} instruct vmls2I(vecD dst, vecD src1, vecD src2) @@ -15489,7 +15980,7 @@ instruct vmls2I(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla64); %} instruct vmls4I(vecX dst, vecX src1, vecX src2) @@ -15503,7 +15994,7 @@ instruct vmls4I(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmla128); %} // --------------------------------- DIV -------------------------------------- @@ -15519,7 +16010,7 @@ instruct vdiv2F(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp64); %} instruct vdiv4F(vecX dst, vecX src1, vecX src2) @@ -15533,7 +16024,7 @@ instruct vdiv4F(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp128); %} instruct vdiv2D(vecX dst, vecX src1, vecX src2) @@ -15547,7 +16038,7 @@ instruct vdiv2D(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vmuldiv_fp128); %} // --------------------------------- SQRT ------------------------------------- @@ -15561,7 +16052,7 @@ instruct vsqrt2D(vecX dst, vecX src) __ fsqrt(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vsqrt_fp128); %} // --------------------------------- ABS -------------------------------------- @@ -15576,7 +16067,7 @@ instruct vabs2F(vecD dst, vecD src) __ fabs(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp64); %} instruct vabs4F(vecX dst, vecX src) @@ -15589,7 +16080,7 @@ instruct vabs4F(vecX dst, vecX src) __ fabs(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp128); %} instruct vabs2D(vecX dst, vecX src) @@ -15602,7 +16093,7 @@ instruct vabs2D(vecX dst, vecX src) __ fabs(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp128); %} // --------------------------------- NEG -------------------------------------- @@ -15617,7 +16108,7 @@ instruct vneg2F(vecD dst, vecD src) __ fneg(as_FloatRegister($dst$$reg), __ T2S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp64); %} instruct vneg4F(vecX dst, vecX src) @@ -15630,7 +16121,7 @@ instruct vneg4F(vecX dst, vecX src) __ fneg(as_FloatRegister($dst$$reg), __ T4S, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp128); %} instruct vneg2D(vecX dst, vecX src) @@ -15643,7 +16134,7 @@ instruct vneg2D(vecX dst, vecX src) __ fneg(as_FloatRegister($dst$$reg), __ T2D, as_FloatRegister($src$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vunop_fp128); %} // --------------------------------- AND -------------------------------------- @@ -15660,7 +16151,7 @@ instruct vand8B(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical64); %} instruct vand16B(vecX dst, vecX src1, vecX src2) @@ -15674,7 +16165,7 @@ instruct vand16B(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical128); %} // --------------------------------- OR --------------------------------------- @@ -15691,7 +16182,7 @@ instruct vor8B(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical64); %} instruct vor16B(vecX dst, vecX src1, vecX src2) @@ -15705,7 +16196,7 @@ instruct vor16B(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical128); %} // --------------------------------- XOR -------------------------------------- @@ -15722,7 +16213,7 @@ instruct vxor8B(vecD dst, vecD src1, vecD src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical64); %} instruct vxor16B(vecX dst, vecX src1, vecX src2) @@ -15736,7 +16227,7 @@ instruct vxor16B(vecX dst, vecX src1, vecX src2) as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vlogical128); %} // ------------------------------ Shift --------------------------------------- @@ -15747,7 +16238,7 @@ instruct vshiftcntL(vecX dst, iRegIorL2I cnt) %{ ins_encode %{ __ dup(as_FloatRegister($dst$$reg), __ T16B, as_Register($cnt$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} // Right shifts on aarch64 SIMD are implemented as left shift by -ve amount @@ -15758,7 +16249,7 @@ instruct vshiftcntR(vecX dst, iRegIorL2I cnt) %{ __ dup(as_FloatRegister($dst$$reg), __ T16B, as_Register($cnt$$reg)); __ negr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($dst$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vdup_reg_reg128); %} instruct vsll8B(vecD dst, vecD src, vecX shift) %{ @@ -15773,7 +16264,7 @@ instruct vsll8B(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64); %} instruct vsll16B(vecX dst, vecX src, vecX shift) %{ @@ -15787,7 +16278,7 @@ instruct vsll16B(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsrl8B(vecD dst, vecD src, vecX shift) %{ @@ -15801,7 +16292,7 @@ instruct vsrl8B(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64); %} instruct vsrl16B(vecX dst, vecX src, vecX shift) %{ @@ -15814,7 +16305,7 @@ instruct vsrl16B(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsll8B_imm(vecD dst, vecD src, immI shift) %{ @@ -15834,7 +16325,7 @@ instruct vsll8B_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), sh); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsll16B_imm(vecX dst, vecX src, immI shift) %{ @@ -15853,7 +16344,7 @@ instruct vsll16B_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), sh); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsra8B_imm(vecD dst, vecD src, immI shift) %{ @@ -15869,7 +16360,7 @@ instruct vsra8B_imm(vecD dst, vecD src, immI shift) %{ __ sshr(as_FloatRegister($dst$$reg), __ T8B, as_FloatRegister($src$$reg), sh); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsra16B_imm(vecX dst, vecX src, immI shift) %{ @@ -15884,7 +16375,7 @@ instruct vsra16B_imm(vecX dst, vecX src, immI shift) %{ __ sshr(as_FloatRegister($dst$$reg), __ T16B, as_FloatRegister($src$$reg), sh); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsrl8B_imm(vecD dst, vecD src, immI shift) %{ @@ -15904,7 +16395,7 @@ instruct vsrl8B_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), -sh & 7); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsrl16B_imm(vecX dst, vecX src, immI shift) %{ @@ -15923,7 +16414,7 @@ instruct vsrl16B_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -sh & 7); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsll4S(vecD dst, vecD src, vecX shift) %{ @@ -15938,7 +16429,7 @@ instruct vsll4S(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64); %} instruct vsll8S(vecX dst, vecX src, vecX shift) %{ @@ -15952,7 +16443,7 @@ instruct vsll8S(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsrl4S(vecD dst, vecD src, vecX shift) %{ @@ -15966,7 +16457,7 @@ instruct vsrl4S(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64); %} instruct vsrl8S(vecX dst, vecX src, vecX shift) %{ @@ -15979,7 +16470,7 @@ instruct vsrl8S(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsll4S_imm(vecD dst, vecD src, immI shift) %{ @@ -15999,7 +16490,7 @@ instruct vsll4S_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), sh); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsll8S_imm(vecX dst, vecX src, immI shift) %{ @@ -16018,7 +16509,7 @@ instruct vsll8S_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), sh); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsra4S_imm(vecD dst, vecD src, immI shift) %{ @@ -16034,7 +16525,7 @@ instruct vsra4S_imm(vecD dst, vecD src, immI shift) %{ __ sshr(as_FloatRegister($dst$$reg), __ T4H, as_FloatRegister($src$$reg), sh); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsra8S_imm(vecX dst, vecX src, immI shift) %{ @@ -16049,7 +16540,7 @@ instruct vsra8S_imm(vecX dst, vecX src, immI shift) %{ __ sshr(as_FloatRegister($dst$$reg), __ T8H, as_FloatRegister($src$$reg), sh); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsrl4S_imm(vecD dst, vecD src, immI shift) %{ @@ -16069,7 +16560,7 @@ instruct vsrl4S_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), -sh & 15); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsrl8S_imm(vecX dst, vecX src, immI shift) %{ @@ -16088,7 +16579,7 @@ instruct vsrl8S_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -sh & 15); } %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsll2I(vecD dst, vecD src, vecX shift) %{ @@ -16102,7 +16593,7 @@ instruct vsll2I(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsll4I(vecX dst, vecX src, vecX shift) %{ @@ -16116,7 +16607,7 @@ instruct vsll4I(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsrl2I(vecD dst, vecD src, vecX shift) %{ @@ -16129,7 +16620,7 @@ instruct vsrl2I(vecD dst, vecD src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsrl4I(vecX dst, vecX src, vecX shift) %{ @@ -16142,7 +16633,7 @@ instruct vsrl4I(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsll2I_imm(vecD dst, vecD src, immI shift) %{ @@ -16155,7 +16646,7 @@ instruct vsll2I_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), (int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsll4I_imm(vecX dst, vecX src, immI shift) %{ @@ -16168,7 +16659,7 @@ instruct vsll4I_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), (int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsra2I_imm(vecD dst, vecD src, immI shift) %{ @@ -16181,7 +16672,7 @@ instruct vsra2I_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsra4I_imm(vecX dst, vecX src, immI shift) %{ @@ -16194,7 +16685,7 @@ instruct vsra4I_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsrl2I_imm(vecD dst, vecD src, immI shift) %{ @@ -16207,7 +16698,7 @@ instruct vsrl2I_imm(vecD dst, vecD src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift64_imm); %} instruct vsrl4I_imm(vecX dst, vecX src, immI shift) %{ @@ -16220,7 +16711,7 @@ instruct vsrl4I_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 31); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsll2L(vecX dst, vecX src, vecX shift) %{ @@ -16234,7 +16725,7 @@ instruct vsll2L(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsrl2L(vecX dst, vecX src, vecX shift) %{ @@ -16247,7 +16738,7 @@ instruct vsrl2L(vecX dst, vecX src, vecX shift) %{ as_FloatRegister($src$$reg), as_FloatRegister($shift$$reg)); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsll2L_imm(vecX dst, vecX src, immI shift) %{ @@ -16260,7 +16751,7 @@ instruct vsll2L_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), (int)$shift$$constant & 63); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128); %} instruct vsra2L_imm(vecX dst, vecX src, immI shift) %{ @@ -16273,7 +16764,7 @@ instruct vsra2L_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 63); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} instruct vsrl2L_imm(vecX dst, vecX src, immI shift) %{ @@ -16286,7 +16777,7 @@ instruct vsrl2L_imm(vecX dst, vecX src, immI shift) %{ as_FloatRegister($src$$reg), -(int)$shift$$constant & 63); %} - ins_pipe(pipe_class_default); + ins_pipe(vshift128_imm); %} //----------PEEPHOLE RULES----------------------------------------------------- diff --git a/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp index 865b4c20374..3b51e62e7ca 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp @@ -256,6 +256,7 @@ void PatchingStub::emit_code(LIR_Assembler* ce) { void DeoptimizeStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); + ce->store_parameter(_trap_request, 0); __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::deoptimize_id))); ce->add_call_info_here(_info); DEBUG_ONLY(__ should_not_reach_here()); diff --git a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp index 210b29e1e75..e32a3c76b79 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_LIRAssembler_aarch64.cpp @@ -3169,7 +3169,8 @@ void LIR_Assembler::atomic_op(LIR_Code code, LIR_Opr src, LIR_Opr data, LIR_Opr Register obj = as_reg(data); Register dst = as_reg(dest); if (is_oop && UseCompressedOops) { - __ encode_heap_oop(obj); + __ encode_heap_oop(rscratch1, obj); + obj = rscratch1; } assert_different_registers(obj, addr.base(), tmp, rscratch2, dst); Label again; diff --git a/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp index 77498c35972..b1df18ade63 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp @@ -1066,7 +1066,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { { StubFrame f(sasm, "deoptimize", dont_gc_arguments); OopMap* oop_map = save_live_registers(sasm); - int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize)); + f.load_argument(0, c_rarg1); + int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, deoptimize), c_rarg1); + oop_maps = new OopMapSet(); oop_maps->add_gc_map(call_offset, oop_map); restore_live_registers(sasm); @@ -1148,9 +1150,6 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { #if INCLUDE_ALL_GCS -// Registers to be saved around calls to g1_wb_pre or g1_wb_post -#define G1_SAVE_REGS (RegSet::range(r0, r18) - RegSet::of(rscratch1, rscratch2)) - case g1_pre_barrier_slow_id: { StubFrame f(sasm, "g1_pre_barrier", dont_gc_arguments); @@ -1192,10 +1191,10 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ b(done); __ bind(runtime); - __ push(G1_SAVE_REGS, sp); + __ push_call_clobbered_registers(); f.load_argument(0, pre_val); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), pre_val, thread); - __ pop(G1_SAVE_REGS, sp); + __ pop_call_clobbered_registers(); __ bind(done); } break; @@ -1223,45 +1222,49 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + DirtyCardQueue::byte_offset_of_buf())); - const Register card_addr = rscratch2; - ExternalAddress cardtable((address) ct->byte_map_base); + const Register card_offset = rscratch2; + // LR is free here, so we can use it to hold the byte_map_base. + const Register byte_map_base = lr; - f.load_argument(0, card_addr); - __ lsr(card_addr, card_addr, CardTableModRefBS::card_shift); - unsigned long offset; - __ adrp(rscratch1, cardtable, offset); - __ add(card_addr, card_addr, rscratch1); - __ ldrb(rscratch1, Address(card_addr, offset)); + assert_different_registers(card_offset, byte_map_base, rscratch1); + + f.load_argument(0, card_offset); + __ lsr(card_offset, card_offset, CardTableModRefBS::card_shift); + __ load_byte_map_base(byte_map_base); + __ ldrb(rscratch1, Address(byte_map_base, card_offset)); __ cmpw(rscratch1, (int)G1SATBCardTableModRefBS::g1_young_card_val()); __ br(Assembler::EQ, done); assert((int)CardTableModRefBS::dirty_card_val() == 0, "must be 0"); __ membar(Assembler::StoreLoad); - __ ldrb(rscratch1, Address(card_addr, offset)); + __ ldrb(rscratch1, Address(byte_map_base, card_offset)); __ cbzw(rscratch1, done); // storing region crossing non-NULL, card is clean. // dirty card and log. - __ strb(zr, Address(card_addr, offset)); + __ strb(zr, Address(byte_map_base, card_offset)); + + // Convert card offset into an address in card_addr + Register card_addr = card_offset; + __ add(card_addr, byte_map_base, card_addr); __ ldr(rscratch1, queue_index); __ cbz(rscratch1, runtime); __ sub(rscratch1, rscratch1, wordSize); __ str(rscratch1, queue_index); - const Register buffer_addr = r0; + // Reuse LR to hold buffer_addr + const Register buffer_addr = lr; - __ push(RegSet::of(r0, r1), sp); __ ldr(buffer_addr, buffer); __ str(card_addr, Address(buffer_addr, rscratch1)); - __ pop(RegSet::of(r0, r1), sp); __ b(done); __ bind(runtime); - __ push(G1_SAVE_REGS, sp); + __ push_call_clobbered_registers(); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), card_addr, thread); - __ pop(G1_SAVE_REGS, sp); + __ pop_call_clobbered_registers(); __ bind(done); } diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp index 5ac81bec145..e4f0c4ac902 100644 --- a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp @@ -2296,6 +2296,30 @@ void MacroAssembler::c_stub_prolog(int gp_arg_count, int fp_arg_count, int ret_t } #endif +void MacroAssembler::push_call_clobbered_registers() { + push(RegSet::range(r0, r18) - RegSet::of(rscratch1, rscratch2), sp); + + // Push v0-v7, v16-v31. + for (int i = 30; i >= 0; i -= 2) { + if (i <= v7->encoding() || i >= v16->encoding()) { + stpd(as_FloatRegister(i), as_FloatRegister(i+1), + Address(pre(sp, -2 * wordSize))); + } + } +} + +void MacroAssembler::pop_call_clobbered_registers() { + + for (int i = 0; i < 32; i += 2) { + if (i <= v7->encoding() || i >= v16->encoding()) { + ldpd(as_FloatRegister(i), as_FloatRegister(i+1), + Address(post(sp, 2 * wordSize))); + } + } + + pop(RegSet::range(r0, r18) - RegSet::of(rscratch1, rscratch2), sp); +} + void MacroAssembler::push_CPU_state(bool save_vectors) { push(0x3fffffff, sp); // integer registers except lr & sp @@ -3094,12 +3118,7 @@ void MacroAssembler::store_check(Register obj) { assert(CardTableModRefBS::dirty_card_val() == 0, "must be"); - { - ExternalAddress cardtable((address) ct->byte_map_base); - unsigned long offset; - adrp(rscratch1, cardtable, offset); - assert(offset == 0, "byte_map_base is misaligned"); - } + load_byte_map_base(rscratch1); if (UseCondCardMark) { Label L_already_dirty; @@ -3591,12 +3610,10 @@ void MacroAssembler::g1_write_barrier_post(Register store_addr, lsr(card_addr, store_addr, CardTableModRefBS::card_shift); - unsigned long offset; - adrp(tmp2, cardtable, offset); - // get the address of the card + load_byte_map_base(tmp2); add(card_addr, card_addr, tmp2); - ldrb(tmp2, Address(card_addr, offset)); + ldrb(tmp2, Address(card_addr)); cmpw(tmp2, (int)G1SATBCardTableModRefBS::g1_young_card_val()); br(Assembler::EQ, done); @@ -3604,13 +3621,13 @@ void MacroAssembler::g1_write_barrier_post(Register store_addr, membar(Assembler::StoreLoad); - ldrb(tmp2, Address(card_addr, offset)); + ldrb(tmp2, Address(card_addr)); cbzw(tmp2, done); // storing a region crossing, non-NULL oop, card is clean. // dirty card and log. - strb(zr, Address(card_addr, offset)); + strb(zr, Address(card_addr)); ldr(rscratch1, queue_index); cbz(rscratch1, runtime); @@ -3933,7 +3950,7 @@ void MacroAssembler::bang_stack_size(Register size, Register tmp) { // was post-decremented.) Skip this address by starting at i=1, and // touch a few more pages below. N.B. It is important to touch all // the way down to and including i=StackShadowPages. - for (int i = 0; i < (JavaThread::stack_shadow_zone_size() / os::vm_page_size()) - 1; i++) { + for (int i = 0; i < (int)(JavaThread::stack_shadow_zone_size() / os::vm_page_size()) - 1; i++) { // this could be any sized move but this is can be a debugging crumb // so the bigger the better. lea(tmp, Address(tmp, -os::vm_page_size())); @@ -3966,6 +3983,9 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, unsigned long &byt long offset_low = dest_page - low_page; long offset_high = dest_page - high_page; + assert(is_valid_AArch64_address(dest.target()), "bad address"); + assert(dest.getMode() == Address::literal, "ADRP must be applied to a literal address"); + InstructionMark im(this); code_section()->relocate(inst_mark(), dest.rspec()); // 8143067: Ensure that the adrp can reach the dest from anywhere within @@ -3977,11 +3997,26 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, unsigned long &byt long offset = dest_page - pc_page; offset = (offset & ((1<<20)-1)) << 12; _adrp(reg1, pc()+offset); - movk(reg1, ((unsigned long)dest.target() >> 32) & 0xffff, 32); + movk(reg1, (unsigned long)dest.target() >> 32, 32); } byte_offset = (unsigned long)dest.target() & 0xfff; } +void MacroAssembler::load_byte_map_base(Register reg) { + jbyte *byte_map_base = + ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base; + + if (is_valid_AArch64_address((address)byte_map_base)) { + // Strictly speaking the byte_map_base isn't an address at all, + // and it might even be negative. + unsigned long offset; + adrp(reg, ExternalAddress((address)byte_map_base), offset); + assert(offset == 0, "misaligned card table base"); + } else { + mov(reg, (uint64_t)byte_map_base); + } +} + void MacroAssembler::build_frame(int framesize) { assert(framesize > 0, "framesize must be > 0"); if (framesize < ((1 << 9) + 2 * wordSize)) { diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp index 99afc3b5522..4256d3a0989 100644 --- a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp +++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.hpp @@ -437,6 +437,13 @@ public: void push(RegSet regs, Register stack) { if (regs.bits()) push(regs.bits(), stack); } void pop(RegSet regs, Register stack) { if (regs.bits()) pop(regs.bits(), stack); } + // Push and pop everything that might be clobbered by a native + // runtime call except rscratch1 and rscratch2. (They are always + // scratch, so we don't have to protect them.) Only save the lower + // 64 bits of each vector register. + void push_call_clobbered_registers(); + void pop_call_clobbered_registers(); + // now mov instructions for loading absolute addresses and 32 or // 64 bit integers @@ -1116,6 +1123,15 @@ public: // of your data. Address form_address(Register Rd, Register base, long byte_offset, int shift); + // Return true iff an address is within the 48-bit AArch64 address + // space. + bool is_valid_AArch64_address(address a) { + return ((uint64_t)a >> 48) == 0; + } + + // Load the base of the cardtable byte map into reg. + void load_byte_map_base(Register reg); + // Prolog generator routines to support switch between x86 code and // generated ARM code diff --git a/hotspot/src/cpu/aarch64/vm/relocInfo_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/relocInfo_aarch64.cpp index 40b53127487..9b80b0b48ec 100644 --- a/hotspot/src/cpu/aarch64/vm/relocInfo_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/relocInfo_aarch64.cpp @@ -87,7 +87,6 @@ void Relocation::pd_set_call_destination(address x) { return; } } - assert(addr() != x, "call instruction in an infinite loop"); MacroAssembler::pd_patch_instruction(addr(), x); assert(pd_call_destination(addr()) == x, "fail in reloc"); } diff --git a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp index 32e92a99e7d..6d8042f604a 100644 --- a/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/stubGenerator_aarch64.cpp @@ -744,7 +744,7 @@ class StubGenerator: public StubCodeGenerator { __ sub(end, end, start); // number of bytes to copy const Register count = end; // 'end' register contains bytes count now - __ mov(scratch, (address)ct->byte_map_base); + __ load_byte_map_base(scratch); __ add(start, start, scratch); if (UseConcMarkSweepGC) { __ membar(__ StoreStore); diff --git a/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp index 112c4e1d535..e37c0881d5c 100644 --- a/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/templateInterpreterGenerator_aarch64.cpp @@ -930,7 +930,7 @@ address TemplateInterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return NULL; } /** @@ -1056,7 +1056,7 @@ void TemplateInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { // an interpreter frame with greater than a page of locals, so each page // needs to be checked. Only true for non-native. if (UseStackBanging) { - const int size_t n_shadow_pages = JavaThread::stack_shadow_zone_size() / os::vm_page_size(); + const int n_shadow_pages = JavaThread::stack_shadow_zone_size() / os::vm_page_size(); const int start_page = native_call ? n_shadow_pages : 1; const int page_size = os::vm_page_size(); for (int pages = start_page; pages <= n_shadow_pages ; pages++) { @@ -1398,8 +1398,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { { Label no_reguard; __ lea(rscratch1, Address(rthread, in_bytes(JavaThread::stack_guard_state_offset()))); - __ ldrb(rscratch1, Address(rscratch1)); - __ cmp(rscratch1, JavaThread::stack_guard_yellow_disabled); + __ ldrw(rscratch1, Address(rscratch1)); + __ cmp(rscratch1, JavaThread::stack_guard_yellow_reserved_disabled); __ br(Assembler::NE, no_reguard); __ pusha(); // XXX only save smashed registers diff --git a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp index d48f4ee3a25..6de94bee33d 100644 --- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp @@ -121,7 +121,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 256); FLAG_SET_DEFAULT(PrefetchFieldsAhead, 256); FLAG_SET_DEFAULT(PrefetchCopyIntervalInBytes, 256); - FLAG_SET_DEFAULT(UseSSE42Intrinsics, true); unsigned long auxv = getauxval(AT_HWCAP); diff --git a/hotspot/src/cpu/ppc/vm/abstractInterpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/abstractInterpreter_ppc.cpp index ef83f924eb7..eaff19ddf71 100644 --- a/hotspot/src/cpu/ppc/vm/abstractInterpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/abstractInterpreter_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015 SAP AG. All rights reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/assembler_ppc.cpp b/hotspot/src/cpu/ppc/vm/assembler_ppc.cpp index 66e4f08c3d4..c09703ff916 100644 --- a/hotspot/src/cpu/ppc/vm/assembler_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/assembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp index 035bd4abf26..3cae863f41c 100644 --- a/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp b/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp index 9248ae5cb6f..8393a579151 100644 --- a/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp +++ b/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/bytes_ppc.hpp b/hotspot/src/cpu/ppc/vm/bytes_ppc.hpp index 2a8fc59c892..be005cbbfba 100644 --- a/hotspot/src/cpu/ppc/vm/bytes_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/bytes_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_CodeStubs_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_CodeStubs_ppc.cpp index a19c7b7de11..eca7d87a22e 100644 --- a/hotspot/src/cpu/ppc/vm/c1_CodeStubs_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_CodeStubs_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_Defs_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_Defs_ppc.hpp index 73edcaba207..4f7075c17fa 100644 --- a/hotspot/src/cpu/ppc/vm/c1_Defs_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_Defs_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_FpuStackSim_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_FpuStackSim_ppc.hpp index 19f85034ba9..6f2f90ba0da 100644 --- a/hotspot/src/cpu/ppc/vm/c1_FpuStackSim_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_FpuStackSim_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.cpp index 90afc7873ce..5a50b965371 100644 --- a/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.hpp index 5eee5ffca1f..8ce25e97b87 100644 --- a/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_FrameMap_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.cpp index 8a64bab4be4..b7bc0cbf50d 100644 --- a/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.hpp index 03d1faedbf9..543156c0951 100644 --- a/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_LIRAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_LIRGenerator_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_LIRGenerator_ppc.cpp index 909d0136011..cba04106f8c 100644 --- a/hotspot/src/cpu/ppc/vm/c1_LIRGenerator_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_LIRGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.cpp index 7c73b1c70a8..026540f25b2 100644 --- a/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.hpp index d0f6002929e..261db8d77ef 100644 --- a/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_LinearScan_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.cpp index f41a83b1c71..851b0b644ce 100644 --- a/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.hpp index 9989baa8700..4f40b20b551 100644 --- a/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_MacroAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_Runtime1_ppc.cpp b/hotspot/src/cpu/ppc/vm/c1_Runtime1_ppc.cpp index 5fbaa4beb4e..1aa158b4a8f 100644 --- a/hotspot/src/cpu/ppc/vm/c1_Runtime1_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c1_Runtime1_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c1_globals_ppc.hpp b/hotspot/src/cpu/ppc/vm/c1_globals_ppc.hpp index 234248e387e..b641852e8a3 100644 --- a/hotspot/src/cpu/ppc/vm/c1_globals_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c1_globals_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp b/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp index 9934d1f0143..2a5ae0f248f 100644 --- a/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/c2_init_ppc.cpp b/hotspot/src/cpu/ppc/vm/c2_init_ppc.cpp index a9fe522671c..4df27d43d1b 100644 --- a/hotspot/src/cpu/ppc/vm/c2_init_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/c2_init_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/codeBuffer_ppc.hpp b/hotspot/src/cpu/ppc/vm/codeBuffer_ppc.hpp index 1855aea1f29..ae618ceffd0 100644 --- a/hotspot/src/cpu/ppc/vm/codeBuffer_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/codeBuffer_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/compiledIC_ppc.cpp b/hotspot/src/cpu/ppc/vm/compiledIC_ppc.cpp index 197d3069817..68dabc4b031 100644 --- a/hotspot/src/cpu/ppc/vm/compiledIC_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/compiledIC_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/copy_ppc.hpp b/hotspot/src/cpu/ppc/vm/copy_ppc.hpp index 0094c0c6d30..06a5a010033 100644 --- a/hotspot/src/cpu/ppc/vm/copy_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/copy_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/debug_ppc.cpp b/hotspot/src/cpu/ppc/vm/debug_ppc.cpp index dc91efc78bb..5427a1e4644 100644 --- a/hotspot/src/cpu/ppc/vm/debug_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/debug_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/depChecker_ppc.hpp b/hotspot/src/cpu/ppc/vm/depChecker_ppc.hpp index 33f04c06050..07ed6267640 100644 --- a/hotspot/src/cpu/ppc/vm/depChecker_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/depChecker_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/disassembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/disassembler_ppc.hpp index eb2a81d390e..dc98a8bb06a 100644 --- a/hotspot/src/cpu/ppc/vm/disassembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/disassembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/frame_ppc.cpp b/hotspot/src/cpu/ppc/vm/frame_ppc.cpp index 29921834ac1..08463758370 100644 --- a/hotspot/src/cpu/ppc/vm/frame_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/frame_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/frame_ppc.hpp b/hotspot/src/cpu/ppc/vm/frame_ppc.hpp index 3b90da157a5..8115a20640e 100644 --- a/hotspot/src/cpu/ppc/vm/frame_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/frame_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp b/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp index 2c90cd2a6a1..7fce0585f10 100644 --- a/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp +++ b/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp b/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp index f7aa82d6cde..ba981816f4a 100644 --- a/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/globals_ppc.hpp b/hotspot/src/cpu/ppc/vm/globals_ppc.hpp index b3a1ef5d729..695014fd836 100644 --- a/hotspot/src/cpu/ppc/vm/globals_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/globals_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/icBuffer_ppc.cpp b/hotspot/src/cpu/ppc/vm/icBuffer_ppc.cpp index 8e16078b3d7..6e2c34341a5 100644 --- a/hotspot/src/cpu/ppc/vm/icBuffer_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/icBuffer_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/icache_ppc.cpp b/hotspot/src/cpu/ppc/vm/icache_ppc.cpp index 641841b9463..916d3d72590 100644 --- a/hotspot/src/cpu/ppc/vm/icache_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/icache_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/icache_ppc.hpp b/hotspot/src/cpu/ppc/vm/icache_ppc.hpp index 4941f5586f8..fc9790f2133 100644 --- a/hotspot/src/cpu/ppc/vm/icache_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/icache_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp index 6c88cede5b8..8a75af011bd 100644 --- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp +++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.cpp b/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.cpp index 98ab1041dde..7869ef1238c 100644 --- a/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.hpp b/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.hpp index da98715a102..d71781cbc83 100644 --- a/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/interpreterRT_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp b/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp index 26ca0dbf181..2b66bbce292 100644 --- a/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/jniFastGetField_ppc.cpp b/hotspot/src/cpu/ppc/vm/jniFastGetField_ppc.cpp index b16be25c5c0..8447e1c37d9 100644 --- a/hotspot/src/cpu/ppc/vm/jniFastGetField_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/jniFastGetField_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/jniTypes_ppc.hpp b/hotspot/src/cpu/ppc/vm/jniTypes_ppc.hpp index 9178bd442ad..7179e998931 100644 --- a/hotspot/src/cpu/ppc/vm/jniTypes_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/jniTypes_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/jni_ppc.h b/hotspot/src/cpu/ppc/vm/jni_ppc.h index da8cc361c14..64e4b9bf061 100644 --- a/hotspot/src/cpu/ppc/vm/jni_ppc.h +++ b/hotspot/src/cpu/ppc/vm/jni_ppc.h @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp index 452d5db41b2..e9da9714579 100644 --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2016 SAP SE. 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 @@ -3172,11 +3172,12 @@ void MacroAssembler::clear_memory_doubleword(Register base_ptr, Register cnt_dwo // // Assumes that result differs from all other registers. // -// Haystack, needle are the addresses of jchar-arrays. -// NeedleChar is needle[0] if it is known at compile time. -// Haycnt is the length of the haystack. We assume haycnt >=1. +// 'haystack' is the addresses of a jchar-array. +// 'needle' is either the character to search for or R0. +// 'needleChar' is the character to search for if 'needle' == R0.. +// 'haycnt' is the length of the haystack. We assume 'haycnt' >=1. // -// Preserves haystack, haycnt, kills all other registers. +// Preserves haystack, haycnt, needle and kills all other registers. // // If needle == R0, we search for the constant needleChar. void MacroAssembler::string_indexof_1(Register result, Register haystack, Register haycnt, @@ -3186,13 +3187,11 @@ void MacroAssembler::string_indexof_1(Register result, Register haystack, Regist assert_different_registers(result, haystack, haycnt, needle, tmp1, tmp2); Label L_InnerLoop, L_FinalCheck, L_Found1, L_Found2, L_Found3, L_NotFound, L_End; - Register needle0 = needle, // Contains needle[0]. - addr = tmp1, + Register addr = tmp1, ch1 = tmp2, ch2 = R0; -//2 (variable) or 3 (const): - if (needle != R0) lhz(needle0, 0, needle); // Preload needle character, needle has len==1. +//3: dcbtct(haystack, 0x00); // Indicate R/O access to haystack. srwi_(tmp2, haycnt, 1); // Shift right by exact_log2(UNROLL_FACTOR). @@ -3203,8 +3202,8 @@ void MacroAssembler::string_indexof_1(Register result, Register haystack, Regist bind(L_InnerLoop); // Main work horse (2x unrolled search loop). lhz(ch1, 0, addr); // Load characters from haystack. lhz(ch2, 2, addr); - (needle != R0) ? cmpw(CCR0, ch1, needle0) : cmplwi(CCR0, ch1, needleChar); - (needle != R0) ? cmpw(CCR1, ch2, needle0) : cmplwi(CCR1, ch2, needleChar); + (needle != R0) ? cmpw(CCR0, ch1, needle) : cmplwi(CCR0, ch1, needleChar); + (needle != R0) ? cmpw(CCR1, ch2, needle) : cmplwi(CCR1, ch2, needleChar); beq(CCR0, L_Found1); // Did we find the needle? beq(CCR1, L_Found2); addi(addr, addr, 4); @@ -3214,7 +3213,7 @@ void MacroAssembler::string_indexof_1(Register result, Register haystack, Regist andi_(R0, haycnt, 1); beq(CCR0, L_NotFound); lhz(ch1, 0, addr); // One position left at which we have to compare. - (needle != R0) ? cmpw(CCR1, ch1, needle0) : cmplwi(CCR1, ch1, needleChar); + (needle != R0) ? cmpw(CCR1, ch1, needle) : cmplwi(CCR1, ch1, needleChar); beq(CCR1, L_Found3); //21: bind(L_NotFound); @@ -3399,7 +3398,15 @@ void MacroAssembler::string_compare(Register str1_reg, Register str2_reg, Regist chr2_reg = cnt2_reg, addr_diff = str2_reg; + // 'cnt_reg' contains the number of characters in the string's character array for the + // pre-CompactStrings strings implementation and the number of bytes in the string's + // byte array for the CompactStrings strings implementation. + const int HAS_COMPACT_STRING = java_lang_String::has_coder_field() ? 1 : 0; // '1' = byte array, '0' = char array + // Offset 0 should be 32 byte aligned. +//-6: + srawi(cnt1_reg, cnt1_reg, HAS_COMPACT_STRING); + srawi(cnt2_reg, cnt2_reg, HAS_COMPACT_STRING); //-4: dcbtct(str1_reg, 0x00); // Indicate R/O access to str1. dcbtct(str2_reg, 0x00); // Indicate R/O access to str2. @@ -3478,14 +3485,21 @@ void MacroAssembler::char_arrays_equals(Register str1_reg, Register str2_reg, Re Register index_reg = tmp5_reg; Register cbc_iter = tmp4_reg; + // 'cnt_reg' contains the number of characters in the string's character array for the + // pre-CompactStrings strings implementation and the number of bytes in the string's + // byte array for the CompactStrings strings implementation. + const int HAS_COMPACT_STRING = java_lang_String::has_coder_field() ? 1 : 0; // '1' = byte array, '0' = char array + //-1: dcbtct(str1_reg, 0x00); // Indicate R/O access to str1. dcbtct(str2_reg, 0x00); // Indicate R/O access to str2. //1: - andi(cbc_iter, cnt_reg, 4-1); // Remaining iterations after 4 java characters per iteration loop. + // cbc_iter: remaining characters after the '4 java characters per iteration' loop. + rlwinm(cbc_iter, cnt_reg, 32 - HAS_COMPACT_STRING, 30, 31); // (cnt_reg % (HAS_COMPACT_STRING ? 8 : 4)) >> HAS_COMPACT_STRING li(index_reg, 0); // init li(result_reg, 0); // assume false - srwi_(tmp2_reg, cnt_reg, exact_log2(4)); // Div: 4 java characters per iteration (main loop). + // tmp2_reg: units of 4 java characters (i.e. 8 bytes) per iteration (main loop). + srwi_(tmp2_reg, cnt_reg, exact_log2(4 << HAS_COMPACT_STRING)); // cnt_reg / (HAS_COMPACT_STRING ? 8 : 4) cmpwi(CCR1, cbc_iter, 0); // CCR1 = (cbc_iter==0) beq(CCR0, Linit_cbc); // too short @@ -3526,6 +3540,11 @@ void MacroAssembler::char_arrays_equalsImm(Register str1_reg, Register str2_reg, assert(sizeof(jchar) == 2, "must be"); assert(cntval >= 0 && ((cntval & 0x7fff) == cntval), "wrong immediate"); + // 'cntval' contains the number of characters in the string's character array for the + // pre-CompactStrings strings implementation and the number of bytes in the string's + // byte array for the CompactStrings strings implementation. + cntval >>= (java_lang_String::has_coder_field() ? 1 : 0); // '1' = byte array strings, '0' = char array strings + Label Ldone_false; if (cntval < 16) { // short case @@ -3652,9 +3671,9 @@ int MacroAssembler::crc32_table_columns(Register table, Register tc0, Register t assert_different_registers(table, tc0, tc1, tc2); assert(table == tc3, "must be!"); - if (ix0 != 0) addi(tc0, table, ix0); - if (ix1 != 0) addi(tc1, table, ix1); - if (ix2 != 0) addi(tc2, table, ix2); + addi(tc0, table, ix0); + addi(tc1, table, ix1); + addi(tc2, table, ix2); if (ix3 != 0) addi(tc3, table, ix3); return ix3; @@ -3720,14 +3739,14 @@ void MacroAssembler::update_byteLoop_crc32(Register crc, Register buf, Register const int mainLoop_alignment = loopAlignment ? 32 : 4; // (InputForNewCode > 4 ? InputForNewCode : 32) : 4; // Process all bytes in a single-byte loop. - cmpdi(CCR0, len, 0); // Anything to do? - mtctr(len); + clrldi_(len, len, 32); // Enforce 32 bit. Anything to do? beq(CCR0, L_done); if (invertCRC) { nand(crc, crc, crc); // ~c } + mtctr(len); align(mainLoop_alignment); BIND(L_mainLoop); lbz(data, 0, buf); // Byte from buffer, zero-extended. @@ -3943,7 +3962,7 @@ void MacroAssembler::kernel_crc32_1word(Register crc, Register buf, Register len #else Register crc_rv = tmp; // Load_reverse needs separate registers to work on. // Occupies tmp, but frees up crc. - load_reverse_32(crc_rv, crc); // evert byte order because we are dealing with big-endian data. + load_reverse_32(crc_rv, crc); // Revert byte order because we are dealing with big-endian data. tmp = crc; #endif diff --git a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp index df58832b160..925e73a82d1 100644 --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp index 62843482074..a15f07528b6 100644 --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp b/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp index 9aa479a0e09..6fd569a1122 100644 --- a/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp b/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp index 12e10dbc573..dea4bfca545 100644 --- a/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/methodHandles_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/methodHandles_ppc.hpp b/hotspot/src/cpu/ppc/vm/methodHandles_ppc.hpp index 63fd6070272..144e1f0f369 100644 --- a/hotspot/src/cpu/ppc/vm/methodHandles_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/methodHandles_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/nativeInst_ppc.cpp b/hotspot/src/cpu/ppc/vm/nativeInst_ppc.cpp index 37956925d0d..08704344050 100644 --- a/hotspot/src/cpu/ppc/vm/nativeInst_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/nativeInst_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/nativeInst_ppc.hpp b/hotspot/src/cpu/ppc/vm/nativeInst_ppc.hpp index f0f6b6a87d9..586dcaa9380 100644 --- a/hotspot/src/cpu/ppc/vm/nativeInst_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/nativeInst_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/ppc.ad b/hotspot/src/cpu/ppc/vm/ppc.ad index 7636e367d85..e9dfa9c5bf1 100644 --- a/hotspot/src/cpu/ppc/vm/ppc.ad +++ b/hotspot/src/cpu/ppc/vm/ppc.ad @@ -1,6 +1,6 @@ // // Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. -// Copyright 2012, 2015 SAP AG. All rights reserved. +// Copyright (c) 2012, 2015 SAP SE. 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 @@ -956,36 +956,40 @@ static int cc_to_biint(int cc, int flags_reg) { // the instruction. The padding must match the size of a NOP instruction. int string_indexOf_imm1_charNode::compute_padding(int current_offset) const { - return (3*4-current_offset)&31; + return (3*4-current_offset)&31; // see MacroAssembler::string_indexof_1 } int string_indexOf_imm1Node::compute_padding(int current_offset) const { - return (2*4-current_offset)&31; + return (3*4-current_offset)&31; // see MacroAssembler::string_indexof_1 +} + +int string_indexOfCharNode::compute_padding(int current_offset) const { + return (3*4-current_offset)&31; // see MacroAssembler::string_indexof_1 } int string_indexOf_immNode::compute_padding(int current_offset) const { - return (3*4-current_offset)&31; + return (3*4-current_offset)&31; // see MacroAssembler::string_indexof(constant needlecount) } int string_indexOfNode::compute_padding(int current_offset) const { - return (1*4-current_offset)&31; + return (1*4-current_offset)&31; // see MacroAssembler::string_indexof(variable needlecount) } int string_compareNode::compute_padding(int current_offset) const { - return (4*4-current_offset)&31; + return (2*4-current_offset)&31; // see MacroAssembler::string_compare } int string_equals_immNode::compute_padding(int current_offset) const { - if (opnd_array(3)->constant() < 16) return 0; // Don't insert nops for short version (loop completely unrolled). - return (2*4-current_offset)&31; + if (opnd_array(3)->constant() < 16) return 0; // For strlen < 16 no nops because loop completely unrolled + return (2*4-current_offset)&31; // Genral case - see MacroAssembler::char_arrays_equalsImm } int string_equalsNode::compute_padding(int current_offset) const { - return (7*4-current_offset)&31; + return (7*4-current_offset)&31; // see MacroAssembler::char_arrays_equals } int inlineCallClearArrayNode::compute_padding(int current_offset) const { - return (2*4-current_offset)&31; + return (2*4-current_offset)&31; // see MacroAssembler::clear_memory_doubleword } //============================================================================= @@ -2025,6 +2029,8 @@ const bool Matcher::match_rule_supported(int opcode) { return SpecialStringEquals && !CompactStrings; case Op_StrIndexOf: return SpecialStringIndexOf && !CompactStrings; + case Op_StrIndexOfChar: + return SpecialStringIndexOf && !CompactStrings; } return true; // Per default match rules are supported. @@ -11034,11 +11040,11 @@ instruct inlineCallClearArray(rarg1RegL cnt, rarg2RegP base, Universe dummy, reg instruct string_indexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, immP needleImm, immL offsetImm, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, - flagsRegCR0 cr0, flagsRegCR1 cr1) %{ + flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ predicate(SpecialStringIndexOf && !CompactStrings); // 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_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1); + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); ins_cost(150); format %{ "String IndexOf CSCL1 $haystack[0..$haycnt], $needleImm+$offsetImm[0..$needlecntImm]" @@ -11050,10 +11056,23 @@ instruct string_indexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIsrc h immPOper *needleOper = (immPOper *)$needleImm; const TypeOopPtr *t = needleOper->type()->isa_oopptr(); ciTypeArray* needle_values = t->const_oop()->as_type_array(); // Pointer to live char * - + jchar chr; + if (java_lang_String::has_coder_field()) { + // New compact strings byte array strings +#ifdef VM_LITTLE_ENDIAN + chr = (((jchar)needle_values->element_value(1).as_byte()) << 8) | + (jchar)needle_values->element_value(0).as_byte(); +#else + chr = (((jchar)needle_values->element_value(0).as_byte()) << 8) | + (jchar)needle_values->element_value(1).as_byte(); +#endif + } else { + // Old char array strings + chr = needle_values->char_at(0); + } __ string_indexof_1($result$$Register, $haystack$$Register, $haycnt$$Register, - R0, needle_values->char_at(0), + R0, chr, $tmp1$$Register, $tmp2$$Register); %} ins_pipe(pipe_class_compare); @@ -11073,12 +11092,13 @@ instruct string_indexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIsrc h instruct string_indexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, rscratch2RegP needle, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, - flagsRegCR0 cr0, flagsRegCR1 cr1) %{ + flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); effect(USE_KILL needle, /* TDEF needle, */ TEMP_DEF result, - TEMP tmp1, TEMP tmp2); + TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); // Required for EA: check if it is still a type_array. - predicate(SpecialStringIndexOf && !CompactStrings && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && + predicate(SpecialStringIndexOf && !CompactStrings && + n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); ins_cost(180); @@ -11091,17 +11111,54 @@ instruct string_indexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt Node *ndl = in(operand_index($needle)); // The node that defines needle. ciTypeArray* needle_values = ndl->bottom_type()->is_aryptr()->const_oop()->as_type_array(); guarantee(needle_values, "sanity"); - if (needle_values != NULL) { - __ string_indexof_1($result$$Register, - $haystack$$Register, $haycnt$$Register, - R0, needle_values->char_at(0), - $tmp1$$Register, $tmp2$$Register); + jchar chr; + if (java_lang_String::has_coder_field()) { + // New compact strings byte array strings +#ifdef VM_LITTLE_ENDIAN + chr = (((jchar)needle_values->element_value(1).as_byte()) << 8) | + (jchar)needle_values->element_value(0).as_byte(); +#else + chr = (((jchar)needle_values->element_value(0).as_byte()) << 8) | + (jchar)needle_values->element_value(1).as_byte(); +#endif } else { - __ string_indexof_1($result$$Register, - $haystack$$Register, $haycnt$$Register, - $needle$$Register, 0, - $tmp1$$Register, $tmp2$$Register); + // Old char array strings + chr = needle_values->char_at(0); } + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + R0, chr, + $tmp1$$Register, $tmp2$$Register); + %} + ins_pipe(pipe_class_compare); +%} + +// String_IndexOfChar +// +// Assumes register result differs from all input registers. +// +// Preserves registers haystack, haycnt +// Kills registers tmp1, tmp2 +// Defines registers result +// +// Use dst register classes if register gets killed, as it is the case for tmp registers! +instruct string_indexOfChar(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, + iRegIsrc ch, iRegIdst tmp1, iRegIdst tmp2, + flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ + match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); + predicate(SpecialStringIndexOf && !CompactStrings); + ins_cost(180); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String IndexOfChar $haystack[0..$haycnt], $ch" + " -> $result \t// KILL $haycnt, $tmp1, $tmp2, $cr0, $cr1" %} + ins_encode %{ + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + $ch$$Register, 0 /* this is not used if the character is already in a register */, + $tmp1$$Register, $tmp2$$Register); %} ins_pipe(pipe_class_compare); %} @@ -11120,10 +11177,10 @@ instruct string_indexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt instruct string_indexOf_imm(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, iRegPsrc needle, uimmI15 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, iRegIdst tmp3, iRegIdst tmp4, iRegIdst tmp5, - flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ + flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6, regCTR ctr) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); 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); + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6, KILL ctr); // Required for EA: check if it is still a type_array. predicate(SpecialStringIndexOf && !CompactStrings && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); @@ -11153,11 +11210,11 @@ instruct string_indexOf_imm(iRegIdst result, iRegPsrc haystack, rscratch1RegI ha // Use dst register classes if register gets killed, as it is the case for tmp registers! instruct string_indexOf(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, iRegPsrc needle, rscratch2RegI needlecnt, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, - flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ + flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6, regCTR ctr) %{ match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecnt))); effect(USE_KILL haycnt, USE_KILL needlecnt, /*better: TDEF haycnt, TDEF needlecnt,*/ TEMP_DEF result, - TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr0, KILL cr1, KILL cr6); + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr0, KILL cr1, KILL cr6, KILL ctr); predicate(SpecialStringIndexOf && !CompactStrings); // See Matcher::match_rule_supported. ins_cost(300); diff --git a/hotspot/src/cpu/ppc/vm/ppc_64.ad b/hotspot/src/cpu/ppc/vm/ppc_64.ad index 4c1be69601a..712f18fe969 100644 --- a/hotspot/src/cpu/ppc/vm/ppc_64.ad +++ b/hotspot/src/cpu/ppc/vm/ppc_64.ad @@ -1,6 +1,6 @@ // // Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. -// Copyright 2012, 2013 SAP AG. All rights reserved. +// Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/registerMap_ppc.hpp b/hotspot/src/cpu/ppc/vm/registerMap_ppc.hpp index b3d89c2ba09..06fdff2e891 100644 --- a/hotspot/src/cpu/ppc/vm/registerMap_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/registerMap_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/register_definitions_ppc.cpp b/hotspot/src/cpu/ppc/vm/register_definitions_ppc.cpp index 6b002d2efd2..2a2d968e44d 100644 --- a/hotspot/src/cpu/ppc/vm/register_definitions_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/register_definitions_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/register_ppc.cpp b/hotspot/src/cpu/ppc/vm/register_ppc.cpp index edff7485a97..e0b07c98168 100644 --- a/hotspot/src/cpu/ppc/vm/register_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/register_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/register_ppc.hpp b/hotspot/src/cpu/ppc/vm/register_ppc.hpp index def1f6e3872..8f6dda40eea 100644 --- a/hotspot/src/cpu/ppc/vm/register_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/register_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 @@ -609,13 +609,11 @@ REGISTER_DECLARATION(Register, R26_tmp6, R26); REGISTER_DECLARATION(Register, R27_tmp7, R27); REGISTER_DECLARATION(Register, R28_tmp8, R28); REGISTER_DECLARATION(Register, R29_tmp9, R29); -#ifndef CC_INTERP REGISTER_DECLARATION(Register, R24_dispatch_addr, R24); REGISTER_DECLARATION(Register, R25_templateTableBase, R25); REGISTER_DECLARATION(Register, R26_monitor, R26); REGISTER_DECLARATION(Register, R27_constPoolCache, R27); REGISTER_DECLARATION(Register, R28_mdx, R28); -#endif // CC_INTERP REGISTER_DECLARATION(Register, R19_inline_cache_reg, R19); REGISTER_DECLARATION(Register, R29_TOC, R29); @@ -638,12 +636,9 @@ REGISTER_DECLARATION(Register, R29_TOC, R29); #define R26_monitor AS_REGISTER(Register, R26) #define R27_constPoolCache AS_REGISTER(Register, R27) #define R28_mdx AS_REGISTER(Register, R28) -#endif #define R19_inline_cache_reg AS_REGISTER(Register, R19) #define R29_TOC AS_REGISTER(Register, R29) - -#define CCR4_is_synced AS_REGISTER(ConditionRegister, CCR4) #endif // Scratch registers are volatile. diff --git a/hotspot/src/cpu/ppc/vm/relocInfo_ppc.cpp b/hotspot/src/cpu/ppc/vm/relocInfo_ppc.cpp index 9c5065d0dd4..44b2d79e515 100644 --- a/hotspot/src/cpu/ppc/vm/relocInfo_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/relocInfo_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/relocInfo_ppc.hpp b/hotspot/src/cpu/ppc/vm/relocInfo_ppc.hpp index 3cd7da47ec8..3778e0c674d 100644 --- a/hotspot/src/cpu/ppc/vm/relocInfo_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/relocInfo_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/runtime_ppc.cpp b/hotspot/src/cpu/ppc/vm/runtime_ppc.cpp index e887877d14e..77353f891e7 100644 --- a/hotspot/src/cpu/ppc/vm/runtime_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/runtime_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp index 9d0f9da7696..c945027a50d 100644 --- a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 SAP SE. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016 SAP SE. 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 @@ -3400,9 +3400,9 @@ static void reverse_words(unsigned long *s, unsigned long *d, int len) { void SharedRuntime::montgomery_multiply(jint *a_ints, jint *b_ints, jint *n_ints, jint len, jlong inv, jint *m_ints) { + len = len & 0x7fffFFFF; // C2 does not respect int to long conversion for stub calls. assert(len % 2 == 0, "array length in montgomery_multiply must be even"); int longwords = len/2; - assert(longwords > 0, "unsupported"); // Make very sure we don't use so much space that the stack might // overflow. 512 jints corresponds to an 16384-bit integer and @@ -3430,9 +3430,9 @@ void SharedRuntime::montgomery_multiply(jint *a_ints, jint *b_ints, jint *n_ints void SharedRuntime::montgomery_square(jint *a_ints, jint *n_ints, jint len, jlong inv, jint *m_ints) { + len = len & 0x7fffFFFF; // C2 does not respect int to long conversion for stub calls. assert(len % 2 == 0, "array length in montgomery_square must be even"); int longwords = len/2; - assert(longwords > 0, "unsupported"); // Make very sure we don't use so much space that the stack might // overflow. 512 jints corresponds to an 16384-bit integer and diff --git a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp index 78082716d85..c3f6922fe2d 100644 --- a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016 SAP SE. 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 @@ -1070,6 +1070,12 @@ class StubGenerator: public StubCodeGenerator { return start; } + inline void assert_positive_int(Register count) { +#ifdef ASSERT + __ srdi_(R0, count, 31); + __ asm_assert_eq("missing zero extend", 0xAFFE); +#endif + } // Generate overlap test for array copy stubs. // @@ -1082,10 +1088,7 @@ class StubGenerator: public StubCodeGenerator { Register tmp1 = R6_ARG4; Register tmp2 = R7_ARG5; -#ifdef ASSERT - __ srdi_(tmp2, R5_ARG3, 31); - __ asm_assert_eq("missing zero extend", 0xAFFE); -#endif + assert_positive_int(R5_ARG3); __ subf(tmp1, R3_ARG1, R4_ARG2); // distance in bytes __ sldi(tmp2, R5_ARG3, log2_elem_size); // size in bytes @@ -1125,14 +1128,15 @@ class StubGenerator: public StubCodeGenerator { address generate_disjoint_byte_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); + assert_positive_int(R5_ARG3); Register tmp1 = R6_ARG4; Register tmp2 = R7_ARG5; Register tmp3 = R8_ARG6; Register tmp4 = R9_ARG7; - Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8, l_9; + // Don't try anything fancy if arrays don't have many elements. __ li(tmp3, 0); __ cmpwi(CCR0, R5_ARG3, 17); @@ -1257,6 +1261,7 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_byte_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); + assert_positive_int(R5_ARG3); Register tmp1 = R6_ARG4; Register tmp2 = R7_ARG5; @@ -1349,8 +1354,10 @@ class StubGenerator: public StubCodeGenerator { Register tmp4 = R9_ARG7; address start = __ function_entry(); + assert_positive_int(R5_ARG3); Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8; + // don't try anything fancy if arrays don't have many elements __ li(tmp3, 0); __ cmpwi(CCR0, R5_ARG3, 9); @@ -1479,6 +1486,7 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_short_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); + assert_positive_int(R5_ARG3); Register tmp1 = R6_ARG4; Register tmp2 = R7_ARG5; @@ -1521,6 +1529,7 @@ class StubGenerator: public StubCodeGenerator { Register tmp4 = R0; Label l_1, l_2, l_3, l_4, l_5, l_6; + // for short arrays, just do single element copy __ li(tmp3, 0); __ cmpwi(CCR0, R5_ARG3, 5); @@ -1603,6 +1612,7 @@ class StubGenerator: public StubCodeGenerator { address generate_disjoint_int_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); + assert_positive_int(R5_ARG3); generate_disjoint_int_copy_core(aligned); __ li(R3_RET, 0); // return 0 __ blr(); @@ -1688,7 +1698,7 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_int_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); - + assert_positive_int(R5_ARG3); address nooverlap_target = aligned ? STUB_ENTRY(arrayof_jint_disjoint_arraycopy) : STUB_ENTRY(jint_disjoint_arraycopy); @@ -1775,6 +1785,7 @@ class StubGenerator: public StubCodeGenerator { address generate_disjoint_long_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); + assert_positive_int(R5_ARG3); generate_disjoint_long_copy_core(aligned); __ li(R3_RET, 0); // return 0 __ blr(); @@ -1858,7 +1869,7 @@ class StubGenerator: public StubCodeGenerator { address generate_conjoint_long_copy(bool aligned, const char * name) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); - + assert_positive_int(R5_ARG3); address nooverlap_target = aligned ? STUB_ENTRY(arrayof_jlong_disjoint_arraycopy) : STUB_ENTRY(jlong_disjoint_arraycopy); @@ -1885,7 +1896,7 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); - + assert_positive_int(R5_ARG3); address nooverlap_target = aligned ? STUB_ENTRY(arrayof_oop_disjoint_arraycopy) : STUB_ENTRY(oop_disjoint_arraycopy); @@ -1922,7 +1933,7 @@ class StubGenerator: public StubCodeGenerator { address generate_disjoint_oop_copy(bool aligned, const char * name, bool dest_uninitialized) { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); - + assert_positive_int(R5_ARG3); gen_write_ref_array_pre_barrier(R3_ARG1, R4_ARG2, R5_ARG3, dest_uninitialized, R9_ARG7); // save some arguments, disjoint_long_copy_core destroys them. @@ -1996,7 +2007,24 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, "StubRoutines", name); address start = __ function_entry(); - // TODO: Assert that int is 64 bit sign extended and arrays are not conjoint. + // Assert that int is 64 bit sign extended and arrays are not conjoint. +#ifdef ASSERT + { + assert_positive_int(R5_ARG3); + const Register tmp1 = R11_scratch1, tmp2 = R12_scratch2; + Label no_overlap; + __ subf(tmp1, R3_ARG1, R4_ARG2); // distance in bytes + __ sldi(tmp2, R5_ARG3, LogBytesPerHeapOop); // size in bytes + __ cmpld(CCR0, R3_ARG1, R4_ARG2); // Use unsigned comparison! + __ cmpld(CCR1, tmp1, tmp2); + __ crnand(CCR0, Assembler::less, CCR1, Assembler::less); + // Overlaps if Src before dst and distance smaller than size. + // Branch to forward copy routine otherwise. + __ blt(CCR0, no_overlap); + __ stop("overlap in checkcast_copy", 0x9543); + __ bind(no_overlap); + } +#endif gen_write_ref_array_pre_barrier(R3_from, R4_to, R5_count, dest_uninitialized, R12_tmp, /* preserve: */ R6_ckoff, R7_ckval); @@ -2445,12 +2473,14 @@ class StubGenerator: public StubCodeGenerator { STUB_ENTRY(checkcast_arraycopy)); // fill routines - StubRoutines::_jbyte_fill = generate_fill(T_BYTE, false, "jbyte_fill"); - StubRoutines::_jshort_fill = generate_fill(T_SHORT, false, "jshort_fill"); - StubRoutines::_jint_fill = generate_fill(T_INT, false, "jint_fill"); - StubRoutines::_arrayof_jbyte_fill = generate_fill(T_BYTE, true, "arrayof_jbyte_fill"); - StubRoutines::_arrayof_jshort_fill = generate_fill(T_SHORT, true, "arrayof_jshort_fill"); - StubRoutines::_arrayof_jint_fill = generate_fill(T_INT, true, "arrayof_jint_fill"); + if (OptimizeFill) { + StubRoutines::_jbyte_fill = generate_fill(T_BYTE, false, "jbyte_fill"); + StubRoutines::_jshort_fill = generate_fill(T_SHORT, false, "jshort_fill"); + StubRoutines::_jint_fill = generate_fill(T_INT, false, "jint_fill"); + StubRoutines::_arrayof_jbyte_fill = generate_fill(T_BYTE, true, "arrayof_jbyte_fill"); + StubRoutines::_arrayof_jshort_fill = generate_fill(T_SHORT, true, "arrayof_jshort_fill"); + StubRoutines::_arrayof_jint_fill = generate_fill(T_INT, true, "arrayof_jint_fill"); + } } // Safefetch stubs. @@ -2535,6 +2565,11 @@ class StubGenerator: public StubCodeGenerator { BLOCK_COMMENT("Entry:"); + // C2 does not respect int to long conversion for stub calls. + __ clrldi(xlen, xlen, 32); + __ clrldi(ylen, ylen, 32); + __ clrldi(zlen, zlen, 32); + // Save non-volatile regs (frameless). int current_offs = 8; __ std(R24, -current_offs, R1_SP); current_offs += 8; diff --git a/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.cpp index 1f49fe1de26..bed7f40a501 100644 --- a/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.cpp +++ b/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.hpp b/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.hpp index cd3a8bfb9d4..a005677ffa0 100644 --- a/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.hpp +++ b/hotspot/src/cpu/ppc/vm/stubRoutines_ppc_64.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.cpp b/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.cpp index 7182874c16d..19b732a2ad6 100644 --- a/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015 SAP AG. All rights reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp index b2cad669399..c59e7b4b38b 100644 --- a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp +++ b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013, 2015 SAP AG. All rights reserved. + * Copyright (c) 2013, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.hpp b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.hpp index 1dfa8841c64..943f12abb0f 100644 --- a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.hpp +++ b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013, 2014 SAP AG. All rights reserved. + * Copyright (c) 2013, 2014 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vmStructs_ppc.hpp b/hotspot/src/cpu/ppc/vm/vmStructs_ppc.hpp index bb4db41095d..c5297f395e1 100644 --- a/hotspot/src/cpu/ppc/vm/vmStructs_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/vmStructs_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp index fec10b9268e..51bc85d869d 100644 --- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.hpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.hpp index 63b9ba09793..46fdd6b6470 100644 --- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vmreg_ppc.cpp b/hotspot/src/cpu/ppc/vm/vmreg_ppc.cpp index a2a83f2f361..5931bdf7b76 100644 --- a/hotspot/src/cpu/ppc/vm/vmreg_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/vmreg_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vmreg_ppc.hpp b/hotspot/src/cpu/ppc/vm/vmreg_ppc.hpp index 4fe7bdb62e1..29f99765e88 100644 --- a/hotspot/src/cpu/ppc/vm/vmreg_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/vmreg_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vmreg_ppc.inline.hpp b/hotspot/src/cpu/ppc/vm/vmreg_ppc.inline.hpp index 16e5e5d8fd7..6113d458ebf 100644 --- a/hotspot/src/cpu/ppc/vm/vmreg_ppc.inline.hpp +++ b/hotspot/src/cpu/ppc/vm/vmreg_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/cpu/ppc/vm/vtableStubs_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/vtableStubs_ppc_64.cpp index 3dc20a15ebe..9345db639e4 100644 --- a/hotspot/src/cpu/ppc/vm/vtableStubs_ppc_64.cpp +++ b/hotspot/src/cpu/ppc/vm/vtableStubs_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp index 82e9bbddf2a..128f6c31171 100644 --- a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp @@ -1805,9 +1805,7 @@ void LIR_Assembler::fpop() { void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr thread, LIR_Opr dest, LIR_Op* op) { switch (code) { - case lir_sin: - case lir_tan: - case lir_cos: { + case lir_tan: { assert(thread->is_valid(), "preserve the thread object for performance reasons"); assert(dest->as_double_reg() == F0, "the result will be in f0/f1"); break; diff --git a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp index 2e4bb6bac7d..fbd0e1ce6b4 100644 --- a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp @@ -205,12 +205,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register void C1_MacroAssembler::initialize_body(Register base, Register index) { - assert_different_registers(base, index); - Label loop; - bind(loop); - subcc(index, HeapWordSize, index); - brx(Assembler::greaterEqual, true, Assembler::pt, loop); - delayed()->st_ptr(G0, base, index); + zero_memory(base, index); } @@ -237,7 +232,7 @@ void C1_MacroAssembler::allocate_object( } try_allocate(obj, noreg, obj_size * wordSize, t2, t3, slow_case); - initialize_object(obj, klass, noreg, obj_size * HeapWordSize, t1, t2); + initialize_object(obj, klass, noreg, obj_size * HeapWordSize, t1, t2, /* is_tlab_allocated */ UseTLAB); } void C1_MacroAssembler::initialize_object( @@ -246,7 +241,8 @@ void C1_MacroAssembler::initialize_object( Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise int con_size_in_bytes, // object size in bytes if known at compile time Register t1, // temp register - Register t2 // temp register + Register t2, // temp register + bool is_tlab_allocated // the object was allocated in a TLAB; relevant for the implementation of ZeroTLAB ) { const int hdr_size_in_bytes = instanceOopDesc::header_size() * HeapWordSize; @@ -269,31 +265,33 @@ void C1_MacroAssembler::initialize_object( #endif - // initialize body - const int threshold = 5 * HeapWordSize; // approximate break even point for code size - if (var_size_in_bytes != noreg) { - // use a loop - add(obj, hdr_size_in_bytes, t1); // compute address of first element - sub(var_size_in_bytes, hdr_size_in_bytes, t2); // compute size of body - initialize_body(t1, t2); + if (!(UseTLAB && ZeroTLAB && is_tlab_allocated)) { + // initialize body + const int threshold = 5 * HeapWordSize; // approximate break even point for code size + if (var_size_in_bytes != noreg) { + // use a loop + add(obj, hdr_size_in_bytes, t1); // compute address of first element + sub(var_size_in_bytes, hdr_size_in_bytes, t2); // compute size of body + initialize_body(t1, t2); #ifndef _LP64 - } else if (con_size_in_bytes < threshold * 2) { - // on v9 we can do double word stores to fill twice as much space. - assert(hdr_size_in_bytes % 8 == 0, "double word aligned"); - assert(con_size_in_bytes % 8 == 0, "double word aligned"); - for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += 2 * HeapWordSize) stx(G0, obj, i); + } else if (con_size_in_bytes < threshold * 2) { + // on v9 we can do double word stores to fill twice as much space. + assert(hdr_size_in_bytes % 8 == 0, "double word aligned"); + assert(con_size_in_bytes % 8 == 0, "double word aligned"); + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += 2 * HeapWordSize) stx(G0, obj, i); #endif - } else if (con_size_in_bytes <= threshold) { - // use explicit NULL stores - for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += HeapWordSize) st_ptr(G0, obj, i); - } else if (con_size_in_bytes > hdr_size_in_bytes) { - // use a loop - const Register base = t1; - const Register index = t2; - add(obj, hdr_size_in_bytes, base); // compute address of first element - // compute index = number of words to clear - set(con_size_in_bytes - hdr_size_in_bytes, index); - initialize_body(base, index); + } else if (con_size_in_bytes <= threshold) { + // use explicit NULL stores + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += HeapWordSize) st_ptr(G0, obj, i); + } else if (con_size_in_bytes > hdr_size_in_bytes) { + // use a loop + const Register base = t1; + const Register index = t2; + add(obj, hdr_size_in_bytes, base); // compute address of first element + // compute index = number of words to clear + set(con_size_in_bytes - hdr_size_in_bytes, index); + initialize_body(base, index); + } } if (CURRENT_ENV->dtrace_alloc_probes()) { diff --git a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp index 492ca678d89..695ac7f4d27 100644 --- a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp @@ -50,7 +50,8 @@ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise int con_size_in_bytes, // object size in bytes if known at compile time Register t1, // temp register - Register t2 // temp register + Register t2, // temp register + bool is_tlab_allocated // the object was allocated in a TLAB; relevant for the implementation of ZeroTLAB ); // allocation of fixed-size objects diff --git a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp index 45bfab26f45..283f260886d 100644 --- a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp @@ -435,7 +435,7 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ tlab_allocate(O0_obj, G1_obj_size, 0, G3_t1, slow_path); - __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2); + __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2, /* is_tlab_allocated */ true); __ verify_oop(O0_obj); __ mov(O0, I0); __ ret(); @@ -447,7 +447,7 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ eden_allocate(O0_obj, G1_obj_size, 0, G3_t1, G4_t2, slow_path); __ incr_allocated_bytes(G1_obj_size, G3_t1, G4_t2); - __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2); + __ initialize_object(O0_obj, G5_klass, G1_obj_size, 0, G3_t1, G4_t2, /* is_tlab_allocated */ false); __ verify_oop(O0_obj); __ mov(O0, I0); __ ret(); @@ -542,7 +542,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ ldub(klass_lh, G3_t1, klass_lh_header_size_offset); __ sub(G1_arr_size, G3_t1, O1_t2); // body length __ add(O0_obj, G3_t1, G3_t1); // body start - __ initialize_body(G3_t1, O1_t2); + if (!ZeroTLAB) { + __ initialize_body(G3_t1, O1_t2); + } __ verify_oop(O0_obj); __ retl(); __ delayed()->nop(); diff --git a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp index ae572661df9..9524e707ff4 100644 --- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp @@ -3457,11 +3457,27 @@ void MacroAssembler::tlab_refill(Label& retry, Label& try_eden, Label& slow_case add(top, t1, top); // t1 is tlab_size sub(top, ThreadLocalAllocBuffer::alignment_reserve_in_bytes(), top); st_ptr(top, G2_thread, in_bytes(JavaThread::tlab_end_offset())); + + if (ZeroTLAB) { + // This is a fast TLAB refill, therefore the GC is not notified of it. + // So compiled code must fill the new TLAB with zeroes. + ld_ptr(G2_thread, in_bytes(JavaThread::tlab_start_offset()), t2); + zero_memory(t2, t1); + } verify_tlab(); ba(retry); delayed()->nop(); } +void MacroAssembler::zero_memory(Register base, Register index) { + assert_different_registers(base, index); + Label loop; + bind(loop); + subcc(index, HeapWordSize, index); + brx(Assembler::greaterEqual, true, Assembler::pt, loop); + delayed()->st_ptr(G0, base, index); +} + void MacroAssembler::incr_allocated_bytes(RegisterOrConstant size_in_bytes, Register t1, Register t2) { // Bump total bytes allocated by this thread diff --git a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp index eb6188a3b9a..fe0c36ace97 100644 --- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp @@ -1278,6 +1278,7 @@ public: Label& slow_case // continuation point if fast allocation fails ); void tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); + void zero_memory(Register base, Register index); void incr_allocated_bytes(RegisterOrConstant size_in_bytes, Register t1, Register t2); diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp index 9a7fd0069cf..222d9ba60e2 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp @@ -1891,6 +1891,12 @@ void Assembler::divl(Register src) { // Unsigned emit_int8((unsigned char)(0xF0 | encode)); } +void Assembler::imull(Register src) { + int encode = prefix_and_encode(src->encoding()); + emit_int8((unsigned char)0xF7); + emit_int8((unsigned char)(0xE8 | encode)); +} + void Assembler::imull(Register dst, Register src) { int encode = prefix_and_encode(dst->encoding(), src->encoding()); emit_int8(0x0F); @@ -4112,6 +4118,14 @@ void Assembler::testb(Register dst, int imm8) { emit_arith_b(0xF6, 0xC0, dst, imm8); } +void Assembler::testb(Address dst, int imm8) { + InstructionMark im(this); + prefix(dst); + emit_int8((unsigned char)0xF6); + emit_operand(rax, dst, 1); + emit_int8(imm8); +} + void Assembler::testl(Register dst, int32_t imm32) { // not using emit_arith because test // doesn't support sign-extension of diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index 45ecc01f24a..167f44325fa 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -1205,6 +1205,7 @@ private: void idivq(Register src); #endif + void imull(Register src); void imull(Register dst, Register src); void imull(Register dst, Register src, int value); void imull(Register dst, Address src); @@ -1727,6 +1728,7 @@ private: void subss(XMMRegister dst, XMMRegister src); void testb(Register dst, int imm8); + void testb(Address dst, int imm8); void testl(Register dst, int32_t imm32); void testl(Register dst, Register src); diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp index 963c1b77df1..fb36620c19b 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp @@ -2368,15 +2368,6 @@ void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, L case lir_log10 : __ flog10() ; break; case lir_abs : __ fabs() ; break; case lir_sqrt : __ fsqrt(); break; - case lir_sin : - // Should consider not saving rbx, if not necessary - __ trigfunc('s', op->as_Op2()->fpu_stack_size()); - break; - case lir_cos : - // Should consider not saving rbx, if not necessary - assert(op->as_Op2()->fpu_stack_size() <= 6, "sin and cos need two free stack slots"); - __ trigfunc('c', op->as_Op2()->fpu_stack_size()); - break; case lir_tan : // Should consider not saving rbx, if not necessary __ trigfunc('t', op->as_Op2()->fpu_stack_size()); diff --git a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp index 63073ac4648..b545d0b6989 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp @@ -811,7 +811,8 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { assert(x->number_of_arguments() == 1 || (x->number_of_arguments() == 2 && x->id() == vmIntrinsics::_dpow), "wrong type"); if (x->id() == vmIntrinsics::_dexp || x->id() == vmIntrinsics::_dlog || - x->id() == vmIntrinsics::_dpow) { + x->id() == vmIntrinsics::_dpow || x->id() == vmIntrinsics::_dcos || + x->id() == vmIntrinsics::_dsin) { do_LibmIntrinsic(x); return; } @@ -821,11 +822,10 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { bool use_fpu = false; if (UseSSE >= 2) { switch(x->id()) { - case vmIntrinsics::_dsin: - case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: case vmIntrinsics::_dlog10: use_fpu = true; + break; } } else { value.set_destroys_register(); @@ -870,8 +870,6 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { switch(x->id()) { case vmIntrinsics::_dabs: __ abs (calc_input, calc_result, LIR_OprFact::illegalOpr); break; case vmIntrinsics::_dsqrt: __ sqrt (calc_input, calc_result, LIR_OprFact::illegalOpr); break; - case vmIntrinsics::_dsin: __ sin (calc_input, calc_result, tmp1, tmp2); break; - case vmIntrinsics::_dcos: __ cos (calc_input, calc_result, tmp1, tmp2); break; case vmIntrinsics::_dtan: __ tan (calc_input, calc_result, tmp1, tmp2); break; case vmIntrinsics::_dlog10: __ log10(calc_input, calc_result, tmp1); break; default: ShouldNotReachHere(); @@ -923,19 +921,31 @@ void LIRGenerator::do_LibmIntrinsic(Intrinsic* x) { case vmIntrinsics::_dlog: if (VM_Version::supports_sse2()) { __ call_runtime_leaf(StubRoutines::dlog(), getThreadTemp(), result_reg, cc->args()); - } - else { + } else { __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dlog), getThreadTemp(), result_reg, cc->args()); } break; case vmIntrinsics::_dpow: if (VM_Version::supports_sse2()) { __ call_runtime_leaf(StubRoutines::dpow(), getThreadTemp(), result_reg, cc->args()); - } - else { + } else { __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dpow), getThreadTemp(), result_reg, cc->args()); } break; + case vmIntrinsics::_dsin: + if (VM_Version::supports_sse2() && StubRoutines::dsin() != NULL) { + __ call_runtime_leaf(StubRoutines::dsin(), getThreadTemp(), result_reg, cc->args()); + } else { + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dsin), getThreadTemp(), result_reg, cc->args()); + } + break; + case vmIntrinsics::_dcos: + if (VM_Version::supports_sse2() && StubRoutines::dcos() != NULL) { + __ call_runtime_leaf(StubRoutines::dcos(), getThreadTemp(), result_reg, cc->args()); + } else { + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dcos), getThreadTemp(), result_reg, cc->args()); + } + break; default: ShouldNotReachHere(); } #else @@ -949,8 +959,23 @@ void LIRGenerator::do_LibmIntrinsic(Intrinsic* x) { case vmIntrinsics::_dpow: __ call_runtime_leaf(StubRoutines::dpow(), getThreadTemp(), result_reg, cc->args()); break; + case vmIntrinsics::_dsin: + if (StubRoutines::dsin() != NULL) { + __ call_runtime_leaf(StubRoutines::dsin(), getThreadTemp(), result_reg, cc->args()); + } else { + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dsin), getThreadTemp(), result_reg, cc->args()); + } + break; + case vmIntrinsics::_dcos: + if (StubRoutines::dcos() != NULL) { + __ call_runtime_leaf(StubRoutines::dcos(), getThreadTemp(), result_reg, cc->args()); + } else { + __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dcos), getThreadTemp(), result_reg, cc->args()); + } + break; + default: ShouldNotReachHere(); } -#endif +#endif // _LP64 __ move(result_reg, calc_result); } diff --git a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp index 52924b6e8dc..c3108727fec 100644 --- a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.cpp @@ -811,9 +811,7 @@ void FpuStackAllocator::handle_op2(LIR_Op2* op2) { } - case lir_tan: - case lir_sin: - case lir_cos: { + case lir_tan: { // sin, cos and exp need two temporary fpu stack slots, so there are two temporary // registers (stored in right and temp of the operation). // the stack allocator must guarantee that the stack slots are really free, diff --git a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp index 3902d974bc0..a5e533696b2 100644 --- a/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c1_LinearScan_x86.hpp @@ -67,9 +67,7 @@ inline bool LinearScan::is_caller_save(int assigned_reg) { inline void LinearScan::pd_add_temps(LIR_Op* op) { switch (op->code()) { - case lir_tan: - case lir_sin: - case lir_cos: { + case lir_tan: { // The slow path for these functions may need to save and // restore all live registers but we don't want to save and // restore everything all the time, so mark the xmms as being diff --git a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp index 76d160ac113..24cdaa46024 100644 --- a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp @@ -182,54 +182,13 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // preserves obj, destroys len_in_bytes void C1_MacroAssembler::initialize_body(Register obj, Register len_in_bytes, int hdr_size_in_bytes, Register t1) { + assert(hdr_size_in_bytes >= 0, "header size must be positive or 0"); Label done; - assert(obj != len_in_bytes && obj != t1 && t1 != len_in_bytes, "registers must be different"); - assert((hdr_size_in_bytes & (BytesPerWord - 1)) == 0, "header size is not a multiple of BytesPerWord"); - Register index = len_in_bytes; - // index is positive and ptr sized - subptr(index, hdr_size_in_bytes); - jcc(Assembler::zero, done); - // initialize topmost word, divide index by 2, check if odd and test if zero - // note: for the remaining code to work, index must be a multiple of BytesPerWord -#ifdef ASSERT - { Label L; - testptr(index, BytesPerWord - 1); - jcc(Assembler::zero, L); - stop("index is not a multiple of BytesPerWord"); - bind(L); - } -#endif - xorptr(t1, t1); // use _zero reg to clear memory (shorter code) - if (UseIncDec) { - shrptr(index, 3); // divide by 8/16 and set carry flag if bit 2 was set - } else { - shrptr(index, 2); // use 2 instructions to avoid partial flag stall - shrptr(index, 1); - } -#ifndef _LP64 - // index could have been not a multiple of 8 (i.e., bit 2 was set) - { Label even; - // note: if index was a multiple of 8, than it cannot - // be 0 now otherwise it must have been 0 before - // => if it is even, we don't need to check for 0 again - jcc(Assembler::carryClear, even); - // clear topmost word (no jump needed if conditional assignment would work here) - movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - 0*BytesPerWord), t1); - // index could be 0 now, need to check again - jcc(Assembler::zero, done); - bind(even); - } -#endif // !_LP64 - // initialize remaining object fields: rdx is a multiple of 2 now - { Label loop; - bind(loop); - movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - 1*BytesPerWord), t1); - NOT_LP64(movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - 2*BytesPerWord), t1);) - decrement(index); - jcc(Assembler::notZero, loop); - } - // done + // len_in_bytes is positive and ptr sized + subptr(len_in_bytes, hdr_size_in_bytes); + jcc(Assembler::zero, done); + zero_memory(obj, len_in_bytes, hdr_size_in_bytes, t1); bind(done); } @@ -241,47 +200,49 @@ void C1_MacroAssembler::allocate_object(Register obj, Register t1, Register t2, try_allocate(obj, noreg, object_size * BytesPerWord, t1, t2, slow_case); - initialize_object(obj, klass, noreg, object_size * HeapWordSize, t1, t2); + initialize_object(obj, klass, noreg, object_size * HeapWordSize, t1, t2, UseTLAB); } -void C1_MacroAssembler::initialize_object(Register obj, Register klass, Register var_size_in_bytes, int con_size_in_bytes, Register t1, Register t2) { +void C1_MacroAssembler::initialize_object(Register obj, Register klass, Register var_size_in_bytes, int con_size_in_bytes, Register t1, Register t2, bool is_tlab_allocated) { assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, "con_size_in_bytes is not multiple of alignment"); const int hdr_size_in_bytes = instanceOopDesc::header_size() * HeapWordSize; initialize_header(obj, klass, noreg, t1, t2); - // clear rest of allocated space - const Register t1_zero = t1; - const Register index = t2; - const int threshold = 6 * BytesPerWord; // approximate break even point for code size (see comments below) - if (var_size_in_bytes != noreg) { - mov(index, var_size_in_bytes); - initialize_body(obj, index, hdr_size_in_bytes, t1_zero); - } else if (con_size_in_bytes <= threshold) { - // use explicit null stores - // code size = 2 + 3*n bytes (n = number of fields to clear) - xorptr(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) - for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += BytesPerWord) - movptr(Address(obj, i), t1_zero); - } else if (con_size_in_bytes > hdr_size_in_bytes) { - // use loop to null out the fields - // code size = 16 bytes for even n (n = number of fields to clear) - // initialize last object field first if odd number of fields - xorptr(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) - movptr(index, (con_size_in_bytes - hdr_size_in_bytes) >> 3); - // initialize last object field if constant size is odd - if (((con_size_in_bytes - hdr_size_in_bytes) & 4) != 0) - movptr(Address(obj, con_size_in_bytes - (1*BytesPerWord)), t1_zero); - // initialize remaining object fields: rdx is a multiple of 2 - { Label loop; - bind(loop); - movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - (1*BytesPerWord)), - t1_zero); - NOT_LP64(movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - (2*BytesPerWord)), - t1_zero);) - decrement(index); - jcc(Assembler::notZero, loop); + if (!(UseTLAB && ZeroTLAB && is_tlab_allocated)) { + // clear rest of allocated space + const Register t1_zero = t1; + const Register index = t2; + const int threshold = 6 * BytesPerWord; // approximate break even point for code size (see comments below) + if (var_size_in_bytes != noreg) { + mov(index, var_size_in_bytes); + initialize_body(obj, index, hdr_size_in_bytes, t1_zero); + } else if (con_size_in_bytes <= threshold) { + // use explicit null stores + // code size = 2 + 3*n bytes (n = number of fields to clear) + xorptr(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) + for (int i = hdr_size_in_bytes; i < con_size_in_bytes; i += BytesPerWord) + movptr(Address(obj, i), t1_zero); + } else if (con_size_in_bytes > hdr_size_in_bytes) { + // use loop to null out the fields + // code size = 16 bytes for even n (n = number of fields to clear) + // initialize last object field first if odd number of fields + xorptr(t1_zero, t1_zero); // use t1_zero reg to clear memory (shorter code) + movptr(index, (con_size_in_bytes - hdr_size_in_bytes) >> 3); + // initialize last object field if constant size is odd + if (((con_size_in_bytes - hdr_size_in_bytes) & 4) != 0) + movptr(Address(obj, con_size_in_bytes - (1*BytesPerWord)), t1_zero); + // initialize remaining object fields: rdx is a multiple of 2 + { Label loop; + bind(loop); + movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - (1*BytesPerWord)), + t1_zero); + NOT_LP64(movptr(Address(obj, index, Address::times_8, hdr_size_in_bytes - (2*BytesPerWord)), + t1_zero);) + decrement(index); + jcc(Assembler::notZero, loop); + } } } diff --git a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp index 5695dad2251..829db55b196 100644 --- a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp @@ -65,7 +65,8 @@ Register var_size_in_bytes, // object size in bytes if unknown at compile time; invalid otherwise int con_size_in_bytes, // object size in bytes if known at compile time Register t1, // temp register - Register t2 // temp register + Register t2, // temp register + bool is_tlab_allocated // the object was allocated in a TLAB; relevant for the implementation of ZeroTLAB ); // allocation of fixed-size objects diff --git a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp index c977465cfc5..1d057e20adf 100644 --- a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -1040,7 +1040,7 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ tlab_allocate(obj, obj_size, 0, t1, t2, slow_path); - __ initialize_object(obj, klass, obj_size, 0, t1, t2); + __ initialize_object(obj, klass, obj_size, 0, t1, t2, /* is_tlab_allocated */ true); __ verify_oop(obj); __ pop(rbx); __ pop(rdi); @@ -1053,7 +1053,7 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ eden_allocate(obj, obj_size, 0, t1, slow_path); __ incr_allocated_bytes(thread, obj_size, 0); - __ initialize_object(obj, klass, obj_size, 0, t1, t2); + __ initialize_object(obj, klass, obj_size, 0, t1, t2, /* is_tlab_allocated */ false); __ verify_oop(obj); __ pop(rbx); __ pop(rdi); @@ -1169,7 +1169,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { __ andptr(t1, Klass::_lh_header_size_mask); __ subptr(arr_size, t1); // body length __ addptr(t1, obj); // body start - __ initialize_body(t1, arr_size, 0, t2); + if (!ZeroTLAB) { + __ initialize_body(t1, arr_size, 0, t2); + } __ verify_oop(obj); __ ret(0); diff --git a/hotspot/src/cpu/x86/vm/globals_x86.hpp b/hotspot/src/cpu/x86/vm/globals_x86.hpp index 57dc051ab5f..1596645f00c 100644 --- a/hotspot/src/cpu/x86/vm/globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -184,9 +184,18 @@ define_pd_global(bool, PreserveFramePointer, false); product(bool, UseCountTrailingZerosInstruction, false, \ "Use count trailing zeros instruction") \ \ + product(bool, UseSSE42Intrinsics, false, \ + "SSE4.2 versions of intrinsics") \ + \ product(bool, UseBMI1Instructions, false, \ "Use BMI1 instructions") \ \ product(bool, UseBMI2Instructions, false, \ - "Use BMI2 instructions") + "Use BMI2 instructions") \ + \ + diagnostic(bool, UseLibmSinIntrinsic, true, \ + "Use Libm Sin Intrinsic") \ + \ + diagnostic(bool, UseLibmCosIntrinsic, true, \ + "Use Libm Cos Intrinsic") #endif // CPU_X86_VM_GLOBALS_X86_HPP diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_32.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_32.cpp new file mode 100644 index 00000000000..923e8fc39b5 --- /dev/null +++ b/hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_32.cpp @@ -0,0 +1,4571 @@ +/* + * Copyright (c) 2015, Intel Corporation. + * Intel Math Library (LIBM) Source Code + * + * 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "asm/assembler.inline.hpp" +#include "runtime/stubRoutines.hpp" +#include "macroAssembler_x86.hpp" + +#ifdef _MSC_VER +#define ALIGNED_(x) __declspec(align(x)) +#else +#define ALIGNED_(x) __attribute__ ((aligned(x))) +#endif + +// The 32 bit code is at most SSE2 compliant + +/******************************************************************************/ +// ALGORITHM DESCRIPTION - EXP() +// --------------------- +// +// Description: +// Let K = 64 (table size). +// x x/log(2) n +// e = 2 = 2 * T[j] * (1 + P(y)) +// where +// x = m*log(2)/K + y, y in [-log(2)/K..log(2)/K] +// m = n*K + j, m,n,j - signed integer, j in [-K/2..K/2] +// j/K +// values of 2 are tabulated as T[j] = T_hi[j] ( 1 + T_lo[j]). +// +// P(y) is a minimax polynomial approximation of exp(x)-1 +// on small interval [-log(2)/K..log(2)/K] (were calculated by Maple V). +// +// To avoid problems with arithmetic overflow and underflow, +// n n1 n2 +// value of 2 is safely computed as 2 * 2 where n1 in [-BIAS/2..BIAS/2] +// where BIAS is a value of exponent bias. +// +// Special cases: +// exp(NaN) = NaN +// exp(+INF) = +INF +// exp(-INF) = 0 +// exp(x) = 1 for subnormals +// for finite argument, only exp(0)=1 is exact +// For IEEE double +// if x > 709.782712893383973096 then exp(x) overflow +// if x < -745.133219101941108420 then exp(x) underflow +// +/******************************************************************************/ + +ALIGNED_(16) juint _static_const_table[] = +{ + 0x00000000UL, 0xfff00000UL, 0x00000000UL, 0xfff00000UL, 0xffffffc0UL, + 0x00000000UL, 0xffffffc0UL, 0x00000000UL, 0x0000ffc0UL, 0x00000000UL, + 0x0000ffc0UL, 0x00000000UL, 0x00000000UL, 0x43380000UL, 0x00000000UL, + 0x43380000UL, 0x652b82feUL, 0x40571547UL, 0x652b82feUL, 0x40571547UL, + 0xfefa0000UL, 0x3f862e42UL, 0xfefa0000UL, 0x3f862e42UL, 0xbc9e3b3aUL, + 0x3d1cf79aUL, 0xbc9e3b3aUL, 0x3d1cf79aUL, 0xfffffffeUL, 0x3fdfffffUL, + 0xfffffffeUL, 0x3fdfffffUL, 0xe3289860UL, 0x3f56c15cUL, 0x555b9e25UL, + 0x3fa55555UL, 0xc090cf0fUL, 0x3f811115UL, 0x55548ba1UL, 0x3fc55555UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0e03754dUL, + 0x3cad7bbfUL, 0x3e778060UL, 0x00002c9aUL, 0x3567f613UL, 0x3c8cd252UL, + 0xd3158574UL, 0x000059b0UL, 0x61e6c861UL, 0x3c60f74eUL, 0x18759bc8UL, + 0x00008745UL, 0x5d837b6cUL, 0x3c979aa6UL, 0x6cf9890fUL, 0x0000b558UL, + 0x702f9cd1UL, 0x3c3ebe3dUL, 0x32d3d1a2UL, 0x0000e3ecUL, 0x1e63bcd8UL, + 0x3ca3516eUL, 0xd0125b50UL, 0x00011301UL, 0x26f0387bUL, 0x3ca4c554UL, + 0xaea92ddfUL, 0x0001429aUL, 0x62523fb6UL, 0x3ca95153UL, 0x3c7d517aUL, + 0x000172b8UL, 0x3f1353bfUL, 0x3c8b898cUL, 0xeb6fcb75UL, 0x0001a35bUL, + 0x3e3a2f5fUL, 0x3c9aecf7UL, 0x3168b9aaUL, 0x0001d487UL, 0x44a6c38dUL, + 0x3c8a6f41UL, 0x88628cd6UL, 0x0002063bUL, 0xe3a8a894UL, 0x3c968efdUL, + 0x6e756238UL, 0x0002387aUL, 0x981fe7f2UL, 0x3c80472bUL, 0x65e27cddUL, + 0x00026b45UL, 0x6d09ab31UL, 0x3c82f7e1UL, 0xf51fdee1UL, 0x00029e9dUL, + 0x720c0ab3UL, 0x3c8b3782UL, 0xa6e4030bUL, 0x0002d285UL, 0x4db0abb6UL, + 0x3c834d75UL, 0x0a31b715UL, 0x000306feUL, 0x5dd3f84aUL, 0x3c8fdd39UL, + 0xb26416ffUL, 0x00033c08UL, 0xcc187d29UL, 0x3ca12f8cUL, 0x373aa9caUL, + 0x000371a7UL, 0x738b5e8bUL, 0x3ca7d229UL, 0x34e59ff6UL, 0x0003a7dbUL, + 0xa72a4c6dUL, 0x3c859f48UL, 0x4c123422UL, 0x0003dea6UL, 0x259d9205UL, + 0x3ca8b846UL, 0x21f72e29UL, 0x0004160aUL, 0x60c2ac12UL, 0x3c4363edUL, + 0x6061892dUL, 0x00044e08UL, 0xdaa10379UL, 0x3c6ecce1UL, 0xb5c13cd0UL, + 0x000486a2UL, 0xbb7aafb0UL, 0x3c7690ceUL, 0xd5362a27UL, 0x0004bfdaUL, + 0x9b282a09UL, 0x3ca083ccUL, 0x769d2ca6UL, 0x0004f9b2UL, 0xc1aae707UL, + 0x3ca509b0UL, 0x569d4f81UL, 0x0005342bUL, 0x18fdd78eUL, 0x3c933505UL, + 0x36b527daUL, 0x00056f47UL, 0xe21c5409UL, 0x3c9063e1UL, 0xdd485429UL, + 0x0005ab07UL, 0x2b64c035UL, 0x3c9432e6UL, 0x15ad2148UL, 0x0005e76fUL, + 0x99f08c0aUL, 0x3ca01284UL, 0xb03a5584UL, 0x0006247eUL, 0x0073dc06UL, + 0x3c99f087UL, 0x82552224UL, 0x00066238UL, 0x0da05571UL, 0x3c998d4dUL, + 0x667f3bccUL, 0x0006a09eUL, 0x86ce4786UL, 0x3ca52bb9UL, 0x3c651a2eUL, + 0x0006dfb2UL, 0x206f0dabUL, 0x3ca32092UL, 0xe8ec5f73UL, 0x00071f75UL, + 0x8e17a7a6UL, 0x3ca06122UL, 0x564267c8UL, 0x00075febUL, 0x461e9f86UL, + 0x3ca244acUL, 0x73eb0186UL, 0x0007a114UL, 0xabd66c55UL, 0x3c65ebe1UL, + 0x36cf4e62UL, 0x0007e2f3UL, 0xbbff67d0UL, 0x3c96fe9fUL, 0x994cce12UL, + 0x00082589UL, 0x14c801dfUL, 0x3c951f14UL, 0x9b4492ecUL, 0x000868d9UL, + 0xc1f0eab4UL, 0x3c8db72fUL, 0x422aa0dbUL, 0x0008ace5UL, 0x59f35f44UL, + 0x3c7bf683UL, 0x99157736UL, 0x0008f1aeUL, 0x9c06283cUL, 0x3ca360baUL, + 0xb0cdc5e4UL, 0x00093737UL, 0x20f962aaUL, 0x3c95e8d1UL, 0x9fde4e4fUL, + 0x00097d82UL, 0x2b91ce27UL, 0x3c71affcUL, 0x82a3f090UL, 0x0009c491UL, + 0x589a2ebdUL, 0x3c9b6d34UL, 0x7b5de564UL, 0x000a0c66UL, 0x9ab89880UL, + 0x3c95277cUL, 0xb23e255cUL, 0x000a5503UL, 0x6e735ab3UL, 0x3c846984UL, + 0x5579fdbfUL, 0x000a9e6bUL, 0x92cb3387UL, 0x3c8c1a77UL, 0x995ad3adUL, + 0x000ae89fUL, 0xdc2d1d96UL, 0x3ca22466UL, 0xb84f15faUL, 0x000b33a2UL, + 0xb19505aeUL, 0x3ca1112eUL, 0xf2fb5e46UL, 0x000b7f76UL, 0x0a5fddcdUL, + 0x3c74ffd7UL, 0x904bc1d2UL, 0x000bcc1eUL, 0x30af0cb3UL, 0x3c736eaeUL, + 0xdd85529cUL, 0x000c199bUL, 0xd10959acUL, 0x3c84e08fUL, 0x2e57d14bUL, + 0x000c67f1UL, 0x6c921968UL, 0x3c676b2cUL, 0xdcef9069UL, 0x000cb720UL, + 0x36df99b3UL, 0x3c937009UL, 0x4a07897bUL, 0x000d072dUL, 0xa63d07a7UL, + 0x3c74a385UL, 0xdcfba487UL, 0x000d5818UL, 0xd5c192acUL, 0x3c8e5a50UL, + 0x03db3285UL, 0x000da9e6UL, 0x1c4a9792UL, 0x3c98bb73UL, 0x337b9b5eUL, + 0x000dfc97UL, 0x603a88d3UL, 0x3c74b604UL, 0xe78b3ff6UL, 0x000e502eUL, + 0x92094926UL, 0x3c916f27UL, 0xa2a490d9UL, 0x000ea4afUL, 0x41aa2008UL, + 0x3c8ec3bcUL, 0xee615a27UL, 0x000efa1bUL, 0x31d185eeUL, 0x3c8a64a9UL, + 0x5b6e4540UL, 0x000f5076UL, 0x4d91cd9dUL, 0x3c77893bUL, 0x819e90d8UL, + 0x000fa7c1UL, 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x7ff00000UL, + 0x00000000UL, 0x00000000UL, 0xffffffffUL, 0x7fefffffUL, 0x00000000UL, + 0x00100000UL +}; + +//registers, +// input: (rbp + 8) +// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 +// rax, rdx, rcx, rbx (tmp) + +// Code generated by Intel C compiler for LIBM library + +void MacroAssembler::fast_exp(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; + Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; + Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2, L_2TAG_PACKET_10_0_2, L_2TAG_PACKET_11_0_2; + Label L_2TAG_PACKET_12_0_2, L_2TAG_PACKET_13_0_2, B1_3, B1_5, start; + + assert_different_registers(tmp, eax, ecx, edx); + jmp(start); + address static_const_table = (address)_static_const_table; + + bind(start); + subl(rsp, 120); + movl(Address(rsp, 64), tmp); + lea(tmp, ExternalAddress(static_const_table)); + movdqu(xmm0, Address(rsp, 128)); + unpcklpd(xmm0, xmm0); + movdqu(xmm1, Address(tmp, 64)); // 0x652b82feUL, 0x40571547UL, 0x652b82feUL, 0x40571547UL + movdqu(xmm6, Address(tmp, 48)); // 0x00000000UL, 0x43380000UL, 0x00000000UL, 0x43380000UL + movdqu(xmm2, Address(tmp, 80)); // 0xfefa0000UL, 0x3f862e42UL, 0xfefa0000UL, 0x3f862e42UL + movdqu(xmm3, Address(tmp, 96)); // 0xbc9e3b3aUL, 0x3d1cf79aUL, 0xbc9e3b3aUL, 0x3d1cf79aUL + pextrw(eax, xmm0, 3); + andl(eax, 32767); + movl(edx, 16527); + subl(edx, eax); + subl(eax, 15504); + orl(edx, eax); + cmpl(edx, INT_MIN); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); + mulpd(xmm1, xmm0); + addpd(xmm1, xmm6); + movapd(xmm7, xmm1); + subpd(xmm1, xmm6); + mulpd(xmm2, xmm1); + movdqu(xmm4, Address(tmp, 128)); // 0xe3289860UL, 0x3f56c15cUL, 0x555b9e25UL, 0x3fa55555UL + mulpd(xmm3, xmm1); + movdqu(xmm5, Address(tmp, 144)); // 0xc090cf0fUL, 0x3f811115UL, 0x55548ba1UL, 0x3fc55555UL + subpd(xmm0, xmm2); + movdl(eax, xmm7); + movl(ecx, eax); + andl(ecx, 63); + shll(ecx, 4); + sarl(eax, 6); + movl(edx, eax); + movdqu(xmm6, Address(tmp, 16)); // 0xffffffc0UL, 0x00000000UL, 0xffffffc0UL, 0x00000000UL + pand(xmm7, xmm6); + movdqu(xmm6, Address(tmp, 32)); // 0x0000ffc0UL, 0x00000000UL, 0x0000ffc0UL, 0x00000000UL + paddq(xmm7, xmm6); + psllq(xmm7, 46); + subpd(xmm0, xmm3); + movdqu(xmm2, Address(tmp, ecx, Address::times_1, 160)); + mulpd(xmm4, xmm0); + movapd(xmm6, xmm0); + movapd(xmm1, xmm0); + mulpd(xmm6, xmm6); + mulpd(xmm0, xmm6); + addpd(xmm5, xmm4); + mulsd(xmm0, xmm6); + mulpd(xmm6, Address(tmp, 112)); // 0xfffffffeUL, 0x3fdfffffUL, 0xfffffffeUL, 0x3fdfffffUL + addsd(xmm1, xmm2); + unpckhpd(xmm2, xmm2); + mulpd(xmm0, xmm5); + addsd(xmm1, xmm0); + por(xmm2, xmm7); + unpckhpd(xmm0, xmm0); + addsd(xmm0, xmm1); + addsd(xmm0, xmm6); + addl(edx, 894); + cmpl(edx, 1916); + jcc (Assembler::above, L_2TAG_PACKET_1_0_2); + mulsd(xmm0, xmm2); + addsd(xmm0, xmm2); + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_1_0_2); + fnstcw(Address(rsp, 24)); + movzwl(edx, Address(rsp, 24)); + orl(edx, 768); + movw(Address(rsp, 28), edx); + fldcw(Address(rsp, 28)); + movl(edx, eax); + sarl(eax, 1); + subl(edx, eax); + movdqu(xmm6, Address(tmp, 0)); // 0x00000000UL, 0xfff00000UL, 0x00000000UL, 0xfff00000UL + pandn(xmm6, xmm2); + addl(eax, 1023); + movdl(xmm3, eax); + psllq(xmm3, 52); + por(xmm6, xmm3); + addl(edx, 1023); + movdl(xmm4, edx); + psllq(xmm4, 52); + movsd(Address(rsp, 8), xmm0); + fld_d(Address(rsp, 8)); + movsd(Address(rsp, 16), xmm6); + fld_d(Address(rsp, 16)); + fmula(1); + faddp(1); + movsd(Address(rsp, 8), xmm4); + fld_d(Address(rsp, 8)); + fmulp(1); + fstp_d(Address(rsp, 8)); + movsd(xmm0,Address(rsp, 8)); + fldcw(Address(rsp, 24)); + pextrw(ecx, xmm0, 3); + andl(ecx, 32752); + cmpl(ecx, 32752); + jcc(Assembler::greaterEqual, L_2TAG_PACKET_3_0_2); + cmpl(ecx, 0); + jcc(Assembler::equal, L_2TAG_PACKET_4_0_2); + jmp(L_2TAG_PACKET_2_0_2); + cmpl(ecx, INT_MIN); + jcc(Assembler::less, L_2TAG_PACKET_3_0_2); + cmpl(ecx, -1064950997); + jcc(Assembler::less, L_2TAG_PACKET_2_0_2); + jcc(Assembler::greater, L_2TAG_PACKET_4_0_2); + movl(edx, Address(rsp, 128)); + cmpl(edx ,-17155601); + jcc(Assembler::less, L_2TAG_PACKET_2_0_2); + jmp(L_2TAG_PACKET_4_0_2); + + bind(L_2TAG_PACKET_3_0_2); + movl(edx, 14); + jmp(L_2TAG_PACKET_5_0_2); + + bind(L_2TAG_PACKET_4_0_2); + movl(edx, 15); + + bind(L_2TAG_PACKET_5_0_2); + movsd(Address(rsp, 0), xmm0); + movsd(xmm0, Address(rsp, 128)); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_7_0_2); + cmpl(eax, 2146435072); + jcc(Assembler::greaterEqual, L_2TAG_PACKET_8_0_2); + movl(eax, Address(rsp, 132)); + cmpl(eax, INT_MIN); + jcc(Assembler::greaterEqual, L_2TAG_PACKET_9_0_2); + movsd(xmm0, Address(tmp, 1208)); // 0xffffffffUL, 0x7fefffffUL + mulsd(xmm0, xmm0); + movl(edx, 14); + jmp(L_2TAG_PACKET_5_0_2); + + bind(L_2TAG_PACKET_9_0_2); + movsd(xmm0, Address(tmp, 1216)); + mulsd(xmm0, xmm0); + movl(edx, 15); + jmp(L_2TAG_PACKET_5_0_2); + + bind(L_2TAG_PACKET_8_0_2); + movl(edx, Address(rsp, 128)); + cmpl(eax, 2146435072); + jcc(Assembler::above, L_2TAG_PACKET_10_0_2); + cmpl(edx, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_10_0_2); + movl(eax, Address(rsp, 132)); + cmpl(eax, 2146435072); + jcc(Assembler::notEqual, L_2TAG_PACKET_11_0_2); + movsd(xmm0, Address(tmp, 1192)); // 0x00000000UL, 0x7ff00000UL + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_11_0_2); + movsd(xmm0, Address(tmp, 1200)); // 0x00000000UL, 0x00000000UL + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_10_0_2); + movsd(xmm0, Address(rsp, 128)); + addsd(xmm0, xmm0); + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_0_0_2); + movl(eax, Address(rsp, 132)); + andl(eax, 2147483647); + cmpl(eax, 1083179008); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_7_0_2); + movsd(xmm0, Address(rsp, 128)); + addsd(xmm0, Address(tmp, 1184)); // 0x00000000UL, 0x3ff00000UL + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_2_0_2); + movsd(Address(rsp, 48), xmm0); + fld_d(Address(rsp, 48)); + + bind(L_2TAG_PACKET_6_0_2); + movl(tmp, Address(rsp, 64)); +} + +/******************************************************************************/ +// ALGORITHM DESCRIPTION - LOG() +// --------------------- +// +// x=2^k * mx, mx in [1,2) +// +// Get B~1/mx based on the output of rcpss instruction (B0) +// B = int((B0*2^7+0.5))/2^7 +// +// Reduced argument: r=B*mx-1.0 (computed accurately in high and low parts) +// +// Result: k*log(2) - log(B) + p(r) if |x-1| >= small value (2^-6) and +// p(r) is a degree 7 polynomial +// -log(B) read from data table (high, low parts) +// Result is formed from high and low parts +// +// Special cases: +// log(NaN) = quiet NaN, and raise invalid exception +// log(+INF) = that INF +// log(0) = -INF with divide-by-zero exception raised +// log(1) = +0 +// log(x) = NaN with invalid exception raised if x < -0, including -INF +// +/******************************************************************************/ + +ALIGNED_(16) juint _static_const_table_log[] = +{ + 0xfefa3800UL, 0x3fe62e42UL, 0x93c76730UL, 0x3d2ef357UL, 0xaa241800UL, + 0x3fe5ee82UL, 0x0cda46beUL, 0x3d220238UL, 0x5c364800UL, 0x3fe5af40UL, + 0xac10c9fbUL, 0x3d2dfa63UL, 0x26bb8c00UL, 0x3fe5707aUL, 0xff3303ddUL, + 0x3d09980bUL, 0x26867800UL, 0x3fe5322eUL, 0x5d257531UL, 0x3d05ccc4UL, + 0x835a5000UL, 0x3fe4f45aUL, 0x6d93b8fbUL, 0xbd2e6c51UL, 0x6f970c00UL, + 0x3fe4b6fdUL, 0xed4c541cUL, 0x3cef7115UL, 0x27e8a400UL, 0x3fe47a15UL, + 0xf94d60aaUL, 0xbd22cb6aUL, 0xf2f92400UL, 0x3fe43d9fUL, 0x481051f7UL, + 0xbcfd984fUL, 0x2125cc00UL, 0x3fe4019cUL, 0x30f0c74cUL, 0xbd26ce79UL, + 0x0c36c000UL, 0x3fe3c608UL, 0x7cfe13c2UL, 0xbd02b736UL, 0x17197800UL, + 0x3fe38ae2UL, 0xbb5569a4UL, 0xbd218b7aUL, 0xad9d8c00UL, 0x3fe35028UL, + 0x9527e6acUL, 0x3d10b83fUL, 0x44340800UL, 0x3fe315daUL, 0xc5a0ed9cUL, + 0xbd274e93UL, 0x57b0e000UL, 0x3fe2dbf5UL, 0x07b9dc11UL, 0xbd17a6e5UL, + 0x6d0ec000UL, 0x3fe2a278UL, 0xe797882dUL, 0x3d206d2bUL, 0x1134dc00UL, + 0x3fe26962UL, 0x05226250UL, 0xbd0b61f1UL, 0xd8bebc00UL, 0x3fe230b0UL, + 0x6e48667bUL, 0x3d12fc06UL, 0x5fc61800UL, 0x3fe1f863UL, 0xc9fe81d3UL, + 0xbd2a7242UL, 0x49ae6000UL, 0x3fe1c078UL, 0xed70e667UL, 0x3cccacdeUL, + 0x40f23c00UL, 0x3fe188eeUL, 0xf8ab4650UL, 0x3d14cc4eUL, 0xf6f29800UL, + 0x3fe151c3UL, 0xa293ae49UL, 0xbd2edd97UL, 0x23c75c00UL, 0x3fe11af8UL, + 0xbb9ddcb2UL, 0xbd258647UL, 0x8611cc00UL, 0x3fe0e489UL, 0x07801742UL, + 0x3d1c2998UL, 0xe2d05400UL, 0x3fe0ae76UL, 0x887e7e27UL, 0x3d1f486bUL, + 0x0533c400UL, 0x3fe078bfUL, 0x41edf5fdUL, 0x3d268122UL, 0xbe760400UL, + 0x3fe04360UL, 0xe79539e0UL, 0xbd04c45fUL, 0xe5b20800UL, 0x3fe00e5aUL, + 0xb1727b1cUL, 0xbd053ba3UL, 0xaf7a4800UL, 0x3fdfb358UL, 0x3c164935UL, + 0x3d0085faUL, 0xee031800UL, 0x3fdf4aa7UL, 0x6f014a8bUL, 0x3d12cde5UL, + 0x56b41000UL, 0x3fdee2a1UL, 0x5a470251UL, 0x3d2f27f4UL, 0xc3ddb000UL, + 0x3fde7b42UL, 0x5372bd08UL, 0xbd246550UL, 0x1a272800UL, 0x3fde148aUL, + 0x07322938UL, 0xbd1326b2UL, 0x484c9800UL, 0x3fddae75UL, 0x60dc616aUL, + 0xbd1ea42dUL, 0x46def800UL, 0x3fdd4902UL, 0xe9a767a8UL, 0x3d235bafUL, + 0x18064800UL, 0x3fdce42fUL, 0x3ec7a6b0UL, 0xbd0797c3UL, 0xc7455800UL, + 0x3fdc7ff9UL, 0xc15249aeUL, 0xbd29b6ddUL, 0x693fa000UL, 0x3fdc1c60UL, + 0x7fe8e180UL, 0x3d2cec80UL, 0x1b80e000UL, 0x3fdbb961UL, 0xf40a666dUL, + 0x3d27d85bUL, 0x04462800UL, 0x3fdb56faUL, 0x2d841995UL, 0x3d109525UL, + 0x5248d000UL, 0x3fdaf529UL, 0x52774458UL, 0xbd217cc5UL, 0x3c8ad800UL, + 0x3fda93edUL, 0xbea77a5dUL, 0x3d1e36f2UL, 0x0224f800UL, 0x3fda3344UL, + 0x7f9d79f5UL, 0x3d23c645UL, 0xea15f000UL, 0x3fd9d32bUL, 0x10d0c0b0UL, + 0xbd26279eUL, 0x43135800UL, 0x3fd973a3UL, 0xa502d9f0UL, 0xbd152313UL, + 0x635bf800UL, 0x3fd914a8UL, 0x2ee6307dUL, 0xbd1766b5UL, 0xa88b3000UL, + 0x3fd8b639UL, 0xe5e70470UL, 0xbd205ae1UL, 0x776dc800UL, 0x3fd85855UL, + 0x3333778aUL, 0x3d2fd56fUL, 0x3bd81800UL, 0x3fd7fafaUL, 0xc812566aUL, + 0xbd272090UL, 0x687cf800UL, 0x3fd79e26UL, 0x2efd1778UL, 0x3d29ec7dUL, + 0x76c67800UL, 0x3fd741d8UL, 0x49dc60b3UL, 0x3d2d8b09UL, 0xe6af1800UL, + 0x3fd6e60eUL, 0x7c222d87UL, 0x3d172165UL, 0x3e9c6800UL, 0x3fd68ac8UL, + 0x2756eba0UL, 0x3d20a0d3UL, 0x0b3ab000UL, 0x3fd63003UL, 0xe731ae00UL, + 0xbd2db623UL, 0xdf596000UL, 0x3fd5d5bdUL, 0x08a465dcUL, 0xbd0a0b2aUL, + 0x53c8d000UL, 0x3fd57bf7UL, 0xee5d40efUL, 0x3d1fadedUL, 0x0738a000UL, + 0x3fd522aeUL, 0x8164c759UL, 0x3d2ebe70UL, 0x9e173000UL, 0x3fd4c9e0UL, + 0x1b0ad8a4UL, 0xbd2e2089UL, 0xc271c800UL, 0x3fd4718dUL, 0x0967d675UL, + 0xbd2f27ceUL, 0x23d5e800UL, 0x3fd419b4UL, 0xec90e09dUL, 0x3d08e436UL, + 0x77333000UL, 0x3fd3c252UL, 0xb606bd5cUL, 0x3d183b54UL, 0x76be1000UL, + 0x3fd36b67UL, 0xb0f177c8UL, 0x3d116ecdUL, 0xe1d36000UL, 0x3fd314f1UL, + 0xd3213cb8UL, 0xbd28e27aUL, 0x7cdc9000UL, 0x3fd2bef0UL, 0x4a5004f4UL, + 0x3d2a9cfaUL, 0x1134d800UL, 0x3fd26962UL, 0xdf5bb3b6UL, 0x3d2c93c1UL, + 0x6d0eb800UL, 0x3fd21445UL, 0xba46baeaUL, 0x3d0a87deUL, 0x635a6800UL, + 0x3fd1bf99UL, 0x5147bdb7UL, 0x3d2ca6edUL, 0xcbacf800UL, 0x3fd16b5cUL, + 0xf7a51681UL, 0x3d2b9acdUL, 0x8227e800UL, 0x3fd1178eUL, 0x63a5f01cUL, + 0xbd2c210eUL, 0x67616000UL, 0x3fd0c42dUL, 0x163ceae9UL, 0x3d27188bUL, + 0x604d5800UL, 0x3fd07138UL, 0x16ed4e91UL, 0x3cf89cdbUL, 0x5626c800UL, + 0x3fd01eaeUL, 0x1485e94aUL, 0xbd16f08cUL, 0x6cb3b000UL, 0x3fcf991cUL, + 0xca0cdf30UL, 0x3d1bcbecUL, 0xe4dd0000UL, 0x3fcef5adUL, 0x65bb8e11UL, + 0xbcca2115UL, 0xffe71000UL, 0x3fce530eUL, 0x6041f430UL, 0x3cc21227UL, + 0xb0d49000UL, 0x3fcdb13dUL, 0xf715b035UL, 0xbd2aff2aUL, 0xf2656000UL, + 0x3fcd1037UL, 0x75b6f6e4UL, 0xbd084a7eUL, 0xc6f01000UL, 0x3fcc6ffbUL, + 0xc5962bd2UL, 0xbcf1ec72UL, 0x383be000UL, 0x3fcbd087UL, 0x595412b6UL, + 0xbd2d4bc4UL, 0x575bd000UL, 0x3fcb31d8UL, 0x4eace1aaUL, 0xbd0c358dUL, + 0x3c8ae000UL, 0x3fca93edUL, 0x50562169UL, 0xbd287243UL, 0x07089000UL, + 0x3fc9f6c4UL, 0x6865817aUL, 0x3d29904dUL, 0xdcf70000UL, 0x3fc95a5aUL, + 0x58a0ff6fUL, 0x3d07f228UL, 0xeb390000UL, 0x3fc8beafUL, 0xaae92cd1UL, + 0xbd073d54UL, 0x6551a000UL, 0x3fc823c1UL, 0x9a631e83UL, 0x3d1e0ddbUL, + 0x85445000UL, 0x3fc7898dUL, 0x70914305UL, 0xbd1c6610UL, 0x8b757000UL, + 0x3fc6f012UL, 0xe59c21e1UL, 0xbd25118dUL, 0xbe8c1000UL, 0x3fc6574eUL, + 0x2c3c2e78UL, 0x3d19cf8bUL, 0x6b544000UL, 0x3fc5bf40UL, 0xeb68981cUL, + 0xbd127023UL, 0xe4a1b000UL, 0x3fc527e5UL, 0xe5697dc7UL, 0x3d2633e8UL, + 0x8333b000UL, 0x3fc4913dUL, 0x54fdb678UL, 0x3d258379UL, 0xa5993000UL, + 0x3fc3fb45UL, 0x7e6a354dUL, 0xbd2cd1d8UL, 0xb0159000UL, 0x3fc365fcUL, + 0x234b7289UL, 0x3cc62fa8UL, 0x0c868000UL, 0x3fc2d161UL, 0xcb81b4a1UL, + 0x3d039d6cUL, 0x2a49c000UL, 0x3fc23d71UL, 0x8fd3df5cUL, 0x3d100d23UL, + 0x7e23f000UL, 0x3fc1aa2bUL, 0x44389934UL, 0x3d2ca78eUL, 0x8227e000UL, + 0x3fc1178eUL, 0xce2d07f2UL, 0x3d21ef78UL, 0xb59e4000UL, 0x3fc08598UL, + 0x7009902cUL, 0xbd27e5ddUL, 0x39dbe000UL, 0x3fbfe891UL, 0x4fa10afdUL, + 0xbd2534d6UL, 0x830a2000UL, 0x3fbec739UL, 0xafe645e0UL, 0xbd2dc068UL, + 0x63844000UL, 0x3fbda727UL, 0x1fa71733UL, 0x3d1a8940UL, 0x01bc4000UL, + 0x3fbc8858UL, 0xc65aacd3UL, 0x3d2646d1UL, 0x8dad6000UL, 0x3fbb6ac8UL, + 0x2bf768e5UL, 0xbd139080UL, 0x40b1c000UL, 0x3fba4e76UL, 0xb94407c8UL, + 0xbd0e42b6UL, 0x5d594000UL, 0x3fb9335eUL, 0x3abd47daUL, 0x3d23115cUL, + 0x2f40e000UL, 0x3fb8197eUL, 0xf96ffdf7UL, 0x3d0f80dcUL, 0x0aeac000UL, + 0x3fb700d3UL, 0xa99ded32UL, 0x3cec1e8dUL, 0x4d97a000UL, 0x3fb5e95aUL, + 0x3c5d1d1eUL, 0xbd2c6906UL, 0x5d208000UL, 0x3fb4d311UL, 0x82f4e1efUL, + 0xbcf53a25UL, 0xa7d1e000UL, 0x3fb3bdf5UL, 0xa5db4ed7UL, 0x3d2cc85eUL, + 0xa4472000UL, 0x3fb2aa04UL, 0xae9c697dUL, 0xbd20b6e8UL, 0xd1466000UL, + 0x3fb1973bUL, 0x560d9e9bUL, 0xbd25325dUL, 0xb59e4000UL, 0x3fb08598UL, + 0x7009902cUL, 0xbd17e5ddUL, 0xc006c000UL, 0x3faeea31UL, 0x4fc93b7bUL, + 0xbd0e113eUL, 0xcdddc000UL, 0x3faccb73UL, 0x47d82807UL, 0xbd1a68f2UL, + 0xd0fb0000UL, 0x3faaaef2UL, 0x353bb42eUL, 0x3d20fc1aUL, 0x149fc000UL, + 0x3fa894aaUL, 0xd05a267dUL, 0xbd197995UL, 0xf2d4c000UL, 0x3fa67c94UL, + 0xec19afa2UL, 0xbd029efbUL, 0xd42e0000UL, 0x3fa466aeUL, 0x75bdfd28UL, + 0xbd2c1673UL, 0x2f8d0000UL, 0x3fa252f3UL, 0xe021b67bUL, 0x3d283e9aUL, + 0x89e74000UL, 0x3fa0415dUL, 0x5cf1d753UL, 0x3d0111c0UL, 0xec148000UL, + 0x3f9c63d2UL, 0x3f9eb2f3UL, 0x3d2578c6UL, 0x28c90000UL, 0x3f984925UL, + 0x325a0c34UL, 0xbd2aa0baUL, 0x25980000UL, 0x3f9432a9UL, 0x928637feUL, + 0x3d098139UL, 0x58938000UL, 0x3f902056UL, 0x06e2f7d2UL, 0xbd23dc5bUL, + 0xa3890000UL, 0x3f882448UL, 0xda74f640UL, 0xbd275577UL, 0x75890000UL, + 0x3f801015UL, 0x999d2be8UL, 0xbd10c76bUL, 0x59580000UL, 0x3f700805UL, + 0xcb31c67bUL, 0x3d2166afUL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x80000000UL, 0xfefa3800UL, 0x3fa62e42UL, 0x93c76730UL, 0x3ceef357UL, + 0x92492492UL, 0x3fc24924UL, 0x00000000UL, 0xbfd00000UL, 0x3d6fb175UL, + 0xbfc5555eUL, 0x55555555UL, 0x3fd55555UL, 0x9999999aUL, 0x3fc99999UL, + 0x00000000UL, 0xbfe00000UL, 0x00000000UL, 0xffffe000UL, 0x00000000UL, + 0xffffe000UL +}; +//registers, +// input: xmm0 +// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 +// rax, rdx, rcx, rbx (tmp) + +void MacroAssembler::fast_log(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; + Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; + Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2; + Label L_2TAG_PACKET_10_0_2, start; + + assert_different_registers(tmp, eax, ecx, edx); + jmp(start); + address static_const_table = (address)_static_const_table_log; + + bind(start); + subl(rsp, 104); + movl(Address(rsp, 40), tmp); + lea(tmp, ExternalAddress(static_const_table)); + xorpd(xmm2, xmm2); + movl(eax, 16368); + pinsrw(xmm2, eax, 3); + xorpd(xmm3, xmm3); + movl(edx, 30704); + pinsrw(xmm3, edx, 3); + movsd(xmm0, Address(rsp, 112)); + movapd(xmm1, xmm0); + movl(ecx, 32768); + movdl(xmm4, ecx); + movsd(xmm5, Address(tmp, 2128)); // 0x00000000UL, 0xffffe000UL + pextrw(eax, xmm0, 3); + por(xmm0, xmm2); + psllq(xmm0, 5); + movl(ecx, 16352); + psrlq(xmm0, 34); + rcpss(xmm0, xmm0); + psllq(xmm1, 12); + pshufd(xmm6, xmm5, 228); + psrlq(xmm1, 12); + subl(eax, 16); + cmpl(eax, 32736); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); + + bind(L_2TAG_PACKET_1_0_2); + paddd(xmm0, xmm4); + por(xmm1, xmm3); + movdl(edx, xmm0); + psllq(xmm0, 29); + pand(xmm5, xmm1); + pand(xmm0, xmm6); + subsd(xmm1, xmm5); + mulpd(xmm5, xmm0); + andl(eax, 32752); + subl(eax, ecx); + cvtsi2sdl(xmm7, eax); + mulsd(xmm1, xmm0); + movsd(xmm6, Address(tmp, 2064)); // 0xfefa3800UL, 0x3fa62e42UL + movdqu(xmm3, Address(tmp, 2080)); // 0x92492492UL, 0x3fc24924UL, 0x00000000UL, 0xbfd00000UL + subsd(xmm5, xmm2); + andl(edx, 16711680); + shrl(edx, 12); + movdqu(xmm0, Address(tmp, edx)); + movdqu(xmm4, Address(tmp, 2096)); // 0x3d6fb175UL, 0xbfc5555eUL, 0x55555555UL, 0x3fd55555UL + addsd(xmm1, xmm5); + movdqu(xmm2, Address(tmp, 2112)); // 0x9999999aUL, 0x3fc99999UL, 0x00000000UL, 0xbfe00000UL + mulsd(xmm6, xmm7); + pshufd(xmm5, xmm1, 68); + mulsd(xmm7, Address(tmp, 2072)); // 0x93c76730UL, 0x3ceef357UL, 0x92492492UL, 0x3fc24924UL + mulsd(xmm3, xmm1); + addsd(xmm0, xmm6); + mulpd(xmm4, xmm5); + mulpd(xmm5, xmm5); + pshufd(xmm6, xmm0, 228); + addsd(xmm0, xmm1); + addpd(xmm4, xmm2); + mulpd(xmm3, xmm5); + subsd(xmm6, xmm0); + mulsd(xmm4, xmm1); + pshufd(xmm2, xmm0, 238); + addsd(xmm1, xmm6); + mulsd(xmm5, xmm5); + addsd(xmm7, xmm2); + addpd(xmm4, xmm3); + addsd(xmm1, xmm7); + mulpd(xmm4, xmm5); + addsd(xmm1, xmm4); + pshufd(xmm5, xmm4, 238); + addsd(xmm1, xmm5); + addsd(xmm0, xmm1); + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_0_0_2); + movsd(xmm0, Address(rsp, 112)); + movdqu(xmm1, xmm0); + addl(eax, 16); + cmpl(eax, 32768); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_3_0_2); + cmpl(eax, 16); + jcc(Assembler::below, L_2TAG_PACKET_4_0_2); + + bind(L_2TAG_PACKET_5_0_2); + addsd(xmm0, xmm0); + jmp(L_2TAG_PACKET_2_0_2); + + bind(L_2TAG_PACKET_6_0_2); + jcc(Assembler::above, L_2TAG_PACKET_5_0_2); + cmpl(edx, 0); + jcc(Assembler::above, L_2TAG_PACKET_5_0_2); + jmp(L_2TAG_PACKET_7_0_2); + + bind(L_2TAG_PACKET_3_0_2); + movdl(edx, xmm1); + psrlq(xmm1, 32); + movdl(ecx, xmm1); + addl(ecx, ecx); + cmpl(ecx, -2097152); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_6_0_2); + orl(edx, ecx); + cmpl(edx, 0); + jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); + + bind(L_2TAG_PACKET_7_0_2); + xorpd(xmm1, xmm1); + xorpd(xmm0, xmm0); + movl(eax, 32752); + pinsrw(xmm1, eax, 3); + movl(edx, 3); + mulsd(xmm0, xmm1); + + bind(L_2TAG_PACKET_9_0_2); + movsd(Address(rsp, 0), xmm0); + movsd(xmm0, Address(rsp, 112)); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_10_0_2); + + bind(L_2TAG_PACKET_8_0_2); + xorpd(xmm1, xmm1); + xorpd(xmm0, xmm0); + movl(eax, 49136); + pinsrw(xmm0, eax, 3); + divsd(xmm0, xmm1); + movl(edx, 2); + jmp(L_2TAG_PACKET_9_0_2); + + bind(L_2TAG_PACKET_4_0_2); + movdl(edx, xmm1); + psrlq(xmm1, 32); + movdl(ecx, xmm1); + orl(edx, ecx); + cmpl(edx, 0); + jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); + xorpd(xmm1, xmm1); + movl(eax, 18416); + pinsrw(xmm1, eax, 3); + mulsd(xmm0, xmm1); + movapd(xmm1, xmm0); + pextrw(eax, xmm0, 3); + por(xmm0, xmm2); + psllq(xmm0, 5); + movl(ecx, 18416); + psrlq(xmm0, 34); + rcpss(xmm0, xmm0); + psllq(xmm1, 12); + pshufd(xmm6, xmm5, 228); + psrlq(xmm1, 12); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_2_0_2); + movsd(Address(rsp, 24), xmm0); + fld_d(Address(rsp, 24)); + + bind(L_2TAG_PACKET_10_0_2); + movl(tmp, Address(rsp, 40)); +} + +/******************************************************************************/ +// ALGORITHM DESCRIPTION - POW() +// --------------------- +// +// Let x=2^k * mx, mx in [1,2) +// +// log2(x) calculation: +// +// Get B~1/mx based on the output of rcpps instruction (B0) +// B = int((B0*LH*2^9+0.5))/2^9 +// LH is a short approximation for log2(e) +// +// Reduced argument, scaled by LH: +// r=B*mx-LH (computed accurately in high and low parts) +// +// log2(x) result: k - log2(B) + p(r) +// p(r) is a degree 8 polynomial +// -log2(B) read from data table (high, low parts) +// log2(x) is formed from high and low parts +// For |x| in [1-1/32, 1+1/16), a slower but more accurate computation +// based om the same table design is performed. +// +// Main path is taken if | floor(log2(|log2(|x|)|) + floor(log2|y|) | < 8, +// to filter out all potential OF/UF cases. +// exp2(y*log2(x)) is computed using an 8-bit index table and a degree 5 +// polynomial +// +// Special cases: +// pow(-0,y) = -INF and raises the divide-by-zero exception for y an odd +// integer < 0. +// pow(-0,y) = +INF and raises the divide-by-zero exception for y < 0 and +// not an odd integer. +// pow(-0,y) = -0 for y an odd integer > 0. +// pow(-0,y) = +0 for y > 0 and not an odd integer. +// pow(-1,-INF) = NaN. +// pow(+1,y) = NaN for any y, even a NaN. +// pow(x,-0) = 1 for any x, even a NaN. +// pow(x,y) = a NaN and raises the invalid exception for finite x < 0 and +// finite non-integer y. +// pow(x,-INF) = +INF for |x|<1. +// pow(x,-INF) = +0 for |x|>1. +// pow(x,+INF) = +0 for |x|<1. +// pow(x,+INF) = +INF for |x|>1. +// pow(-INF,y) = -0 for y an odd integer < 0. +// pow(-INF,y) = +0 for y < 0 and not an odd integer. +// pow(-INF,y) = -INF for y an odd integer > 0. +// pow(-INF,y) = +INF for y > 0 and not an odd integer. +// pow(+INF,y) = +0 for y <0. +// pow(+INF,y) = +INF for y >0. +// +/******************************************************************************/ + +ALIGNED_(16) juint _static_const_table_pow[] = +{ + 0x00000000UL, 0xbfd61a00UL, 0x00000000UL, 0xbf5dabe1UL, 0xf8000000UL, + 0xffffffffUL, 0x00000000UL, 0xfffff800UL, 0x00000000UL, 0x3ff00000UL, + 0x00000000UL, 0x00000000UL, 0x20000000UL, 0x3feff00aUL, 0x96621f95UL, + 0x3e5b1856UL, 0xe0000000UL, 0x3fefe019UL, 0xe5916f9eUL, 0xbe325278UL, + 0x00000000UL, 0x3fefd02fUL, 0x859a1062UL, 0x3e595fb7UL, 0xc0000000UL, + 0x3fefc049UL, 0xb245f18fUL, 0xbe529c38UL, 0xe0000000UL, 0x3fefb069UL, + 0xad2880a7UL, 0xbe501230UL, 0x60000000UL, 0x3fefa08fUL, 0xc8e72420UL, + 0x3e597bd1UL, 0x80000000UL, 0x3fef90baUL, 0xc30c4500UL, 0xbe5d6c75UL, + 0xe0000000UL, 0x3fef80eaUL, 0x02c63f43UL, 0x3e2e1318UL, 0xc0000000UL, + 0x3fef7120UL, 0xb3d4ccccUL, 0xbe44c52aUL, 0x00000000UL, 0x3fef615cUL, + 0xdbd91397UL, 0xbe4e7d6cUL, 0xa0000000UL, 0x3fef519cUL, 0x65c5cd68UL, + 0xbe522dc8UL, 0xa0000000UL, 0x3fef41e2UL, 0x46d1306cUL, 0xbe5a840eUL, + 0xe0000000UL, 0x3fef322dUL, 0xd2980e94UL, 0x3e5071afUL, 0xa0000000UL, + 0x3fef227eUL, 0x773abadeUL, 0xbe5891e5UL, 0xa0000000UL, 0x3fef12d4UL, + 0xdc6bf46bUL, 0xbe5cccbeUL, 0xe0000000UL, 0x3fef032fUL, 0xbc7247faUL, + 0xbe2bab83UL, 0x80000000UL, 0x3feef390UL, 0xbcaa1e46UL, 0xbe53bb3bUL, + 0x60000000UL, 0x3feee3f6UL, 0x5f6c682dUL, 0xbe54c619UL, 0x80000000UL, + 0x3feed461UL, 0x5141e368UL, 0xbe4b6d86UL, 0xe0000000UL, 0x3feec4d1UL, + 0xec678f76UL, 0xbe369af6UL, 0x80000000UL, 0x3feeb547UL, 0x41301f55UL, + 0xbe2d4312UL, 0x60000000UL, 0x3feea5c2UL, 0x676da6bdUL, 0xbe4d8dd0UL, + 0x60000000UL, 0x3fee9642UL, 0x57a891c4UL, 0x3e51f991UL, 0xa0000000UL, + 0x3fee86c7UL, 0xe4eb491eUL, 0x3e579bf9UL, 0x20000000UL, 0x3fee7752UL, + 0xfddc4a2cUL, 0xbe3356e6UL, 0xc0000000UL, 0x3fee67e1UL, 0xd75b5bf1UL, + 0xbe449531UL, 0x80000000UL, 0x3fee5876UL, 0xbd423b8eUL, 0x3df54fe4UL, + 0x60000000UL, 0x3fee4910UL, 0x330e51b9UL, 0x3e54289cUL, 0x80000000UL, + 0x3fee39afUL, 0x8651a95fUL, 0xbe55aad6UL, 0xa0000000UL, 0x3fee2a53UL, + 0x5e98c708UL, 0xbe2fc4a9UL, 0xe0000000UL, 0x3fee1afcUL, 0x0989328dUL, + 0x3e23958cUL, 0x40000000UL, 0x3fee0babUL, 0xee642abdUL, 0xbe425dd8UL, + 0xa0000000UL, 0x3fedfc5eUL, 0xc394d236UL, 0x3e526362UL, 0x20000000UL, + 0x3feded17UL, 0xe104aa8eUL, 0x3e4ce247UL, 0xc0000000UL, 0x3fedddd4UL, + 0x265a9be4UL, 0xbe5bb77aUL, 0x40000000UL, 0x3fedce97UL, 0x0ecac52fUL, + 0x3e4a7cb1UL, 0xe0000000UL, 0x3fedbf5eUL, 0x124cb3b8UL, 0x3e257024UL, + 0x80000000UL, 0x3fedb02bUL, 0xe6d4febeUL, 0xbe2033eeUL, 0x20000000UL, + 0x3feda0fdUL, 0x39cca00eUL, 0xbe3ddabcUL, 0xc0000000UL, 0x3fed91d3UL, + 0xef8a552aUL, 0xbe543390UL, 0x40000000UL, 0x3fed82afUL, 0xb8e85204UL, + 0x3e513850UL, 0xe0000000UL, 0x3fed738fUL, 0x3d59fe08UL, 0xbe5db728UL, + 0x40000000UL, 0x3fed6475UL, 0x3aa7ead1UL, 0x3e58804bUL, 0xc0000000UL, + 0x3fed555fUL, 0xf8a35ba9UL, 0xbe5298b0UL, 0x00000000UL, 0x3fed464fUL, + 0x9a88dd15UL, 0x3e5a8cdbUL, 0x40000000UL, 0x3fed3743UL, 0xb0b0a190UL, + 0x3e598635UL, 0x80000000UL, 0x3fed283cUL, 0xe2113295UL, 0xbe5c1119UL, + 0x80000000UL, 0x3fed193aUL, 0xafbf1728UL, 0xbe492e9cUL, 0x60000000UL, + 0x3fed0a3dUL, 0xe4a4ccf3UL, 0x3e19b90eUL, 0x20000000UL, 0x3fecfb45UL, + 0xba3cbeb8UL, 0x3e406b50UL, 0xc0000000UL, 0x3fecec51UL, 0x110f7dddUL, + 0x3e0d6806UL, 0x40000000UL, 0x3fecdd63UL, 0x7dd7d508UL, 0xbe5a8943UL, + 0x80000000UL, 0x3fecce79UL, 0x9b60f271UL, 0xbe50676aUL, 0x80000000UL, + 0x3fecbf94UL, 0x0b9ad660UL, 0x3e59174fUL, 0x60000000UL, 0x3fecb0b4UL, + 0x00823d9cUL, 0x3e5bbf72UL, 0x20000000UL, 0x3feca1d9UL, 0x38a6ec89UL, + 0xbe4d38f9UL, 0x80000000UL, 0x3fec9302UL, 0x3a0b7d8eUL, 0x3e53dbfdUL, + 0xc0000000UL, 0x3fec8430UL, 0xc6826b34UL, 0xbe27c5c9UL, 0xc0000000UL, + 0x3fec7563UL, 0x0c706381UL, 0xbe593653UL, 0x60000000UL, 0x3fec669bUL, + 0x7df34ec7UL, 0x3e461ab5UL, 0xe0000000UL, 0x3fec57d7UL, 0x40e5e7e8UL, + 0xbe5c3daeUL, 0x00000000UL, 0x3fec4919UL, 0x5602770fUL, 0xbe55219dUL, + 0xc0000000UL, 0x3fec3a5eUL, 0xec7911ebUL, 0x3e5a5d25UL, 0x60000000UL, + 0x3fec2ba9UL, 0xb39ea225UL, 0xbe53c00bUL, 0x80000000UL, 0x3fec1cf8UL, + 0x967a212eUL, 0x3e5a8ddfUL, 0x60000000UL, 0x3fec0e4cUL, 0x580798bdUL, + 0x3e5f53abUL, 0x00000000UL, 0x3febffa5UL, 0xb8282df6UL, 0xbe46b874UL, + 0x20000000UL, 0x3febf102UL, 0xe33a6729UL, 0x3e54963fUL, 0x00000000UL, + 0x3febe264UL, 0x3b53e88aUL, 0xbe3adce1UL, 0x60000000UL, 0x3febd3caUL, + 0xc2585084UL, 0x3e5cde9fUL, 0x80000000UL, 0x3febc535UL, 0xa335c5eeUL, + 0xbe39fd9cUL, 0x20000000UL, 0x3febb6a5UL, 0x7325b04dUL, 0x3e42ba15UL, + 0x60000000UL, 0x3feba819UL, 0x1564540fUL, 0x3e3a9f35UL, 0x40000000UL, + 0x3feb9992UL, 0x83fff592UL, 0xbe5465ceUL, 0xa0000000UL, 0x3feb8b0fUL, + 0xb9da63d3UL, 0xbe4b1a0aUL, 0x80000000UL, 0x3feb7c91UL, 0x6d6f1ea4UL, + 0x3e557657UL, 0x00000000UL, 0x3feb6e18UL, 0x5e80a1bfUL, 0x3e4ddbb6UL, + 0x00000000UL, 0x3feb5fa3UL, 0x1c9eacb5UL, 0x3e592877UL, 0xa0000000UL, + 0x3feb5132UL, 0x6d40beb3UL, 0xbe51858cUL, 0xa0000000UL, 0x3feb42c6UL, + 0xd740c67bUL, 0x3e427ad2UL, 0x40000000UL, 0x3feb345fUL, 0xa3e0cceeUL, + 0xbe5c2fc4UL, 0x40000000UL, 0x3feb25fcUL, 0x8e752b50UL, 0xbe3da3c2UL, + 0xc0000000UL, 0x3feb179dUL, 0xa892e7deUL, 0x3e1fb481UL, 0xc0000000UL, + 0x3feb0943UL, 0x21ed71e9UL, 0xbe365206UL, 0x20000000UL, 0x3feafaeeUL, + 0x0e1380a3UL, 0x3e5c5b7bUL, 0x20000000UL, 0x3feaec9dUL, 0x3c3d640eUL, + 0xbe5dbbd0UL, 0x60000000UL, 0x3feade50UL, 0x8f97a715UL, 0x3e3a8ec5UL, + 0x20000000UL, 0x3fead008UL, 0x23ab2839UL, 0x3e2fe98aUL, 0x40000000UL, + 0x3feac1c4UL, 0xf4bbd50fUL, 0x3e54d8f6UL, 0xe0000000UL, 0x3feab384UL, + 0x14757c4dUL, 0xbe48774cUL, 0xc0000000UL, 0x3feaa549UL, 0x7c7b0eeaUL, + 0x3e5b51bbUL, 0x20000000UL, 0x3fea9713UL, 0xf56f7013UL, 0x3e386200UL, + 0xe0000000UL, 0x3fea88e0UL, 0xbe428ebeUL, 0xbe514af5UL, 0xe0000000UL, + 0x3fea7ab2UL, 0x8d0e4496UL, 0x3e4f9165UL, 0x60000000UL, 0x3fea6c89UL, + 0xdbacc5d5UL, 0xbe5c063bUL, 0x20000000UL, 0x3fea5e64UL, 0x3f19d970UL, + 0xbe5a0c8cUL, 0x20000000UL, 0x3fea5043UL, 0x09ea3e6bUL, 0x3e5065dcUL, + 0x80000000UL, 0x3fea4226UL, 0x78df246cUL, 0x3e5e05f6UL, 0x40000000UL, + 0x3fea340eUL, 0x4057d4a0UL, 0x3e431b2bUL, 0x40000000UL, 0x3fea25faUL, + 0x82867bb5UL, 0x3e4b76beUL, 0xa0000000UL, 0x3fea17eaUL, 0x9436f40aUL, + 0xbe5aad39UL, 0x20000000UL, 0x3fea09dfUL, 0x4b5253b3UL, 0x3e46380bUL, + 0x00000000UL, 0x3fe9fbd8UL, 0x8fc52466UL, 0xbe386f9bUL, 0x20000000UL, + 0x3fe9edd5UL, 0x22d3f344UL, 0xbe538347UL, 0x60000000UL, 0x3fe9dfd6UL, + 0x1ac33522UL, 0x3e5dbc53UL, 0x00000000UL, 0x3fe9d1dcUL, 0xeabdff1dUL, + 0x3e40fc0cUL, 0xe0000000UL, 0x3fe9c3e5UL, 0xafd30e73UL, 0xbe585e63UL, + 0xe0000000UL, 0x3fe9b5f3UL, 0xa52f226aUL, 0xbe43e8f9UL, 0x20000000UL, + 0x3fe9a806UL, 0xecb8698dUL, 0xbe515b36UL, 0x80000000UL, 0x3fe99a1cUL, + 0xf2b4e89dUL, 0x3e48b62bUL, 0x20000000UL, 0x3fe98c37UL, 0x7c9a88fbUL, + 0x3e44414cUL, 0x00000000UL, 0x3fe97e56UL, 0xda015741UL, 0xbe5d13baUL, + 0xe0000000UL, 0x3fe97078UL, 0x5fdace06UL, 0x3e51b947UL, 0x00000000UL, + 0x3fe962a0UL, 0x956ca094UL, 0x3e518785UL, 0x40000000UL, 0x3fe954cbUL, + 0x01164c1dUL, 0x3e5d5b57UL, 0xc0000000UL, 0x3fe946faUL, 0xe63b3767UL, + 0xbe4f84e7UL, 0x40000000UL, 0x3fe9392eUL, 0xe57cc2a9UL, 0x3e34eda3UL, + 0xe0000000UL, 0x3fe92b65UL, 0x8c75b544UL, 0x3e5766a0UL, 0xc0000000UL, + 0x3fe91da1UL, 0x37d1d087UL, 0xbe5e2ab1UL, 0x80000000UL, 0x3fe90fe1UL, + 0xa953dc20UL, 0x3e5fa1f3UL, 0x80000000UL, 0x3fe90225UL, 0xdbd3f369UL, + 0x3e47d6dbUL, 0xa0000000UL, 0x3fe8f46dUL, 0x1c9be989UL, 0xbe5e2b0aUL, + 0xa0000000UL, 0x3fe8e6b9UL, 0x3c93d76aUL, 0x3e5c8618UL, 0xe0000000UL, + 0x3fe8d909UL, 0x2182fc9aUL, 0xbe41aa9eUL, 0x20000000UL, 0x3fe8cb5eUL, + 0xe6b3539dUL, 0xbe530d19UL, 0x60000000UL, 0x3fe8bdb6UL, 0x49e58cc3UL, + 0xbe3bb374UL, 0xa0000000UL, 0x3fe8b012UL, 0xa7cfeb8fUL, 0x3e56c412UL, + 0x00000000UL, 0x3fe8a273UL, 0x8d52bc19UL, 0x3e1429b8UL, 0x60000000UL, + 0x3fe894d7UL, 0x4dc32c6cUL, 0xbe48604cUL, 0xc0000000UL, 0x3fe8873fUL, + 0x0c868e56UL, 0xbe564ee5UL, 0x00000000UL, 0x3fe879acUL, 0x56aee828UL, + 0x3e5e2fd8UL, 0x60000000UL, 0x3fe86c1cUL, 0x7ceab8ecUL, 0x3e493365UL, + 0xc0000000UL, 0x3fe85e90UL, 0x78d4dadcUL, 0xbe4f7f25UL, 0x00000000UL, + 0x3fe85109UL, 0x0ccd8280UL, 0x3e31e7a2UL, 0x40000000UL, 0x3fe84385UL, + 0x34ba4e15UL, 0x3e328077UL, 0x80000000UL, 0x3fe83605UL, 0xa670975aUL, + 0xbe53eee5UL, 0xa0000000UL, 0x3fe82889UL, 0xf61b77b2UL, 0xbe43a20aUL, + 0xa0000000UL, 0x3fe81b11UL, 0x13e6643bUL, 0x3e5e5fe5UL, 0xc0000000UL, + 0x3fe80d9dUL, 0x82cc94e8UL, 0xbe5ff1f9UL, 0xa0000000UL, 0x3fe8002dUL, + 0x8a0c9c5dUL, 0xbe42b0e7UL, 0x60000000UL, 0x3fe7f2c1UL, 0x22a16f01UL, + 0x3e5d9ea0UL, 0x20000000UL, 0x3fe7e559UL, 0xc38cd451UL, 0x3e506963UL, + 0xc0000000UL, 0x3fe7d7f4UL, 0x9902bc71UL, 0x3e4503d7UL, 0x40000000UL, + 0x3fe7ca94UL, 0xdef2a3c0UL, 0x3e3d98edUL, 0xa0000000UL, 0x3fe7bd37UL, + 0xed49abb0UL, 0x3e24c1ffUL, 0xe0000000UL, 0x3fe7afdeUL, 0xe3b0be70UL, + 0xbe40c467UL, 0x00000000UL, 0x3fe7a28aUL, 0xaf9f193cUL, 0xbe5dff6cUL, + 0xe0000000UL, 0x3fe79538UL, 0xb74cf6b6UL, 0xbe258ed0UL, 0xa0000000UL, + 0x3fe787ebUL, 0x1d9127c7UL, 0x3e345fb0UL, 0x40000000UL, 0x3fe77aa2UL, + 0x1028c21dUL, 0xbe4619bdUL, 0xa0000000UL, 0x3fe76d5cUL, 0x7cb0b5e4UL, + 0x3e40f1a2UL, 0xe0000000UL, 0x3fe7601aUL, 0x2b1bc4adUL, 0xbe32e8bbUL, + 0xe0000000UL, 0x3fe752dcUL, 0x6839f64eUL, 0x3e41f57bUL, 0xc0000000UL, + 0x3fe745a2UL, 0xc4121f7eUL, 0xbe52c40aUL, 0x60000000UL, 0x3fe7386cUL, + 0xd6852d72UL, 0xbe5c4e6bUL, 0xc0000000UL, 0x3fe72b39UL, 0x91d690f7UL, + 0xbe57f88fUL, 0xe0000000UL, 0x3fe71e0aUL, 0x627a2159UL, 0xbe4425d5UL, + 0xc0000000UL, 0x3fe710dfUL, 0x50a54033UL, 0x3e422b7eUL, 0x60000000UL, + 0x3fe703b8UL, 0x3b0b5f91UL, 0x3e5d3857UL, 0xe0000000UL, 0x3fe6f694UL, + 0x84d628a2UL, 0xbe51f090UL, 0x00000000UL, 0x3fe6e975UL, 0x306d8894UL, + 0xbe414d83UL, 0xe0000000UL, 0x3fe6dc58UL, 0x30bf24aaUL, 0xbe4650caUL, + 0x80000000UL, 0x3fe6cf40UL, 0xd4628d69UL, 0xbe5db007UL, 0xc0000000UL, + 0x3fe6c22bUL, 0xa2aae57bUL, 0xbe31d279UL, 0xc0000000UL, 0x3fe6b51aUL, + 0x860edf7eUL, 0xbe2d4c4aUL, 0x80000000UL, 0x3fe6a80dUL, 0xf3559341UL, + 0xbe5f7e98UL, 0xe0000000UL, 0x3fe69b03UL, 0xa885899eUL, 0xbe5c2011UL, + 0xe0000000UL, 0x3fe68dfdUL, 0x2bdc6d37UL, 0x3e224a82UL, 0xa0000000UL, + 0x3fe680fbUL, 0xc12ad1b9UL, 0xbe40cf56UL, 0x00000000UL, 0x3fe673fdUL, + 0x1bcdf659UL, 0xbdf52f2dUL, 0x00000000UL, 0x3fe66702UL, 0x5df10408UL, + 0x3e5663e0UL, 0xc0000000UL, 0x3fe65a0aUL, 0xa4070568UL, 0xbe40b12fUL, + 0x00000000UL, 0x3fe64d17UL, 0x71c54c47UL, 0x3e5f5e8bUL, 0x00000000UL, + 0x3fe64027UL, 0xbd4b7e83UL, 0x3e42ead6UL, 0xa0000000UL, 0x3fe6333aUL, + 0x61598bd2UL, 0xbe4c48d4UL, 0xc0000000UL, 0x3fe62651UL, 0x6f538d61UL, + 0x3e548401UL, 0xa0000000UL, 0x3fe6196cUL, 0x14344120UL, 0xbe529af6UL, + 0x00000000UL, 0x3fe60c8bUL, 0x5982c587UL, 0xbe3e1e4fUL, 0x00000000UL, + 0x3fe5ffadUL, 0xfe51d4eaUL, 0xbe4c897aUL, 0x80000000UL, 0x3fe5f2d2UL, + 0xfd46ebe1UL, 0x3e552e00UL, 0xa0000000UL, 0x3fe5e5fbUL, 0xa4695699UL, + 0x3e5ed471UL, 0x60000000UL, 0x3fe5d928UL, 0x80d118aeUL, 0x3e456b61UL, + 0xa0000000UL, 0x3fe5cc58UL, 0x304c330bUL, 0x3e54dc29UL, 0x80000000UL, + 0x3fe5bf8cUL, 0x0af2dedfUL, 0xbe3aa9bdUL, 0xe0000000UL, 0x3fe5b2c3UL, + 0x15fc9258UL, 0xbe479a37UL, 0xc0000000UL, 0x3fe5a5feUL, 0x9292c7eaUL, + 0x3e188650UL, 0x20000000UL, 0x3fe5993dUL, 0x33b4d380UL, 0x3e5d6d93UL, + 0x20000000UL, 0x3fe58c7fUL, 0x02fd16c7UL, 0x3e2fe961UL, 0xa0000000UL, + 0x3fe57fc4UL, 0x4a05edb6UL, 0xbe4d55b4UL, 0xa0000000UL, 0x3fe5730dUL, + 0x3d443abbUL, 0xbe5e6954UL, 0x00000000UL, 0x3fe5665aUL, 0x024acfeaUL, + 0x3e50e61bUL, 0x00000000UL, 0x3fe559aaUL, 0xcc9edd09UL, 0xbe325403UL, + 0x60000000UL, 0x3fe54cfdUL, 0x1fe26950UL, 0x3e5d500eUL, 0x60000000UL, + 0x3fe54054UL, 0x6c5ae164UL, 0xbe4a79b4UL, 0xc0000000UL, 0x3fe533aeUL, + 0x154b0287UL, 0xbe401571UL, 0xa0000000UL, 0x3fe5270cUL, 0x0673f401UL, + 0xbe56e56bUL, 0xe0000000UL, 0x3fe51a6dUL, 0x751b639cUL, 0x3e235269UL, + 0xa0000000UL, 0x3fe50dd2UL, 0x7c7b2bedUL, 0x3ddec887UL, 0xc0000000UL, + 0x3fe5013aUL, 0xafab4e17UL, 0x3e5e7575UL, 0x60000000UL, 0x3fe4f4a6UL, + 0x2e308668UL, 0x3e59aed6UL, 0x80000000UL, 0x3fe4e815UL, 0xf33e2a76UL, + 0xbe51f184UL, 0xe0000000UL, 0x3fe4db87UL, 0x839f3e3eUL, 0x3e57db01UL, + 0xc0000000UL, 0x3fe4cefdUL, 0xa9eda7bbUL, 0x3e535e0fUL, 0x00000000UL, + 0x3fe4c277UL, 0x2a8f66a5UL, 0x3e5ce451UL, 0xc0000000UL, 0x3fe4b5f3UL, + 0x05192456UL, 0xbe4e8518UL, 0xc0000000UL, 0x3fe4a973UL, 0x4aa7cd1dUL, + 0x3e46784aUL, 0x40000000UL, 0x3fe49cf7UL, 0x8e23025eUL, 0xbe5749f2UL, + 0x00000000UL, 0x3fe4907eUL, 0x18d30215UL, 0x3e360f39UL, 0x20000000UL, + 0x3fe48408UL, 0x63dcf2f3UL, 0x3e5e00feUL, 0xc0000000UL, 0x3fe47795UL, + 0x46182d09UL, 0xbe5173d9UL, 0xa0000000UL, 0x3fe46b26UL, 0x8f0e62aaUL, + 0xbe48f281UL, 0xe0000000UL, 0x3fe45ebaUL, 0x5775c40cUL, 0xbe56aad4UL, + 0x60000000UL, 0x3fe45252UL, 0x0fe25f69UL, 0x3e48bd71UL, 0x40000000UL, + 0x3fe445edUL, 0xe9989ec5UL, 0x3e590d97UL, 0x80000000UL, 0x3fe4398bUL, + 0xb3d9ffe3UL, 0x3e479dbcUL, 0x20000000UL, 0x3fe42d2dUL, 0x388e4d2eUL, + 0xbe5eed80UL, 0xe0000000UL, 0x3fe420d1UL, 0x6f797c18UL, 0x3e554b4cUL, + 0x20000000UL, 0x3fe4147aUL, 0x31048bb4UL, 0xbe5b1112UL, 0x80000000UL, + 0x3fe40825UL, 0x2efba4f9UL, 0x3e48ebc7UL, 0x40000000UL, 0x3fe3fbd4UL, + 0x50201119UL, 0x3e40b701UL, 0x40000000UL, 0x3fe3ef86UL, 0x0a4db32cUL, + 0x3e551de8UL, 0xa0000000UL, 0x3fe3e33bUL, 0x0c9c148bUL, 0xbe50c1f6UL, + 0x20000000UL, 0x3fe3d6f4UL, 0xc9129447UL, 0x3e533fa0UL, 0x00000000UL, + 0x3fe3cab0UL, 0xaae5b5a0UL, 0xbe22b68eUL, 0x20000000UL, 0x3fe3be6fUL, + 0x02305e8aUL, 0xbe54fc08UL, 0x60000000UL, 0x3fe3b231UL, 0x7f908258UL, + 0x3e57dc05UL, 0x00000000UL, 0x3fe3a5f7UL, 0x1a09af78UL, 0x3e08038bUL, + 0xe0000000UL, 0x3fe399bfUL, 0x490643c1UL, 0xbe5dbe42UL, 0xe0000000UL, + 0x3fe38d8bUL, 0x5e8ad724UL, 0xbe3c2b72UL, 0x20000000UL, 0x3fe3815bUL, + 0xc67196b6UL, 0x3e1713cfUL, 0xa0000000UL, 0x3fe3752dUL, 0x6182e429UL, + 0xbe3ec14cUL, 0x40000000UL, 0x3fe36903UL, 0xab6eb1aeUL, 0x3e5a2cc5UL, + 0x40000000UL, 0x3fe35cdcUL, 0xfe5dc064UL, 0xbe5c5878UL, 0x40000000UL, + 0x3fe350b8UL, 0x0ba6b9e4UL, 0x3e51619bUL, 0x80000000UL, 0x3fe34497UL, + 0x857761aaUL, 0x3e5fff53UL, 0x00000000UL, 0x3fe3387aUL, 0xf872d68cUL, + 0x3e484f4dUL, 0xa0000000UL, 0x3fe32c5fUL, 0x087e97c2UL, 0x3e52842eUL, + 0x80000000UL, 0x3fe32048UL, 0x73d6d0c0UL, 0xbe503edfUL, 0x80000000UL, + 0x3fe31434UL, 0x0c1456a1UL, 0xbe5f72adUL, 0xa0000000UL, 0x3fe30823UL, + 0x83a1a4d5UL, 0xbe5e65ccUL, 0xe0000000UL, 0x3fe2fc15UL, 0x855a7390UL, + 0xbe506438UL, 0x40000000UL, 0x3fe2f00bUL, 0xa2898287UL, 0x3e3d22a2UL, + 0xe0000000UL, 0x3fe2e403UL, 0x8b56f66fUL, 0xbe5aa5fdUL, 0x80000000UL, + 0x3fe2d7ffUL, 0x52db119aUL, 0x3e3a2e3dUL, 0x60000000UL, 0x3fe2cbfeUL, + 0xe2ddd4c0UL, 0xbe586469UL, 0x40000000UL, 0x3fe2c000UL, 0x6b01bf10UL, + 0x3e352b9dUL, 0x40000000UL, 0x3fe2b405UL, 0xb07a1cdfUL, 0x3e5c5cdaUL, + 0x80000000UL, 0x3fe2a80dUL, 0xc7b5f868UL, 0xbe5668b3UL, 0xc0000000UL, + 0x3fe29c18UL, 0x185edf62UL, 0xbe563d66UL, 0x00000000UL, 0x3fe29027UL, + 0xf729e1ccUL, 0x3e59a9a0UL, 0x80000000UL, 0x3fe28438UL, 0x6433c727UL, + 0xbe43cc89UL, 0x00000000UL, 0x3fe2784dUL, 0x41782631UL, 0xbe30750cUL, + 0xa0000000UL, 0x3fe26c64UL, 0x914911b7UL, 0xbe58290eUL, 0x40000000UL, + 0x3fe2607fUL, 0x3dcc73e1UL, 0xbe4269cdUL, 0x00000000UL, 0x3fe2549dUL, + 0x2751bf70UL, 0xbe5a6998UL, 0xc0000000UL, 0x3fe248bdUL, 0x4248b9fbUL, + 0xbe4ddb00UL, 0x80000000UL, 0x3fe23ce1UL, 0xf35cf82fUL, 0x3e561b71UL, + 0x60000000UL, 0x3fe23108UL, 0x8e481a2dUL, 0x3e518fb9UL, 0x60000000UL, + 0x3fe22532UL, 0x5ab96edcUL, 0xbe5fafc5UL, 0x40000000UL, 0x3fe2195fUL, + 0x80943911UL, 0xbe07f819UL, 0x40000000UL, 0x3fe20d8fUL, 0x386f2d6cUL, + 0xbe54ba8bUL, 0x40000000UL, 0x3fe201c2UL, 0xf29664acUL, 0xbe5eb815UL, + 0x20000000UL, 0x3fe1f5f8UL, 0x64f03390UL, 0x3e5e320cUL, 0x20000000UL, + 0x3fe1ea31UL, 0x747ff696UL, 0x3e5ef0a5UL, 0x40000000UL, 0x3fe1de6dUL, + 0x3e9ceb51UL, 0xbe5f8d27UL, 0x20000000UL, 0x3fe1d2acUL, 0x4ae0b55eUL, + 0x3e5faa21UL, 0x20000000UL, 0x3fe1c6eeUL, 0x28569a5eUL, 0x3e598a4fUL, + 0x20000000UL, 0x3fe1bb33UL, 0x54b33e07UL, 0x3e46130aUL, 0x20000000UL, + 0x3fe1af7bUL, 0x024f1078UL, 0xbe4dbf93UL, 0x00000000UL, 0x3fe1a3c6UL, + 0xb0783bfaUL, 0x3e419248UL, 0xe0000000UL, 0x3fe19813UL, 0x2f02b836UL, + 0x3e4e02b7UL, 0xc0000000UL, 0x3fe18c64UL, 0x28dec9d4UL, 0x3e09064fUL, + 0x80000000UL, 0x3fe180b8UL, 0x45cbf406UL, 0x3e5b1f46UL, 0x40000000UL, + 0x3fe1750fUL, 0x03d9964cUL, 0x3e5b0a79UL, 0x00000000UL, 0x3fe16969UL, + 0x8b5b882bUL, 0xbe238086UL, 0xa0000000UL, 0x3fe15dc5UL, 0x73bad6f8UL, + 0xbdf1fca4UL, 0x20000000UL, 0x3fe15225UL, 0x5385769cUL, 0x3e5e8d76UL, + 0xa0000000UL, 0x3fe14687UL, 0x1676dc6bUL, 0x3e571d08UL, 0x20000000UL, + 0x3fe13aedUL, 0xa8c41c7fUL, 0xbe598a25UL, 0x60000000UL, 0x3fe12f55UL, + 0xc4e1aaf0UL, 0x3e435277UL, 0xa0000000UL, 0x3fe123c0UL, 0x403638e1UL, + 0xbe21aa7cUL, 0xc0000000UL, 0x3fe1182eUL, 0x557a092bUL, 0xbdd0116bUL, + 0xc0000000UL, 0x3fe10c9fUL, 0x7d779f66UL, 0x3e4a61baUL, 0xc0000000UL, + 0x3fe10113UL, 0x2b09c645UL, 0xbe5d586eUL, 0x20000000UL, 0x3fe0ea04UL, + 0xea2cad46UL, 0x3e5aa97cUL, 0x20000000UL, 0x3fe0d300UL, 0x23190e54UL, + 0x3e50f1a7UL, 0xa0000000UL, 0x3fe0bc07UL, 0x1379a5a6UL, 0xbe51619dUL, + 0x60000000UL, 0x3fe0a51aUL, 0x926a3d4aUL, 0x3e5cf019UL, 0xa0000000UL, + 0x3fe08e38UL, 0xa8c24358UL, 0x3e35241eUL, 0x20000000UL, 0x3fe07762UL, + 0x24317e7aUL, 0x3e512cfaUL, 0x00000000UL, 0x3fe06097UL, 0xfd9cf274UL, + 0xbe55bef3UL, 0x00000000UL, 0x3fe049d7UL, 0x3689b49dUL, 0xbe36d26dUL, + 0x40000000UL, 0x3fe03322UL, 0xf72ef6c4UL, 0xbe54cd08UL, 0xa0000000UL, + 0x3fe01c78UL, 0x23702d2dUL, 0xbe5900bfUL, 0x00000000UL, 0x3fe005daUL, + 0x3f59c14cUL, 0x3e57d80bUL, 0x40000000UL, 0x3fdfde8dUL, 0xad67766dUL, + 0xbe57fad4UL, 0x40000000UL, 0x3fdfb17cUL, 0x644f4ae7UL, 0x3e1ee43bUL, + 0x40000000UL, 0x3fdf8481UL, 0x903234d2UL, 0x3e501a86UL, 0x40000000UL, + 0x3fdf579cUL, 0xafe9e509UL, 0xbe267c3eUL, 0x00000000UL, 0x3fdf2acdUL, + 0xb7dfda0bUL, 0xbe48149bUL, 0x40000000UL, 0x3fdefe13UL, 0x3b94305eUL, + 0x3e5f4ea7UL, 0x80000000UL, 0x3fded16fUL, 0x5d95da61UL, 0xbe55c198UL, + 0x00000000UL, 0x3fdea4e1UL, 0x406960c9UL, 0xbdd99a19UL, 0x00000000UL, + 0x3fde7868UL, 0xd22f3539UL, 0x3e470c78UL, 0x80000000UL, 0x3fde4c04UL, + 0x83eec535UL, 0xbe3e1232UL, 0x40000000UL, 0x3fde1fb6UL, 0x3dfbffcbUL, + 0xbe4b7d71UL, 0x40000000UL, 0x3fddf37dUL, 0x7e1be4e0UL, 0xbe5b8f8fUL, + 0x40000000UL, 0x3fddc759UL, 0x46dae887UL, 0xbe350458UL, 0x80000000UL, + 0x3fdd9b4aUL, 0xed6ecc49UL, 0xbe5f0045UL, 0x80000000UL, 0x3fdd6f50UL, + 0x2e9e883cUL, 0x3e2915daUL, 0x80000000UL, 0x3fdd436bUL, 0xf0bccb32UL, + 0x3e4a68c9UL, 0x80000000UL, 0x3fdd179bUL, 0x9bbfc779UL, 0xbe54a26aUL, + 0x00000000UL, 0x3fdcebe0UL, 0x7cea33abUL, 0x3e43c6b7UL, 0x40000000UL, + 0x3fdcc039UL, 0xe740fd06UL, 0x3e5526c2UL, 0x40000000UL, 0x3fdc94a7UL, + 0x9eadeb1aUL, 0xbe396d8dUL, 0xc0000000UL, 0x3fdc6929UL, 0xf0a8f95aUL, + 0xbe5c0ab2UL, 0x80000000UL, 0x3fdc3dc0UL, 0x6ee2693bUL, 0x3e0992e6UL, + 0xc0000000UL, 0x3fdc126bUL, 0x5ac6b581UL, 0xbe2834b6UL, 0x40000000UL, + 0x3fdbe72bUL, 0x8cc226ffUL, 0x3e3596a6UL, 0x00000000UL, 0x3fdbbbffUL, + 0xf92a74bbUL, 0x3e3c5813UL, 0x00000000UL, 0x3fdb90e7UL, 0x479664c0UL, + 0xbe50d644UL, 0x00000000UL, 0x3fdb65e3UL, 0x5004975bUL, 0xbe55258fUL, + 0x00000000UL, 0x3fdb3af3UL, 0xe4b23194UL, 0xbe588407UL, 0xc0000000UL, + 0x3fdb1016UL, 0xe65d4d0aUL, 0x3e527c26UL, 0x80000000UL, 0x3fdae54eUL, + 0x814fddd6UL, 0x3e5962a2UL, 0x40000000UL, 0x3fdaba9aUL, 0xe19d0913UL, + 0xbe562f4eUL, 0x80000000UL, 0x3fda8ff9UL, 0x43cfd006UL, 0xbe4cfdebUL, + 0x40000000UL, 0x3fda656cUL, 0x686f0a4eUL, 0x3e5e47a8UL, 0xc0000000UL, + 0x3fda3af2UL, 0x7200d410UL, 0x3e5e1199UL, 0xc0000000UL, 0x3fda108cUL, + 0xabd2266eUL, 0x3e5ee4d1UL, 0x40000000UL, 0x3fd9e63aUL, 0x396f8f2cUL, + 0x3e4dbffbUL, 0x00000000UL, 0x3fd9bbfbUL, 0xe32b25ddUL, 0x3e5c3a54UL, + 0x40000000UL, 0x3fd991cfUL, 0x431e4035UL, 0xbe457925UL, 0x80000000UL, + 0x3fd967b6UL, 0x7bed3dd3UL, 0x3e40c61dUL, 0x00000000UL, 0x3fd93db1UL, + 0xd7449365UL, 0x3e306419UL, 0x80000000UL, 0x3fd913beUL, 0x1746e791UL, + 0x3e56fcfcUL, 0x40000000UL, 0x3fd8e9dfUL, 0xf3a9028bUL, 0xbe5041b9UL, + 0xc0000000UL, 0x3fd8c012UL, 0x56840c50UL, 0xbe26e20aUL, 0x40000000UL, + 0x3fd89659UL, 0x19763102UL, 0xbe51f466UL, 0x80000000UL, 0x3fd86cb2UL, + 0x7032de7cUL, 0xbe4d298aUL, 0x80000000UL, 0x3fd8431eUL, 0xdeb39fabUL, + 0xbe4361ebUL, 0x40000000UL, 0x3fd8199dUL, 0x5d01cbe0UL, 0xbe5425b3UL, + 0x80000000UL, 0x3fd7f02eUL, 0x3ce99aa9UL, 0x3e146fa8UL, 0x80000000UL, + 0x3fd7c6d2UL, 0xd1a262b9UL, 0xbe5a1a69UL, 0xc0000000UL, 0x3fd79d88UL, + 0x8606c236UL, 0x3e423a08UL, 0x80000000UL, 0x3fd77451UL, 0x8fd1e1b7UL, + 0x3e5a6a63UL, 0xc0000000UL, 0x3fd74b2cUL, 0xe491456aUL, 0x3e42c1caUL, + 0x40000000UL, 0x3fd7221aUL, 0x4499a6d7UL, 0x3e36a69aUL, 0x00000000UL, + 0x3fd6f91aUL, 0x5237df94UL, 0xbe0f8f02UL, 0x00000000UL, 0x3fd6d02cUL, + 0xb6482c6eUL, 0xbe5abcf7UL, 0x00000000UL, 0x3fd6a750UL, 0x1919fd61UL, + 0xbe57ade2UL, 0x00000000UL, 0x3fd67e86UL, 0xaa7a994dUL, 0xbe3f3fbdUL, + 0x00000000UL, 0x3fd655ceUL, 0x67db014cUL, 0x3e33c550UL, 0x00000000UL, + 0x3fd62d28UL, 0xa82856b7UL, 0xbe1409d1UL, 0xc0000000UL, 0x3fd60493UL, + 0x1e6a300dUL, 0x3e55d899UL, 0x80000000UL, 0x3fd5dc11UL, 0x1222bd5cUL, + 0xbe35bfc0UL, 0xc0000000UL, 0x3fd5b3a0UL, 0x6e8dc2d3UL, 0x3e5d4d79UL, + 0x00000000UL, 0x3fd58b42UL, 0xe0e4ace6UL, 0xbe517303UL, 0x80000000UL, + 0x3fd562f4UL, 0xb306e0a8UL, 0x3e5edf0fUL, 0xc0000000UL, 0x3fd53ab8UL, + 0x6574bc54UL, 0x3e5ee859UL, 0x80000000UL, 0x3fd5128eUL, 0xea902207UL, + 0x3e5f6188UL, 0xc0000000UL, 0x3fd4ea75UL, 0x9f911d79UL, 0x3e511735UL, + 0x80000000UL, 0x3fd4c26eUL, 0xf9c77397UL, 0xbe5b1643UL, 0x40000000UL, + 0x3fd49a78UL, 0x15fc9258UL, 0x3e479a37UL, 0x80000000UL, 0x3fd47293UL, + 0xd5a04dd9UL, 0xbe426e56UL, 0xc0000000UL, 0x3fd44abfUL, 0xe04042f5UL, + 0x3e56f7c6UL, 0x40000000UL, 0x3fd422fdUL, 0x1d8bf2c8UL, 0x3e5d8810UL, + 0x00000000UL, 0x3fd3fb4cUL, 0x88a8ddeeUL, 0xbe311454UL, 0xc0000000UL, + 0x3fd3d3abUL, 0x3e3b5e47UL, 0xbe5d1b72UL, 0x40000000UL, 0x3fd3ac1cUL, + 0xc2ab5d59UL, 0x3e31b02bUL, 0xc0000000UL, 0x3fd3849dUL, 0xd4e34b9eUL, + 0x3e51cb2fUL, 0x40000000UL, 0x3fd35d30UL, 0x177204fbUL, 0xbe2b8cd7UL, + 0x80000000UL, 0x3fd335d3UL, 0xfcd38c82UL, 0xbe4356e1UL, 0x80000000UL, + 0x3fd30e87UL, 0x64f54accUL, 0xbe4e6224UL, 0x00000000UL, 0x3fd2e74cUL, + 0xaa7975d9UL, 0x3e5dc0feUL, 0x80000000UL, 0x3fd2c021UL, 0x516dab3fUL, + 0xbe50ffa3UL, 0x40000000UL, 0x3fd29907UL, 0x2bfb7313UL, 0x3e5674a2UL, + 0xc0000000UL, 0x3fd271fdUL, 0x0549fc99UL, 0x3e385d29UL, 0xc0000000UL, + 0x3fd24b04UL, 0x55b63073UL, 0xbe500c6dUL, 0x00000000UL, 0x3fd2241cUL, + 0x3f91953aUL, 0x3e389977UL, 0xc0000000UL, 0x3fd1fd43UL, 0xa1543f71UL, + 0xbe3487abUL, 0xc0000000UL, 0x3fd1d67bUL, 0x4ec8867cUL, 0x3df6a2dcUL, + 0x00000000UL, 0x3fd1afc4UL, 0x4328e3bbUL, 0x3e41d9c0UL, 0x80000000UL, + 0x3fd1891cUL, 0x2e1cda84UL, 0x3e3bdd87UL, 0x40000000UL, 0x3fd16285UL, + 0x4b5331aeUL, 0xbe53128eUL, 0x00000000UL, 0x3fd13bfeUL, 0xb9aec164UL, + 0xbe52ac98UL, 0xc0000000UL, 0x3fd11586UL, 0xd91e1316UL, 0xbe350630UL, + 0x80000000UL, 0x3fd0ef1fUL, 0x7cacc12cUL, 0x3e3f5219UL, 0x40000000UL, + 0x3fd0c8c8UL, 0xbce277b7UL, 0x3e3d30c0UL, 0x00000000UL, 0x3fd0a281UL, + 0x2a63447dUL, 0xbe541377UL, 0x80000000UL, 0x3fd07c49UL, 0xfac483b5UL, + 0xbe5772ecUL, 0xc0000000UL, 0x3fd05621UL, 0x36b8a570UL, 0xbe4fd4bdUL, + 0xc0000000UL, 0x3fd03009UL, 0xbae505f7UL, 0xbe450388UL, 0x80000000UL, + 0x3fd00a01UL, 0x3e35aeadUL, 0xbe5430fcUL, 0x80000000UL, 0x3fcfc811UL, + 0x707475acUL, 0x3e38806eUL, 0x80000000UL, 0x3fcf7c3fUL, 0xc91817fcUL, + 0xbe40cceaUL, 0x80000000UL, 0x3fcf308cUL, 0xae05d5e9UL, 0xbe4919b8UL, + 0x80000000UL, 0x3fcee4f8UL, 0xae6cc9e6UL, 0xbe530b94UL, 0x00000000UL, + 0x3fce9983UL, 0x1efe3e8eUL, 0x3e57747eUL, 0x00000000UL, 0x3fce4e2dUL, + 0xda78d9bfUL, 0xbe59a608UL, 0x00000000UL, 0x3fce02f5UL, 0x8abe2c2eUL, + 0x3e4a35adUL, 0x00000000UL, 0x3fcdb7dcUL, 0x1495450dUL, 0xbe0872ccUL, + 0x80000000UL, 0x3fcd6ce1UL, 0x86ee0ba0UL, 0xbe4f59a0UL, 0x00000000UL, + 0x3fcd2205UL, 0xe81ca888UL, 0x3e5402c3UL, 0x00000000UL, 0x3fccd747UL, + 0x3b4424b9UL, 0x3e5dfdc3UL, 0x80000000UL, 0x3fcc8ca7UL, 0xd305b56cUL, + 0x3e202da6UL, 0x00000000UL, 0x3fcc4226UL, 0x399a6910UL, 0xbe482a1cUL, + 0x80000000UL, 0x3fcbf7c2UL, 0x747f7938UL, 0xbe587372UL, 0x80000000UL, + 0x3fcbad7cUL, 0x6fc246a0UL, 0x3e50d83dUL, 0x00000000UL, 0x3fcb6355UL, + 0xee9e9be5UL, 0xbe5c35bdUL, 0x80000000UL, 0x3fcb194aUL, 0x8416c0bcUL, + 0x3e546d4fUL, 0x00000000UL, 0x3fcacf5eUL, 0x49f7f08fUL, 0x3e56da76UL, + 0x00000000UL, 0x3fca858fUL, 0x5dc30de2UL, 0x3e5f390cUL, 0x00000000UL, + 0x3fca3bdeUL, 0x950583b6UL, 0xbe5e4169UL, 0x80000000UL, 0x3fc9f249UL, + 0x33631553UL, 0x3e52aeb1UL, 0x00000000UL, 0x3fc9a8d3UL, 0xde8795a6UL, + 0xbe59a504UL, 0x00000000UL, 0x3fc95f79UL, 0x076bf41eUL, 0x3e5122feUL, + 0x80000000UL, 0x3fc9163cUL, 0x2914c8e7UL, 0x3e3dd064UL, 0x00000000UL, + 0x3fc8cd1dUL, 0x3a30eca3UL, 0xbe21b4aaUL, 0x80000000UL, 0x3fc8841aUL, + 0xb2a96650UL, 0xbe575444UL, 0x80000000UL, 0x3fc83b34UL, 0x2376c0cbUL, + 0xbe2a74c7UL, 0x80000000UL, 0x3fc7f26bUL, 0xd8a0b653UL, 0xbe5181b6UL, + 0x00000000UL, 0x3fc7a9bfUL, 0x32257882UL, 0xbe4a78b4UL, 0x00000000UL, + 0x3fc7612fUL, 0x1eee8bd9UL, 0xbe1bfe9dUL, 0x80000000UL, 0x3fc718bbUL, + 0x0c603cc4UL, 0x3e36fdc9UL, 0x80000000UL, 0x3fc6d064UL, 0x3728b8cfUL, + 0xbe1e542eUL, 0x80000000UL, 0x3fc68829UL, 0xc79a4067UL, 0x3e5c380fUL, + 0x00000000UL, 0x3fc6400bUL, 0xf69eac69UL, 0x3e550a84UL, 0x80000000UL, + 0x3fc5f808UL, 0xb7a780a4UL, 0x3e5d9224UL, 0x80000000UL, 0x3fc5b022UL, + 0xad9dfb1eUL, 0xbe55242fUL, 0x00000000UL, 0x3fc56858UL, 0x659b18beUL, + 0xbe4bfda3UL, 0x80000000UL, 0x3fc520a9UL, 0x66ee3631UL, 0xbe57d769UL, + 0x80000000UL, 0x3fc4d916UL, 0x1ec62819UL, 0x3e2427f7UL, 0x80000000UL, + 0x3fc4919fUL, 0xdec25369UL, 0xbe435431UL, 0x00000000UL, 0x3fc44a44UL, + 0xa8acfc4bUL, 0xbe3c62e8UL, 0x00000000UL, 0x3fc40304UL, 0xcf1d3eabUL, + 0xbdfba29fUL, 0x80000000UL, 0x3fc3bbdfUL, 0x79aba3eaUL, 0xbdf1b7c8UL, + 0x80000000UL, 0x3fc374d6UL, 0xb8d186daUL, 0xbe5130cfUL, 0x80000000UL, + 0x3fc32de8UL, 0x9d74f152UL, 0x3e2285b6UL, 0x00000000UL, 0x3fc2e716UL, + 0x50ae7ca9UL, 0xbe503920UL, 0x80000000UL, 0x3fc2a05eUL, 0x6caed92eUL, + 0xbe533924UL, 0x00000000UL, 0x3fc259c2UL, 0x9cb5034eUL, 0xbe510e31UL, + 0x80000000UL, 0x3fc21340UL, 0x12c4d378UL, 0xbe540b43UL, 0x80000000UL, + 0x3fc1ccd9UL, 0xcc418706UL, 0x3e59887aUL, 0x00000000UL, 0x3fc1868eUL, + 0x921f4106UL, 0xbe528e67UL, 0x80000000UL, 0x3fc1405cUL, 0x3969441eUL, + 0x3e5d8051UL, 0x00000000UL, 0x3fc0fa46UL, 0xd941ef5bUL, 0x3e5f9079UL, + 0x80000000UL, 0x3fc0b44aUL, 0x5a3e81b2UL, 0xbe567691UL, 0x00000000UL, + 0x3fc06e69UL, 0x9d66afe7UL, 0xbe4d43fbUL, 0x00000000UL, 0x3fc028a2UL, + 0x0a92a162UL, 0xbe52f394UL, 0x00000000UL, 0x3fbfc5eaUL, 0x209897e5UL, + 0x3e529e37UL, 0x00000000UL, 0x3fbf3ac5UL, 0x8458bd7bUL, 0x3e582831UL, + 0x00000000UL, 0x3fbeafd5UL, 0xb8d8b4b8UL, 0xbe486b4aUL, 0x00000000UL, + 0x3fbe2518UL, 0xe0a3b7b6UL, 0x3e5bafd2UL, 0x00000000UL, 0x3fbd9a90UL, + 0x2bf2710eUL, 0x3e383b2bUL, 0x00000000UL, 0x3fbd103cUL, 0x73eb6ab7UL, + 0xbe56d78dUL, 0x00000000UL, 0x3fbc861bUL, 0x32ceaff5UL, 0xbe32dc5aUL, + 0x00000000UL, 0x3fbbfc2eUL, 0xbee04cb7UL, 0xbe4a71a4UL, 0x00000000UL, + 0x3fbb7274UL, 0x35ae9577UL, 0x3e38142fUL, 0x00000000UL, 0x3fbae8eeUL, + 0xcbaddab4UL, 0xbe5490f0UL, 0x00000000UL, 0x3fba5f9aUL, 0x95ce1114UL, + 0x3e597c71UL, 0x00000000UL, 0x3fb9d67aUL, 0x6d7c0f78UL, 0x3e3abc2dUL, + 0x00000000UL, 0x3fb94d8dUL, 0x2841a782UL, 0xbe566cbcUL, 0x00000000UL, + 0x3fb8c4d2UL, 0x6ed429c6UL, 0xbe3cfff9UL, 0x00000000UL, 0x3fb83c4aUL, + 0xe4a49fbbUL, 0xbe552964UL, 0x00000000UL, 0x3fb7b3f4UL, 0x2193d81eUL, + 0xbe42fa72UL, 0x00000000UL, 0x3fb72bd0UL, 0xdd70c122UL, 0x3e527a8cUL, + 0x00000000UL, 0x3fb6a3dfUL, 0x03108a54UL, 0xbe450393UL, 0x00000000UL, + 0x3fb61c1fUL, 0x30ff7954UL, 0x3e565840UL, 0x00000000UL, 0x3fb59492UL, + 0xdedd460cUL, 0xbe5422b5UL, 0x00000000UL, 0x3fb50d36UL, 0x950f9f45UL, + 0xbe5313f6UL, 0x00000000UL, 0x3fb4860bUL, 0x582cdcb1UL, 0x3e506d39UL, + 0x00000000UL, 0x3fb3ff12UL, 0x7216d3a6UL, 0x3e4aa719UL, 0x00000000UL, + 0x3fb3784aUL, 0x57a423fdUL, 0x3e5a9b9fUL, 0x00000000UL, 0x3fb2f1b4UL, + 0x7a138b41UL, 0xbe50b418UL, 0x00000000UL, 0x3fb26b4eUL, 0x2fbfd7eaUL, + 0x3e23a53eUL, 0x00000000UL, 0x3fb1e519UL, 0x18913ccbUL, 0x3e465fc1UL, + 0x00000000UL, 0x3fb15f15UL, 0x7ea24e21UL, 0x3e042843UL, 0x00000000UL, + 0x3fb0d941UL, 0x7c6d9c77UL, 0x3e59f61eUL, 0x00000000UL, 0x3fb0539eUL, + 0x114efd44UL, 0x3e4ccab7UL, 0x00000000UL, 0x3faf9c56UL, 0x1777f657UL, + 0x3e552f65UL, 0x00000000UL, 0x3fae91d2UL, 0xc317b86aUL, 0xbe5a61e0UL, + 0x00000000UL, 0x3fad87acUL, 0xb7664efbUL, 0xbe41f64eUL, 0x00000000UL, + 0x3fac7de6UL, 0x5d3d03a9UL, 0x3e0807a0UL, 0x00000000UL, 0x3fab7480UL, + 0x743c38ebUL, 0xbe3726e1UL, 0x00000000UL, 0x3faa6b78UL, 0x06a253f1UL, + 0x3e5ad636UL, 0x00000000UL, 0x3fa962d0UL, 0xa35f541bUL, 0x3e5a187aUL, + 0x00000000UL, 0x3fa85a88UL, 0x4b86e446UL, 0xbe508150UL, 0x00000000UL, + 0x3fa7529cUL, 0x2589cacfUL, 0x3e52938aUL, 0x00000000UL, 0x3fa64b10UL, + 0xaf6b11f2UL, 0xbe3454cdUL, 0x00000000UL, 0x3fa543e2UL, 0x97506fefUL, + 0xbe5fdec5UL, 0x00000000UL, 0x3fa43d10UL, 0xe75f7dd9UL, 0xbe388dd3UL, + 0x00000000UL, 0x3fa3369cUL, 0xa4139632UL, 0xbdea5177UL, 0x00000000UL, + 0x3fa23086UL, 0x352d6f1eUL, 0xbe565ad6UL, 0x00000000UL, 0x3fa12accUL, + 0x77449eb7UL, 0xbe50d5c7UL, 0x00000000UL, 0x3fa0256eUL, 0x7478da78UL, + 0x3e404724UL, 0x00000000UL, 0x3f9e40dcUL, 0xf59cef7fUL, 0xbe539d0aUL, + 0x00000000UL, 0x3f9c3790UL, 0x1511d43cUL, 0x3e53c2c8UL, 0x00000000UL, + 0x3f9a2f00UL, 0x9b8bff3cUL, 0xbe43b3e1UL, 0x00000000UL, 0x3f982724UL, + 0xad1e22a5UL, 0x3e46f0bdUL, 0x00000000UL, 0x3f962000UL, 0x130d9356UL, + 0x3e475ba0UL, 0x00000000UL, 0x3f941994UL, 0x8f86f883UL, 0xbe513d0bUL, + 0x00000000UL, 0x3f9213dcUL, 0x914d0dc8UL, 0xbe534335UL, 0x00000000UL, + 0x3f900ed8UL, 0x2d73e5e7UL, 0xbe22ba75UL, 0x00000000UL, 0x3f8c1510UL, + 0xc5b7d70eUL, 0x3e599c5dUL, 0x00000000UL, 0x3f880de0UL, 0x8a27857eUL, + 0xbe3d28c8UL, 0x00000000UL, 0x3f840810UL, 0xda767328UL, 0x3e531b3dUL, + 0x00000000UL, 0x3f8003b0UL, 0x77bacaf3UL, 0xbe5f04e3UL, 0x00000000UL, + 0x3f780150UL, 0xdf4b0720UL, 0x3e5a8bffUL, 0x00000000UL, 0x3f6ffc40UL, + 0x34c48e71UL, 0xbe3fcd99UL, 0x00000000UL, 0x3f5ff6c0UL, 0x1ad218afUL, + 0xbe4c78a7UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x80000000UL, + 0x00000000UL, 0xfffff800UL, 0x00000000UL, 0xfffff800UL, 0x00000000UL, + 0x3ff72000UL, 0x161bb241UL, 0xbf5dabe1UL, 0x6dc96112UL, 0xbf836578UL, + 0xee241472UL, 0xbf9b0301UL, 0x9f95985aUL, 0xbfb528dbUL, 0xb3841d2aUL, + 0xbfd619b6UL, 0x518775e3UL, 0x3f9004f2UL, 0xac8349bbUL, 0x3fa76c9bUL, + 0x486ececcUL, 0x3fc4635eUL, 0x161bb241UL, 0xbf5dabe1UL, 0x9f95985aUL, + 0xbfb528dbUL, 0xf8b5787dUL, 0x3ef2531eUL, 0x486ececbUL, 0x3fc4635eUL, + 0x412055ccUL, 0xbdd61bb2UL, 0x00000000UL, 0xfffffff8UL, 0x00000000UL, + 0xffffffffUL, 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x3b700000UL, + 0xfa5abcbfUL, 0x3ff00b1aUL, 0xa7609f71UL, 0xbc84f6b2UL, 0xa9fb3335UL, + 0x3ff0163dUL, 0x9ab8cdb7UL, 0x3c9b6129UL, 0x143b0281UL, 0x3ff02168UL, + 0x0fc54eb6UL, 0xbc82bf31UL, 0x3e778061UL, 0x3ff02c9aUL, 0x535b085dUL, + 0xbc719083UL, 0x2e11bbccUL, 0x3ff037d4UL, 0xeeade11aUL, 0x3c656811UL, + 0xe86e7f85UL, 0x3ff04315UL, 0x1977c96eUL, 0xbc90a31cUL, 0x72f654b1UL, + 0x3ff04e5fUL, 0x3aa0d08cUL, 0x3c84c379UL, 0xd3158574UL, 0x3ff059b0UL, + 0xa475b465UL, 0x3c8d73e2UL, 0x0e3c1f89UL, 0x3ff0650aUL, 0x5799c397UL, + 0xbc95cb7bUL, 0x29ddf6deUL, 0x3ff0706bUL, 0xe2b13c27UL, 0xbc8c91dfUL, + 0x2b72a836UL, 0x3ff07bd4UL, 0x54458700UL, 0x3c832334UL, 0x18759bc8UL, + 0x3ff08745UL, 0x4bb284ffUL, 0x3c6186beUL, 0xf66607e0UL, 0x3ff092bdUL, + 0x800a3fd1UL, 0xbc968063UL, 0xcac6f383UL, 0x3ff09e3eUL, 0x18316136UL, + 0x3c914878UL, 0x9b1f3919UL, 0x3ff0a9c7UL, 0x873d1d38UL, 0x3c85d16cUL, + 0x6cf9890fUL, 0x3ff0b558UL, 0x4adc610bUL, 0x3c98a62eUL, 0x45e46c85UL, + 0x3ff0c0f1UL, 0x06d21cefUL, 0x3c94f989UL, 0x2b7247f7UL, 0x3ff0cc92UL, + 0x16e24f71UL, 0x3c901edcUL, 0x23395decUL, 0x3ff0d83bUL, 0xe43f316aUL, + 0xbc9bc14dUL, 0x32d3d1a2UL, 0x3ff0e3ecUL, 0x27c57b52UL, 0x3c403a17UL, + 0x5fdfa9c5UL, 0x3ff0efa5UL, 0xbc54021bUL, 0xbc949db9UL, 0xaffed31bUL, + 0x3ff0fb66UL, 0xc44ebd7bUL, 0xbc6b9bedUL, 0x28d7233eUL, 0x3ff10730UL, + 0x1692fdd5UL, 0x3c8d46ebUL, 0xd0125b51UL, 0x3ff11301UL, 0x39449b3aUL, + 0xbc96c510UL, 0xab5e2ab6UL, 0x3ff11edbUL, 0xf703fb72UL, 0xbc9ca454UL, + 0xc06c31ccUL, 0x3ff12abdUL, 0xb36ca5c7UL, 0xbc51b514UL, 0x14f204abUL, + 0x3ff136a8UL, 0xba48dcf0UL, 0xbc67108fUL, 0xaea92de0UL, 0x3ff1429aUL, + 0x9af1369eUL, 0xbc932fbfUL, 0x934f312eUL, 0x3ff14e95UL, 0x39bf44abUL, + 0xbc8b91e8UL, 0xc8a58e51UL, 0x3ff15a98UL, 0xb9eeab0aUL, 0x3c82406aUL, + 0x5471c3c2UL, 0x3ff166a4UL, 0x82ea1a32UL, 0x3c58f23bUL, 0x3c7d517bUL, + 0x3ff172b8UL, 0xb9d78a76UL, 0xbc819041UL, 0x8695bbc0UL, 0x3ff17ed4UL, + 0xe2ac5a64UL, 0x3c709e3fUL, 0x388c8deaUL, 0x3ff18af9UL, 0xd1970f6cUL, + 0xbc911023UL, 0x58375d2fUL, 0x3ff19726UL, 0x85f17e08UL, 0x3c94aaddUL, + 0xeb6fcb75UL, 0x3ff1a35bUL, 0x7b4968e4UL, 0x3c8e5b4cUL, 0xf8138a1cUL, + 0x3ff1af99UL, 0xa4b69280UL, 0x3c97bf85UL, 0x84045cd4UL, 0x3ff1bbe0UL, + 0x352ef607UL, 0xbc995386UL, 0x95281c6bUL, 0x3ff1c82fUL, 0x8010f8c9UL, + 0x3c900977UL, 0x3168b9aaUL, 0x3ff1d487UL, 0x00a2643cUL, 0x3c9e016eUL, + 0x5eb44027UL, 0x3ff1e0e7UL, 0x088cb6deUL, 0xbc96fdd8UL, 0x22fcd91dUL, + 0x3ff1ed50UL, 0x027bb78cUL, 0xbc91df98UL, 0x8438ce4dUL, 0x3ff1f9c1UL, + 0xa097af5cUL, 0xbc9bf524UL, 0x88628cd6UL, 0x3ff2063bUL, 0x814a8495UL, + 0x3c8dc775UL, 0x3578a819UL, 0x3ff212beUL, 0x2cfcaac9UL, 0x3c93592dUL, + 0x917ddc96UL, 0x3ff21f49UL, 0x9494a5eeUL, 0x3c82a97eUL, 0xa27912d1UL, + 0x3ff22bddUL, 0x5577d69fUL, 0x3c8d34fbUL, 0x6e756238UL, 0x3ff2387aUL, + 0xb6c70573UL, 0x3c99b07eUL, 0xfb82140aUL, 0x3ff2451fUL, 0x911ca996UL, + 0x3c8acfccUL, 0x4fb2a63fUL, 0x3ff251ceUL, 0xbef4f4a4UL, 0x3c8ac155UL, + 0x711ece75UL, 0x3ff25e85UL, 0x4ac31b2cUL, 0x3c93e1a2UL, 0x65e27cddUL, + 0x3ff26b45UL, 0x9940e9d9UL, 0x3c82bd33UL, 0x341ddf29UL, 0x3ff2780eUL, + 0x05f9e76cUL, 0x3c9e067cUL, 0xe1f56381UL, 0x3ff284dfUL, 0x8c3f0d7eUL, + 0xbc9a4c3aUL, 0x7591bb70UL, 0x3ff291baUL, 0x28401cbdUL, 0xbc82cc72UL, + 0xf51fdee1UL, 0x3ff29e9dUL, 0xafad1255UL, 0x3c8612e8UL, 0x66d10f13UL, + 0x3ff2ab8aUL, 0x191690a7UL, 0xbc995743UL, 0xd0dad990UL, 0x3ff2b87fUL, + 0xd6381aa4UL, 0xbc410adcUL, 0x39771b2fUL, 0x3ff2c57eUL, 0xa6eb5124UL, + 0xbc950145UL, 0xa6e4030bUL, 0x3ff2d285UL, 0x54db41d5UL, 0x3c900247UL, + 0x1f641589UL, 0x3ff2df96UL, 0xfbbce198UL, 0x3c9d16cfUL, 0xa93e2f56UL, + 0x3ff2ecafUL, 0x45d52383UL, 0x3c71ca0fUL, 0x4abd886bUL, 0x3ff2f9d2UL, + 0x532bda93UL, 0xbc653c55UL, 0x0a31b715UL, 0x3ff306feUL, 0xd23182e4UL, + 0x3c86f46aUL, 0xedeeb2fdUL, 0x3ff31432UL, 0xf3f3fcd1UL, 0x3c8959a3UL, + 0xfc4cd831UL, 0x3ff32170UL, 0x8e18047cUL, 0x3c8a9ce7UL, 0x3ba8ea32UL, + 0x3ff32eb8UL, 0x3cb4f318UL, 0xbc9c45e8UL, 0xb26416ffUL, 0x3ff33c08UL, + 0x843659a6UL, 0x3c932721UL, 0x66e3fa2dUL, 0x3ff34962UL, 0x930881a4UL, + 0xbc835a75UL, 0x5f929ff1UL, 0x3ff356c5UL, 0x5c4e4628UL, 0xbc8b5ceeUL, + 0xa2de883bUL, 0x3ff36431UL, 0xa06cb85eUL, 0xbc8c3144UL, 0x373aa9cbUL, + 0x3ff371a7UL, 0xbf42eae2UL, 0xbc963aeaUL, 0x231e754aUL, 0x3ff37f26UL, + 0x9eceb23cUL, 0xbc99f5caUL, 0x6d05d866UL, 0x3ff38caeUL, 0x3c9904bdUL, + 0xbc9e958dUL, 0x1b7140efUL, 0x3ff39a40UL, 0xfc8e2934UL, 0xbc99a9a5UL, + 0x34e59ff7UL, 0x3ff3a7dbUL, 0xd661f5e3UL, 0xbc75e436UL, 0xbfec6cf4UL, + 0x3ff3b57fUL, 0xe26fff18UL, 0x3c954c66UL, 0xc313a8e5UL, 0x3ff3c32dUL, + 0x375d29c3UL, 0xbc9efff8UL, 0x44ede173UL, 0x3ff3d0e5UL, 0x8c284c71UL, + 0x3c7fe8d0UL, 0x4c123422UL, 0x3ff3dea6UL, 0x11f09ebcUL, 0x3c8ada09UL, + 0xdf1c5175UL, 0x3ff3ec70UL, 0x7b8c9bcaUL, 0xbc8af663UL, 0x04ac801cUL, + 0x3ff3fa45UL, 0xf956f9f3UL, 0xbc97d023UL, 0xc367a024UL, 0x3ff40822UL, + 0xb6f4d048UL, 0x3c8bddf8UL, 0x21f72e2aUL, 0x3ff4160aUL, 0x1c309278UL, + 0xbc5ef369UL, 0x2709468aUL, 0x3ff423fbUL, 0xc0b314ddUL, 0xbc98462dUL, + 0xd950a897UL, 0x3ff431f5UL, 0xe35f7999UL, 0xbc81c7ddUL, 0x3f84b9d4UL, + 0x3ff43ffaUL, 0x9704c003UL, 0x3c8880beUL, 0x6061892dUL, 0x3ff44e08UL, + 0x04ef80d0UL, 0x3c489b7aUL, 0x42a7d232UL, 0x3ff45c20UL, 0x82fb1f8eUL, + 0xbc686419UL, 0xed1d0057UL, 0x3ff46a41UL, 0xd1648a76UL, 0x3c9c944bUL, + 0x668b3237UL, 0x3ff4786dUL, 0xed445733UL, 0xbc9c20f0UL, 0xb5c13cd0UL, + 0x3ff486a2UL, 0xb69062f0UL, 0x3c73c1a3UL, 0xe192aed2UL, 0x3ff494e1UL, + 0x5e499ea0UL, 0xbc83b289UL, 0xf0d7d3deUL, 0x3ff4a32aUL, 0xf3d1be56UL, + 0x3c99cb62UL, 0xea6db7d7UL, 0x3ff4b17dUL, 0x7f2897f0UL, 0xbc8125b8UL, + 0xd5362a27UL, 0x3ff4bfdaUL, 0xafec42e2UL, 0x3c7d4397UL, 0xb817c114UL, + 0x3ff4ce41UL, 0x690abd5dUL, 0x3c905e29UL, 0x99fddd0dUL, 0x3ff4dcb2UL, + 0xbc6a7833UL, 0x3c98ecdbUL, 0x81d8abffUL, 0x3ff4eb2dUL, 0x2e5d7a52UL, + 0xbc95257dUL, 0x769d2ca7UL, 0x3ff4f9b2UL, 0xd25957e3UL, 0xbc94b309UL, + 0x7f4531eeUL, 0x3ff50841UL, 0x49b7465fUL, 0x3c7a249bUL, 0xa2cf6642UL, + 0x3ff516daUL, 0x69bd93efUL, 0xbc8f7685UL, 0xe83f4eefUL, 0x3ff5257dUL, + 0x43efef71UL, 0xbc7c998dUL, 0x569d4f82UL, 0x3ff5342bUL, 0x1db13cadUL, + 0xbc807abeUL, 0xf4f6ad27UL, 0x3ff542e2UL, 0x192d5f7eUL, 0x3c87926dUL, + 0xca5d920fUL, 0x3ff551a4UL, 0xefede59bUL, 0xbc8d689cUL, 0xdde910d2UL, + 0x3ff56070UL, 0x168eebf0UL, 0xbc90fb6eUL, 0x36b527daUL, 0x3ff56f47UL, + 0x011d93adUL, 0x3c99bb2cUL, 0xdbe2c4cfUL, 0x3ff57e27UL, 0x8a57b9c4UL, + 0xbc90b98cUL, 0xd497c7fdUL, 0x3ff58d12UL, 0x5b9a1de8UL, 0x3c8295e1UL, + 0x27ff07ccUL, 0x3ff59c08UL, 0xe467e60fUL, 0xbc97e2ceUL, 0xdd485429UL, + 0x3ff5ab07UL, 0x054647adUL, 0x3c96324cUL, 0xfba87a03UL, 0x3ff5ba11UL, + 0x4c233e1aUL, 0xbc9b77a1UL, 0x8a5946b7UL, 0x3ff5c926UL, 0x816986a2UL, + 0x3c3c4b1bUL, 0x90998b93UL, 0x3ff5d845UL, 0xa8b45643UL, 0xbc9cd6a7UL, + 0x15ad2148UL, 0x3ff5e76fUL, 0x3080e65eUL, 0x3c9ba6f9UL, 0x20dceb71UL, + 0x3ff5f6a3UL, 0xe3cdcf92UL, 0xbc89eaddUL, 0xb976dc09UL, 0x3ff605e1UL, + 0x9b56de47UL, 0xbc93e242UL, 0xe6cdf6f4UL, 0x3ff6152aUL, 0x4ab84c27UL, + 0x3c9e4b3eUL, 0xb03a5585UL, 0x3ff6247eUL, 0x7e40b497UL, 0xbc9383c1UL, + 0x1d1929fdUL, 0x3ff633ddUL, 0xbeb964e5UL, 0x3c984710UL, 0x34ccc320UL, + 0x3ff64346UL, 0x759d8933UL, 0xbc8c483cUL, 0xfebc8fb7UL, 0x3ff652b9UL, + 0xc9a73e09UL, 0xbc9ae3d5UL, 0x82552225UL, 0x3ff66238UL, 0x87591c34UL, + 0xbc9bb609UL, 0xc70833f6UL, 0x3ff671c1UL, 0x586c6134UL, 0xbc8e8732UL, + 0xd44ca973UL, 0x3ff68155UL, 0x44f73e65UL, 0x3c6038aeUL, 0xb19e9538UL, + 0x3ff690f4UL, 0x9aeb445dUL, 0x3c8804bdUL, 0x667f3bcdUL, 0x3ff6a09eUL, + 0x13b26456UL, 0xbc9bdd34UL, 0xfa75173eUL, 0x3ff6b052UL, 0x2c9a9d0eUL, + 0x3c7a38f5UL, 0x750bdabfUL, 0x3ff6c012UL, 0x67ff0b0dUL, 0xbc728956UL, + 0xddd47645UL, 0x3ff6cfdcUL, 0xb6f17309UL, 0x3c9c7aa9UL, 0x3c651a2fUL, + 0x3ff6dfb2UL, 0x683c88abUL, 0xbc6bbe3aUL, 0x98593ae5UL, 0x3ff6ef92UL, + 0x9e1ac8b2UL, 0xbc90b974UL, 0xf9519484UL, 0x3ff6ff7dUL, 0x25860ef6UL, + 0xbc883c0fUL, 0x66f42e87UL, 0x3ff70f74UL, 0xd45aa65fUL, 0x3c59d644UL, + 0xe8ec5f74UL, 0x3ff71f75UL, 0x86887a99UL, 0xbc816e47UL, 0x86ead08aUL, + 0x3ff72f82UL, 0x2cd62c72UL, 0xbc920aa0UL, 0x48a58174UL, 0x3ff73f9aUL, + 0x6c65d53cUL, 0xbc90a8d9UL, 0x35d7cbfdUL, 0x3ff74fbdUL, 0x618a6e1cUL, + 0x3c9047fdUL, 0x564267c9UL, 0x3ff75febUL, 0x57316dd3UL, 0xbc902459UL, + 0xb1ab6e09UL, 0x3ff77024UL, 0x169147f8UL, 0x3c9b7877UL, 0x4fde5d3fUL, + 0x3ff78069UL, 0x0a02162dUL, 0x3c9866b8UL, 0x38ac1cf6UL, 0x3ff790b9UL, + 0x62aadd3eUL, 0x3c9349a8UL, 0x73eb0187UL, 0x3ff7a114UL, 0xee04992fUL, + 0xbc841577UL, 0x0976cfdbUL, 0x3ff7b17bUL, 0x8468dc88UL, 0xbc9bebb5UL, + 0x0130c132UL, 0x3ff7c1edUL, 0xd1164dd6UL, 0x3c9f124cUL, 0x62ff86f0UL, + 0x3ff7d26aUL, 0xfb72b8b4UL, 0x3c91bddbUL, 0x36cf4e62UL, 0x3ff7e2f3UL, + 0xba15797eUL, 0x3c705d02UL, 0x8491c491UL, 0x3ff7f387UL, 0xcf9311aeUL, + 0xbc807f11UL, 0x543e1a12UL, 0x3ff80427UL, 0x626d972bUL, 0xbc927c86UL, + 0xadd106d9UL, 0x3ff814d2UL, 0x0d151d4dUL, 0x3c946437UL, 0x994cce13UL, + 0x3ff82589UL, 0xd41532d8UL, 0xbc9d4c1dUL, 0x1eb941f7UL, 0x3ff8364cUL, + 0x31df2bd5UL, 0x3c999b9aUL, 0x4623c7adUL, 0x3ff8471aUL, 0xa341cdfbUL, + 0xbc88d684UL, 0x179f5b21UL, 0x3ff857f4UL, 0xf8b216d0UL, 0xbc5ba748UL, + 0x9b4492edUL, 0x3ff868d9UL, 0x9bd4f6baUL, 0xbc9fc6f8UL, 0xd931a436UL, + 0x3ff879caUL, 0xd2db47bdUL, 0x3c85d2d7UL, 0xd98a6699UL, 0x3ff88ac7UL, + 0xf37cb53aUL, 0x3c9994c2UL, 0xa478580fUL, 0x3ff89bd0UL, 0x4475202aUL, + 0x3c9d5395UL, 0x422aa0dbUL, 0x3ff8ace5UL, 0x56864b27UL, 0x3c96e9f1UL, + 0xbad61778UL, 0x3ff8be05UL, 0xfc43446eUL, 0x3c9ecb5eUL, 0x16b5448cUL, + 0x3ff8cf32UL, 0x32e9e3aaUL, 0xbc70d55eUL, 0x5e0866d9UL, 0x3ff8e06aUL, + 0x6fc9b2e6UL, 0xbc97114aUL, 0x99157736UL, 0x3ff8f1aeUL, 0xa2e3976cUL, + 0x3c85cc13UL, 0xd0282c8aUL, 0x3ff902feUL, 0x85fe3fd2UL, 0x3c9592caUL, + 0x0b91ffc6UL, 0x3ff9145bUL, 0x2e582524UL, 0xbc9dd679UL, 0x53aa2fe2UL, + 0x3ff925c3UL, 0xa639db7fUL, 0xbc83455fUL, 0xb0cdc5e5UL, 0x3ff93737UL, + 0x81b57ebcUL, 0xbc675fc7UL, 0x2b5f98e5UL, 0x3ff948b8UL, 0x797d2d99UL, + 0xbc8dc3d6UL, 0xcbc8520fUL, 0x3ff95a44UL, 0x96a5f039UL, 0xbc764b7cUL, + 0x9a7670b3UL, 0x3ff96bddUL, 0x7f19c896UL, 0xbc5ba596UL, 0x9fde4e50UL, + 0x3ff97d82UL, 0x7c1b85d1UL, 0xbc9d185bUL, 0xe47a22a2UL, 0x3ff98f33UL, + 0xa24c78ecUL, 0x3c7cabdaUL, 0x70ca07baUL, 0x3ff9a0f1UL, 0x91cee632UL, + 0xbc9173bdUL, 0x4d53fe0dUL, 0x3ff9b2bbUL, 0x4df6d518UL, 0xbc9dd84eUL, + 0x82a3f090UL, 0x3ff9c491UL, 0xb071f2beUL, 0x3c7c7c46UL, 0x194bb8d5UL, + 0x3ff9d674UL, 0xa3dd8233UL, 0xbc9516beUL, 0x19e32323UL, 0x3ff9e863UL, + 0x78e64c6eUL, 0x3c7824caUL, 0x8d07f29eUL, 0x3ff9fa5eUL, 0xaaf1faceUL, + 0xbc84a9ceUL, 0x7b5de565UL, 0x3ffa0c66UL, 0x5d1cd533UL, 0xbc935949UL, + 0xed8eb8bbUL, 0x3ffa1e7aUL, 0xee8be70eUL, 0x3c9c6618UL, 0xec4a2d33UL, + 0x3ffa309bUL, 0x7ddc36abUL, 0x3c96305cUL, 0x80460ad8UL, 0x3ffa42c9UL, + 0x589fb120UL, 0xbc9aa780UL, 0xb23e255dUL, 0x3ffa5503UL, 0xdb8d41e1UL, + 0xbc9d2f6eUL, 0x8af46052UL, 0x3ffa674aUL, 0x30670366UL, 0x3c650f56UL, + 0x1330b358UL, 0x3ffa799eUL, 0xcac563c7UL, 0x3c9bcb7eUL, 0x53c12e59UL, + 0x3ffa8bfeUL, 0xb2ba15a9UL, 0xbc94f867UL, 0x5579fdbfUL, 0x3ffa9e6bUL, + 0x0ef7fd31UL, 0x3c90fac9UL, 0x21356ebaUL, 0x3ffab0e5UL, 0xdae94545UL, + 0x3c889c31UL, 0xbfd3f37aUL, 0x3ffac36bUL, 0xcae76cd0UL, 0xbc8f9234UL, + 0x3a3c2774UL, 0x3ffad5ffUL, 0xb6b1b8e5UL, 0x3c97ef3bUL, 0x995ad3adUL, + 0x3ffae89fUL, 0x345dcc81UL, 0x3c97a1cdUL, 0xe622f2ffUL, 0x3ffafb4cUL, + 0x0f315ecdUL, 0xbc94b2fcUL, 0x298db666UL, 0x3ffb0e07UL, 0x4c80e425UL, + 0xbc9bdef5UL, 0x6c9a8952UL, 0x3ffb20ceUL, 0x4a0756ccUL, 0x3c94dd02UL, + 0xb84f15fbUL, 0x3ffb33a2UL, 0x3084d708UL, 0xbc62805eUL, 0x15b749b1UL, + 0x3ffb4684UL, 0xe9df7c90UL, 0xbc7f763dUL, 0x8de5593aUL, 0x3ffb5972UL, + 0xbbba6de3UL, 0xbc9c71dfUL, 0x29f1c52aUL, 0x3ffb6c6eUL, 0x52883f6eUL, + 0x3c92a8f3UL, 0xf2fb5e47UL, 0x3ffb7f76UL, 0x7e54ac3bUL, 0xbc75584fUL, + 0xf22749e4UL, 0x3ffb928cUL, 0x54cb65c6UL, 0xbc9b7216UL, 0x30a1064aUL, + 0x3ffba5b0UL, 0x0e54292eUL, 0xbc9efcd3UL, 0xb79a6f1fUL, 0x3ffbb8e0UL, + 0xc9696205UL, 0xbc3f52d1UL, 0x904bc1d2UL, 0x3ffbcc1eUL, 0x7a2d9e84UL, + 0x3c823dd0UL, 0xc3f3a207UL, 0x3ffbdf69UL, 0x60ea5b53UL, 0xbc3c2623UL, + 0x5bd71e09UL, 0x3ffbf2c2UL, 0x3f6b9c73UL, 0xbc9efdcaUL, 0x6141b33dUL, + 0x3ffc0628UL, 0xa1fbca34UL, 0xbc8d8a5aUL, 0xdd85529cUL, 0x3ffc199bUL, + 0x895048ddUL, 0x3c811065UL, 0xd9fa652cUL, 0x3ffc2d1cUL, 0x17c8a5d7UL, + 0xbc96e516UL, 0x5fffd07aUL, 0x3ffc40abUL, 0xe083c60aUL, 0x3c9b4537UL, + 0x78fafb22UL, 0x3ffc5447UL, 0x2493b5afUL, 0x3c912f07UL, 0x2e57d14bUL, + 0x3ffc67f1UL, 0xff483cadUL, 0x3c92884dUL, 0x8988c933UL, 0x3ffc7ba8UL, + 0xbe255559UL, 0xbc8e76bbUL, 0x9406e7b5UL, 0x3ffc8f6dUL, 0x48805c44UL, + 0x3c71acbcUL, 0x5751c4dbUL, 0x3ffca340UL, 0xd10d08f5UL, 0xbc87f2beUL, + 0xdcef9069UL, 0x3ffcb720UL, 0xd1e949dbUL, 0x3c7503cbUL, 0x2e6d1675UL, + 0x3ffccb0fUL, 0x86009092UL, 0xbc7d220fUL, 0x555dc3faUL, 0x3ffcdf0bUL, + 0x53829d72UL, 0xbc8dd83bUL, 0x5b5bab74UL, 0x3ffcf315UL, 0xb86dff57UL, + 0xbc9a08e9UL, 0x4a07897cUL, 0x3ffd072dUL, 0x43797a9cUL, 0xbc9cbc37UL, + 0x2b08c968UL, 0x3ffd1b53UL, 0x219a36eeUL, 0x3c955636UL, 0x080d89f2UL, + 0x3ffd2f87UL, 0x719d8578UL, 0xbc9d487bUL, 0xeacaa1d6UL, 0x3ffd43c8UL, + 0xbf5a1614UL, 0x3c93db53UL, 0xdcfba487UL, 0x3ffd5818UL, 0xd75b3707UL, + 0x3c82ed02UL, 0xe862e6d3UL, 0x3ffd6c76UL, 0x4a8165a0UL, 0x3c5fe87aUL, + 0x16c98398UL, 0x3ffd80e3UL, 0x8beddfe8UL, 0xbc911ec1UL, 0x71ff6075UL, + 0x3ffd955dUL, 0xbb9af6beUL, 0x3c9a052dUL, 0x03db3285UL, 0x3ffda9e6UL, + 0x696db532UL, 0x3c9c2300UL, 0xd63a8315UL, 0x3ffdbe7cUL, 0x926b8be4UL, + 0xbc9b76f1UL, 0xf301b460UL, 0x3ffdd321UL, 0x78f018c3UL, 0x3c92da57UL, + 0x641c0658UL, 0x3ffde7d5UL, 0x8e79ba8fUL, 0xbc9ca552UL, 0x337b9b5fUL, + 0x3ffdfc97UL, 0x4f184b5cUL, 0xbc91a5cdUL, 0x6b197d17UL, 0x3ffe1167UL, + 0xbd5c7f44UL, 0xbc72b529UL, 0x14f5a129UL, 0x3ffe2646UL, 0x817a1496UL, + 0xbc97b627UL, 0x3b16ee12UL, 0x3ffe3b33UL, 0x31fdc68bUL, 0xbc99f4a4UL, + 0xe78b3ff6UL, 0x3ffe502eUL, 0x80a9cc8fUL, 0x3c839e89UL, 0x24676d76UL, + 0x3ffe6539UL, 0x7522b735UL, 0xbc863ff8UL, 0xfbc74c83UL, 0x3ffe7a51UL, + 0xca0c8de2UL, 0x3c92d522UL, 0x77cdb740UL, 0x3ffe8f79UL, 0x80b054b1UL, + 0xbc910894UL, 0xa2a490daUL, 0x3ffea4afUL, 0x179c2893UL, 0xbc9e9c23UL, + 0x867cca6eUL, 0x3ffeb9f4UL, 0x2293e4f2UL, 0x3c94832fUL, 0x2d8e67f1UL, + 0x3ffecf48UL, 0xb411ad8cUL, 0xbc9c93f3UL, 0xa2188510UL, 0x3ffee4aaUL, + 0xa487568dUL, 0x3c91c68dUL, 0xee615a27UL, 0x3ffefa1bUL, 0x86a4b6b0UL, + 0x3c9dc7f4UL, 0x1cb6412aUL, 0x3fff0f9cUL, 0x65181d45UL, 0xbc932200UL, + 0x376bba97UL, 0x3fff252bUL, 0xbf0d8e43UL, 0x3c93a1a5UL, 0x48dd7274UL, + 0x3fff3ac9UL, 0x3ed837deUL, 0xbc795a5aUL, 0x5b6e4540UL, 0x3fff5076UL, + 0x2dd8a18bUL, 0x3c99d3e1UL, 0x798844f8UL, 0x3fff6632UL, 0x3539343eUL, + 0x3c9fa37bUL, 0xad9cbe14UL, 0x3fff7bfdUL, 0xd006350aUL, 0xbc9dbb12UL, + 0x02243c89UL, 0x3fff91d8UL, 0xa779f689UL, 0xbc612ea8UL, 0x819e90d8UL, + 0x3fffa7c1UL, 0xf3a5931eUL, 0x3c874853UL, 0x3692d514UL, 0x3fffbdbaUL, + 0x15098eb6UL, 0xbc796773UL, 0x2b8f71f1UL, 0x3fffd3c2UL, 0x966579e7UL, + 0x3c62eb74UL, 0x6b2a23d9UL, 0x3fffe9d9UL, 0x7442fde3UL, 0x3c74a603UL, + 0xe78a6731UL, 0x3f55d87fUL, 0xd704a0c0UL, 0x3fac6b08UL, 0x6fba4e77UL, + 0x3f83b2abUL, 0xff82c58fUL, 0x3fcebfbdUL, 0xfefa39efUL, 0x3fe62e42UL, + 0x00000000UL, 0x00000000UL, 0xfefa39efUL, 0x3fe62e42UL, 0xfefa39efUL, + 0xbfe62e42UL, 0xf8000000UL, 0xffffffffUL, 0xf8000000UL, 0xffffffffUL, + 0x00000000UL, 0x80000000UL, 0x00000000UL, 0x00000000UL + +}; + +//registers, +// input: xmm0, xmm1 +// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 +// eax, edx, ecx, ebx + +// Code generated by Intel C compiler for LIBM library + +void MacroAssembler::fast_pow(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; + Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; + Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2, L_2TAG_PACKET_10_0_2, L_2TAG_PACKET_11_0_2; + Label L_2TAG_PACKET_12_0_2, L_2TAG_PACKET_13_0_2, L_2TAG_PACKET_14_0_2, L_2TAG_PACKET_15_0_2; + Label L_2TAG_PACKET_16_0_2, L_2TAG_PACKET_17_0_2, L_2TAG_PACKET_18_0_2, L_2TAG_PACKET_19_0_2; + Label L_2TAG_PACKET_20_0_2, L_2TAG_PACKET_21_0_2, L_2TAG_PACKET_22_0_2, L_2TAG_PACKET_23_0_2; + Label L_2TAG_PACKET_24_0_2, L_2TAG_PACKET_25_0_2, L_2TAG_PACKET_26_0_2, L_2TAG_PACKET_27_0_2; + Label L_2TAG_PACKET_28_0_2, L_2TAG_PACKET_29_0_2, L_2TAG_PACKET_30_0_2, L_2TAG_PACKET_31_0_2; + Label L_2TAG_PACKET_32_0_2, L_2TAG_PACKET_33_0_2, L_2TAG_PACKET_34_0_2, L_2TAG_PACKET_35_0_2; + Label L_2TAG_PACKET_36_0_2, L_2TAG_PACKET_37_0_2, L_2TAG_PACKET_38_0_2, L_2TAG_PACKET_39_0_2; + Label L_2TAG_PACKET_40_0_2, L_2TAG_PACKET_41_0_2, L_2TAG_PACKET_42_0_2, L_2TAG_PACKET_43_0_2; + Label L_2TAG_PACKET_44_0_2, L_2TAG_PACKET_45_0_2, L_2TAG_PACKET_46_0_2, L_2TAG_PACKET_47_0_2; + Label L_2TAG_PACKET_48_0_2, L_2TAG_PACKET_49_0_2, L_2TAG_PACKET_50_0_2, L_2TAG_PACKET_51_0_2; + Label L_2TAG_PACKET_52_0_2, L_2TAG_PACKET_53_0_2, L_2TAG_PACKET_54_0_2, L_2TAG_PACKET_55_0_2; + Label L_2TAG_PACKET_56_0_2, L_2TAG_PACKET_57_0_2, L_2TAG_PACKET_58_0_2, start; + + assert_different_registers(tmp, eax, ecx, edx); + + address static_const_table_pow = (address)_static_const_table_pow; + + bind(start); + subl(rsp, 120); + movl(Address(rsp, 64), tmp); + lea(tmp, ExternalAddress(static_const_table_pow)); + movsd(xmm0, Address(rsp, 128)); + movsd(xmm1, Address(rsp, 136)); + xorpd(xmm2, xmm2); + movl(eax, 16368); + pinsrw(xmm2, eax, 3); + movl(ecx, 1069088768); + movdl(xmm7, ecx); + movsd(Address(rsp, 16), xmm1); + xorpd(xmm1, xmm1); + movl(edx, 30704); + pinsrw(xmm1, edx, 3); + movsd(Address(rsp, 8), xmm0); + movdqu(xmm3, xmm0); + movl(edx, 8192); + movdl(xmm4, edx); + movdqu(xmm6, Address(tmp, 8240)); + pextrw(eax, xmm0, 3); + por(xmm0, xmm2); + psllq(xmm0, 5); + movsd(xmm2, Address(tmp, 8256)); + psrlq(xmm0, 34); + movl(edx, eax); + andl(edx, 32752); + subl(edx, 16368); + movl(ecx, edx); + sarl(edx, 31); + addl(ecx, edx); + xorl(ecx, edx); + rcpss(xmm0, xmm0); + psllq(xmm3, 12); + addl(ecx, 16); + bsrl(ecx, ecx); + psrlq(xmm3, 12); + movl(Address(rsp, 24), rsi); + subl(eax, 16); + cmpl(eax, 32736); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); + movl(rsi, 0); + + bind(L_2TAG_PACKET_1_0_2); + mulss(xmm0, xmm7); + movl(edx, -1); + subl(ecx, 4); + shll(edx); + movdl(xmm5, edx); + por(xmm3, xmm1); + subl(eax, 16351); + cmpl(eax, 1); + jcc(Assembler::belowEqual, L_2TAG_PACKET_2_0_2); + paddd(xmm0, xmm4); + psllq(xmm5, 32); + movdl(edx, xmm0); + psllq(xmm0, 29); + pand(xmm5, xmm3); + + bind(L_2TAG_PACKET_3_0_2); + pand(xmm0, xmm6); + subsd(xmm3, xmm5); + subl(eax, 1); + sarl(eax, 4); + cvtsi2sdl(xmm7, eax); + mulpd(xmm5, xmm0); + + bind(L_2TAG_PACKET_4_0_2); + mulsd(xmm3, xmm0); + movdqu(xmm1, Address(tmp, 8272)); + subsd(xmm5, xmm2); + movdqu(xmm4, Address(tmp, 8288)); + movl(ecx, eax); + sarl(eax, 31); + addl(ecx, eax); + xorl(eax, ecx); + addl(eax, 1); + bsrl(eax, eax); + unpcklpd(xmm5, xmm3); + movdqu(xmm6, Address(tmp, 8304)); + addsd(xmm3, xmm5); + andl(edx, 16760832); + shrl(edx, 10); + addpd(xmm5, Address(tmp, edx, Address::times_1, -3616)); + movdqu(xmm0, Address(tmp, 8320)); + pshufd(xmm2, xmm3, 68); + mulsd(xmm3, xmm3); + mulpd(xmm1, xmm2); + mulpd(xmm4, xmm2); + addsd(xmm5, xmm7); + mulsd(xmm2, xmm3); + addpd(xmm6, xmm1); + mulsd(xmm3, xmm3); + addpd(xmm0, xmm4); + movsd(xmm1, Address(rsp, 16)); + movzwl(ecx, Address(rsp, 22)); + pshufd(xmm7, xmm5, 238); + movsd(xmm4, Address(tmp, 8368)); + mulpd(xmm6, xmm2); + pshufd(xmm3, xmm3, 68); + mulpd(xmm0, xmm2); + shll(eax, 4); + subl(eax, 15872); + andl(ecx, 32752); + addl(eax, ecx); + mulpd(xmm3, xmm6); + cmpl(eax, 624); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_5_0_2); + xorpd(xmm6, xmm6); + movl(edx, 17080); + pinsrw(xmm6, edx, 3); + movdqu(xmm2, xmm1); + pand(xmm4, xmm1); + subsd(xmm1, xmm4); + mulsd(xmm4, xmm5); + addsd(xmm0, xmm7); + mulsd(xmm1, xmm5); + movdqu(xmm7, xmm6); + addsd(xmm6, xmm4); + addpd(xmm3, xmm0); + movdl(edx, xmm6); + subsd(xmm6, xmm7); + pshufd(xmm0, xmm3, 238); + subsd(xmm4, xmm6); + addsd(xmm0, xmm3); + movl(ecx, edx); + andl(edx, 255); + addl(edx, edx); + movdqu(xmm5, Address(tmp, edx, Address::times_8, 8384)); + addsd(xmm4, xmm1); + mulsd(xmm2, xmm0); + movdqu(xmm7, Address(tmp, 12480)); + movdqu(xmm3, Address(tmp, 12496)); + shll(ecx, 12); + xorl(ecx, rsi); + andl(ecx, -1048576); + movdl(xmm6, ecx); + addsd(xmm2, xmm4); + movsd(xmm1, Address(tmp, 12512)); + pshufd(xmm0, xmm2, 68); + pshufd(xmm4, xmm2, 68); + mulpd(xmm0, xmm0); + movl(rsi, Address(rsp, 24)); + mulpd(xmm7, xmm4); + pshufd(xmm6, xmm6, 17); + mulsd(xmm1, xmm2); + mulsd(xmm0, xmm0); + paddd(xmm5, xmm6); + addpd(xmm3, xmm7); + mulsd(xmm1, xmm5); + pshufd(xmm6, xmm5, 238); + mulpd(xmm0, xmm3); + addsd(xmm1, xmm6); + pshufd(xmm3, xmm0, 238); + mulsd(xmm0, xmm5); + mulsd(xmm3, xmm5); + addsd(xmm0, xmm1); + addsd(xmm0, xmm3); + addsd(xmm0, xmm5); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_7_0_2); + movsd(xmm0, Address(rsp, 128)); + movsd(xmm1, Address(rsp, 136)); + mulsd(xmm0, xmm1); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_0_0_2); + addl(eax, 16); + movl(edx, 32752); + andl(edx, eax); + cmpl(edx, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); + testl(eax, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_9_0_2); + + bind(L_2TAG_PACKET_10_0_2); + movl(ecx, Address(rsp, 16)); + xorl(edx, edx); + testl(ecx, ecx); + movl(ecx, 1); + cmovl(Assembler::notEqual, edx, ecx); + orl(edx, Address(rsp, 20)); + cmpl(edx, 1072693248); + jcc(Assembler::equal, L_2TAG_PACKET_7_0_2); + movsd(xmm0, Address(rsp, 8)); + movsd(xmm3, Address(rsp, 8)); + movdl(edx, xmm3); + psrlq(xmm3, 32); + movdl(ecx, xmm3); + orl(edx, ecx); + cmpl(edx, 0); + jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); + xorpd(xmm3, xmm3); + movl(eax, 18416); + pinsrw(xmm3, eax, 3); + mulsd(xmm0, xmm3); + xorpd(xmm2, xmm2); + movl(eax, 16368); + pinsrw(xmm2, eax, 3); + movdqu(xmm3, xmm0); + pextrw(eax, xmm0, 3); + por(xmm0, xmm2); + movl(ecx, 18416); + psllq(xmm0, 5); + movsd(xmm2, Address(tmp, 8256)); + psrlq(xmm0, 34); + rcpss(xmm0, xmm0); + psllq(xmm3, 12); + movdqu(xmm6, Address(tmp, 8240)); + psrlq(xmm3, 12); + mulss(xmm0, xmm7); + movl(edx, -1024); + movdl(xmm5, edx); + por(xmm3, xmm1); + paddd(xmm0, xmm4); + psllq(xmm5, 32); + movdl(edx, xmm0); + psllq(xmm0, 29); + pand(xmm5, xmm3); + movl(rsi, 0); + pand(xmm0, xmm6); + subsd(xmm3, xmm5); + andl(eax, 32752); + subl(eax, 18416); + sarl(eax, 4); + cvtsi2sdl(xmm7, eax); + mulpd(xmm5, xmm0); + jmp(L_2TAG_PACKET_4_0_2); + + bind(L_2TAG_PACKET_12_0_2); + movl(ecx, Address(rsp, 16)); + xorl(edx, edx); + testl(ecx, ecx); + movl(ecx, 1); + cmovl(Assembler::notEqual, edx, ecx); + orl(edx, Address(rsp, 20)); + cmpl(edx, 1072693248); + jcc(Assembler::equal, L_2TAG_PACKET_7_0_2); + movsd(xmm0, Address(rsp, 8)); + movsd(xmm3, Address(rsp, 8)); + movdl(edx, xmm3); + psrlq(xmm3, 32); + movdl(ecx, xmm3); + orl(edx, ecx); + cmpl(edx, 0); + jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); + xorpd(xmm3, xmm3); + movl(eax, 18416); + pinsrw(xmm3, eax, 3); + mulsd(xmm0, xmm3); + xorpd(xmm2, xmm2); + movl(eax, 16368); + pinsrw(xmm2, eax, 3); + movdqu(xmm3, xmm0); + pextrw(eax, xmm0, 3); + por(xmm0, xmm2); + movl(ecx, 18416); + psllq(xmm0, 5); + movsd(xmm2, Address(tmp, 8256)); + psrlq(xmm0, 34); + rcpss(xmm0, xmm0); + psllq(xmm3, 12); + movdqu(xmm6, Address(tmp, 8240)); + psrlq(xmm3, 12); + mulss(xmm0, xmm7); + movl(edx, -1024); + movdl(xmm5, edx); + por(xmm3, xmm1); + paddd(xmm0, xmm4); + psllq(xmm5, 32); + movdl(edx, xmm0); + psllq(xmm0, 29); + pand(xmm5, xmm3); + movl(rsi, INT_MIN); + pand(xmm0, xmm6); + subsd(xmm3, xmm5); + andl(eax, 32752); + subl(eax, 18416); + sarl(eax, 4); + cvtsi2sdl(xmm7, eax); + mulpd(xmm5, xmm0); + jmp(L_2TAG_PACKET_4_0_2); + + bind(L_2TAG_PACKET_5_0_2); + cmpl(eax, 0); + jcc(Assembler::less, L_2TAG_PACKET_13_0_2); + cmpl(eax, 752); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_14_0_2); + + bind(L_2TAG_PACKET_15_0_2); + addsd(xmm0, xmm7); + movsd(xmm2, Address(tmp, 12544)); + addpd(xmm3, xmm0); + xorpd(xmm6, xmm6); + movl(eax, 17080); + pinsrw(xmm6, eax, 3); + pshufd(xmm0, xmm3, 238); + addsd(xmm0, xmm3); + movdqu(xmm3, xmm5); + addsd(xmm5, xmm0); + movdqu(xmm4, xmm2); + subsd(xmm3, xmm5); + movdqu(xmm7, xmm5); + pand(xmm5, xmm2); + movdqu(xmm2, xmm1); + pand(xmm4, xmm1); + subsd(xmm7, xmm5); + addsd(xmm0, xmm3); + subsd(xmm1, xmm4); + mulsd(xmm4, xmm5); + addsd(xmm0, xmm7); + mulsd(xmm2, xmm0); + movdqu(xmm7, xmm6); + mulsd(xmm1, xmm5); + addsd(xmm6, xmm4); + movdl(eax, xmm6); + subsd(xmm6, xmm7); + addsd(xmm2, xmm1); + movdqu(xmm7, Address(tmp, 12480)); + movdqu(xmm3, Address(tmp, 12496)); + subsd(xmm4, xmm6); + pextrw(edx, xmm6, 3); + movl(ecx, eax); + andl(eax, 255); + addl(eax, eax); + movdqu(xmm5, Address(tmp, eax, Address::times_8, 8384)); + addsd(xmm2, xmm4); + sarl(ecx, 8); + movl(eax, ecx); + sarl(ecx, 1); + subl(eax, ecx); + shll(ecx, 20); + xorl(ecx, rsi); + movdl(xmm6, ecx); + movsd(xmm1, Address(tmp, 12512)); + andl(edx, 32767); + cmpl(edx, 16529); + jcc(Assembler::above, L_2TAG_PACKET_14_0_2); + pshufd(xmm0, xmm2, 68); + pshufd(xmm4, xmm2, 68); + mulpd(xmm0, xmm0); + mulpd(xmm7, xmm4); + pshufd(xmm6, xmm6, 17); + mulsd(xmm1, xmm2); + mulsd(xmm0, xmm0); + paddd(xmm5, xmm6); + addpd(xmm3, xmm7); + mulsd(xmm1, xmm5); + pshufd(xmm6, xmm5, 238); + mulpd(xmm0, xmm3); + addsd(xmm1, xmm6); + pshufd(xmm3, xmm0, 238); + mulsd(xmm0, xmm5); + mulsd(xmm3, xmm5); + shll(eax, 4); + xorpd(xmm4, xmm4); + addl(eax, 16368); + pinsrw(xmm4, eax, 3); + addsd(xmm0, xmm1); + movl(rsi, Address(rsp, 24)); + addsd(xmm0, xmm3); + movdqu(xmm1, xmm0); + addsd(xmm0, xmm5); + mulsd(xmm0, xmm4); + pextrw(eax, xmm0, 3); + andl(eax, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_16_0_2); + cmpl(eax, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_17_0_2); + + bind(L_2TAG_PACKET_18_0_2); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_8_0_2); + movsd(xmm1, Address(rsp, 16)); + movsd(xmm0, Address(rsp, 8)); + movdqu(xmm2, xmm0); + movdl(eax, xmm2); + psrlq(xmm2, 20); + movdl(edx, xmm2); + orl(eax, edx); + jcc(Assembler::equal, L_2TAG_PACKET_19_0_2); + addsd(xmm0, xmm0); + movdl(eax, xmm1); + psrlq(xmm1, 32); + movdl(edx, xmm1); + movl(ecx, edx); + addl(edx, edx); + orl(eax, edx); + jcc(Assembler::equal, L_2TAG_PACKET_20_0_2); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_20_0_2); + xorpd(xmm0, xmm0); + movl(eax, 16368); + pinsrw(xmm0, eax, 3); + movl(edx, 29); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_22_0_2); + movsd(xmm0, Address(rsp, 16)); + addpd(xmm0, xmm0); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_19_0_2); + movdl(eax, xmm1); + movdqu(xmm2, xmm1); + psrlq(xmm1, 32); + movdl(edx, xmm1); + movl(ecx, edx); + addl(edx, edx); + orl(eax, edx); + jcc(Assembler::equal, L_2TAG_PACKET_23_0_2); + pextrw(eax, xmm2, 3); + andl(eax, 32752); + cmpl(eax, 32752); + jcc(Assembler::notEqual, L_2TAG_PACKET_24_0_2); + movdl(eax, xmm2); + psrlq(xmm2, 20); + movdl(edx, xmm2); + orl(eax, edx); + jcc(Assembler::notEqual, L_2TAG_PACKET_22_0_2); + + bind(L_2TAG_PACKET_24_0_2); + pextrw(eax, xmm0, 3); + testl(eax, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_25_0_2); + testl(ecx, INT_MIN); + jcc(Assembler::notEqual, L_2TAG_PACKET_26_0_2); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_27_0_2); + movsd(xmm1, Address(rsp, 16)); + movdl(eax, xmm1); + testl(eax, 1); + jcc(Assembler::notEqual, L_2TAG_PACKET_28_0_2); + testl(eax, 2); + jcc(Assembler::notEqual, L_2TAG_PACKET_29_0_2); + jmp(L_2TAG_PACKET_28_0_2); + + bind(L_2TAG_PACKET_25_0_2); + shrl(ecx, 20); + andl(ecx, 2047); + cmpl(ecx, 1075); + jcc(Assembler::above, L_2TAG_PACKET_28_0_2); + jcc(Assembler::equal, L_2TAG_PACKET_30_0_2); + cmpl(ecx, 1074); + jcc(Assembler::above, L_2TAG_PACKET_27_0_2); + cmpl(ecx, 1023); + jcc(Assembler::below, L_2TAG_PACKET_28_0_2); + movsd(xmm1, Address(rsp, 16)); + movl(eax, 17208); + xorpd(xmm3, xmm3); + pinsrw(xmm3, eax, 3); + movdqu(xmm4, xmm3); + addsd(xmm3, xmm1); + subsd(xmm4, xmm3); + addsd(xmm1, xmm4); + pextrw(eax, xmm1, 3); + andl(eax, 32752); + jcc(Assembler::notEqual, L_2TAG_PACKET_28_0_2); + movdl(eax, xmm3); + andl(eax, 1); + jcc(Assembler::equal, L_2TAG_PACKET_28_0_2); + + bind(L_2TAG_PACKET_29_0_2); + movsd(xmm1, Address(rsp, 16)); + pextrw(eax, xmm1, 3); + andl(eax, 32768); + jcc(Assembler::equal, L_2TAG_PACKET_18_0_2); + xorpd(xmm0, xmm0); + movl(eax, 32768); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_28_0_2); + movsd(xmm1, Address(rsp, 16)); + pextrw(eax, xmm1, 3); + andl(eax, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_26_0_2); + + bind(L_2TAG_PACKET_31_0_2); + xorpd(xmm0, xmm0); + movl(eax, 32752); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_30_0_2); + movsd(xmm1, Address(rsp, 16)); + movdl(eax, xmm1); + andl(eax, 1); + jcc(Assembler::equal, L_2TAG_PACKET_28_0_2); + jmp(L_2TAG_PACKET_29_0_2); + + bind(L_2TAG_PACKET_32_0_2); + movdl(eax, xmm1); + psrlq(xmm1, 20); + movdl(edx, xmm1); + orl(eax, edx); + jcc(Assembler::equal, L_2TAG_PACKET_33_0_2); + movsd(xmm0, Address(rsp, 16)); + addsd(xmm0, xmm0); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_33_0_2); + movsd(xmm0, Address(rsp, 8)); + pextrw(eax, xmm0, 3); + cmpl(eax, 49136); + jcc(Assembler::notEqual, L_2TAG_PACKET_34_0_2); + movdl(ecx, xmm0); + psrlq(xmm0, 20); + movdl(edx, xmm0); + orl(ecx, edx); + jcc(Assembler::notEqual, L_2TAG_PACKET_34_0_2); + xorpd(xmm0, xmm0); + movl(eax, 32760); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_34_0_2); + movsd(xmm1, Address(rsp, 16)); + andl(eax, 32752); + subl(eax, 16368); + pextrw(edx, xmm1, 3); + xorpd(xmm0, xmm0); + xorl(eax, edx); + andl(eax, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_18_0_2); + movl(ecx, 32752); + pinsrw(xmm0, ecx, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_35_0_2); + movdl(eax, xmm1); + cmpl(edx, 17184); + jcc(Assembler::above, L_2TAG_PACKET_36_0_2); + testl(eax, 1); + jcc(Assembler::notEqual, L_2TAG_PACKET_37_0_2); + testl(eax, 2); + jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); + jmp(L_2TAG_PACKET_39_0_2); + + bind(L_2TAG_PACKET_36_0_2); + testl(eax, 1); + jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); + jmp(L_2TAG_PACKET_39_0_2); + + bind(L_2TAG_PACKET_9_0_2); + movsd(xmm2, Address(rsp, 8)); + movdl(eax, xmm2); + psrlq(xmm2, 31); + movdl(ecx, xmm2); + orl(eax, ecx); + jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); + movsd(xmm1, Address(rsp, 16)); + pextrw(edx, xmm1, 3); + movdl(eax, xmm1); + movdqu(xmm2, xmm1); + psrlq(xmm2, 32); + movdl(ecx, xmm2); + addl(ecx, ecx); + orl(ecx, eax); + jcc(Assembler::equal, L_2TAG_PACKET_40_0_2); + andl(edx, 32752); + cmpl(edx, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_32_0_2); + cmpl(edx, 17200); + jcc(Assembler::above, L_2TAG_PACKET_38_0_2); + cmpl(edx, 17184); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_35_0_2); + cmpl(edx, 16368); + jcc(Assembler::below, L_2TAG_PACKET_37_0_2); + movl(eax, 17208); + xorpd(xmm2, xmm2); + pinsrw(xmm2, eax, 3); + movdqu(xmm4, xmm2); + addsd(xmm2, xmm1); + subsd(xmm4, xmm2); + addsd(xmm1, xmm4); + pextrw(eax, xmm1, 3); + andl(eax, 32767); + jcc(Assembler::notEqual, L_2TAG_PACKET_37_0_2); + movdl(eax, xmm2); + andl(eax, 1); + jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); + + bind(L_2TAG_PACKET_39_0_2); + xorpd(xmm1, xmm1); + movl(edx, 30704); + pinsrw(xmm1, edx, 3); + movsd(xmm2, Address(tmp, 8256)); + movsd(xmm4, Address(rsp, 8)); + pextrw(eax, xmm4, 3); + movl(edx, 8192); + movdl(xmm4, edx); + andl(eax, 32767); + subl(eax, 16); + jcc(Assembler::less, L_2TAG_PACKET_12_0_2); + movl(edx, eax); + andl(edx, 32752); + subl(edx, 16368); + movl(ecx, edx); + sarl(edx, 31); + addl(ecx, edx); + xorl(ecx, edx); + addl(ecx, 16); + bsrl(ecx, ecx); + movl(rsi, INT_MIN); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_37_0_2); + xorpd(xmm1, xmm1); + movl(eax, 32752); + pinsrw(xmm1, eax, 3); + xorpd(xmm0, xmm0); + mulsd(xmm0, xmm1); + movl(edx, 28); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_38_0_2); + xorpd(xmm1, xmm1); + movl(edx, 30704); + pinsrw(xmm1, edx, 3); + movsd(xmm2, Address(tmp, 8256)); + movsd(xmm4, Address(rsp, 8)); + pextrw(eax, xmm4, 3); + movl(edx, 8192); + movdl(xmm4, edx); + andl(eax, 32767); + subl(eax, 16); + jcc(Assembler::less, L_2TAG_PACKET_10_0_2); + movl(edx, eax); + andl(edx, 32752); + subl(edx, 16368); + movl(ecx, edx); + sarl(edx, 31); + addl(ecx, edx); + xorl(ecx, edx); + addl(ecx, 16); + bsrl(ecx, ecx); + movl(rsi, 0); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_23_0_2); + xorpd(xmm0, xmm0); + movl(eax, 16368); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_26_0_2); + xorpd(xmm0, xmm0); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_13_0_2); + addl(eax, 384); + cmpl(eax, 0); + jcc(Assembler::less, L_2TAG_PACKET_41_0_2); + mulsd(xmm5, xmm1); + addsd(xmm0, xmm7); + shrl(rsi, 31); + addpd(xmm3, xmm0); + pshufd(xmm0, xmm3, 238); + addsd(xmm3, xmm0); + movsd(xmm4, Address(tmp, rsi, Address::times_8, 12528)); + mulsd(xmm1, xmm3); + xorpd(xmm0, xmm0); + movl(eax, 16368); + shll(rsi, 15); + orl(eax, rsi); + pinsrw(xmm0, eax, 3); + addsd(xmm5, xmm1); + movl(rsi, Address(rsp, 24)); + mulsd(xmm5, xmm4); + addsd(xmm0, xmm5); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_41_0_2); + movl(rsi, Address(rsp, 24)); + xorpd(xmm0, xmm0); + movl(eax, 16368); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_40_0_2); + xorpd(xmm0, xmm0); + movl(eax, 16368); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_42_0_2); + xorpd(xmm0, xmm0); + movl(eax, 16368); + pinsrw(xmm0, eax, 3); + movl(edx, 26); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_11_0_2); + movsd(xmm1, Address(rsp, 16)); + movdqu(xmm2, xmm1); + pextrw(eax, xmm1, 3); + andl(eax, 32752); + cmpl(eax, 32752); + jcc(Assembler::notEqual, L_2TAG_PACKET_43_0_2); + movdl(eax, xmm2); + psrlq(xmm2, 20); + movdl(edx, xmm2); + orl(eax, edx); + jcc(Assembler::notEqual, L_2TAG_PACKET_22_0_2); + + bind(L_2TAG_PACKET_43_0_2); + movdl(eax, xmm1); + psrlq(xmm1, 32); + movdl(edx, xmm1); + movl(ecx, edx); + addl(edx, edx); + orl(eax, edx); + jcc(Assembler::equal, L_2TAG_PACKET_42_0_2); + shrl(edx, 21); + cmpl(edx, 1075); + jcc(Assembler::above, L_2TAG_PACKET_44_0_2); + jcc(Assembler::equal, L_2TAG_PACKET_45_0_2); + cmpl(edx, 1023); + jcc(Assembler::below, L_2TAG_PACKET_44_0_2); + movsd(xmm1, Address(rsp, 16)); + movl(eax, 17208); + xorpd(xmm3, xmm3); + pinsrw(xmm3, eax, 3); + movdqu(xmm4, xmm3); + addsd(xmm3, xmm1); + subsd(xmm4, xmm3); + addsd(xmm1, xmm4); + pextrw(eax, xmm1, 3); + andl(eax, 32752); + jcc(Assembler::notEqual, L_2TAG_PACKET_44_0_2); + movdl(eax, xmm3); + andl(eax, 1); + jcc(Assembler::equal, L_2TAG_PACKET_44_0_2); + + bind(L_2TAG_PACKET_46_0_2); + movsd(xmm0, Address(rsp, 8)); + testl(ecx, INT_MIN); + jcc(Assembler::notEqual, L_2TAG_PACKET_47_0_2); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_45_0_2); + movsd(xmm1, Address(rsp, 16)); + movdl(eax, xmm1); + testl(eax, 1); + jcc(Assembler::notEqual, L_2TAG_PACKET_46_0_2); + + bind(L_2TAG_PACKET_44_0_2); + testl(ecx, INT_MIN); + jcc(Assembler::equal, L_2TAG_PACKET_26_0_2); + xorpd(xmm0, xmm0); + + bind(L_2TAG_PACKET_47_0_2); + movl(eax, 16368); + xorpd(xmm1, xmm1); + pinsrw(xmm1, eax, 3); + divsd(xmm1, xmm0); + movdqu(xmm0, xmm1); + movl(edx, 27); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_14_0_2); + movsd(xmm2, Address(rsp, 8)); + movsd(xmm6, Address(rsp, 16)); + pextrw(eax, xmm2, 3); + pextrw(edx, xmm6, 3); + movl(ecx, 32752); + andl(ecx, edx); + cmpl(ecx, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_48_0_2); + andl(eax, 32752); + subl(eax, 16368); + xorl(edx, eax); + testl(edx, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_49_0_2); + + bind(L_2TAG_PACKET_50_0_2); + movl(eax, 32736); + pinsrw(xmm0, eax, 3); + shrl(rsi, 16); + orl(eax, rsi); + pinsrw(xmm1, eax, 3); + movl(rsi, Address(rsp, 24)); + mulsd(xmm0, xmm1); + + bind(L_2TAG_PACKET_17_0_2); + movl(edx, 24); + + bind(L_2TAG_PACKET_21_0_2); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_49_0_2); + movl(eax, 16); + pinsrw(xmm0, eax, 3); + mulsd(xmm0, xmm0); + testl(rsi, INT_MIN); + jcc(Assembler::equal, L_2TAG_PACKET_51_0_2); + movsd(xmm2, Address(tmp, 12560)); + xorpd(xmm0, xmm2); + + bind(L_2TAG_PACKET_51_0_2); + movl(rsi, Address(rsp, 24)); + movl(edx, 25); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_16_0_2); + pextrw(ecx, xmm5, 3); + pextrw(edx, xmm4, 3); + movl(eax, -1); + andl(ecx, 32752); + subl(ecx, 16368); + andl(edx, 32752); + addl(edx, ecx); + movl(ecx, -31); + sarl(edx, 4); + subl(ecx, edx); + jcc(Assembler::lessEqual, L_2TAG_PACKET_52_0_2); + cmpl(ecx, 20); + jcc(Assembler::above, L_2TAG_PACKET_53_0_2); + shll(eax); + + bind(L_2TAG_PACKET_52_0_2); + movdl(xmm0, eax); + psllq(xmm0, 32); + pand(xmm0, xmm5); + subsd(xmm5, xmm0); + addsd(xmm5, xmm1); + mulsd(xmm0, xmm4); + mulsd(xmm5, xmm4); + addsd(xmm0, xmm5); + + bind(L_2TAG_PACKET_53_0_2); + movl(edx, 25); + jmp(L_2TAG_PACKET_21_0_2); + + bind(L_2TAG_PACKET_2_0_2); + movzwl(ecx, Address(rsp, 22)); + movl(edx, INT_MIN); + movdl(xmm1, edx); + xorpd(xmm7, xmm7); + paddd(xmm0, xmm4); + psllq(xmm5, 32); + movdl(edx, xmm0); + psllq(xmm0, 29); + paddq(xmm1, xmm3); + pand(xmm5, xmm1); + andl(ecx, 32752); + cmpl(ecx, 16560); + jcc(Assembler::below, L_2TAG_PACKET_3_0_2); + pand(xmm0, xmm6); + subsd(xmm3, xmm5); + addl(eax, 16351); + shrl(eax, 4); + subl(eax, 1022); + cvtsi2sdl(xmm7, eax); + mulpd(xmm5, xmm0); + movsd(xmm4, Address(tmp, 0)); + mulsd(xmm3, xmm0); + movsd(xmm6, Address(tmp, 0)); + subsd(xmm5, xmm2); + movsd(xmm1, Address(tmp, 8)); + pshufd(xmm2, xmm3, 68); + unpcklpd(xmm5, xmm3); + addsd(xmm3, xmm5); + movsd(xmm0, Address(tmp, 8)); + andl(edx, 16760832); + shrl(edx, 10); + addpd(xmm7, Address(tmp, edx, Address::times_1, -3616)); + mulsd(xmm4, xmm5); + mulsd(xmm0, xmm5); + mulsd(xmm6, xmm2); + mulsd(xmm1, xmm2); + movdqu(xmm2, xmm5); + mulsd(xmm4, xmm5); + addsd(xmm5, xmm0); + movdqu(xmm0, xmm7); + addsd(xmm2, xmm3); + addsd(xmm7, xmm5); + mulsd(xmm6, xmm2); + subsd(xmm0, xmm7); + movdqu(xmm2, xmm7); + addsd(xmm7, xmm4); + addsd(xmm0, xmm5); + subsd(xmm2, xmm7); + addsd(xmm4, xmm2); + pshufd(xmm2, xmm5, 238); + movdqu(xmm5, xmm7); + addsd(xmm7, xmm2); + addsd(xmm4, xmm0); + movdqu(xmm0, Address(tmp, 8272)); + subsd(xmm5, xmm7); + addsd(xmm6, xmm4); + movdqu(xmm4, xmm7); + addsd(xmm5, xmm2); + addsd(xmm7, xmm1); + movdqu(xmm2, Address(tmp, 8336)); + subsd(xmm4, xmm7); + addsd(xmm6, xmm5); + addsd(xmm4, xmm1); + pshufd(xmm5, xmm7, 238); + movdqu(xmm1, xmm7); + addsd(xmm7, xmm5); + subsd(xmm1, xmm7); + addsd(xmm1, xmm5); + movdqu(xmm5, Address(tmp, 8352)); + pshufd(xmm3, xmm3, 68); + addsd(xmm6, xmm4); + addsd(xmm6, xmm1); + movdqu(xmm1, Address(tmp, 8304)); + mulpd(xmm0, xmm3); + mulpd(xmm2, xmm3); + pshufd(xmm4, xmm3, 68); + mulpd(xmm3, xmm3); + addpd(xmm0, xmm1); + addpd(xmm5, xmm2); + mulsd(xmm4, xmm3); + movsd(xmm2, Address(tmp, 16)); + mulpd(xmm3, xmm3); + movsd(xmm1, Address(rsp, 16)); + movzwl(ecx, Address(rsp, 22)); + mulpd(xmm0, xmm4); + pextrw(eax, xmm7, 3); + mulpd(xmm5, xmm4); + mulpd(xmm0, xmm3); + movsd(xmm4, Address(tmp, 8376)); + pand(xmm2, xmm7); + addsd(xmm5, xmm6); + subsd(xmm7, xmm2); + addpd(xmm5, xmm0); + andl(eax, 32752); + subl(eax, 16368); + andl(ecx, 32752); + cmpl(ecx, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_48_0_2); + addl(ecx, eax); + cmpl(ecx, 16576); + jcc(Assembler::aboveEqual, L_2TAG_PACKET_54_0_2); + pshufd(xmm0, xmm5, 238); + pand(xmm4, xmm1); + movdqu(xmm3, xmm1); + addsd(xmm5, xmm0); + subsd(xmm1, xmm4); + xorpd(xmm6, xmm6); + movl(edx, 17080); + pinsrw(xmm6, edx, 3); + addsd(xmm7, xmm5); + mulsd(xmm4, xmm2); + mulsd(xmm1, xmm2); + movdqu(xmm5, xmm6); + mulsd(xmm3, xmm7); + addsd(xmm6, xmm4); + addsd(xmm1, xmm3); + movdqu(xmm7, Address(tmp, 12480)); + movdl(edx, xmm6); + subsd(xmm6, xmm5); + movdqu(xmm3, Address(tmp, 12496)); + movsd(xmm2, Address(tmp, 12512)); + subsd(xmm4, xmm6); + movl(ecx, edx); + andl(edx, 255); + addl(edx, edx); + movdqu(xmm5, Address(tmp, edx, Address::times_8, 8384)); + addsd(xmm4, xmm1); + pextrw(edx, xmm6, 3); + shrl(ecx, 8); + movl(eax, ecx); + shrl(ecx, 1); + subl(eax, ecx); + shll(ecx, 20); + movdl(xmm6, ecx); + pshufd(xmm0, xmm4, 68); + pshufd(xmm1, xmm4, 68); + mulpd(xmm0, xmm0); + mulpd(xmm7, xmm1); + pshufd(xmm6, xmm6, 17); + mulsd(xmm2, xmm4); + andl(edx, 32767); + cmpl(edx, 16529); + jcc(Assembler::above, L_2TAG_PACKET_14_0_2); + mulsd(xmm0, xmm0); + paddd(xmm5, xmm6); + addpd(xmm3, xmm7); + mulsd(xmm2, xmm5); + pshufd(xmm6, xmm5, 238); + mulpd(xmm0, xmm3); + addsd(xmm2, xmm6); + pshufd(xmm3, xmm0, 238); + addl(eax, 1023); + shll(eax, 20); + orl(eax, rsi); + movdl(xmm4, eax); + mulsd(xmm0, xmm5); + mulsd(xmm3, xmm5); + addsd(xmm0, xmm2); + psllq(xmm4, 32); + addsd(xmm0, xmm3); + movdqu(xmm1, xmm0); + addsd(xmm0, xmm5); + movl(rsi, Address(rsp, 24)); + mulsd(xmm0, xmm4); + pextrw(eax, xmm0, 3); + andl(eax, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_16_0_2); + cmpl(eax, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_17_0_2); + + bind(L_2TAG_PACKET_55_0_2); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_6_0_2); + + bind(L_2TAG_PACKET_48_0_2); + movl(rsi, Address(rsp, 24)); + + bind(L_2TAG_PACKET_56_0_2); + movsd(xmm0, Address(rsp, 8)); + movsd(xmm1, Address(rsp, 16)); + addsd(xmm1, xmm1); + xorpd(xmm2, xmm2); + movl(eax, 49136); + pinsrw(xmm2, eax, 3); + addsd(xmm2, xmm0); + pextrw(eax, xmm2, 3); + cmpl(eax, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_57_0_2); + xorpd(xmm0, xmm0); + movl(eax, 32760); + pinsrw(xmm0, eax, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_57_0_2); + movdl(edx, xmm1); + movdqu(xmm3, xmm1); + psrlq(xmm3, 20); + movdl(ecx, xmm3); + orl(ecx, edx); + jcc(Assembler::equal, L_2TAG_PACKET_58_0_2); + addsd(xmm1, xmm1); + movdqu(xmm0, xmm1); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_58_0_2); + pextrw(eax, xmm0, 3); + andl(eax, 32752); + pextrw(edx, xmm1, 3); + xorpd(xmm0, xmm0); + subl(eax, 16368); + xorl(eax, edx); + testl(eax, 32768); + jcc(Assembler::notEqual, L_2TAG_PACKET_18_0_2); + movl(edx, 32752); + pinsrw(xmm0, edx, 3); + jmp(L_2TAG_PACKET_18_0_2); + + bind(L_2TAG_PACKET_54_0_2); + pextrw(eax, xmm1, 3); + pextrw(ecx, xmm2, 3); + xorl(eax, ecx); + testl(eax, 32768); + jcc(Assembler::equal, L_2TAG_PACKET_50_0_2); + jmp(L_2TAG_PACKET_49_0_2); + + bind(L_2TAG_PACKET_6_0_2); + movl(tmp, Address(rsp, 64)); + +} + +/******************************************************************************/ +// ALGORITHM DESCRIPTION - SIN() +// --------------------- +// +// 1. RANGE REDUCTION +// +// We perform an initial range reduction from X to r with +// +// X =~= N * pi/32 + r +// +// so that |r| <= pi/64 + epsilon. We restrict inputs to those +// where |N| <= 932560. Beyond this, the range reduction is +// insufficiently accurate. For extremely small inputs, +// denormalization can occur internally, impacting performance. +// This means that the main path is actually only taken for +// 2^-252 <= |X| < 90112. +// +// To avoid branches, we perform the range reduction to full +// accuracy each time. +// +// X - N * (P_1 + P_2 + P_3) +// +// where P_1 and P_2 are 32-bit numbers (so multiplication by N +// is exact) and P_3 is a 53-bit number. Together, these +// approximate pi well enough for all cases in the restricted +// range. +// +// The main reduction sequence is: +// +// y = 32/pi * x +// N = integer(y) +// (computed by adding and subtracting off SHIFTER) +// +// m_1 = N * P_1 +// m_2 = N * P_2 +// r_1 = x - m_1 +// r = r_1 - m_2 +// (this r can be used for most of the calculation) +// +// c_1 = r_1 - r +// m_3 = N * P_3 +// c_2 = c_1 - m_2 +// c = c_2 - m_3 +// +// 2. MAIN ALGORITHM +// +// The algorithm uses a table lookup based on B = M * pi / 32 +// where M = N mod 64. The stored values are: +// sigma closest power of 2 to cos(B) +// C_hl 53-bit cos(B) - sigma +// S_hi + S_lo 2 * 53-bit sin(B) +// +// The computation is organized as follows: +// +// sin(B + r + c) = [sin(B) + sigma * r] + +// r * (cos(B) - sigma) + +// sin(B) * [cos(r + c) - 1] + +// cos(B) * [sin(r + c) - r] +// +// which is approximately: +// +// [S_hi + sigma * r] + +// C_hl * r + +// S_lo + S_hi * [(cos(r) - 1) - r * c] + +// (C_hl + sigma) * [(sin(r) - r) + c] +// +// and this is what is actually computed. We separate this sum +// into four parts: +// +// hi + med + pols + corr +// +// where +// +// hi = S_hi + sigma r +// med = C_hl * r +// pols = S_hi * (cos(r) - 1) + (C_hl + sigma) * (sin(r) - r) +// corr = S_lo + c * ((C_hl + sigma) - S_hi * r) +// +// 3. POLYNOMIAL +// +// The polynomial S_hi * (cos(r) - 1) + (C_hl + sigma) * +// (sin(r) - r) can be rearranged freely, since it is quite +// small, so we exploit parallelism to the fullest. +// +// psc4 = SC_4 * r_1 +// msc4 = psc4 * r +// r2 = r * r +// msc2 = SC_2 * r2 +// r4 = r2 * r2 +// psc3 = SC_3 + msc4 +// psc1 = SC_1 + msc2 +// msc3 = r4 * psc3 +// sincospols = psc1 + msc3 +// pols = sincospols * +// +// +// 4. CORRECTION TERM +// +// This is where the "c" component of the range reduction is +// taken into account; recall that just "r" is used for most of +// the calculation. +// +// -c = m_3 - c_2 +// -d = S_hi * r - (C_hl + sigma) +// corr = -c * -d + S_lo +// +// 5. COMPENSATED SUMMATIONS +// +// The two successive compensated summations add up the high +// and medium parts, leaving just the low parts to add up at +// the end. +// +// rs = sigma * r +// res_int = S_hi + rs +// k_0 = S_hi - res_int +// k_2 = k_0 + rs +// med = C_hl * r +// res_hi = res_int + med +// k_1 = res_int - res_hi +// k_3 = k_1 + med +// +// 6. FINAL SUMMATION +// +// We now add up all the small parts: +// +// res_lo = pols(hi) + pols(lo) + corr + k_1 + k_3 +// +// Now the overall result is just: +// +// res_hi + res_lo +// +// 7. SMALL ARGUMENTS +// +// If |x| < SNN (SNN meaning the smallest normal number), we +// simply perform 0.1111111 cdots 1111 * x. For SNN <= |x|, we +// do 2^-55 * (2^55 * x - x). +// +// Special cases: +// sin(NaN) = quiet NaN, and raise invalid exception +// sin(INF) = NaN and raise invalid exception +// sin(+/-0) = +/-0 +// +/******************************************************************************/ + +ALIGNED_(8) juint _zero_none[] = +{ + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xbff00000UL +}; + +ALIGNED_(4) juint __4onpi_d[] = +{ + 0x6dc9c883UL, 0x3ff45f30UL +}; + +ALIGNED_(4) juint _TWO_32H[] = +{ + 0x00000000UL, 0x41f80000UL +}; + +ALIGNED_(4) juint _pi04_3d[] = +{ + 0x54442d00UL, 0x3fe921fbUL, 0x98cc5180UL, 0x3ce84698UL, 0xcbb5bf6cUL, + 0xb9dfc8f8UL +}; + +ALIGNED_(4) juint _pi04_5d[] = +{ + 0x54400000UL, 0x3fe921fbUL, 0x1a600000UL, 0x3dc0b461UL, 0x2e000000UL, + 0x3b93198aUL, 0x25200000UL, 0x396b839aUL, 0x533e63a0UL, 0x37027044UL +}; + +ALIGNED_(4) juint _SCALE[] = +{ + 0x00000000UL, 0x32600000UL +}; + +ALIGNED_(4) juint _zeros[] = +{ + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x80000000UL +}; + +ALIGNED_(4) juint _pi04_2d[] = +{ + 0x54400000UL, 0x3fe921fbUL, 0x1a626331UL, 0x3dc0b461UL +}; + +ALIGNED_(4) juint _TWO_12H[] = +{ + 0x00000000UL, 0x40b80000UL +}; + +ALIGNED_(2) jushort __4onpi_31l[] = +{ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x836e, 0xa2f9, + 0x40d8, 0x0000, 0x0000, 0x0000, 0x2a50, 0x9c88, 0x40b7, 0x0000, 0x0000, 0x0000, + 0xabe8, 0xfe13, 0x4099, 0x0000, 0x0000, 0x0000, 0x6ee0, 0xfa9a, 0x4079, 0x0000, + 0x0000, 0x0000, 0x9580, 0xdb62, 0x4058, 0x0000, 0x0000, 0x0000, 0x1c82, 0xc9e2, + 0x403d, 0x0000, 0x0000, 0x0000, 0xb1c0, 0xff28, 0x4019, 0x0000, 0x0000, 0x0000, + 0xef14, 0xaf7a, 0x3ffe, 0x0000, 0x0000, 0x0000, 0x48dc, 0xc36e, 0x3fdf, 0x0000, + 0x0000, 0x0000, 0x3740, 0xe909, 0x3fbe, 0x0000, 0x0000, 0x0000, 0x924a, 0xb801, + 0x3fa2, 0x0000, 0x0000, 0x0000, 0x3a32, 0xdd41, 0x3f83, 0x0000, 0x0000, 0x0000, + 0x8778, 0x873f, 0x3f62, 0x0000, 0x0000, 0x0000, 0x1298, 0xb1cb, 0x3f44, 0x0000, + 0x0000, 0x0000, 0xa208, 0x9cfb, 0x3f26, 0x0000, 0x0000, 0x0000, 0xbaec, 0xd7d4, + 0x3f06, 0x0000, 0x0000, 0x0000, 0xd338, 0x8909, 0x3ee7, 0x0000, 0x0000, 0x0000, + 0x68b8, 0xe04d, 0x3ec7, 0x0000, 0x0000, 0x0000, 0x4e64, 0xdf90, 0x3eaa, 0x0000, + 0x0000, 0x0000, 0xc1a8, 0xeb1c, 0x3e89, 0x0000, 0x0000, 0x0000, 0x2720, 0xce7d, + 0x3e6a, 0x0000, 0x0000, 0x0000, 0x77b8, 0x8bf1, 0x3e4b, 0x0000, 0x0000, 0x0000, + 0xec7e, 0xe4a0, 0x3e2e, 0x0000, 0x0000, 0x0000, 0xffbc, 0xf12f, 0x3e0f, 0x0000, + 0x0000, 0x0000, 0xfdc0, 0xb301, 0x3deb, 0x0000, 0x0000, 0x0000, 0xc5ac, 0x9788, + 0x3dd1, 0x0000, 0x0000, 0x0000, 0x47da, 0x829b, 0x3db2, 0x0000, 0x0000, 0x0000, + 0xd9e4, 0xa6cf, 0x3d93, 0x0000, 0x0000, 0x0000, 0x36e8, 0xf961, 0x3d73, 0x0000, + 0x0000, 0x0000, 0xf668, 0xf463, 0x3d54, 0x0000, 0x0000, 0x0000, 0x5168, 0xf2ff, + 0x3d35, 0x0000, 0x0000, 0x0000, 0x758e, 0xea4f, 0x3d17, 0x0000, 0x0000, 0x0000, + 0xf17a, 0xebe5, 0x3cf8, 0x0000, 0x0000, 0x0000, 0x9cfa, 0x9e83, 0x3cd9, 0x0000, + 0x0000, 0x0000, 0xa4ba, 0xe294, 0x3cba, 0x0000, 0x0000, 0x0000, 0xd7ec, 0x9afe, + 0x3c9a, 0x0000, 0x0000, 0x0000, 0xae80, 0x8fc6, 0x3c79, 0x0000, 0x0000, 0x0000, + 0x3304, 0x8560, 0x3c5c, 0x0000, 0x0000, 0x0000, 0x6d70, 0xdf8f, 0x3c3b, 0x0000, + 0x0000, 0x0000, 0x3ef0, 0xafc3, 0x3c1e, 0x0000, 0x0000, 0x0000, 0xd0d8, 0x826b, + 0x3bfe, 0x0000, 0x0000, 0x0000, 0x1c80, 0xed4f, 0x3bdd, 0x0000, 0x0000, 0x0000, + 0x730c, 0xb0af, 0x3bc1, 0x0000, 0x0000, 0x0000, 0x6660, 0xc219, 0x3ba2, 0x0000, + 0x0000, 0x0000, 0x940c, 0xabe2, 0x3b83, 0x0000, 0x0000, 0x0000, 0xdffc, 0x8408, + 0x3b64, 0x0000, 0x0000, 0x0000, 0x6b98, 0xc402, 0x3b45, 0x0000, 0x0000, 0x0000, + 0x1818, 0x9cc4, 0x3b26, 0x0000, 0x0000, 0x0000, 0x5390, 0xaab6, 0x3b05, 0x0000, + 0x0000, 0x0000, 0xb070, 0xd464, 0x3ae9, 0x0000, 0x0000, 0x0000, 0x231a, 0x9ef0, + 0x3aca, 0x0000, 0x0000, 0x0000, 0x0670, 0xd1f1, 0x3aaa, 0x0000, 0x0000, 0x0000, + 0x7738, 0xd9f3, 0x3a8a, 0x0000, 0x0000, 0x0000, 0xa834, 0x8092, 0x3a6c, 0x0000, + 0x0000, 0x0000, 0xb45c, 0xce23, 0x3a4d, 0x0000, 0x0000, 0x0000, 0x36e8, 0xb0e5, + 0x3a2d, 0x0000, 0x0000, 0x0000, 0xd156, 0xaf44, 0x3a10, 0x0000, 0x0000, 0x0000, + 0x9f52, 0x8c82, 0x39f1, 0x0000, 0x0000, 0x0000, 0x829c, 0xff83, 0x39d1, 0x0000, + 0x0000, 0x0000, 0x7d06, 0xefc6, 0x39b3, 0x0000, 0x0000, 0x0000, 0x93e0, 0xb0b7, + 0x3992, 0x0000, 0x0000, 0x0000, 0xedde, 0xc193, 0x3975, 0x0000, 0x0000, 0x0000, + 0xbbc0, 0xcf49, 0x3952, 0x0000, 0x0000, 0x0000, 0xbdf0, 0xd63c, 0x3937, 0x0000, + 0x0000, 0x0000, 0x1f34, 0x9f3a, 0x3918, 0x0000, 0x0000, 0x0000, 0x3f8e, 0xe579, + 0x38f9, 0x0000, 0x0000, 0x0000, 0x90c8, 0xc3f8, 0x38d9, 0x0000, 0x0000, 0x0000, + 0x48c0, 0xf8f8, 0x38b7, 0x0000, 0x0000, 0x0000, 0xed56, 0xafa6, 0x389c, 0x0000, + 0x0000, 0x0000, 0x8218, 0xb969, 0x387d, 0x0000, 0x0000, 0x0000, 0x1852, 0xec57, + 0x385e, 0x0000, 0x0000, 0x0000, 0x670c, 0xd674, 0x383e, 0x0000, 0x0000, 0x0000, + 0xad40, 0xc2c4, 0x3820, 0x0000, 0x0000, 0x0000, 0x2e80, 0xa696, 0x3801, 0x0000, + 0x0000, 0x0000, 0xd800, 0xc467, 0x37dc, 0x0000, 0x0000, 0x0000, 0x3c72, 0xc5ae, + 0x37c3, 0x0000, 0x0000, 0x0000, 0xb006, 0xac69, 0x37a4, 0x0000, 0x0000, 0x0000, + 0x34a0, 0x8cdf, 0x3782, 0x0000, 0x0000, 0x0000, 0x9ed2, 0xd25e, 0x3766, 0x0000, + 0x0000, 0x0000, 0x6fec, 0xaaaa, 0x3747, 0x0000, 0x0000, 0x0000, 0x6040, 0xfb5c, + 0x3726, 0x0000, 0x0000, 0x0000, 0x764c, 0xa3fc, 0x3708, 0x0000, 0x0000, 0x0000, + 0xb254, 0x954e, 0x36e9, 0x0000, 0x0000, 0x0000, 0x3e1c, 0xf5dc, 0x36ca, 0x0000, + 0x0000, 0x0000, 0x7b06, 0xc635, 0x36ac, 0x0000, 0x0000, 0x0000, 0xa8ba, 0xd738, + 0x368d, 0x0000, 0x0000, 0x0000, 0x06cc, 0xb24e, 0x366d, 0x0000, 0x0000, 0x0000, + 0x7108, 0xac76, 0x364f, 0x0000, 0x0000, 0x0000, 0x2324, 0xa7cb, 0x3630, 0x0000, + 0x0000, 0x0000, 0xac40, 0xef15, 0x360f, 0x0000, 0x0000, 0x0000, 0xae46, 0xd516, + 0x35f2, 0x0000, 0x0000, 0x0000, 0x615e, 0xe003, 0x35d3, 0x0000, 0x0000, 0x0000, + 0x0cf0, 0xefe7, 0x35b1, 0x0000, 0x0000, 0x0000, 0xfb50, 0xf98c, 0x3595, 0x0000, + 0x0000, 0x0000, 0x0abc, 0xf333, 0x3575, 0x0000, 0x0000, 0x0000, 0xdd60, 0xca3f, + 0x3555, 0x0000, 0x0000, 0x0000, 0x7eb6, 0xd87f, 0x3538, 0x0000, 0x0000, 0x0000, + 0x44f4, 0xb291, 0x3519, 0x0000, 0x0000, 0x0000, 0xff80, 0xc982, 0x34f6, 0x0000, + 0x0000, 0x0000, 0x9de0, 0xd9b8, 0x34db, 0x0000, 0x0000, 0x0000, 0xcd42, 0x9366, + 0x34bc, 0x0000, 0x0000, 0x0000, 0xbef0, 0xfaee, 0x349d, 0x0000, 0x0000, 0x0000, + 0xdac4, 0xb6f1, 0x347d, 0x0000, 0x0000, 0x0000, 0xf140, 0x94de, 0x345d, 0x0000, + 0x0000, 0x0000, 0xa218, 0x8b4b, 0x343e, 0x0000, 0x0000, 0x0000, 0x6380, 0xa135, + 0x341e, 0x0000, 0x0000, 0x0000, 0xb184, 0x8cb2, 0x3402, 0x0000, 0x0000, 0x0000, + 0x196e, 0xdc61, 0x33e3, 0x0000, 0x0000, 0x0000, 0x0c00, 0xde05, 0x33c4, 0x0000, + 0x0000, 0x0000, 0xef9a, 0xbd38, 0x33a5, 0x0000, 0x0000, 0x0000, 0xc1a0, 0xdf00, + 0x3385, 0x0000, 0x0000, 0x0000, 0x1090, 0x9973, 0x3365, 0x0000, 0x0000, 0x0000, + 0x4882, 0x8301, 0x3348, 0x0000, 0x0000, 0x0000, 0x7abe, 0xadc7, 0x3329, 0x0000, + 0x0000, 0x0000, 0x7cba, 0xec2b, 0x330a, 0x0000, 0x0000, 0x0000, 0xa520, 0x8f21, + 0x32e9, 0x0000, 0x0000, 0x0000, 0x710c, 0x8d36, 0x32cc, 0x0000, 0x0000, 0x0000, + 0x5212, 0xc6ed, 0x32ad, 0x0000, 0x0000, 0x0000, 0x7308, 0xfd76, 0x328d, 0x0000, + 0x0000, 0x0000, 0x5014, 0xd548, 0x326f, 0x0000, 0x0000, 0x0000, 0xd3f2, 0xb499, + 0x3250, 0x0000, 0x0000, 0x0000, 0x7f74, 0xa606, 0x3230, 0x0000, 0x0000, 0x0000, + 0xf0a8, 0xd720, 0x3212, 0x0000, 0x0000, 0x0000, 0x185c, 0xe20f, 0x31f2, 0x0000, + 0x0000, 0x0000, 0xa5a8, 0x8738, 0x31d4, 0x0000, 0x0000, 0x0000, 0xdd74, 0xcafb, + 0x31b4, 0x0000, 0x0000, 0x0000, 0x98b6, 0xbd8e, 0x3196, 0x0000, 0x0000, 0x0000, + 0xe9de, 0x977f, 0x3177, 0x0000, 0x0000, 0x0000, 0x67c0, 0x818d, 0x3158, 0x0000, + 0x0000, 0x0000, 0xe52a, 0x9322, 0x3139, 0x0000, 0x0000, 0x0000, 0xe568, 0x9b6c, + 0x3119, 0x0000, 0x0000, 0x0000, 0x2358, 0xaa0a, 0x30fa, 0x0000, 0x0000, 0x0000, + 0xe480, 0xe13b, 0x30d9, 0x0000, 0x0000, 0x0000, 0x3024, 0x90a1, 0x30bd, 0x0000, + 0x0000, 0x0000, 0x9620, 0xda30, 0x309d, 0x0000, 0x0000, 0x0000, 0x898a, 0xb388, + 0x307f, 0x0000, 0x0000, 0x0000, 0xb24c, 0xc891, 0x3060, 0x0000, 0x0000, 0x0000, + 0x8056, 0xf98b, 0x3041, 0x0000, 0x0000, 0x0000, 0x72a4, 0xa1ea, 0x3021, 0x0000, + 0x0000, 0x0000, 0x6af8, 0x9488, 0x3001, 0x0000, 0x0000, 0x0000, 0xe00c, 0xdfcb, + 0x2fe4, 0x0000, 0x0000, 0x0000, 0xeeec, 0xc941, 0x2fc4, 0x0000, 0x0000, 0x0000, + 0x53e0, 0xe70f, 0x2fa4, 0x0000, 0x0000, 0x0000, 0x8f60, 0x9c07, 0x2f85, 0x0000, + 0x0000, 0x0000, 0xb328, 0xc3e7, 0x2f68, 0x0000, 0x0000, 0x0000, 0x9404, 0xf8c7, + 0x2f48, 0x0000, 0x0000, 0x0000, 0x38e0, 0xc99f, 0x2f29, 0x0000, 0x0000, 0x0000, + 0x9778, 0xd984, 0x2f09, 0x0000, 0x0000, 0x0000, 0xe700, 0xd142, 0x2eea, 0x0000, + 0x0000, 0x0000, 0xd904, 0x9443, 0x2ecd, 0x0000, 0x0000, 0x0000, 0xd4ba, 0xae7e, + 0x2eae, 0x0000, 0x0000, 0x0000, 0x8e5e, 0x8524, 0x2e8f, 0x0000, 0x0000, 0x0000, + 0xb550, 0xc9ed, 0x2e6e, 0x0000, 0x0000, 0x0000, 0x53b8, 0x8648, 0x2e51, 0x0000, + 0x0000, 0x0000, 0xdae4, 0x87f9, 0x2e32, 0x0000, 0x0000, 0x0000, 0x2942, 0xd966, + 0x2e13, 0x0000, 0x0000, 0x0000, 0x4f28, 0xcf3c, 0x2df3, 0x0000, 0x0000, 0x0000, + 0xfa40, 0xc4ef, 0x2dd1, 0x0000, 0x0000, 0x0000, 0x4424, 0xbca7, 0x2db5, 0x0000, + 0x0000, 0x0000, 0x2e62, 0xcdc5, 0x2d97, 0x0000, 0x0000, 0x0000, 0xed88, 0x996b, + 0x2d78, 0x0000, 0x0000, 0x0000, 0x7c30, 0xd97d, 0x2d56, 0x0000, 0x0000, 0x0000, + 0xed26, 0xbf6e, 0x2d3a, 0x0000, 0x0000, 0x0000, 0x2918, 0x921b, 0x2d1a, 0x0000, + 0x0000, 0x0000, 0x4e24, 0xe84e, 0x2cfb, 0x0000, 0x0000, 0x0000, 0x6dc0, 0x92ec, + 0x2cdd, 0x0000, 0x0000, 0x0000, 0x4f2c, 0xacf8, 0x2cbd, 0x0000, 0x0000, 0x0000, + 0xc634, 0xf094, 0x2c9e, 0x0000, 0x0000, 0x0000, 0xdc70, 0xe5d3, 0x2c7e, 0x0000, + 0x0000, 0x0000, 0x2180, 0xa600, 0x2c5b, 0x0000, 0x0000, 0x0000, 0x8480, 0xd680, + 0x2c3c, 0x0000, 0x0000, 0x0000, 0x8b24, 0xd63b, 0x2c22, 0x0000, 0x0000, 0x0000, + 0x02e0, 0xaa47, 0x2c00, 0x0000, 0x0000, 0x0000, 0x9ad0, 0xee84, 0x2be3, 0x0000, + 0x0000, 0x0000, 0xf7dc, 0xf699, 0x2bc6, 0x0000, 0x0000, 0x0000, 0xddde, 0xe490, + 0x2ba7, 0x0000, 0x0000, 0x0000, 0x34a0, 0xb4fd, 0x2b85, 0x0000, 0x0000, 0x0000, + 0x91b4, 0x8ef6, 0x2b68, 0x0000, 0x0000, 0x0000, 0xa3e0, 0xa2a7, 0x2b47, 0x0000, + 0x0000, 0x0000, 0xcce4, 0x82b3, 0x2b2a, 0x0000, 0x0000, 0x0000, 0xe4be, 0x8207, + 0x2b0c, 0x0000, 0x0000, 0x0000, 0x1d92, 0xab43, 0x2aed, 0x0000, 0x0000, 0x0000, + 0xe818, 0xf9f6, 0x2acd, 0x0000, 0x0000, 0x0000, 0xff12, 0xba80, 0x2aaf, 0x0000, + 0x0000, 0x0000, 0x5254, 0x8529, 0x2a90, 0x0000, 0x0000, 0x0000, 0x1b88, 0xe032, + 0x2a71, 0x0000, 0x0000, 0x0000, 0x3248, 0xd86d, 0x2a50, 0x0000, 0x0000, 0x0000, + 0x3140, 0xc9d5, 0x2a2e, 0x0000, 0x0000, 0x0000, 0x14e6, 0xbd47, 0x2a14, 0x0000, + 0x0000, 0x0000, 0x5c10, 0xe544, 0x29f4, 0x0000, 0x0000, 0x0000, 0x9f50, 0x90b6, + 0x29d4, 0x0000, 0x0000, 0x0000, 0x9850, 0xab55, 0x29b6, 0x0000, 0x0000, 0x0000, + 0x2750, 0x9d07, 0x2998, 0x0000, 0x0000, 0x0000, 0x6700, 0x8bbb, 0x2973, 0x0000, + 0x0000, 0x0000, 0x5dba, 0xed31, 0x295a, 0x0000, 0x0000, 0x0000, 0x61dc, 0x85fe, + 0x293a, 0x0000, 0x0000, 0x0000, 0x9ba2, 0xd6b4, 0x291c, 0x0000, 0x0000, 0x0000, + 0x2d30, 0xe3a5, 0x28fb, 0x0000, 0x0000, 0x0000, 0x6630, 0xb566, 0x28dd, 0x0000, + 0x0000, 0x0000, 0x5ad4, 0xa829, 0x28bf, 0x0000, 0x0000, 0x0000, 0x89d8, 0xe290, + 0x28a0, 0x0000, 0x0000, 0x0000, 0x3916, 0xc428, 0x2881, 0x0000, 0x0000, 0x0000, + 0x0490, 0xbea4, 0x2860, 0x0000, 0x0000, 0x0000, 0xee06, 0x80ee, 0x2843, 0x0000, + 0x0000, 0x0000, 0xfc00, 0xf327, 0x2820, 0x0000, 0x0000, 0x0000, 0xea40, 0xa871, + 0x2800, 0x0000, 0x0000, 0x0000, 0x63d8, 0x9c26, 0x27e4, 0x0000, 0x0000, 0x0000, + 0x07ba, 0xc0c9, 0x27c7, 0x0000, 0x0000, 0x0000, 0x3fa2, 0x9797, 0x27a8, 0x0000, + 0x0000, 0x0000, 0x21c6, 0xfeca, 0x2789, 0x0000, 0x0000, 0x0000, 0xde40, 0x860d, + 0x2768, 0x0000, 0x0000, 0x0000, 0x9cc8, 0x98ce, 0x2749, 0x0000, 0x0000, 0x0000, + 0x3778, 0xa31c, 0x272a, 0x0000, 0x0000, 0x0000, 0xe778, 0xf6e2, 0x270b, 0x0000, + 0x0000, 0x0000, 0x59b8, 0xf841, 0x26ed, 0x0000, 0x0000, 0x0000, 0x02e0, 0xad04, + 0x26cd, 0x0000, 0x0000, 0x0000, 0x5a92, 0x9380, 0x26b0, 0x0000, 0x0000, 0x0000, + 0xc740, 0x8886, 0x268d, 0x0000, 0x0000, 0x0000, 0x0680, 0xfaf8, 0x266c, 0x0000, + 0x0000, 0x0000, 0xfb60, 0x897f, 0x2653, 0x0000, 0x0000, 0x0000, 0x8760, 0xf903, + 0x2634, 0x0000, 0x0000, 0x0000, 0xad2a, 0xc2c8, 0x2615, 0x0000, 0x0000, 0x0000, + 0x2d86, 0x8aef, 0x25f6, 0x0000, 0x0000, 0x0000, 0x1ef4, 0xe627, 0x25d6, 0x0000, + 0x0000, 0x0000, 0x09e4, 0x8020, 0x25b7, 0x0000, 0x0000, 0x0000, 0x7548, 0xd227, + 0x2598, 0x0000, 0x0000, 0x0000, 0x75dc, 0xfb5b, 0x2579, 0x0000, 0x0000, 0x0000, + 0xea84, 0xc8b6, 0x255a, 0x0000, 0x0000, 0x0000, 0xe4d0, 0x8145, 0x253b, 0x0000, + 0x0000, 0x0000, 0x3640, 0x9768, 0x251c, 0x0000, 0x0000, 0x0000, 0x246a, 0xccec, + 0x24fe, 0x0000, 0x0000, 0x0000, 0x51d0, 0xa075, 0x24dd, 0x0000, 0x0000, 0x0000, + 0x4638, 0xa385, 0x24bf, 0x0000, 0x0000, 0x0000, 0xd788, 0xd776, 0x24a1, 0x0000, + 0x0000, 0x0000, 0x1370, 0x8997, 0x2482, 0x0000, 0x0000, 0x0000, 0x1e88, 0x9b67, + 0x2462, 0x0000, 0x0000, 0x0000, 0x6c08, 0xd975, 0x2444, 0x0000, 0x0000, 0x0000, + 0xfdb0, 0xcfc0, 0x2422, 0x0000, 0x0000, 0x0000, 0x3100, 0xc026, 0x2406, 0x0000, + 0x0000, 0x0000, 0xc5b4, 0xae64, 0x23e6, 0x0000, 0x0000, 0x0000, 0x2280, 0xf687, + 0x23c3, 0x0000, 0x0000, 0x0000, 0x2de0, 0x9006, 0x23a9, 0x0000, 0x0000, 0x0000, + 0x24bc, 0xf631, 0x238a, 0x0000, 0x0000, 0x0000, 0xb8d4, 0xa975, 0x236b, 0x0000, + 0x0000, 0x0000, 0xd9a4, 0xb949, 0x234b, 0x0000, 0x0000, 0x0000, 0xb54e, 0xbd39, + 0x232d, 0x0000, 0x0000, 0x0000, 0x4aac, 0x9a52, 0x230e, 0x0000, 0x0000, 0x0000, + 0xbbbc, 0xd085, 0x22ef, 0x0000, 0x0000, 0x0000, 0xdf18, 0xc633, 0x22cf, 0x0000, + 0x0000, 0x0000, 0x16d0, 0xeca5, 0x22af, 0x0000, 0x0000, 0x0000, 0xf2a0, 0xdf6f, + 0x228e, 0x0000, 0x0000, 0x0000, 0x8c44, 0xe86b, 0x2272, 0x0000, 0x0000, 0x0000, + 0x35c0, 0xbbf4, 0x2253, 0x0000, 0x0000, 0x0000, 0x0c40, 0xdafb, 0x2230, 0x0000, + 0x0000, 0x0000, 0x92dc, 0x9935, 0x2216, 0x0000, 0x0000, 0x0000, 0x0ca0, 0xbda6, + 0x21f3, 0x0000, 0x0000, 0x0000, 0x5958, 0xa6fd, 0x21d6, 0x0000, 0x0000, 0x0000, + 0xa3dc, 0x9d7f, 0x21b9, 0x0000, 0x0000, 0x0000, 0x79dc, 0xfcb5, 0x2199, 0x0000, + 0x0000, 0x0000, 0xf264, 0xcebb, 0x217b, 0x0000, 0x0000, 0x0000, 0x0abe, 0x8308, + 0x215c, 0x0000, 0x0000, 0x0000, 0x30ae, 0xb463, 0x213d, 0x0000, 0x0000, 0x0000, + 0x6228, 0xb040, 0x211c, 0x0000, 0x0000, 0x0000, 0xc9b2, 0xf43b, 0x20ff, 0x0000, + 0x0000, 0x0000, 0x3d8e, 0xa4b3, 0x20e0, 0x0000, 0x0000, 0x0000, 0x84e6, 0x8dab, + 0x20c1, 0x0000, 0x0000, 0x0000, 0xa124, 0x9b74, 0x20a1, 0x0000, 0x0000, 0x0000, + 0xc276, 0xd497, 0x2083, 0x0000, 0x0000, 0x0000, 0x6354, 0xa466, 0x2063, 0x0000, + 0x0000, 0x0000, 0x8654, 0xaf0a, 0x2044, 0x0000, 0x0000, 0x0000, 0x1d20, 0xfa5c, + 0x2024, 0x0000, 0x0000, 0x0000, 0xbcd0, 0xf3f0, 0x2004, 0x0000, 0x0000, 0x0000, + 0xedf0, 0xf0b6, 0x1fe7, 0x0000, 0x0000, 0x0000, 0x45bc, 0x9182, 0x1fc9, 0x0000, + 0x0000, 0x0000, 0xe254, 0xdc85, 0x1faa, 0x0000, 0x0000, 0x0000, 0xb898, 0xe9b1, + 0x1f8a, 0x0000, 0x0000, 0x0000, 0x0ebe, 0xe6f0, 0x1f6c, 0x0000, 0x0000, 0x0000, + 0xa9b8, 0xf584, 0x1f4c, 0x0000, 0x0000, 0x0000, 0x12e8, 0xdf6b, 0x1f2e, 0x0000, + 0x0000, 0x0000, 0x9f9e, 0xcd55, 0x1f0f, 0x0000, 0x0000, 0x0000, 0x05a0, 0xec3a, + 0x1eef, 0x0000, 0x0000, 0x0000, 0xd8e0, 0x96f8, 0x1ed1, 0x0000, 0x0000, 0x0000, + 0x3bd4, 0xccc6, 0x1eb1, 0x0000, 0x0000, 0x0000, 0x4910, 0xb87b, 0x1e93, 0x0000, + 0x0000, 0x0000, 0xbefc, 0xd40b, 0x1e73, 0x0000, 0x0000, 0x0000, 0x317e, 0xa406, + 0x1e55, 0x0000, 0x0000, 0x0000, 0x6bb2, 0xc2b2, 0x1e36, 0x0000, 0x0000, 0x0000, + 0xb87e, 0xbb78, 0x1e17, 0x0000, 0x0000, 0x0000, 0xa03c, 0xdbbd, 0x1df7, 0x0000, + 0x0000, 0x0000, 0x5b6c, 0xe3c8, 0x1dd9, 0x0000, 0x0000, 0x0000, 0x8968, 0xca8e, + 0x1dba, 0x0000, 0x0000, 0x0000, 0xc024, 0xe6ab, 0x1d9a, 0x0000, 0x0000, 0x0000, + 0x4110, 0xd4eb, 0x1d7a, 0x0000, 0x0000, 0x0000, 0xa168, 0xbdb5, 0x1d5d, 0x0000, + 0x0000, 0x0000, 0x012e, 0xa5fa, 0x1d3e, 0x0000, 0x0000, 0x0000, 0x6838, 0x9c1f, + 0x1d1e, 0x0000, 0x0000, 0x0000, 0xa158, 0xaa76, 0x1d00, 0x0000, 0x0000, 0x0000, + 0x090a, 0xbd95, 0x1ce1, 0x0000, 0x0000, 0x0000, 0xf73e, 0x8b6d, 0x1cc2, 0x0000, + 0x0000, 0x0000, 0x5fda, 0xbcbf, 0x1ca3, 0x0000, 0x0000, 0x0000, 0xdbe8, 0xb89f, + 0x1c84, 0x0000, 0x0000, 0x0000, 0x6e4c, 0x96c7, 0x1c64, 0x0000, 0x0000, 0x0000, + 0x19c2, 0xf2a4, 0x1c46, 0x0000, 0x0000, 0x0000, 0xb800, 0xf855, 0x1c1e, 0x0000, + 0x0000, 0x0000, 0x87fc, 0x85ff, 0x1c08, 0x0000, 0x0000, 0x0000, 0x1418, 0x839f, + 0x1be9, 0x0000, 0x0000, 0x0000, 0x6186, 0xd9d8, 0x1bca, 0x0000, 0x0000, 0x0000, + 0xf500, 0xabaa, 0x1ba6, 0x0000, 0x0000, 0x0000, 0x7b36, 0xdafe, 0x1b8c, 0x0000, + 0x0000, 0x0000, 0xf394, 0xe6d8, 0x1b6c, 0x0000, 0x0000, 0x0000, 0x6efc, 0x9e55, + 0x1b4e, 0x0000, 0x0000, 0x0000, 0x5e10, 0xc523, 0x1b2e, 0x0000, 0x0000, 0x0000, + 0x8210, 0xb6f9, 0x1b0d, 0x0000, 0x0000, 0x0000, 0x9ab0, 0x96e3, 0x1af1, 0x0000, + 0x0000, 0x0000, 0x3864, 0x92e7, 0x1ad1, 0x0000, 0x0000, 0x0000, 0x9878, 0xdc65, + 0x1ab1, 0x0000, 0x0000, 0x0000, 0xfa20, 0xd6cb, 0x1a94, 0x0000, 0x0000, 0x0000, + 0x6c00, 0xa4e4, 0x1a70, 0x0000, 0x0000, 0x0000, 0xab40, 0xb41b, 0x1a53, 0x0000, + 0x0000, 0x0000, 0x43a4, 0x8ede, 0x1a37, 0x0000, 0x0000, 0x0000, 0x22e0, 0x9314, + 0x1a15, 0x0000, 0x0000, 0x0000, 0x6170, 0xb949, 0x19f8, 0x0000, 0x0000, 0x0000, + 0x6b00, 0xe056, 0x19d8, 0x0000, 0x0000, 0x0000, 0x9ba8, 0xa94c, 0x19b9, 0x0000, + 0x0000, 0x0000, 0xfaa0, 0xaa16, 0x199b, 0x0000, 0x0000, 0x0000, 0x899a, 0xf627, + 0x197d, 0x0000, 0x0000, 0x0000, 0x9f20, 0xfb70, 0x195d, 0x0000, 0x0000, 0x0000, + 0xa4b8, 0xc176, 0x193e, 0x0000, 0x0000, 0x0000, 0xb21c, 0x85c3, 0x1920, 0x0000, + 0x0000, 0x0000, 0x50d2, 0x9b19, 0x1901, 0x0000, 0x0000, 0x0000, 0xd4b0, 0xb708, + 0x18e0, 0x0000, 0x0000, 0x0000, 0xfb88, 0xf510, 0x18c1, 0x0000, 0x0000, 0x0000, + 0x31ec, 0xdc8d, 0x18a3, 0x0000, 0x0000, 0x0000, 0x3c00, 0xbff9, 0x1885, 0x0000, + 0x0000, 0x0000, 0x5020, 0xc30b, 0x1862, 0x0000, 0x0000, 0x0000, 0xd4f0, 0xda0c, + 0x1844, 0x0000, 0x0000, 0x0000, 0x20d2, 0x99a5, 0x1828, 0x0000, 0x0000, 0x0000, + 0x852e, 0xd159, 0x1809, 0x0000, 0x0000, 0x0000, 0x7cd8, 0x97a1, 0x17e9, 0x0000, + 0x0000, 0x0000, 0x423a, 0x997b, 0x17cb, 0x0000, 0x0000, 0x0000, 0xc1c0, 0xbe7d, + 0x17a8, 0x0000, 0x0000, 0x0000, 0xe8bc, 0xdcdd, 0x178d, 0x0000, 0x0000, 0x0000, + 0x8b28, 0xae06, 0x176e, 0x0000, 0x0000, 0x0000, 0x102e, 0xb8d4, 0x174f, 0x0000, + 0x0000, 0x0000, 0xaa00, 0xaa5c, 0x172f, 0x0000, 0x0000, 0x0000, 0x51f0, 0x9fc0, + 0x170e, 0x0000, 0x0000, 0x0000, 0xf858, 0xe181, 0x16f2, 0x0000, 0x0000, 0x0000, + 0x91a8, 0x8162, 0x16d3, 0x0000, 0x0000, 0x0000, 0x5f40, 0xcb6f, 0x16b1, 0x0000, + 0x0000, 0x0000, 0xbb50, 0xe55f, 0x1693, 0x0000, 0x0000, 0x0000, 0xacd2, 0xd895, + 0x1676, 0x0000, 0x0000, 0x0000, 0xef30, 0x97bf, 0x1654, 0x0000, 0x0000, 0x0000, + 0xf700, 0xb3d7, 0x1633, 0x0000, 0x0000, 0x0000, 0x3454, 0xa7b5, 0x1619, 0x0000, + 0x0000, 0x0000, 0x6b00, 0xa929, 0x15f6, 0x0000, 0x0000, 0x0000, 0x9f04, 0x89f7, + 0x15db, 0x0000, 0x0000, 0x0000, 0xad78, 0xd985, 0x15bc, 0x0000, 0x0000, 0x0000, + 0xa46a, 0xae3f, 0x159d, 0x0000, 0x0000, 0x0000, 0x63a0, 0xd0da, 0x157c, 0x0000, + 0x0000, 0x0000, 0x5e90, 0x817d, 0x155e, 0x0000, 0x0000, 0x0000, 0x1494, 0xb13f, + 0x1540, 0x0000, 0x0000, 0x0000, 0x0090, 0x9c40, 0x1521, 0x0000, 0x0000, 0x0000, + 0xdd70, 0xcc86, 0x1500, 0x0000, 0x0000, 0x0000, 0x64f8, 0xdb6f, 0x14e1, 0x0000, + 0x0000, 0x0000, 0xe22c, 0xac17, 0x14c3, 0x0000, 0x0000, 0x0000, 0x60e0, 0xa9ad, + 0x14a3, 0x0000, 0x0000, 0x0000, 0x4640, 0xd658, 0x1481, 0x0000, 0x0000, 0x0000, + 0x6490, 0xa181, 0x1467, 0x0000, 0x0000, 0x0000, 0x1df4, 0xaaa2, 0x1447, 0x0000, + 0x0000, 0x0000, 0xb94a, 0x8f61, 0x1429, 0x0000, 0x0000, 0x0000, 0x5198, 0x9d83, + 0x1409, 0x0000, 0x0000, 0x0000, 0x0f7a, 0xa818, 0x13eb, 0x0000, 0x0000, 0x0000, + 0xc45e, 0xc06c, 0x13cc, 0x0000, 0x0000, 0x0000, 0x4ec0, 0xfa29, 0x13a8, 0x0000, + 0x0000, 0x0000, 0x6418, 0x8cad, 0x138c, 0x0000, 0x0000, 0x0000, 0xbcc8, 0xe7d1, + 0x136f, 0x0000, 0x0000, 0x0000, 0xc934, 0xf9b0, 0x134f, 0x0000, 0x0000, 0x0000, + 0x6ce0, 0x98df, 0x1331, 0x0000, 0x0000, 0x0000, 0x3516, 0xe5e9, 0x1312, 0x0000, + 0x0000, 0x0000, 0xc6c0, 0xef8b, 0x12ef, 0x0000, 0x0000, 0x0000, 0xaf02, 0x913d, + 0x12d4, 0x0000, 0x0000, 0x0000, 0xd230, 0xe1d5, 0x12b5, 0x0000, 0x0000, 0x0000, + 0xfba8, 0xc232, 0x1295, 0x0000, 0x0000, 0x0000, 0x7ba4, 0xabeb, 0x1277, 0x0000, + 0x0000, 0x0000, 0x6e5c, 0xc692, 0x1258, 0x0000, 0x0000, 0x0000, 0x76a2, 0x9756, + 0x1239, 0x0000, 0x0000, 0x0000, 0xe180, 0xe423, 0x1214, 0x0000, 0x0000, 0x0000, + 0x8c3c, 0x90f8, 0x11fb, 0x0000, 0x0000, 0x0000, 0x9f3c, 0x9fd2, 0x11dc, 0x0000, + 0x0000, 0x0000, 0x53e0, 0xb73e, 0x11bd, 0x0000, 0x0000, 0x0000, 0x45be, 0x88d6, + 0x119e, 0x0000, 0x0000, 0x0000, 0x111a, 0x8bc0, 0x117f, 0x0000, 0x0000, 0x0000, + 0xe26a, 0xd7ff, 0x1160, 0x0000, 0x0000, 0x0000, 0xfb60, 0xdd8d, 0x113f, 0x0000, + 0x0000, 0x0000, 0x9370, 0xc108, 0x1120, 0x0000, 0x0000, 0x0000, 0x9654, 0x8baf, + 0x1103, 0x0000, 0x0000, 0x0000, 0xd6ec, 0xd6b9, 0x10e4, 0x0000, 0x0000, 0x0000, + 0x23e4, 0xd7b7, 0x10c4, 0x0000, 0x0000, 0x0000, 0x1aa6, 0xa847, 0x10a6, 0x0000, + 0x0000, 0x0000, 0xbee6, 0x9fef, 0x1087, 0x0000, 0x0000, 0x0000, 0x26d0, 0xa6eb, + 0x1066, 0x0000, 0x0000, 0x0000, 0x5b86, 0xa880, 0x1049, 0x0000, 0x0000, 0x0000, + 0x125c, 0xd971, 0x1029, 0x0000, 0x0000, 0x0000, 0x1f78, 0x9d18, 0x100a, 0x0000, + 0x0000, 0x0000, 0x0e84, 0xb15b, 0x0feb, 0x0000, 0x0000, 0x0000, 0xd0c0, 0xc150, + 0x0fcc, 0x0000, 0x0000, 0x0000, 0xa330, 0xc40c, 0x0fad, 0x0000, 0x0000, 0x0000, + 0x5202, 0xfc2c, 0x0f8f, 0x0000, 0x0000, 0x0000, 0x3f7c, 0xecf5, 0x0f6f, 0x0000, + 0x0000, 0x0000, 0xef44, 0xfdfd, 0x0f50, 0x0000, 0x0000, 0x0000, 0x3f6c, 0xab1b, + 0x0f31, 0x0000, 0x0000, 0x0000, 0xf658, 0x89ec, 0x0f11, 0x0000, 0x0000, 0x0000, + 0xbfc8, 0x9ba8, 0x0ef4, 0x0000, 0x0000, 0x0000, 0x3d40, 0xbe21, 0x0ed5, 0x0000, + 0x0000, 0x0000, 0xbbc4, 0xc70d, 0x0eb6, 0x0000, 0x0000, 0x0000, 0x5158, 0xdb16, + 0x0e96, 0x0000, 0x0000, 0x0000, 0xb5a8, 0xa8d8, 0x0e78, 0x0000, 0x0000, 0x0000, + 0xcccc, 0xb40e, 0x0e58, 0x0000, 0x0000, 0x0000, 0x448c, 0xcb62, 0x0e3a, 0x0000, + 0x0000, 0x0000, 0xf12a, 0x8aed, 0x0e1b, 0x0000, 0x0000, 0x0000, 0x79d0, 0xc59c, + 0x0dfb, 0x0000, 0x0000, 0x0000, 0x06b4, 0xcdc9, 0x0ddd, 0x0000, 0x0000, 0x0000, + 0xae70, 0xa979, 0x0dbe, 0x0000, 0x0000, 0x0000, 0x317c, 0xa8fb, 0x0d9e, 0x0000, + 0x0000, 0x0000, 0x5fe0, 0x8a50, 0x0d7d, 0x0000, 0x0000, 0x0000, 0x70b6, 0xfdfa, + 0x0d61, 0x0000, 0x0000, 0x0000, 0x1640, 0x9dc7, 0x0d41, 0x0000, 0x0000, 0x0000, + 0x9a9c, 0xdc50, 0x0d23, 0x0000, 0x0000, 0x0000, 0x4fcc, 0x9a9b, 0x0d04, 0x0000, + 0x0000, 0x0000, 0x7e48, 0x8f77, 0x0ce5, 0x0000, 0x0000, 0x0000, 0x84e4, 0xd4b9, + 0x0cc6, 0x0000, 0x0000, 0x0000, 0x84e0, 0xbd10, 0x0ca6, 0x0000, 0x0000, 0x0000, + 0x1b0a, 0xc8d9, 0x0c88, 0x0000, 0x0000, 0x0000, 0x6a48, 0xfc81, 0x0c68, 0x0000, + 0x0000, 0x0000, 0x070a, 0xbef6, 0x0c4a, 0x0000, 0x0000, 0x0000, 0x8a70, 0xf096, + 0x0c2b, 0x0000, 0x0000, 0x0000, 0xecc2, 0xc994, 0x0c0c, 0x0000, 0x0000, 0x0000, + 0x1540, 0x9537, 0x0bea, 0x0000, 0x0000, 0x0000, 0x1b02, 0xab5b, 0x0bce, 0x0000, + 0x0000, 0x0000, 0x5dc0, 0xb0c8, 0x0bad, 0x0000, 0x0000, 0x0000, 0xc928, 0xe034, + 0x0b8f, 0x0000, 0x0000, 0x0000, 0x2d12, 0xb4b0, 0x0b71, 0x0000, 0x0000, 0x0000, + 0x8fc2, 0xbb94, 0x0b52, 0x0000, 0x0000, 0x0000, 0xe236, 0xe22f, 0x0b33, 0x0000, + 0x0000, 0x0000, 0xb97c, 0xbe9e, 0x0b13, 0x0000, 0x0000, 0x0000, 0xe1a6, 0xe16d, + 0x0af5, 0x0000, 0x0000, 0x0000, 0xd330, 0xbaf0, 0x0ad6, 0x0000, 0x0000, 0x0000, + 0xc0bc, 0xbbd0, 0x0ab7, 0x0000, 0x0000, 0x0000, 0x8e66, 0xdd9b, 0x0a98, 0x0000, + 0x0000, 0x0000, 0xc95c, 0xf799, 0x0a79, 0x0000, 0x0000, 0x0000, 0xdac0, 0xbe4c, + 0x0a55, 0x0000, 0x0000, 0x0000, 0xafc0, 0xc378, 0x0a37, 0x0000, 0x0000, 0x0000, + 0xa880, 0xe341, 0x0a19, 0x0000, 0x0000, 0x0000, 0xc242, 0x81f6, 0x09fd, 0x0000, + 0x0000, 0x0000, 0x7470, 0xc777, 0x09de, 0x0000, 0x0000, 0x0000, 0x62bc, 0xb684, + 0x09be, 0x0000, 0x0000, 0x0000, 0x43ac, 0x8c58, 0x099f, 0x0000, 0x0000, 0x0000, + 0xcc3c, 0xf9ac, 0x0981, 0x0000, 0x0000, 0x0000, 0x1526, 0xb670, 0x0962, 0x0000, + 0x0000, 0x0000, 0xc9fe, 0xdf50, 0x0943, 0x0000, 0x0000, 0x0000, 0x6ae6, 0xc065, + 0x0924, 0x0000, 0x0000, 0x0000, 0xb114, 0xcf29, 0x0905, 0x0000, 0x0000, 0x0000, + 0xd388, 0x922a, 0x08e4, 0x0000, 0x0000, 0x0000, 0xcf54, 0xb926, 0x08c7, 0x0000, + 0x0000, 0x0000, 0x3826, 0xe855, 0x08a8, 0x0000, 0x0000, 0x0000, 0xe7c8, 0x829b, + 0x0888, 0x0000, 0x0000, 0x0000, 0x546c, 0xa903, 0x086a, 0x0000, 0x0000, 0x0000, + 0x8768, 0x99cc, 0x0849, 0x0000, 0x0000, 0x0000, 0x00ac, 0xf529, 0x082b, 0x0000, + 0x0000, 0x0000, 0x2658, 0x9f0b, 0x080c, 0x0000, 0x0000, 0x0000, 0xfe5c, 0x9e21, + 0x07ee, 0x0000, 0x0000, 0x0000, 0x6da2, 0x9910, 0x07cf, 0x0000, 0x0000, 0x0000, + 0x9220, 0xf9b3, 0x07b0, 0x0000, 0x0000, 0x0000, 0x3d90, 0xa541, 0x0791, 0x0000, + 0x0000, 0x0000, 0x6e4c, 0xe7cc, 0x0771, 0x0000, 0x0000, 0x0000, 0xa8fa, 0xe80a, + 0x0753, 0x0000, 0x0000, 0x0000, 0x4e14, 0xc3a7, 0x0734, 0x0000, 0x0000, 0x0000, + 0xf7e0, 0xbad9, 0x0712, 0x0000, 0x0000, 0x0000, 0xfea0, 0xeff2, 0x06f5, 0x0000, + 0x0000, 0x0000, 0xcef6, 0xbd48, 0x06d7, 0x0000, 0x0000, 0x0000, 0x7544, 0xf559, + 0x06b7, 0x0000, 0x0000, 0x0000, 0x2388, 0xf655, 0x0698, 0x0000, 0x0000, 0x0000, + 0xe900, 0xad56, 0x0676, 0x0000, 0x0000, 0x0000, 0x2cc0, 0x8437, 0x0659, 0x0000, + 0x0000, 0x0000, 0x3068, 0xc544, 0x063b, 0x0000, 0x0000, 0x0000, 0xdc70, 0xe73c, + 0x061b, 0x0000, 0x0000, 0x0000, 0xee50, 0x9d49, 0x05fc, 0x0000, 0x0000, 0x0000, + 0x93d2, 0x81f6, 0x05df, 0x0000, 0x0000, 0x0000, 0x941c, 0xadff, 0x05bf, 0x0000, + 0x0000, 0x0000, 0x2ce2, 0x8e45, 0x05a1, 0x0000, 0x0000, 0x0000, 0x4a60, 0x95fd, + 0x0581, 0x0000, 0x0000, 0x0000, 0x79f8, 0xb83a, 0x0563, 0x0000, 0x0000, 0x0000, + 0xcb58, 0xa1f5, 0x0543, 0x0000, 0x0000, 0x0000, 0x2a3a, 0xdc36, 0x0525, 0x0000, + 0x0000, 0x0000, 0x14ee, 0x890e, 0x0506, 0x0000, 0x0000, 0x0000, 0x8f20, 0xc432, + 0x04e3, 0x0000, 0x0000, 0x0000, 0x8440, 0xb21d, 0x04c6, 0x0000, 0x0000, 0x0000, + 0x5430, 0xf698, 0x04a7, 0x0000, 0x0000, 0x0000, 0x04ae, 0x8b20, 0x048a, 0x0000, + 0x0000, 0x0000, 0x04d0, 0xe872, 0x046b, 0x0000, 0x0000, 0x0000, 0xc78e, 0x8893, + 0x044c, 0x0000, 0x0000, 0x0000, 0x0f78, 0x9895, 0x042b, 0x0000, 0x0000, 0x0000, + 0x11d4, 0xdf2e, 0x040d, 0x0000, 0x0000, 0x0000, 0xe84c, 0x89d5, 0x03ef, 0x0000, + 0x0000, 0x0000, 0xf7be, 0x8a67, 0x03d0, 0x0000, 0x0000, 0x0000, 0x95d0, 0xc906, + 0x03b1, 0x0000, 0x0000, 0x0000, 0x64ce, 0xd96c, 0x0392, 0x0000, 0x0000, 0x0000, + 0x97ba, 0xa16f, 0x0373, 0x0000, 0x0000, 0x0000, 0x463c, 0xc51a, 0x0354, 0x0000, + 0x0000, 0x0000, 0xef0a, 0xe93e, 0x0335, 0x0000, 0x0000, 0x0000, 0x526a, 0xa466, + 0x0316, 0x0000, 0x0000, 0x0000, 0x4140, 0xa94d, 0x02f5, 0x0000, 0x0000, 0x0000, + 0xb4ec, 0xce68, 0x02d8, 0x0000, 0x0000, 0x0000, 0x4fa2, 0x8490, 0x02b9, 0x0000, + 0x0000, 0x0000, 0x4e60, 0xca98, 0x0298, 0x0000, 0x0000, 0x0000, 0x08dc, 0xe09c, + 0x027a, 0x0000, 0x0000, 0x0000, 0x2b90, 0xc7e3, 0x025c, 0x0000, 0x0000, 0x0000, + 0x5a7c, 0xf8ef, 0x023c, 0x0000, 0x0000, 0x0000, 0x5022, 0x9d58, 0x021e, 0x0000, + 0x0000, 0x0000, 0x553a, 0xe242, 0x01ff, 0x0000, 0x0000, 0x0000, 0x7e6e, 0xb54d, + 0x01e0, 0x0000, 0x0000, 0x0000, 0xd2d4, 0xa88c, 0x01c1, 0x0000, 0x0000, 0x0000, + 0x75b6, 0xfe6d, 0x01a2, 0x0000, 0x0000, 0x0000, 0x3bb2, 0xf04c, 0x0183, 0x0000, + 0x0000, 0x0000, 0xc2d0, 0xc046, 0x0163, 0x0000, 0x0000, 0x0000, 0x250c, 0xf9d6, + 0x0145, 0x0000, 0x0000, 0x0000, 0xb7b4, 0x8a0d, 0x0126, 0x0000, 0x0000, 0x0000, + 0x1a72, 0xe4f5, 0x0107, 0x0000, 0x0000, 0x0000, 0x825c, 0xa9b8, 0x00e8, 0x0000, + 0x0000, 0x0000, 0x6c90, 0xc9ad, 0x00c6, 0x0000, 0x0000, 0x0000, 0x4d00, 0xd1bb, + 0x00aa, 0x0000, 0x0000, 0x0000, 0xa4a0, 0xee01, 0x0087, 0x0000, 0x0000, 0x0000, + 0x89a8, 0xbe9f, 0x006b, 0x0000, 0x0000, 0x0000, 0x038e, 0xc80c, 0x004d, 0x0000, + 0x0000, 0x0000, 0xfe26, 0x8384, 0x002e, 0x0000, 0x0000, 0x0000, 0xcd90, 0xca57, + 0x000e, 0x0000 +}; + +void MacroAssembler::libm_reduce_pi04l(Register eax, Register ecx, Register edx, Register ebx, Register esi, Register edi, Register ebp, Register esp) { + Label B1_1, B1_2, B1_3, B1_4, B1_5, B1_6, B1_7, B1_8, B1_9, B1_10, B1_11, B1_12; + Label B1_13, B1_14, B1_15; + + assert_different_registers(ebx, eax, ecx, edx, esi, edi, ebp, esp); + + address zero_none = (address)_zero_none; + address _4onpi_d = (address)__4onpi_d; + address TWO_32H = (address)_TWO_32H; + address pi04_3d = (address)_pi04_3d; + address pi04_5d = (address)_pi04_5d; + address SCALE = (address)_SCALE; + address zeros = (address)_zeros; + address pi04_2d = (address)_pi04_2d; + address TWO_12H = (address)_TWO_12H; + address _4onpi_31l = (address)__4onpi_31l; + + bind(B1_1); + push(ebp); + movl(ebp, esp); + andl(esp, -16); + push(esi); + push(edi); + push(ebx); + subl(esp, 20); + movzwl(ebx, Address(ebp, 16)); + andl(ebx, 32767); + movl(eax, Address(ebp, 20)); + cmpl(ebx, 16413); + movl(esi, Address(ebp, 24)); + movl(Address(esp, 4), eax); + jcc(Assembler::greaterEqual, B1_8); + + bind(B1_2); + fld_x(Address(ebp, 8)); + fld_d(ExternalAddress(_4onpi_d)); //0x6dc9c883UL, 0x3ff45f30UL + fmul(1); + fstp_x(Address(esp, 8)); + movzwl(ecx, Address(esp, 16)); + negl(ecx); + addl(ecx, 30); + movl(eax, Address(esp, 12)); + shrl(eax); + cmpl(Address(esp, 4), 0); + jcc(Assembler::notEqual, B1_4); + + bind(B1_3); + lea(ecx, Address(eax, 1)); + andl(ecx, -2); + jmp(B1_5); + + bind(B1_4); + movl(ecx, eax); + addl(eax, Address(esp, 4)); + movl(edx, eax); + andl(edx, 1); + addl(ecx, edx); + + bind(B1_5); + fld_d(ExternalAddress(TWO_32H)); //0x00000000UL, 0x41f80000UL + cmpl(ebx, 16400); + movl(Address(esp, 0), ecx); + fild_s(Address(esp, 0)); + jcc(Assembler::greaterEqual, B1_7); + + bind(B1_6); + fld_d(ExternalAddress(pi04_3d)); //0x54442d00UL, 0x3fe921fbUL + fmul(1); + fsubp(3); + fxch(1); + fmul(2); + fld_s(2); + fadd(1); + fsubrp(1); + fld_s(0); + fxch(1); + fsuba(3); + fld_d(ExternalAddress(8 + pi04_3d)); //0x98cc5180UL, 0x3ce84698UL + fmul(3); + fsuba(2); + fxch(1); + fsub(2); + fsubrp(1); + faddp(3); + fld_d(ExternalAddress(16 + pi04_3d)); //0xcbb5bf6cUL, 0xb9dfc8f8UL + fmulp(2); + fld_s(1); + fsubr(1); + fsuba(1); + fxch(2); + fsubp(1); + faddp(2); + fxch(1); + jmp(B1_15); + + bind(B1_7); + fld_d(ExternalAddress(pi04_5d)); //0x54400000UL, 0x3fe921fbUL + fmul(1); + fsubp(3); + fxch(1); + fmul(2); + fld_s(2); + fadd(1); + fsubrp(1); + fld_s(0); + fxch(1); + fsuba(3); + fld_d(ExternalAddress(8 + pi04_5d)); //0x1a600000UL, 0x3dc0b461UL + fmul(3); + fsuba(2); + fxch(1); + fsub(2); + fsubrp(1); + faddp(3); + fld_d(ExternalAddress(16 + pi04_5d)); //0x2e000000UL, 0x3b93198aUL + fmul(2); + fld_s(0); + fsubr(2); + fsuba(2); + fxch(1); + fsubp(2); + fxch(1); + faddp(3); + fld_d(ExternalAddress(24 + pi04_5d)); //0x25200000UL, 0x396b839aUL + fmul(2); + fld_s(0); + fsubr(2); + fsuba(2); + fxch(1); + fsubp(2); + fxch(1); + faddp(3); + fld_d(ExternalAddress(32 + pi04_5d)); //0x533e63a0UL, 0x37027044UL + fmulp(2); + fld_s(1); + fsubr(1); + fsuba(1); + fxch(2); + fsubp(1); + faddp(2); + fxch(1); + jmp(B1_15); + + bind(B1_8); + fld_x(Address(ebp, 8)); + addl(ebx, -16417); + fmul_d(as_Address(ExternalAddress(SCALE))); //0x00000000UL, 0x32600000UL + movl(eax, -2078209981); + imull(ebx); + addl(edx, ebx); + movl(ecx, ebx); + sarl(edx, 4); + sarl(ecx, 31); + subl(edx, ecx); + movl(eax, edx); + shll(eax, 5); + fstp_x(Address(ebp, 8)); + fld_x(Address(ebp, 8)); + subl(eax, edx); + movl(Address(ebp, 8), 0); + subl(ebx, eax); + fld_x(Address(ebp, 8)); + cmpl(ebx, 17); + fsuba(1); + jcc(Assembler::less, B1_10); + + bind(B1_9); + lea(eax, Address(noreg, edx, Address::times_8)); + lea(ecx, Address(eax, edx, Address::times_4)); + incl(edx); + fld_x(Address(_4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmul(2); + fld_x(Address(12 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmul(2); + fld_s(0); + fadd(2); + fsuba(2); + fxch(1); + faddp(2); + fld_s(1); + fadd(1); + fstp_x(Address(esp, 8)); + andl(Address(esp, 8), -16777216); + fld_x(Address(esp, 8)); + fsubp(1); + jmp(B1_11); + + bind(B1_10); + fld_d(ExternalAddress(zeros)); //0x00000000UL, 0x00000000UL + fld_s(0); + + bind(B1_11); + fld_s(0); + lea(eax, Address(noreg, edx, Address::times_8)); + fld_s(3); + lea(edx, Address(eax, edx, Address::times_4)); + fld_x(Address(_4onpi_31l, RelocationHolder::none).plus_disp(edx, Address::times_1)); + fmul(6); + movl(Address(esp, 0), edx); + fadda(2); + fxch(2); + fsuba(3); + fxch(2); + faddp(3); + fxch(2); + faddp(3); + fld_x(Address(12 + _4onpi_31l, RelocationHolder::none).plus_disp(edx, Address::times_1)); + fmula(2); + fld_s(2); + fadd(2); + fld_s(0); + fxch(1); + fsubra(3); + fxch(3); + fchs(); + faddp(4); + fxch(3); + faddp(4); + fxch(2); + fadd(3); + fxch(2); + fmul(5); + fadda(2); + fld_s(4); + fld_x(Address(24 + _4onpi_31l, RelocationHolder::none).plus_disp(edx, Address::times_1)); + fmula(1); + fxch(1); + fadda(4); + fxch(4); + fstp_x(Address(esp, 8)); + movzwl(ebx, Address(esp, 16)); + andl(ebx, 32767); + cmpl(ebx, 16415); + jcc(Assembler::greaterEqual, B1_13); + + bind(B1_12); + negl(ebx); + addl(ebx, 30); + movl(ecx, ebx); + movl(eax, Address(esp, 12)); + shrl(eax); + shll(eax); + movl(Address(esp, 12), eax); + movl(Address(esp, 8), 0); + shrl(eax); + jmp(B1_14); + + bind(B1_13); + negl(ebx); + addl(ebx, 30); + movl(ecx, ebx); + movl(edx, Address(esp, 8)); + shrl(edx); + shll(edx); + negl(ecx); + movl(eax, Address(esp, 12)); + shll(eax); + movl(ecx, ebx); + movl(Address(esp, 8), edx); + shrl(edx); + orl(eax, edx); + + bind(B1_14); + fld_x(Address(esp, 8)); + addl(eax, Address(esp, 4)); + fsubp(3); + fmul(6); + fld_s(4); + movl(edx, eax); + andl(edx, 1); + fadd(3); + movl(ecx, Address(esp, 0)); + fsuba(3); + fxch(3); + faddp(5); + fld_s(1); + fxch(3); + fadd_d(Address(zero_none, RelocationHolder::none).plus_disp(edx, Address::times_8)); + fadda(3); + fsub(3); + faddp(2); + fxch(1); + faddp(4); + fld_s(2); + fadd(2); + fsuba(2); + fxch(3); + faddp(2); + fxch(1); + faddp(3); + fld_s(0); + fadd(2); + fsuba(2); + fxch(1); + faddp(2); + fxch(1); + faddp(2); + fld_s(2); + fld_x(Address(36 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmula(1); + fld_s(1); + fadd(3); + fsuba(3); + fxch(2); + faddp(3); + fxch(2); + faddp(3); + fxch(1); + fmul(4); + fld_s(0); + fadd(2); + fsuba(2); + fxch(1); + faddp(2); + fxch(1); + faddp(2); + fld_s(2); + fld_x(Address(48 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmula(1); + fld_s(1); + fadd(3); + fsuba(3); + fxch(2); + faddp(3); + fxch(2); + faddp(3); + fld_s(3); + fxch(2); + fmul(5); + fld_x(Address(60 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmula(3); + fxch(3); + faddp(1); + fld_s(0); + fadd(2); + fsuba(2); + fxch(1); + faddp(2); + fxch(1); + faddp(3); + fld_s(3); + fxch(2); + fmul(5); + fld_x(Address(72 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmula(3); + fxch(3); + faddp(1); + fld_s(0); + fadd(2); + fsuba(2); + fxch(1); + faddp(2); + fxch(1); + faddp(3); + fxch(1); + fmulp(4); + fld_x(Address(84 + _4onpi_31l, RelocationHolder::none).plus_disp(ecx, Address::times_1)); + fmulp(3); + fxch(2); + faddp(3); + fld_s(2); + fadd(2); + fld_d(ExternalAddress(TWO_32H)); //0x00000000UL, 0x41f80000UL + fmul(1); + fadda(1); + fsubp(1); + fsuba(2); + fxch(3); + faddp(2); + faddp(1); + fld_d(ExternalAddress(pi04_2d)); //0x54400000UL, 0x3fe921fbUL + fld_s(0); + fmul(2); + fxch(2); + fadd(3); + fxch(1); + fmulp(3); + fmul_d(as_Address(ExternalAddress(8 + pi04_2d))); //0x1a626331UL, 0x3dc0b461UL + faddp(1); + + bind(B1_15); + fld_d(ExternalAddress(TWO_12H)); //0x00000000UL, 0x40b80000UL + fld_s(2); + fadd(2); + fmula(1); + fstp_x(Address(esp, 8)); + fld_x(Address(esp, 8)); + fadd(1); + fsubrp(1); + fst_d(Address(esi, 0)); + fsubp(2); + faddp(1); + fstp_d(Address(esi, 8)); + addl(esp, 20); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); +} + +ALIGNED_(16) juint _L_2il0floatpacket_0[] = +{ + 0xffffffffUL, 0x7fffffffUL, 0x00000000UL, 0x00000000UL +}; + +ALIGNED_(16) juint _Pi4Inv[] = +{ + 0x6dc9c883UL, 0x3ff45f30UL +}; + +ALIGNED_(16) juint _Pi4x3[] = +{ + 0x54443000UL, 0xbfe921fbUL, 0x3b39a000UL, 0x3d373dcbUL, 0xe0e68948UL, + 0xba845c06UL +}; + +ALIGNED_(16) juint _Pi4x4[] = +{ + 0x54400000UL, 0xbfe921fbUL, 0x1a600000UL, 0xbdc0b461UL, 0x2e000000UL, + 0xbb93198aUL, 0x252049c1UL, 0xb96b839aUL +}; + +ALIGNED_(16) jushort _SP[] = +{ + 0xaaab, 0xaaaa, 0xaaaa, 0xaaaa, 0xbffc, 0x0000, 0x8887, 0x8888, 0x8888, 0x8888, + 0x3ff8, 0x0000, 0xc527, 0x0d00, 0x00d0, 0xd00d, 0xbff2, 0x0000, 0x45f6, 0xb616, + 0x1d2a, 0xb8ef, 0x3fec, 0x0000, 0x825b, 0x3997, 0x2b3f, 0xd732, 0xbfe5, 0x0000, + 0xbf33, 0x8bb4, 0x2fda, 0xb092, 0x3fde, 0x0000, 0x44a6, 0xed1a, 0x29ef, 0xd73e, + 0xbfd6, 0x0000, 0x8610, 0x307f, 0x62a1, 0xc921, 0x3fce, 0x0000 +}; + +ALIGNED_(16) jushort _CP[] = +{ + 0x0000, 0x0000, 0x0000, 0x8000, 0xbffe, 0x0000, 0xaaa5, 0xaaaa, 0xaaaa, 0xaaaa, + 0x3ffa, 0x0000, 0x9c2f, 0x0b60, 0x60b6, 0xb60b, 0xbff5, 0x0000, 0xf024, 0x0cac, + 0x00d0, 0xd00d, 0x3fef, 0x0000, 0x03fe, 0x3f65, 0x7dbb, 0x93f2, 0xbfe9, 0x0000, + 0xd84d, 0xadee, 0xc698, 0x8f76, 0x3fe2, 0x0000, 0xdaba, 0xfe79, 0xea36, 0xc9c9, + 0xbfda, 0x0000, 0x3ac6, 0x0ba0, 0x07ce, 0xd585, 0x3fd2, 0x0000 +}; + +ALIGNED_(16) juint _ones[] = +{ + 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0xbff00000UL +}; + +void MacroAssembler::libm_sincos_huge(XMMRegister xmm0, XMMRegister xmm1, Register eax, Register ecx, Register edx, Register ebx, Register esi, Register edi, Register ebp, Register esp) { + Label B1_1, B1_2, B1_3, B1_4, B1_5, B1_6, B1_7, B1_8, B1_9, B1_10, B1_11, B1_12; + Label B1_13, B1_14, B1_15, B1_16, B1_17, B1_18, B1_19, B1_20, B1_21, B1_22, B1_23; + Label B1_24, B1_25, B1_26, B1_27, B1_28, B1_29, B1_30, B1_31, B1_32, B1_33, B1_34; + Label B1_35, B1_36, B1_37, B1_38, B1_39, B1_40, B1_41, B1_42, B1_43, B1_44, B1_45, B1_46; + + assert_different_registers(ebx, eax, ecx, edx, esi, edi, ebp, esp); + + address L_2il0floatpacket_0 = (address)_L_2il0floatpacket_0; + address Pi4Inv = (address)_Pi4Inv; + address Pi4x3 = (address)_Pi4x3; + address Pi4x4 = (address)_Pi4x4; + address ones = (address)_ones; + address CP = (address)_CP; + address SP = (address)_SP; + + bind(B1_1); + push(ebp); + movl(ebp, esp); + andl(esp, -64); + push(esi); + push(edi); + push(ebx); + subl(esp, 52); + movl(eax, Address(ebp, 16)); + movl(edx, Address(ebp, 20)); + movl(Address(esp, 32), eax); + movl(Address(esp, 36), edx); + + bind(B1_2); + fnstcw(Address(esp, 30)); + + bind(B1_3); + movsd(xmm1, Address(ebp, 8)); + movl(esi, Address(ebp, 12)); + movl(eax, esi); + andl(eax, 2147483647); + andps(xmm1, ExternalAddress(L_2il0floatpacket_0)); //0xffffffffUL, 0x7fffffffUL, 0x00000000UL, 0x00000000UL + shrl(esi, 31); + movl(Address(esp, 40), eax); + cmpl(eax, 1104150528); + movsd(Address(ebp, 8), xmm1); + jcc(Assembler::aboveEqual, B1_11); + + bind(B1_4); + movsd(xmm0, ExternalAddress(Pi4Inv)); //0x6dc9c883UL, 0x3ff45f30UL + mulsd(xmm0, xmm1); + movzwl(edx, Address(esp, 30)); + movl(eax, edx); + andl(eax, 768); + movsd(Address(esp, 0), xmm0); + cmpl(eax, 768); + jcc(Assembler::equal, B1_42); + + bind(B1_5); + orl(edx, -64768); + movw(Address(esp, 28), edx); + + bind(B1_6); + fldcw(Address(esp, 28)); + + bind(B1_7); + movsd(xmm1, Address(ebp, 8)); + movl(ebx, 1); + + bind(B1_8); + movl(Address(esp, 12), ebx); + movl(ebx, Address(esp, 4)); + movl(eax, ebx); + movl(Address(esp, 8), esi); + movl(esi, ebx); + shrl(esi, 20); + andl(eax, 1048575); + movl(ecx, esi); + orl(eax, 1048576); + negl(ecx); + movl(edx, eax); + addl(ecx, 19); + addl(esi, 13); + movl(Address(esp, 24), ecx); + shrl(edx); + movl(ecx, esi); + shll(eax); + movl(ecx, Address(esp, 24)); + movl(esi, Address(esp, 0)); + shrl(esi); + orl(eax, esi); + cmpl(ebx, 1094713344); + movsd(Address(esp, 16), xmm1); + fld_d(Address(esp, 16)); + cmov32(Assembler::below, eax, edx); + movl(esi, Address(esp, 8)); + lea(edx, Address(eax, 1)); + movl(ebx, edx); + andl(ebx, -2); + movl(Address(esp, 16), ebx); + fild_s(Address(esp, 16)); + movl(ebx, Address(esp, 12)); + cmpl(Address(esp, 40), 1094713344); + jcc(Assembler::aboveEqual, B1_10); + + bind(B1_9); + fld_d(ExternalAddress(Pi4x3)); //0x54443000UL, 0xbfe921fbUL + fmul(1); + faddp(2); + fld_d(ExternalAddress(8 + Pi4x3)); //0x3b39a000UL, 0x3d373dcbUL + fmul(1); + faddp(2); + fld_d(ExternalAddress(16 + Pi4x3)); //0xe0e68948UL, 0xba845c06UL + fmulp(1); + faddp(1); + jmp(B1_17); + + bind(B1_10); + fld_d(ExternalAddress(Pi4x4)); //0x54400000UL, 0xbfe921fbUL + fmul(1); + faddp(2); + fld_d(ExternalAddress(8 + Pi4x4)); //0x1a600000UL, 0xbdc0b461UL + fmul(1); + faddp(2); + fld_d(ExternalAddress(16 + Pi4x4)); //0x2e000000UL, 0xbb93198aUL + fmul(1); + faddp(2); + fld_d(ExternalAddress(24 + Pi4x4)); //0x252049c1UL, 0xb96b839aUL + fmulp(1); + faddp(1); + jmp(B1_17); + + bind(B1_11); + movzwl(edx, Address(esp, 30)); + movl(eax, edx); + andl(eax, 768); + cmpl(eax, 768); + jcc(Assembler::equal, B1_43); + bind(B1_12); + orl(edx, -64768); + movw(Address(esp, 28), edx); + + bind(B1_13); + fldcw(Address(esp, 28)); + + bind(B1_14); + movsd(xmm1, Address(ebp, 8)); + movl(ebx, 1); + + bind(B1_15); + movsd(Address(esp, 16), xmm1); + fld_d(Address(esp, 16)); + addl(esp, -32); + lea(eax, Address(esp, 32)); + fstp_x(Address(esp, 0)); + movl(Address(esp, 12), 0); + movl(Address(esp, 16), eax); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dlibm_reduce_pi04l()))); + + bind(B1_46); + addl(esp, 32); + + bind(B1_16); + fld_d(Address(esp, 0)); + lea(edx, Address(eax, 1)); + fld_d(Address(esp, 8)); + faddp(1); + + bind(B1_17); + movl(ecx, edx); + addl(eax, 3); + shrl(ecx, 2); + andl(ecx, 1); + shrl(eax, 2); + xorl(esi, ecx); + movl(ecx, Address(esp, 36)); + andl(eax, 1); + andl(ecx, 3); + cmpl(ecx, 3); + jcc(Assembler::notEqual, B1_25); + + bind(B1_18); + fld_x(ExternalAddress(84 + SP)); //0x8610, 0x307f, 0x62 + fld_s(1); + fmul((2)); + testb(edx, 2); + fmula((1)); + fld_x(ExternalAddress(72 + SP)); //0x44a6, 0xed1a, 0x29 + faddp(2); + fmula(1); + fld_x(ExternalAddress(60 + SP)); //0xbf33, 0x8bb4, 0x2f + faddp(2); + fmula(1); + fld_x(ExternalAddress(48 + SP)); //0x825b, 0x3997, 0x2b + faddp(2); + fmula(1); + fld_x(ExternalAddress(36 + SP)); //0x45f6, 0xb616, 0x1d + faddp(2); + fmula(1); + fld_x(ExternalAddress(24 + SP)); //0xc527, 0x0d00, 0x00 + faddp(2); + fmula(1); + fld_x(ExternalAddress(12 + SP)); //0x8887, 0x8888, 0x88 + faddp(2); + fmula(1); + fld_x(ExternalAddress(SP)); //0xaaab, 0xaaaa, 0xaa + faddp(2); + fmula(1); + fld_x(ExternalAddress(84 + CP)); //0x3ac6, 0x0ba0, 0x07 + fmul(1); + fld_x(ExternalAddress(72 + CP)); //0xdaba, 0xfe79, 0xea + faddp(1); + fmul(1); + fld_x(ExternalAddress(62 + CP)); //0xd84d, 0xadee, 0xc6 + faddp(1); + fmul(1); + fld_x(ExternalAddress(48 + CP)); //0x03fe, 0x3f65, 0x7d + faddp(1); + fmul(1); + fld_x(ExternalAddress(36 + CP)); //0xf024, 0x0cac, 0x00 + faddp(1); + fmul(1); + fld_x(ExternalAddress(24 + CP)); //0x9c2f, 0x0b60, 0x60 + faddp(1); + fmul(1); + fld_x(ExternalAddress(12 + CP)); //0xaaa5, 0xaaaa, 0xaa + faddp(1); + fmul(1); + fld_x(ExternalAddress(CP)); //0x0000, 0x0000, 0x00 + faddp(1); + fmulp(1); + fld_d(Address(ones, RelocationHolder::none).plus_disp(esi, Address::times_8)); + fld_d(Address(ones, RelocationHolder::none).plus_disp(eax, Address::times_8)); + jcc(Assembler::equal, B1_22); + + bind(B1_19); + fmulp(4); + testl(ebx, ebx); + fxch(2); + fmul(3); + movl(eax, Address(esp, 2)); + faddp(3); + fxch(2); + fstp_d(Address(eax, 0)); + fmula(1); + faddp(1); + fstp_d(Address(eax, 8)); + jcc(Assembler::equal, B1_21); + + bind(B1_20); + fldcw(Address(esp, 30)); + + bind(B1_21); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + + bind(B1_22); + fxch(1); + fmulp(4); + testl(ebx, ebx); + fxch(2); + fmul(3); + movl(eax, Address(esp, 32)); + faddp(3); + fxch(2); + fstp_d(Address(eax, 8)); + fmula(1); + faddp(1); + fstp_d(Address(eax, 0)); + jcc(Assembler::equal, B1_24); + + bind(B1_23); + fldcw(Address(esp, 30)); + + bind(B1_24); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + + bind(B1_25); + testb(Address(esp, 36), 2); + jcc(Assembler::equal, B1_33); + + bind(B1_26); + fld_s(0); + testb(edx, 2); + fmul(1); + fld_s(0); + fmul(1); + jcc(Assembler::equal, B1_30); + + bind(B1_27); + fstp_d(2); + fld_x(ExternalAddress(84 + CP)); //0x3ac6, 0x0ba0, 0x07 + testl(ebx, ebx); + fmul(2); + fld_x(ExternalAddress(72 + CP)); //0xdaba, 0xfe79, 0xea + fmul(3); + fld_x(ExternalAddress(60 + CP)); //0xd84d, 0xadee, 0xc6 + movl(eax, Address(rsp, 32)); + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(48 + CP)); //0x03fe, 0x3f65, 0x7d + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(36 + CP)); //0xf024, 0x0cac, 0x00 + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(24 + CP)); //0x9c2f, 0x0b60, 0x60 + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(12 + CP)); //0xaaa5, 0xaaaa, 0xaa + faddp(2); + fxch(1); + fmulp(3); + fld_x(ExternalAddress(CP)); //0x0000, 0x0000, 0x00 + faddp(1); + fmulp(1); + faddp(1); + fld_d(Address(ones, RelocationHolder::none).plus_disp(rsi, Address::times_8)); + fmula(1); + faddp(1); + fstp_d(Address(eax, 8)); + jcc(Assembler::equal, B1_29); + + bind(B1_28); + fldcw(Address(esp, 30)); + + bind(B1_29); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + + bind(B1_30); + fld_x(ExternalAddress(84 + SP)); //0x8610, 0x307f, 0x62 + testl(ebx, ebx); + fmul(1); + fld_x(ExternalAddress(72 + SP)); //0x44a6, 0xed1a, 0x29 + fmul(2); + fld_x(ExternalAddress(60 + SP)); //0xbf33, 0x8bb4, 0x2f + movl(eax, Address(rsp, 32)); + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(48 + SP)); //0x825b, 0x3997, 0x2b + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(36 + SP)); //0x45f6, 0xb616, 0x1d + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(24 + SP)); //0xc527, 0x0d00, 0x00 + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(12 + SP)); //0x8887, 0x8888, 0x88 + faddp(2); + fxch(1); + fmulp(2); + fld_x(ExternalAddress(SP)); //0xaaab, 0xaaaa, 0xaa + faddp(1); + fmulp(2); + faddp(1); + fld_d(Address(ones, RelocationHolder::none).plus_disp(rsi, Address::times_8)); + fmulp(2); + fmul(1); + faddp(1); + fstp_d(Address(eax, 8)); + jcc(Assembler::equal, B1_32); + + bind(B1_31); + fldcw(Address(esp, 30)); + + bind(B1_32); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + + bind(B1_33); + testb(Address(esp, 36), 1); + jcc(Assembler::equal, B1_41); + + bind(B1_34); + fld_s(0); + testb(edx, 2); + fmul(1); + fld_s(0); + fmul(1); + jcc(Assembler::equal, B1_38); + + bind(B1_35); + fld_x(ExternalAddress(84 + SP)); //0x8610, 0x307f, 0x62 + testl(ebx, ebx); + fmul(1); + fld_x(ExternalAddress(72 + SP)); //0x44a6, 0xed1a, 0x29 + fmul(2); + fld_x(ExternalAddress(60 + SP)); //0xbf33, 0x8bb4, 0x2f + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(48 + SP)); //0x825b, 0x3997, 0x2b + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(36 + SP)); //0x45f6, 0xb616, 0x1d + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(24 + SP)); //0xc527, 0x0d00, 0x00 + faddp(2); + fxch(1); + fmul(2); + fld_x(ExternalAddress(12 + SP)); //0x8887, 0x8888, 0x88 + faddp(2); + fxch(1); + fmulp(2); + fld_x(ExternalAddress(SP)); //0xaaab, 0xaaaa, 0xaa + faddp(1); + fmulp(2); + faddp(1); + fld_d(Address(ones, RelocationHolder::none).plus_disp(eax, Address::times_8)); + fmulp(2); + fmul(1); + movl(eax, Address(esp, 32)); + faddp(1); + fstp_d(Address(eax, 0)); + jcc(Assembler::equal, B1_37); + + bind(B1_36); + fldcw(Address(esp, 30)); + + bind(B1_37); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + + bind(B1_38); + fstp_d(2); + fld_x(ExternalAddress(84 + CP)); //0x3ac6, 0x0ba0, 0x07 + testl(ebx, ebx); + fmul(2); + fld_x(ExternalAddress(72 + CP)); //0xdaba, 0xfe79, 0xea + fmul(3); + fld_x(ExternalAddress(60 + CP)); //0xd84d, 0xadee, 0xc6 + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(48 + CP)); //0x03fe, 0x3f65, 0x7d + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(36 + CP)); //0xf024, 0x0cac, 0x00 + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(24 + CP)); //0x9c2f, 0x0b60, 0x60 + faddp(2); + fxch(1); + fmul(3); + fld_x(ExternalAddress(12 + CP)); //0xaaa5, 0xaaaa, 0xaa + faddp(2); + fxch(1); + fmulp(3); + fld_x(ExternalAddress(CP)); //0x0000, 0x0000, 0x00 + faddp(1); + fmulp(1); + faddp(1); + fld_d(Address(ones, RelocationHolder::none).plus_disp(eax, Address::times_8)); + fmula(1); + movl(eax, Address(esp, 32)); + faddp(1); + fstp_d(Address(eax, 0)); + jcc(Assembler::equal, B1_40); + + bind(B1_39); + fldcw(Address(esp, 30)); + bind(B1_40); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + bind(B1_41); + fstp_d(0); + addl(esp, 52); + pop(ebx); + pop(edi); + pop(esi); + movl(esp, ebp); + pop(ebp); + ret(0); + bind(B1_42); + xorl(ebx, ebx); + jmp(B1_8); + bind(B1_43); + xorl(ebx, ebx); + jmp(B1_15); +} + +ALIGNED_(16) juint _static_const_table_sin[] = +{ + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, 0xbf73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, + 0xc0000000UL, 0xbc626d19UL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, + 0xbfa60beaUL, 0x2ed59f06UL, 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, + 0x00000000UL, 0x3ff00000UL, 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, + 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, 0x00000000UL, 0x3ff00000UL, + 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, 0x20000000UL, + 0x3c5e0d89UL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, 0xbfc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0x3ff00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, + 0x20000000UL, 0x3c68076aUL, 0x00000000UL, 0x3ff00000UL, 0x99fcef32UL, + 0x3fca8279UL, 0x667f3bcdUL, 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, + 0x00000000UL, 0x3fe00000UL, 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, + 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, 0x00000000UL, 0x3fe00000UL, + 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, 0xe0000000UL, + 0x3c39f630UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, 0xbf9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0x3fe00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0x3fed906bUL, + 0x20000000UL, 0x3c7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x76acf82dUL, + 0x3fa4a031UL, 0x56c62ddaUL, 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, + 0x00000000UL, 0x3fd00000UL, 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, + 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, 0x00000000UL, 0x3fd00000UL, + 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, 0x40000000UL, + 0xbc887df6UL, 0x00000000UL, 0x3fc00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, + 0x40000000UL, 0xbc887df6UL, 0x00000000UL, 0xbfc00000UL, 0x0e5967d5UL, + 0x3fac1d1fUL, 0xcff75cb0UL, 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, + 0x00000000UL, 0xbfd00000UL, 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, + 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, 0x00000000UL, 0xbfd00000UL, + 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, 0x3fed906bUL, 0x20000000UL, + 0x3c7457e6UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, 0x3f9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, + 0xe0000000UL, 0x3c39f630UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, + 0xbfc133ccUL, 0x6b151741UL, 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, + 0x00000000UL, 0xbfe00000UL, 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, + 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, 0x00000000UL, 0xbfe00000UL, + 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, 0x20000000UL, + 0x3c68076aUL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, 0x3fc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, + 0x20000000UL, 0x3c5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, + 0x3fb37ca1UL, 0xa6aea963UL, 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, + 0x00000000UL, 0xbff00000UL, 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, + 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, 0x00000000UL, 0xbff00000UL, + 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, 0xc0000000UL, + 0xbc626d19UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, 0x3f73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0xbff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, + 0x3f73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0xbff00000UL, 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, + 0xbfc8f8b8UL, 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0xbff00000UL, + 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, + 0x3c75d28dUL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, 0x3fb37ca1UL, + 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, 0x3c672cedUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0xbfde2b5dUL, + 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, + 0x3fc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0xbff00000UL, 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, + 0xbfe44cf3UL, 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0xbff00000UL, + 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, + 0x3c8bdd34UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, 0xbfc133ccUL, + 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, 0x3c82c5e1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0xbfea9b66UL, + 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, + 0x3f9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0xbfe00000UL, 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, + 0xbfed906bUL, 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0xbfe00000UL, + 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, + 0xbc8760b1UL, 0x00000000UL, 0xbfd00000UL, 0x0e5967d5UL, 0x3fac1d1fUL, + 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, 0xbc756217UL, 0x00000000UL, + 0xbfd00000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0xbfefd88dUL, + 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0xbfc00000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, + 0xbfefd88dUL, 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0x3fc00000UL, + 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, + 0xbc756217UL, 0x00000000UL, 0x3fd00000UL, 0x76acf82dUL, 0x3fa4a031UL, + 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, 0xbc8760b1UL, 0x00000000UL, + 0x3fd00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0xbfed906bUL, + 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, + 0xbf9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0x3fe00000UL, 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, + 0xbfea9b66UL, 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0x3fe00000UL, + 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, + 0x3c82c5e1UL, 0x00000000UL, 0x3fe00000UL, 0x99fcef32UL, 0x3fca8279UL, + 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, 0x3c8bdd34UL, 0x00000000UL, + 0x3fe00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0xbfe44cf3UL, + 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, + 0xbfc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0x3ff00000UL, 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, + 0xbfde2b5dUL, 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0x3ff00000UL, + 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, + 0x3c672cedUL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, 0xbfa60beaUL, + 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, 0x3c75d28dUL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0xbfc8f8b8UL, + 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, + 0xbf73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0x3ff00000UL, 0x55555555UL, 0xbfc55555UL, 0x00000000UL, + 0xbfe00000UL, 0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL, + 0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL, 0xa556c734UL, + 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL, 0x1a600000UL, 0x3d90b461UL, + 0x1a600000UL, 0x3d90b461UL, 0x54400000UL, 0x3fb921fbUL, 0x00000000UL, + 0x00000000UL, 0x2e037073UL, 0x3b63198aUL, 0x00000000UL, 0x00000000UL, + 0x6dc9c883UL, 0x40245f30UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x43380000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x43600000UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x3c800000UL, 0x00000000UL, + 0x00000000UL, 0xffffffffUL, 0x3fefffffUL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x80000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x80000000UL, 0x00000000UL, 0x80000000UL, 0x00000000UL, 0x3fe00000UL, + 0x00000000UL, 0x3fe00000UL +}; + +void MacroAssembler::fast_sin(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ebx, Register edx) { + + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; + Label L_2TAG_PACKET_4_0_2, start; + assert_different_registers(eax, ebx, edx); + address static_const_table_sin = (address)_static_const_table_sin; + + bind(start); + subl(rsp, 120); + movl(Address(rsp, 56), ebx); + lea(ebx, ExternalAddress(static_const_table_sin)); + movsd(xmm0, Address(rsp, 128)); + pextrw(eax, xmm0, 3); + andl(eax, 32767); + subl(eax, 12336); + cmpl(eax, 4293); + jcc(Assembler::above, L_2TAG_PACKET_0_0_2); + movsd(xmm1, Address(ebx, 2160)); + mulsd(xmm1, xmm0); + movsd(xmm5, Address(ebx, 2272)); + movdqu(xmm4, Address(ebx, 2256)); + pand(xmm4, xmm0); + por(xmm5, xmm4); + movsd(xmm3, Address(ebx, 2128)); + movdqu(xmm2, Address(ebx, 2112)); + addpd(xmm1, xmm5); + cvttsd2sil(edx, xmm1); + cvtsi2sdl(xmm1, edx); + mulsd(xmm3, xmm1); + unpcklpd(xmm1, xmm1); + addl(edx, 1865216); + movdqu(xmm4, xmm0); + andl(edx, 63); + movdqu(xmm5, Address(ebx, 2096)); + lea(eax, Address(ebx, 0)); + shll(edx, 5); + addl(eax, edx); + mulpd(xmm2, xmm1); + subsd(xmm0, xmm3); + mulsd(xmm1, Address(ebx, 2144)); + subsd(xmm4, xmm3); + movsd(xmm7, Address(eax, 8)); + unpcklpd(xmm0, xmm0); + movapd(xmm3, xmm4); + subsd(xmm4, xmm2); + mulpd(xmm5, xmm0); + subpd(xmm0, xmm2); + movdqu(xmm6, Address(ebx, 2064)); + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm2); + movdqu(xmm2, Address(eax, 0)); + subsd(xmm1, xmm3); + movsd(xmm3, Address(eax, 24)); + addsd(xmm2, xmm3); + subsd(xmm7, xmm2); + mulsd(xmm2, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, Address(ebx, 2080)); + mulsd(xmm4, Address(eax, 0)); + addpd(xmm6, Address(ebx, 2048)); + mulpd(xmm5, xmm0); + movapd(xmm0, xmm3); + addsd(xmm3, Address(eax, 8)); + mulpd(xmm1, xmm7); + movapd(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movsd(xmm5, Address(eax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(eax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm5, xmm0); + addsd(xmm3, xmm7); + addsd(xmm1, xmm5); + addsd(xmm1, xmm3); + addsd(xmm1, xmm6); + unpckhpd(xmm6, xmm6); + addsd(xmm1, xmm6); + addsd(xmm4, xmm1); + movsd(Address(rsp, 0), xmm4); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_0_0_2); + jcc(Assembler::greater, L_2TAG_PACKET_2_0_2); + shrl(eax, 4); + cmpl(eax, 268434685); + jcc(Assembler::notEqual, L_2TAG_PACKET_3_0_2); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_3_0_2); + movsd(xmm3, Address(ebx, 2192)); + mulsd(xmm3, xmm0); + subsd(xmm3, xmm0); + mulsd(xmm3, Address(ebx, 2208)); + movsd(Address(rsp, 0), xmm0); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_2_0_2); + movl(eax, Address(rsp, 132)); + andl(eax, 2146435072); + cmpl(eax, 2146435072); + jcc(Assembler::equal, L_2TAG_PACKET_4_0_2); + subl(rsp, 32); + movsd(Address(rsp, 0), xmm0); + lea(eax, Address(rsp, 40)); + movl(Address(rsp, 8), eax); + movl(eax, 2); + movl(Address(rsp, 12), eax); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dlibm_sin_cos_huge()))); + addl(rsp, 32); + fld_d(Address(rsp, 16)); + jmp(L_2TAG_PACKET_1_0_2); + bind(L_2TAG_PACKET_4_0_2); + fld_d(Address(rsp, 128)); + fmul_d(Address(ebx, 2240)); + bind(L_2TAG_PACKET_1_0_2); + movl(ebx, Address(rsp, 56)); +} + +/******************************************************************************/ +// ALGORITHM DESCRIPTION - COS() +// --------------------- +// +// 1. RANGE REDUCTION +// +// We perform an initial range reduction from X to r with +// +// X =~= N * pi/32 + r +// +// so that |r| <= pi/64 + epsilon. We restrict inputs to those +// where |N| <= 932560. Beyond this, the range reduction is +// insufficiently accurate. For extremely small inputs, +// denormalization can occur internally, impacting performance. +// This means that the main path is actually only taken for +// 2^-252 <= |X| < 90112. +// +// To avoid branches, we perform the range reduction to full +// accuracy each time. +// +// X - N * (P_1 + P_2 + P_3) +// +// where P_1 and P_2 are 32-bit numbers (so multiplication by N +// is exact) and P_3 is a 53-bit number. Together, these +// approximate pi well enough for all cases in the restricted +// range. +// +// The main reduction sequence is: +// +// y = 32/pi * x +// N = integer(y) +// (computed by adding and subtracting off SHIFTER) +// +// m_1 = N * P_1 +// m_2 = N * P_2 +// r_1 = x - m_1 +// r = r_1 - m_2 +// (this r can be used for most of the calculation) +// +// c_1 = r_1 - r +// m_3 = N * P_3 +// c_2 = c_1 - m_2 +// c = c_2 - m_3 +// +// 2. MAIN ALGORITHM +// +// The algorithm uses a table lookup based on B = M * pi / 32 +// where M = N mod 64. The stored values are: +// sigma closest power of 2 to cos(B) +// C_hl 53-bit cos(B) - sigma +// S_hi + S_lo 2 * 53-bit sin(B) +// +// The computation is organized as follows: +// +// sin(B + r + c) = [sin(B) + sigma * r] + +// r * (cos(B) - sigma) + +// sin(B) * [cos(r + c) - 1] + +// cos(B) * [sin(r + c) - r] +// +// which is approximately: +// +// [S_hi + sigma * r] + +// C_hl * r + +// S_lo + S_hi * [(cos(r) - 1) - r * c] + +// (C_hl + sigma) * [(sin(r) - r) + c] +// +// and this is what is actually computed. We separate this sum +// into four parts: +// +// hi + med + pols + corr +// +// where +// +// hi = S_hi + sigma r +// med = C_hl * r +// pols = S_hi * (cos(r) - 1) + (C_hl + sigma) * (sin(r) - r) +// corr = S_lo + c * ((C_hl + sigma) - S_hi * r) +// +// 3. POLYNOMIAL +// +// The polynomial S_hi * (cos(r) - 1) + (C_hl + sigma) * +// (sin(r) - r) can be rearranged freely, since it is quite +// small, so we exploit parallelism to the fullest. +// +// psc4 = SC_4 * r_1 +// msc4 = psc4 * r +// r2 = r * r +// msc2 = SC_2 * r2 +// r4 = r2 * r2 +// psc3 = SC_3 + msc4 +// psc1 = SC_1 + msc2 +// msc3 = r4 * psc3 +// sincospols = psc1 + msc3 +// pols = sincospols * +// +// +// 4. CORRECTION TERM +// +// This is where the "c" component of the range reduction is +// taken into account; recall that just "r" is used for most of +// the calculation. +// +// -c = m_3 - c_2 +// -d = S_hi * r - (C_hl + sigma) +// corr = -c * -d + S_lo +// +// 5. COMPENSATED SUMMATIONS +// +// The two successive compensated summations add up the high +// and medium parts, leaving just the low parts to add up at +// the end. +// +// rs = sigma * r +// res_int = S_hi + rs +// k_0 = S_hi - res_int +// k_2 = k_0 + rs +// med = C_hl * r +// res_hi = res_int + med +// k_1 = res_int - res_hi +// k_3 = k_1 + med +// +// 6. FINAL SUMMATION +// +// We now add up all the small parts: +// +// res_lo = pols(hi) + pols(lo) + corr + k_1 + k_3 +// +// Now the overall result is just: +// +// res_hi + res_lo +// +// 7. SMALL ARGUMENTS +// +// Inputs with |X| < 2^-252 are treated specially as +// 1 - |x|. +// +// Special cases: +// cos(NaN) = quiet NaN, and raise invalid exception +// cos(INF) = NaN and raise invalid exception +// cos(0) = 1 +// +/******************************************************************************/ + +ALIGNED_(16) juint _static_const_table_cos[] = +{ + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, 0xbf73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, + 0xc0000000UL, 0xbc626d19UL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, + 0xbfa60beaUL, 0x2ed59f06UL, 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, + 0x00000000UL, 0x3ff00000UL, 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, + 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, 0x00000000UL, 0x3ff00000UL, + 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, 0x20000000UL, + 0x3c5e0d89UL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, 0xbfc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0x3ff00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, + 0x20000000UL, 0x3c68076aUL, 0x00000000UL, 0x3ff00000UL, 0x99fcef32UL, + 0x3fca8279UL, 0x667f3bcdUL, 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, + 0x00000000UL, 0x3fe00000UL, 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, + 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, 0x00000000UL, 0x3fe00000UL, + 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, 0xe0000000UL, + 0x3c39f630UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, 0xbf9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0x3fe00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0x3fed906bUL, + 0x20000000UL, 0x3c7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x76acf82dUL, + 0x3fa4a031UL, 0x56c62ddaUL, 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, + 0x00000000UL, 0x3fd00000UL, 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, + 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, 0x00000000UL, 0x3fd00000UL, + 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, 0x40000000UL, + 0xbc887df6UL, 0x00000000UL, 0x3fc00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, + 0x40000000UL, 0xbc887df6UL, 0x00000000UL, 0xbfc00000UL, 0x0e5967d5UL, + 0x3fac1d1fUL, 0xcff75cb0UL, 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, + 0x00000000UL, 0xbfd00000UL, 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, + 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, 0x00000000UL, 0xbfd00000UL, + 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, 0x3fed906bUL, 0x20000000UL, + 0x3c7457e6UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, 0x3f9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, + 0xe0000000UL, 0x3c39f630UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, + 0xbfc133ccUL, 0x6b151741UL, 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, + 0x00000000UL, 0xbfe00000UL, 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, + 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, 0x00000000UL, 0xbfe00000UL, + 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, 0x20000000UL, + 0x3c68076aUL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, 0x3fc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, + 0x20000000UL, 0x3c5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, + 0x3fb37ca1UL, 0xa6aea963UL, 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, + 0x00000000UL, 0xbff00000UL, 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, + 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, 0x00000000UL, 0xbff00000UL, + 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, 0xc0000000UL, + 0xbc626d19UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, 0x3f73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0xbff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, + 0x3f73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0xbff00000UL, 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, + 0xbfc8f8b8UL, 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0xbff00000UL, + 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, + 0x3c75d28dUL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, 0x3fb37ca1UL, + 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, 0x3c672cedUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0xbfde2b5dUL, + 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, + 0x3fc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0xbff00000UL, 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, + 0xbfe44cf3UL, 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0xbff00000UL, + 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, + 0x3c8bdd34UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, 0xbfc133ccUL, + 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, 0x3c82c5e1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0xbfea9b66UL, + 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, + 0x3f9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0xbfe00000UL, 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, + 0xbfed906bUL, 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0xbfe00000UL, + 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, + 0xbc8760b1UL, 0x00000000UL, 0xbfd00000UL, 0x0e5967d5UL, 0x3fac1d1fUL, + 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, 0xbc756217UL, 0x00000000UL, + 0xbfd00000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0xbfefd88dUL, + 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0xbfc00000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, + 0xbfefd88dUL, 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0x3fc00000UL, + 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, + 0xbc756217UL, 0x00000000UL, 0x3fd00000UL, 0x76acf82dUL, 0x3fa4a031UL, + 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, 0xbc8760b1UL, 0x00000000UL, + 0x3fd00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0xbfed906bUL, + 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, + 0xbf9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0x3fe00000UL, 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, + 0xbfea9b66UL, 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0x3fe00000UL, + 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, + 0x3c82c5e1UL, 0x00000000UL, 0x3fe00000UL, 0x99fcef32UL, 0x3fca8279UL, + 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, 0x3c8bdd34UL, 0x00000000UL, + 0x3fe00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0xbfe44cf3UL, + 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, + 0xbfc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0x3ff00000UL, 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, + 0xbfde2b5dUL, 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0x3ff00000UL, + 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, + 0x3c672cedUL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, 0xbfa60beaUL, + 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, 0x3c75d28dUL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0xbfc8f8b8UL, + 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, + 0xbf73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0x3ff00000UL, 0x55555555UL, 0xbfc55555UL, 0x00000000UL, + 0xbfe00000UL, 0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL, + 0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL, 0xa556c734UL, + 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL, 0x1a600000UL, 0x3d90b461UL, + 0x1a600000UL, 0x3d90b461UL, 0x54400000UL, 0x3fb921fbUL, 0x00000000UL, + 0x00000000UL, 0x2e037073UL, 0x3b63198aUL, 0x00000000UL, 0x00000000UL, + 0x6dc9c883UL, 0x40245f30UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x43380000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x3ff00000UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x80000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x80000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL +}; + +//registers, +// input: (rbp + 8) +// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 +// rax, rdx, rcx, rbx (tmp) + +// Code generated by Intel C compiler for LIBM library + +void MacroAssembler::fast_cos(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; + Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; + Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2, L_2TAG_PACKET_10_0_2, L_2TAG_PACKET_11_0_2; + Label L_2TAG_PACKET_12_0_2, L_2TAG_PACKET_13_0_2, B1_3, B1_5, start; + + assert_different_registers(tmp, eax, ecx, edx); + + address static_const_table_cos = (address)_static_const_table_cos; + + bind(start); + subl(rsp, 120); + movl(Address(rsp, 56), tmp); + lea(tmp, ExternalAddress(static_const_table_cos)); + movsd(xmm0, Address(rsp, 128)); + pextrw(eax, xmm0, 3); + andl(eax, 32767); + subl(eax, 12336); + cmpl(eax, 4293); + jcc(Assembler::above, L_2TAG_PACKET_0_0_2); + movsd(xmm1, Address(tmp, 2160)); + mulsd(xmm1, xmm0); + movdqu(xmm5, Address(tmp, 2240)); + movsd(xmm4, Address(tmp, 2224)); + pand(xmm4, xmm0); + por(xmm5, xmm4); + movsd(xmm3, Address(tmp, 2128)); + movdqu(xmm2, Address(tmp, 2112)); + addpd(xmm1, xmm5); + cvttsd2sil(edx, xmm1); + cvtsi2sdl(xmm1, edx); + mulsd(xmm3, xmm1); + unpcklpd(xmm1, xmm1); + addl(edx, 1865232); + movdqu(xmm4, xmm0); + andl(edx, 63); + movdqu(xmm5, Address(tmp, 2096)); + lea(eax, Address(tmp, 0)); + shll(edx, 5); + addl(eax, edx); + mulpd(xmm2, xmm1); + subsd(xmm0, xmm3); + mulsd(xmm1, Address(tmp, 2144)); + subsd(xmm4, xmm3); + movsd(xmm7, Address(eax, 8)); + unpcklpd(xmm0, xmm0); + movapd(xmm3, xmm4); + subsd(xmm4, xmm2); + mulpd(xmm5, xmm0); + subpd(xmm0, xmm2); + movdqu(xmm6, Address(tmp, 2064)); + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm2); + movdqu(xmm2, Address(eax, 0)); + subsd(xmm1, xmm3); + movsd(xmm3, Address(eax, 24)); + addsd(xmm2, xmm3); + subsd(xmm7, xmm2); + mulsd(xmm2, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, Address(tmp, 2080)); + mulsd(xmm4, Address(eax, 0)); + addpd(xmm6, Address(tmp, 2048)); + mulpd(xmm5, xmm0); + movapd(xmm0, xmm3); + addsd(xmm3, Address(eax, 8)); + mulpd(xmm1, xmm7); + movapd(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movsd(xmm5, Address(eax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(eax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm5, xmm0); + addsd(xmm3, xmm7); + addsd(xmm1, xmm5); + addsd(xmm1, xmm3); + addsd(xmm1, xmm6); + unpckhpd(xmm6, xmm6); + addsd(xmm1, xmm6); + addsd(xmm4, xmm1); + movsd(Address(rsp, 0), xmm4); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_0_0_2); + jcc(Assembler::greater, L_2TAG_PACKET_2_0_2); + pextrw(eax, xmm0, 3); + andl(eax, 32767); + pinsrw(xmm0, eax, 3); + movsd(xmm1, Address(tmp, 2192)); + subsd(xmm1, xmm0); + movsd(Address(rsp, 0), xmm1); + fld_d(Address(rsp, 0)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_2_0_2); + movl(eax, Address(rsp, 132)); + andl(eax, 2146435072); + cmpl(eax, 2146435072); + jcc(Assembler::equal, L_2TAG_PACKET_3_0_2); + subl(rsp, 32); + movsd(Address(rsp, 0), xmm0); + lea(eax, Address(rsp, 40)); + movl(Address(rsp, 8), eax); + movl(eax, 1); + movl(Address(rsp, 12), eax); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dlibm_sin_cos_huge()))); + addl(rsp, 32); + fld_d(Address(rsp, 8)); + jmp(L_2TAG_PACKET_1_0_2); + + bind(L_2TAG_PACKET_3_0_2); + fld_d(Address(rsp, 128)); + fmul_d(Address(tmp, 2208)); + + bind(L_2TAG_PACKET_1_0_2); + movl(tmp, Address(rsp, 56)); +} + diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86_libm.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_64.cpp similarity index 53% rename from hotspot/src/cpu/x86/vm/macroAssembler_x86_libm.cpp rename to hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_64.cpp index e94f1d7136f..f2f2211d88b 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86_libm.cpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_libm_x86_64.cpp @@ -35,7 +35,7 @@ #define ALIGNED_(x) __attribute__ ((aligned(x))) #endif -// The 32 bit and 64 bit code is at most SSE2 compliant +// The 64 bit code is at most SSE2 compliant /******************************************************************************/ // ALGORITHM DESCRIPTION - EXP() @@ -71,8 +71,6 @@ // /******************************************************************************/ -#ifdef _LP64 - ALIGNED_(16) juint _cv[] = { 0x652b82feUL, 0x40571547UL, 0x652b82feUL, 0x40571547UL, 0xfefa0000UL, @@ -411,273 +409,6 @@ void MacroAssembler::fast_exp(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xm addq(rsp, 24); } -#endif // _LP64 - -#ifndef _LP64 - -ALIGNED_(16) juint _static_const_table[] = -{ - 0x00000000UL, 0xfff00000UL, 0x00000000UL, 0xfff00000UL, 0xffffffc0UL, - 0x00000000UL, 0xffffffc0UL, 0x00000000UL, 0x0000ffc0UL, 0x00000000UL, - 0x0000ffc0UL, 0x00000000UL, 0x00000000UL, 0x43380000UL, 0x00000000UL, - 0x43380000UL, 0x652b82feUL, 0x40571547UL, 0x652b82feUL, 0x40571547UL, - 0xfefa0000UL, 0x3f862e42UL, 0xfefa0000UL, 0x3f862e42UL, 0xbc9e3b3aUL, - 0x3d1cf79aUL, 0xbc9e3b3aUL, 0x3d1cf79aUL, 0xfffffffeUL, 0x3fdfffffUL, - 0xfffffffeUL, 0x3fdfffffUL, 0xe3289860UL, 0x3f56c15cUL, 0x555b9e25UL, - 0x3fa55555UL, 0xc090cf0fUL, 0x3f811115UL, 0x55548ba1UL, 0x3fc55555UL, - 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0e03754dUL, - 0x3cad7bbfUL, 0x3e778060UL, 0x00002c9aUL, 0x3567f613UL, 0x3c8cd252UL, - 0xd3158574UL, 0x000059b0UL, 0x61e6c861UL, 0x3c60f74eUL, 0x18759bc8UL, - 0x00008745UL, 0x5d837b6cUL, 0x3c979aa6UL, 0x6cf9890fUL, 0x0000b558UL, - 0x702f9cd1UL, 0x3c3ebe3dUL, 0x32d3d1a2UL, 0x0000e3ecUL, 0x1e63bcd8UL, - 0x3ca3516eUL, 0xd0125b50UL, 0x00011301UL, 0x26f0387bUL, 0x3ca4c554UL, - 0xaea92ddfUL, 0x0001429aUL, 0x62523fb6UL, 0x3ca95153UL, 0x3c7d517aUL, - 0x000172b8UL, 0x3f1353bfUL, 0x3c8b898cUL, 0xeb6fcb75UL, 0x0001a35bUL, - 0x3e3a2f5fUL, 0x3c9aecf7UL, 0x3168b9aaUL, 0x0001d487UL, 0x44a6c38dUL, - 0x3c8a6f41UL, 0x88628cd6UL, 0x0002063bUL, 0xe3a8a894UL, 0x3c968efdUL, - 0x6e756238UL, 0x0002387aUL, 0x981fe7f2UL, 0x3c80472bUL, 0x65e27cddUL, - 0x00026b45UL, 0x6d09ab31UL, 0x3c82f7e1UL, 0xf51fdee1UL, 0x00029e9dUL, - 0x720c0ab3UL, 0x3c8b3782UL, 0xa6e4030bUL, 0x0002d285UL, 0x4db0abb6UL, - 0x3c834d75UL, 0x0a31b715UL, 0x000306feUL, 0x5dd3f84aUL, 0x3c8fdd39UL, - 0xb26416ffUL, 0x00033c08UL, 0xcc187d29UL, 0x3ca12f8cUL, 0x373aa9caUL, - 0x000371a7UL, 0x738b5e8bUL, 0x3ca7d229UL, 0x34e59ff6UL, 0x0003a7dbUL, - 0xa72a4c6dUL, 0x3c859f48UL, 0x4c123422UL, 0x0003dea6UL, 0x259d9205UL, - 0x3ca8b846UL, 0x21f72e29UL, 0x0004160aUL, 0x60c2ac12UL, 0x3c4363edUL, - 0x6061892dUL, 0x00044e08UL, 0xdaa10379UL, 0x3c6ecce1UL, 0xb5c13cd0UL, - 0x000486a2UL, 0xbb7aafb0UL, 0x3c7690ceUL, 0xd5362a27UL, 0x0004bfdaUL, - 0x9b282a09UL, 0x3ca083ccUL, 0x769d2ca6UL, 0x0004f9b2UL, 0xc1aae707UL, - 0x3ca509b0UL, 0x569d4f81UL, 0x0005342bUL, 0x18fdd78eUL, 0x3c933505UL, - 0x36b527daUL, 0x00056f47UL, 0xe21c5409UL, 0x3c9063e1UL, 0xdd485429UL, - 0x0005ab07UL, 0x2b64c035UL, 0x3c9432e6UL, 0x15ad2148UL, 0x0005e76fUL, - 0x99f08c0aUL, 0x3ca01284UL, 0xb03a5584UL, 0x0006247eUL, 0x0073dc06UL, - 0x3c99f087UL, 0x82552224UL, 0x00066238UL, 0x0da05571UL, 0x3c998d4dUL, - 0x667f3bccUL, 0x0006a09eUL, 0x86ce4786UL, 0x3ca52bb9UL, 0x3c651a2eUL, - 0x0006dfb2UL, 0x206f0dabUL, 0x3ca32092UL, 0xe8ec5f73UL, 0x00071f75UL, - 0x8e17a7a6UL, 0x3ca06122UL, 0x564267c8UL, 0x00075febUL, 0x461e9f86UL, - 0x3ca244acUL, 0x73eb0186UL, 0x0007a114UL, 0xabd66c55UL, 0x3c65ebe1UL, - 0x36cf4e62UL, 0x0007e2f3UL, 0xbbff67d0UL, 0x3c96fe9fUL, 0x994cce12UL, - 0x00082589UL, 0x14c801dfUL, 0x3c951f14UL, 0x9b4492ecUL, 0x000868d9UL, - 0xc1f0eab4UL, 0x3c8db72fUL, 0x422aa0dbUL, 0x0008ace5UL, 0x59f35f44UL, - 0x3c7bf683UL, 0x99157736UL, 0x0008f1aeUL, 0x9c06283cUL, 0x3ca360baUL, - 0xb0cdc5e4UL, 0x00093737UL, 0x20f962aaUL, 0x3c95e8d1UL, 0x9fde4e4fUL, - 0x00097d82UL, 0x2b91ce27UL, 0x3c71affcUL, 0x82a3f090UL, 0x0009c491UL, - 0x589a2ebdUL, 0x3c9b6d34UL, 0x7b5de564UL, 0x000a0c66UL, 0x9ab89880UL, - 0x3c95277cUL, 0xb23e255cUL, 0x000a5503UL, 0x6e735ab3UL, 0x3c846984UL, - 0x5579fdbfUL, 0x000a9e6bUL, 0x92cb3387UL, 0x3c8c1a77UL, 0x995ad3adUL, - 0x000ae89fUL, 0xdc2d1d96UL, 0x3ca22466UL, 0xb84f15faUL, 0x000b33a2UL, - 0xb19505aeUL, 0x3ca1112eUL, 0xf2fb5e46UL, 0x000b7f76UL, 0x0a5fddcdUL, - 0x3c74ffd7UL, 0x904bc1d2UL, 0x000bcc1eUL, 0x30af0cb3UL, 0x3c736eaeUL, - 0xdd85529cUL, 0x000c199bUL, 0xd10959acUL, 0x3c84e08fUL, 0x2e57d14bUL, - 0x000c67f1UL, 0x6c921968UL, 0x3c676b2cUL, 0xdcef9069UL, 0x000cb720UL, - 0x36df99b3UL, 0x3c937009UL, 0x4a07897bUL, 0x000d072dUL, 0xa63d07a7UL, - 0x3c74a385UL, 0xdcfba487UL, 0x000d5818UL, 0xd5c192acUL, 0x3c8e5a50UL, - 0x03db3285UL, 0x000da9e6UL, 0x1c4a9792UL, 0x3c98bb73UL, 0x337b9b5eUL, - 0x000dfc97UL, 0x603a88d3UL, 0x3c74b604UL, 0xe78b3ff6UL, 0x000e502eUL, - 0x92094926UL, 0x3c916f27UL, 0xa2a490d9UL, 0x000ea4afUL, 0x41aa2008UL, - 0x3c8ec3bcUL, 0xee615a27UL, 0x000efa1bUL, 0x31d185eeUL, 0x3c8a64a9UL, - 0x5b6e4540UL, 0x000f5076UL, 0x4d91cd9dUL, 0x3c77893bUL, 0x819e90d8UL, - 0x000fa7c1UL, 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x7ff00000UL, - 0x00000000UL, 0x00000000UL, 0xffffffffUL, 0x7fefffffUL, 0x00000000UL, - 0x00100000UL -}; - -//registers, -// input: (rbp + 8) -// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 -// rax, rdx, rcx, rbx (tmp) - -// Code generated by Intel C compiler for LIBM library - -void MacroAssembler::fast_exp(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { - Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; - Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; - Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2, L_2TAG_PACKET_10_0_2, L_2TAG_PACKET_11_0_2; - Label L_2TAG_PACKET_12_0_2, L_2TAG_PACKET_13_0_2, B1_3, B1_5, start; - - assert_different_registers(tmp, eax, ecx, edx); - jmp(start); - address static_const_table = (address)_static_const_table; - - bind(start); - subl(rsp, 120); - movl(Address(rsp, 64), tmp); - lea(tmp, ExternalAddress(static_const_table)); - movdqu(xmm0, Address(rsp, 128)); - unpcklpd(xmm0, xmm0); - movdqu(xmm1, Address(tmp, 64)); // 0x652b82feUL, 0x40571547UL, 0x652b82feUL, 0x40571547UL - movdqu(xmm6, Address(tmp, 48)); // 0x00000000UL, 0x43380000UL, 0x00000000UL, 0x43380000UL - movdqu(xmm2, Address(tmp, 80)); // 0xfefa0000UL, 0x3f862e42UL, 0xfefa0000UL, 0x3f862e42UL - movdqu(xmm3, Address(tmp, 96)); // 0xbc9e3b3aUL, 0x3d1cf79aUL, 0xbc9e3b3aUL, 0x3d1cf79aUL - pextrw(eax, xmm0, 3); - andl(eax, 32767); - movl(edx, 16527); - subl(edx, eax); - subl(eax, 15504); - orl(edx, eax); - cmpl(edx, INT_MIN); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); - mulpd(xmm1, xmm0); - addpd(xmm1, xmm6); - movapd(xmm7, xmm1); - subpd(xmm1, xmm6); - mulpd(xmm2, xmm1); - movdqu(xmm4, Address(tmp, 128)); // 0xe3289860UL, 0x3f56c15cUL, 0x555b9e25UL, 0x3fa55555UL - mulpd(xmm3, xmm1); - movdqu(xmm5, Address(tmp, 144)); // 0xc090cf0fUL, 0x3f811115UL, 0x55548ba1UL, 0x3fc55555UL - subpd(xmm0, xmm2); - movdl(eax, xmm7); - movl(ecx, eax); - andl(ecx, 63); - shll(ecx, 4); - sarl(eax, 6); - movl(edx, eax); - movdqu(xmm6, Address(tmp, 16)); // 0xffffffc0UL, 0x00000000UL, 0xffffffc0UL, 0x00000000UL - pand(xmm7, xmm6); - movdqu(xmm6, Address(tmp, 32)); // 0x0000ffc0UL, 0x00000000UL, 0x0000ffc0UL, 0x00000000UL - paddq(xmm7, xmm6); - psllq(xmm7, 46); - subpd(xmm0, xmm3); - movdqu(xmm2, Address(tmp, ecx, Address::times_1, 160)); - mulpd(xmm4, xmm0); - movapd(xmm6, xmm0); - movapd(xmm1, xmm0); - mulpd(xmm6, xmm6); - mulpd(xmm0, xmm6); - addpd(xmm5, xmm4); - mulsd(xmm0, xmm6); - mulpd(xmm6, Address(tmp, 112)); // 0xfffffffeUL, 0x3fdfffffUL, 0xfffffffeUL, 0x3fdfffffUL - addsd(xmm1, xmm2); - unpckhpd(xmm2, xmm2); - mulpd(xmm0, xmm5); - addsd(xmm1, xmm0); - por(xmm2, xmm7); - unpckhpd(xmm0, xmm0); - addsd(xmm0, xmm1); - addsd(xmm0, xmm6); - addl(edx, 894); - cmpl(edx, 1916); - jcc (Assembler::above, L_2TAG_PACKET_1_0_2); - mulsd(xmm0, xmm2); - addsd(xmm0, xmm2); - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_1_0_2); - fnstcw(Address(rsp, 24)); - movzwl(edx, Address(rsp, 24)); - orl(edx, 768); - movw(Address(rsp, 28), edx); - fldcw(Address(rsp, 28)); - movl(edx, eax); - sarl(eax, 1); - subl(edx, eax); - movdqu(xmm6, Address(tmp, 0)); // 0x00000000UL, 0xfff00000UL, 0x00000000UL, 0xfff00000UL - pandn(xmm6, xmm2); - addl(eax, 1023); - movdl(xmm3, eax); - psllq(xmm3, 52); - por(xmm6, xmm3); - addl(edx, 1023); - movdl(xmm4, edx); - psllq(xmm4, 52); - movsd(Address(rsp, 8), xmm0); - fld_d(Address(rsp, 8)); - movsd(Address(rsp, 16), xmm6); - fld_d(Address(rsp, 16)); - fmula(1); - faddp(1); - movsd(Address(rsp, 8), xmm4); - fld_d(Address(rsp, 8)); - fmulp(1); - fstp_d(Address(rsp, 8)); - movsd(xmm0,Address(rsp, 8)); - fldcw(Address(rsp, 24)); - pextrw(ecx, xmm0, 3); - andl(ecx, 32752); - cmpl(ecx, 32752); - jcc(Assembler::greaterEqual, L_2TAG_PACKET_3_0_2); - cmpl(ecx, 0); - jcc(Assembler::equal, L_2TAG_PACKET_4_0_2); - jmp(L_2TAG_PACKET_2_0_2); - cmpl(ecx, INT_MIN); - jcc(Assembler::less, L_2TAG_PACKET_3_0_2); - cmpl(ecx, -1064950997); - jcc(Assembler::less, L_2TAG_PACKET_2_0_2); - jcc(Assembler::greater, L_2TAG_PACKET_4_0_2); - movl(edx, Address(rsp, 128)); - cmpl(edx ,-17155601); - jcc(Assembler::less, L_2TAG_PACKET_2_0_2); - jmp(L_2TAG_PACKET_4_0_2); - - bind(L_2TAG_PACKET_3_0_2); - movl(edx, 14); - jmp(L_2TAG_PACKET_5_0_2); - - bind(L_2TAG_PACKET_4_0_2); - movl(edx, 15); - - bind(L_2TAG_PACKET_5_0_2); - movsd(Address(rsp, 0), xmm0); - movsd(xmm0, Address(rsp, 128)); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); - - bind(L_2TAG_PACKET_7_0_2); - cmpl(eax, 2146435072); - jcc(Assembler::greaterEqual, L_2TAG_PACKET_8_0_2); - movl(eax, Address(rsp, 132)); - cmpl(eax, INT_MIN); - jcc(Assembler::greaterEqual, L_2TAG_PACKET_9_0_2); - movsd(xmm0, Address(tmp, 1208)); // 0xffffffffUL, 0x7fefffffUL - mulsd(xmm0, xmm0); - movl(edx, 14); - jmp(L_2TAG_PACKET_5_0_2); - - bind(L_2TAG_PACKET_9_0_2); - movsd(xmm0, Address(tmp, 1216)); - mulsd(xmm0, xmm0); - movl(edx, 15); - jmp(L_2TAG_PACKET_5_0_2); - - bind(L_2TAG_PACKET_8_0_2); - movl(edx, Address(rsp, 128)); - cmpl(eax, 2146435072); - jcc(Assembler::above, L_2TAG_PACKET_10_0_2); - cmpl(edx, 0); - jcc(Assembler::notEqual, L_2TAG_PACKET_10_0_2); - movl(eax, Address(rsp, 132)); - cmpl(eax, 2146435072); - jcc(Assembler::notEqual, L_2TAG_PACKET_11_0_2); - movsd(xmm0, Address(tmp, 1192)); // 0x00000000UL, 0x7ff00000UL - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_11_0_2); - movsd(xmm0, Address(tmp, 1200)); // 0x00000000UL, 0x00000000UL - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_10_0_2); - movsd(xmm0, Address(rsp, 128)); - addsd(xmm0, xmm0); - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_0_0_2); - movl(eax, Address(rsp, 132)); - andl(eax, 2147483647); - cmpl(eax, 1083179008); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_7_0_2); - movsd(xmm0, Address(rsp, 128)); - addsd(xmm0, Address(tmp, 1184)); // 0x00000000UL, 0x3ff00000UL - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_2_0_2); - movsd(Address(rsp, 48), xmm0); - fld_d(Address(rsp, 48)); - - bind(L_2TAG_PACKET_6_0_2); - movl(tmp, Address(rsp, 64)); -} - -#endif // !_LP64 - /******************************************************************************/ // ALGORITHM DESCRIPTION - LOG() // --------------------- @@ -703,8 +434,6 @@ void MacroAssembler::fast_exp(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xm // /******************************************************************************/ -#ifdef _LP64 - ALIGNED_(16) juint _L_tbl[] = { 0xfefa3800UL, 0x3fe62e42UL, 0x93c76730UL, 0x3d2ef357UL, 0xaa241800UL, @@ -1007,298 +736,6 @@ void MacroAssembler::fast_log(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xm addq(rsp, 24); } -#endif // _LP64 - -#ifndef _LP64 - -ALIGNED_(16) juint _static_const_table_log[] = -{ - 0xfefa3800UL, 0x3fe62e42UL, 0x93c76730UL, 0x3d2ef357UL, 0xaa241800UL, - 0x3fe5ee82UL, 0x0cda46beUL, 0x3d220238UL, 0x5c364800UL, 0x3fe5af40UL, - 0xac10c9fbUL, 0x3d2dfa63UL, 0x26bb8c00UL, 0x3fe5707aUL, 0xff3303ddUL, - 0x3d09980bUL, 0x26867800UL, 0x3fe5322eUL, 0x5d257531UL, 0x3d05ccc4UL, - 0x835a5000UL, 0x3fe4f45aUL, 0x6d93b8fbUL, 0xbd2e6c51UL, 0x6f970c00UL, - 0x3fe4b6fdUL, 0xed4c541cUL, 0x3cef7115UL, 0x27e8a400UL, 0x3fe47a15UL, - 0xf94d60aaUL, 0xbd22cb6aUL, 0xf2f92400UL, 0x3fe43d9fUL, 0x481051f7UL, - 0xbcfd984fUL, 0x2125cc00UL, 0x3fe4019cUL, 0x30f0c74cUL, 0xbd26ce79UL, - 0x0c36c000UL, 0x3fe3c608UL, 0x7cfe13c2UL, 0xbd02b736UL, 0x17197800UL, - 0x3fe38ae2UL, 0xbb5569a4UL, 0xbd218b7aUL, 0xad9d8c00UL, 0x3fe35028UL, - 0x9527e6acUL, 0x3d10b83fUL, 0x44340800UL, 0x3fe315daUL, 0xc5a0ed9cUL, - 0xbd274e93UL, 0x57b0e000UL, 0x3fe2dbf5UL, 0x07b9dc11UL, 0xbd17a6e5UL, - 0x6d0ec000UL, 0x3fe2a278UL, 0xe797882dUL, 0x3d206d2bUL, 0x1134dc00UL, - 0x3fe26962UL, 0x05226250UL, 0xbd0b61f1UL, 0xd8bebc00UL, 0x3fe230b0UL, - 0x6e48667bUL, 0x3d12fc06UL, 0x5fc61800UL, 0x3fe1f863UL, 0xc9fe81d3UL, - 0xbd2a7242UL, 0x49ae6000UL, 0x3fe1c078UL, 0xed70e667UL, 0x3cccacdeUL, - 0x40f23c00UL, 0x3fe188eeUL, 0xf8ab4650UL, 0x3d14cc4eUL, 0xf6f29800UL, - 0x3fe151c3UL, 0xa293ae49UL, 0xbd2edd97UL, 0x23c75c00UL, 0x3fe11af8UL, - 0xbb9ddcb2UL, 0xbd258647UL, 0x8611cc00UL, 0x3fe0e489UL, 0x07801742UL, - 0x3d1c2998UL, 0xe2d05400UL, 0x3fe0ae76UL, 0x887e7e27UL, 0x3d1f486bUL, - 0x0533c400UL, 0x3fe078bfUL, 0x41edf5fdUL, 0x3d268122UL, 0xbe760400UL, - 0x3fe04360UL, 0xe79539e0UL, 0xbd04c45fUL, 0xe5b20800UL, 0x3fe00e5aUL, - 0xb1727b1cUL, 0xbd053ba3UL, 0xaf7a4800UL, 0x3fdfb358UL, 0x3c164935UL, - 0x3d0085faUL, 0xee031800UL, 0x3fdf4aa7UL, 0x6f014a8bUL, 0x3d12cde5UL, - 0x56b41000UL, 0x3fdee2a1UL, 0x5a470251UL, 0x3d2f27f4UL, 0xc3ddb000UL, - 0x3fde7b42UL, 0x5372bd08UL, 0xbd246550UL, 0x1a272800UL, 0x3fde148aUL, - 0x07322938UL, 0xbd1326b2UL, 0x484c9800UL, 0x3fddae75UL, 0x60dc616aUL, - 0xbd1ea42dUL, 0x46def800UL, 0x3fdd4902UL, 0xe9a767a8UL, 0x3d235bafUL, - 0x18064800UL, 0x3fdce42fUL, 0x3ec7a6b0UL, 0xbd0797c3UL, 0xc7455800UL, - 0x3fdc7ff9UL, 0xc15249aeUL, 0xbd29b6ddUL, 0x693fa000UL, 0x3fdc1c60UL, - 0x7fe8e180UL, 0x3d2cec80UL, 0x1b80e000UL, 0x3fdbb961UL, 0xf40a666dUL, - 0x3d27d85bUL, 0x04462800UL, 0x3fdb56faUL, 0x2d841995UL, 0x3d109525UL, - 0x5248d000UL, 0x3fdaf529UL, 0x52774458UL, 0xbd217cc5UL, 0x3c8ad800UL, - 0x3fda93edUL, 0xbea77a5dUL, 0x3d1e36f2UL, 0x0224f800UL, 0x3fda3344UL, - 0x7f9d79f5UL, 0x3d23c645UL, 0xea15f000UL, 0x3fd9d32bUL, 0x10d0c0b0UL, - 0xbd26279eUL, 0x43135800UL, 0x3fd973a3UL, 0xa502d9f0UL, 0xbd152313UL, - 0x635bf800UL, 0x3fd914a8UL, 0x2ee6307dUL, 0xbd1766b5UL, 0xa88b3000UL, - 0x3fd8b639UL, 0xe5e70470UL, 0xbd205ae1UL, 0x776dc800UL, 0x3fd85855UL, - 0x3333778aUL, 0x3d2fd56fUL, 0x3bd81800UL, 0x3fd7fafaUL, 0xc812566aUL, - 0xbd272090UL, 0x687cf800UL, 0x3fd79e26UL, 0x2efd1778UL, 0x3d29ec7dUL, - 0x76c67800UL, 0x3fd741d8UL, 0x49dc60b3UL, 0x3d2d8b09UL, 0xe6af1800UL, - 0x3fd6e60eUL, 0x7c222d87UL, 0x3d172165UL, 0x3e9c6800UL, 0x3fd68ac8UL, - 0x2756eba0UL, 0x3d20a0d3UL, 0x0b3ab000UL, 0x3fd63003UL, 0xe731ae00UL, - 0xbd2db623UL, 0xdf596000UL, 0x3fd5d5bdUL, 0x08a465dcUL, 0xbd0a0b2aUL, - 0x53c8d000UL, 0x3fd57bf7UL, 0xee5d40efUL, 0x3d1fadedUL, 0x0738a000UL, - 0x3fd522aeUL, 0x8164c759UL, 0x3d2ebe70UL, 0x9e173000UL, 0x3fd4c9e0UL, - 0x1b0ad8a4UL, 0xbd2e2089UL, 0xc271c800UL, 0x3fd4718dUL, 0x0967d675UL, - 0xbd2f27ceUL, 0x23d5e800UL, 0x3fd419b4UL, 0xec90e09dUL, 0x3d08e436UL, - 0x77333000UL, 0x3fd3c252UL, 0xb606bd5cUL, 0x3d183b54UL, 0x76be1000UL, - 0x3fd36b67UL, 0xb0f177c8UL, 0x3d116ecdUL, 0xe1d36000UL, 0x3fd314f1UL, - 0xd3213cb8UL, 0xbd28e27aUL, 0x7cdc9000UL, 0x3fd2bef0UL, 0x4a5004f4UL, - 0x3d2a9cfaUL, 0x1134d800UL, 0x3fd26962UL, 0xdf5bb3b6UL, 0x3d2c93c1UL, - 0x6d0eb800UL, 0x3fd21445UL, 0xba46baeaUL, 0x3d0a87deUL, 0x635a6800UL, - 0x3fd1bf99UL, 0x5147bdb7UL, 0x3d2ca6edUL, 0xcbacf800UL, 0x3fd16b5cUL, - 0xf7a51681UL, 0x3d2b9acdUL, 0x8227e800UL, 0x3fd1178eUL, 0x63a5f01cUL, - 0xbd2c210eUL, 0x67616000UL, 0x3fd0c42dUL, 0x163ceae9UL, 0x3d27188bUL, - 0x604d5800UL, 0x3fd07138UL, 0x16ed4e91UL, 0x3cf89cdbUL, 0x5626c800UL, - 0x3fd01eaeUL, 0x1485e94aUL, 0xbd16f08cUL, 0x6cb3b000UL, 0x3fcf991cUL, - 0xca0cdf30UL, 0x3d1bcbecUL, 0xe4dd0000UL, 0x3fcef5adUL, 0x65bb8e11UL, - 0xbcca2115UL, 0xffe71000UL, 0x3fce530eUL, 0x6041f430UL, 0x3cc21227UL, - 0xb0d49000UL, 0x3fcdb13dUL, 0xf715b035UL, 0xbd2aff2aUL, 0xf2656000UL, - 0x3fcd1037UL, 0x75b6f6e4UL, 0xbd084a7eUL, 0xc6f01000UL, 0x3fcc6ffbUL, - 0xc5962bd2UL, 0xbcf1ec72UL, 0x383be000UL, 0x3fcbd087UL, 0x595412b6UL, - 0xbd2d4bc4UL, 0x575bd000UL, 0x3fcb31d8UL, 0x4eace1aaUL, 0xbd0c358dUL, - 0x3c8ae000UL, 0x3fca93edUL, 0x50562169UL, 0xbd287243UL, 0x07089000UL, - 0x3fc9f6c4UL, 0x6865817aUL, 0x3d29904dUL, 0xdcf70000UL, 0x3fc95a5aUL, - 0x58a0ff6fUL, 0x3d07f228UL, 0xeb390000UL, 0x3fc8beafUL, 0xaae92cd1UL, - 0xbd073d54UL, 0x6551a000UL, 0x3fc823c1UL, 0x9a631e83UL, 0x3d1e0ddbUL, - 0x85445000UL, 0x3fc7898dUL, 0x70914305UL, 0xbd1c6610UL, 0x8b757000UL, - 0x3fc6f012UL, 0xe59c21e1UL, 0xbd25118dUL, 0xbe8c1000UL, 0x3fc6574eUL, - 0x2c3c2e78UL, 0x3d19cf8bUL, 0x6b544000UL, 0x3fc5bf40UL, 0xeb68981cUL, - 0xbd127023UL, 0xe4a1b000UL, 0x3fc527e5UL, 0xe5697dc7UL, 0x3d2633e8UL, - 0x8333b000UL, 0x3fc4913dUL, 0x54fdb678UL, 0x3d258379UL, 0xa5993000UL, - 0x3fc3fb45UL, 0x7e6a354dUL, 0xbd2cd1d8UL, 0xb0159000UL, 0x3fc365fcUL, - 0x234b7289UL, 0x3cc62fa8UL, 0x0c868000UL, 0x3fc2d161UL, 0xcb81b4a1UL, - 0x3d039d6cUL, 0x2a49c000UL, 0x3fc23d71UL, 0x8fd3df5cUL, 0x3d100d23UL, - 0x7e23f000UL, 0x3fc1aa2bUL, 0x44389934UL, 0x3d2ca78eUL, 0x8227e000UL, - 0x3fc1178eUL, 0xce2d07f2UL, 0x3d21ef78UL, 0xb59e4000UL, 0x3fc08598UL, - 0x7009902cUL, 0xbd27e5ddUL, 0x39dbe000UL, 0x3fbfe891UL, 0x4fa10afdUL, - 0xbd2534d6UL, 0x830a2000UL, 0x3fbec739UL, 0xafe645e0UL, 0xbd2dc068UL, - 0x63844000UL, 0x3fbda727UL, 0x1fa71733UL, 0x3d1a8940UL, 0x01bc4000UL, - 0x3fbc8858UL, 0xc65aacd3UL, 0x3d2646d1UL, 0x8dad6000UL, 0x3fbb6ac8UL, - 0x2bf768e5UL, 0xbd139080UL, 0x40b1c000UL, 0x3fba4e76UL, 0xb94407c8UL, - 0xbd0e42b6UL, 0x5d594000UL, 0x3fb9335eUL, 0x3abd47daUL, 0x3d23115cUL, - 0x2f40e000UL, 0x3fb8197eUL, 0xf96ffdf7UL, 0x3d0f80dcUL, 0x0aeac000UL, - 0x3fb700d3UL, 0xa99ded32UL, 0x3cec1e8dUL, 0x4d97a000UL, 0x3fb5e95aUL, - 0x3c5d1d1eUL, 0xbd2c6906UL, 0x5d208000UL, 0x3fb4d311UL, 0x82f4e1efUL, - 0xbcf53a25UL, 0xa7d1e000UL, 0x3fb3bdf5UL, 0xa5db4ed7UL, 0x3d2cc85eUL, - 0xa4472000UL, 0x3fb2aa04UL, 0xae9c697dUL, 0xbd20b6e8UL, 0xd1466000UL, - 0x3fb1973bUL, 0x560d9e9bUL, 0xbd25325dUL, 0xb59e4000UL, 0x3fb08598UL, - 0x7009902cUL, 0xbd17e5ddUL, 0xc006c000UL, 0x3faeea31UL, 0x4fc93b7bUL, - 0xbd0e113eUL, 0xcdddc000UL, 0x3faccb73UL, 0x47d82807UL, 0xbd1a68f2UL, - 0xd0fb0000UL, 0x3faaaef2UL, 0x353bb42eUL, 0x3d20fc1aUL, 0x149fc000UL, - 0x3fa894aaUL, 0xd05a267dUL, 0xbd197995UL, 0xf2d4c000UL, 0x3fa67c94UL, - 0xec19afa2UL, 0xbd029efbUL, 0xd42e0000UL, 0x3fa466aeUL, 0x75bdfd28UL, - 0xbd2c1673UL, 0x2f8d0000UL, 0x3fa252f3UL, 0xe021b67bUL, 0x3d283e9aUL, - 0x89e74000UL, 0x3fa0415dUL, 0x5cf1d753UL, 0x3d0111c0UL, 0xec148000UL, - 0x3f9c63d2UL, 0x3f9eb2f3UL, 0x3d2578c6UL, 0x28c90000UL, 0x3f984925UL, - 0x325a0c34UL, 0xbd2aa0baUL, 0x25980000UL, 0x3f9432a9UL, 0x928637feUL, - 0x3d098139UL, 0x58938000UL, 0x3f902056UL, 0x06e2f7d2UL, 0xbd23dc5bUL, - 0xa3890000UL, 0x3f882448UL, 0xda74f640UL, 0xbd275577UL, 0x75890000UL, - 0x3f801015UL, 0x999d2be8UL, 0xbd10c76bUL, 0x59580000UL, 0x3f700805UL, - 0xcb31c67bUL, 0x3d2166afUL, 0x00000000UL, 0x00000000UL, 0x00000000UL, - 0x80000000UL, 0xfefa3800UL, 0x3fa62e42UL, 0x93c76730UL, 0x3ceef357UL, - 0x92492492UL, 0x3fc24924UL, 0x00000000UL, 0xbfd00000UL, 0x3d6fb175UL, - 0xbfc5555eUL, 0x55555555UL, 0x3fd55555UL, 0x9999999aUL, 0x3fc99999UL, - 0x00000000UL, 0xbfe00000UL, 0x00000000UL, 0xffffe000UL, 0x00000000UL, - 0xffffe000UL -}; -//registers, -// input: xmm0 -// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 -// rax, rdx, rcx, rbx (tmp) - -void MacroAssembler::fast_log(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { - Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; - Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; - Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2; - Label L_2TAG_PACKET_10_0_2, start; - - assert_different_registers(tmp, eax, ecx, edx); - jmp(start); - address static_const_table = (address)_static_const_table_log; - - bind(start); - subl(rsp, 104); - movl(Address(rsp, 40), tmp); - lea(tmp, ExternalAddress(static_const_table)); - xorpd(xmm2, xmm2); - movl(eax, 16368); - pinsrw(xmm2, eax, 3); - xorpd(xmm3, xmm3); - movl(edx, 30704); - pinsrw(xmm3, edx, 3); - movsd(xmm0, Address(rsp, 112)); - movapd(xmm1, xmm0); - movl(ecx, 32768); - movdl(xmm4, ecx); - movsd(xmm5, Address(tmp, 2128)); // 0x00000000UL, 0xffffe000UL - pextrw(eax, xmm0, 3); - por(xmm0, xmm2); - psllq(xmm0, 5); - movl(ecx, 16352); - psrlq(xmm0, 34); - rcpss(xmm0, xmm0); - psllq(xmm1, 12); - pshufd(xmm6, xmm5, 228); - psrlq(xmm1, 12); - subl(eax, 16); - cmpl(eax, 32736); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); - - bind(L_2TAG_PACKET_1_0_2); - paddd(xmm0, xmm4); - por(xmm1, xmm3); - movdl(edx, xmm0); - psllq(xmm0, 29); - pand(xmm5, xmm1); - pand(xmm0, xmm6); - subsd(xmm1, xmm5); - mulpd(xmm5, xmm0); - andl(eax, 32752); - subl(eax, ecx); - cvtsi2sdl(xmm7, eax); - mulsd(xmm1, xmm0); - movsd(xmm6, Address(tmp, 2064)); // 0xfefa3800UL, 0x3fa62e42UL - movdqu(xmm3, Address(tmp, 2080)); // 0x92492492UL, 0x3fc24924UL, 0x00000000UL, 0xbfd00000UL - subsd(xmm5, xmm2); - andl(edx, 16711680); - shrl(edx, 12); - movdqu(xmm0, Address(tmp, edx)); - movdqu(xmm4, Address(tmp, 2096)); // 0x3d6fb175UL, 0xbfc5555eUL, 0x55555555UL, 0x3fd55555UL - addsd(xmm1, xmm5); - movdqu(xmm2, Address(tmp, 2112)); // 0x9999999aUL, 0x3fc99999UL, 0x00000000UL, 0xbfe00000UL - mulsd(xmm6, xmm7); - pshufd(xmm5, xmm1, 68); - mulsd(xmm7, Address(tmp, 2072)); // 0x93c76730UL, 0x3ceef357UL, 0x92492492UL, 0x3fc24924UL - mulsd(xmm3, xmm1); - addsd(xmm0, xmm6); - mulpd(xmm4, xmm5); - mulpd(xmm5, xmm5); - pshufd(xmm6, xmm0, 228); - addsd(xmm0, xmm1); - addpd(xmm4, xmm2); - mulpd(xmm3, xmm5); - subsd(xmm6, xmm0); - mulsd(xmm4, xmm1); - pshufd(xmm2, xmm0, 238); - addsd(xmm1, xmm6); - mulsd(xmm5, xmm5); - addsd(xmm7, xmm2); - addpd(xmm4, xmm3); - addsd(xmm1, xmm7); - mulpd(xmm4, xmm5); - addsd(xmm1, xmm4); - pshufd(xmm5, xmm4, 238); - addsd(xmm1, xmm5); - addsd(xmm0, xmm1); - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_0_0_2); - movsd(xmm0, Address(rsp, 112)); - movdqu(xmm1, xmm0); - addl(eax, 16); - cmpl(eax, 32768); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_3_0_2); - cmpl(eax, 16); - jcc(Assembler::below, L_2TAG_PACKET_4_0_2); - - bind(L_2TAG_PACKET_5_0_2); - addsd(xmm0, xmm0); - jmp(L_2TAG_PACKET_2_0_2); - - bind(L_2TAG_PACKET_6_0_2); - jcc(Assembler::above, L_2TAG_PACKET_5_0_2); - cmpl(edx, 0); - jcc(Assembler::above, L_2TAG_PACKET_5_0_2); - jmp(L_2TAG_PACKET_7_0_2); - - bind(L_2TAG_PACKET_3_0_2); - movdl(edx, xmm1); - psrlq(xmm1, 32); - movdl(ecx, xmm1); - addl(ecx, ecx); - cmpl(ecx, -2097152); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_6_0_2); - orl(edx, ecx); - cmpl(edx, 0); - jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); - - bind(L_2TAG_PACKET_7_0_2); - xorpd(xmm1, xmm1); - xorpd(xmm0, xmm0); - movl(eax, 32752); - pinsrw(xmm1, eax, 3); - movl(edx, 3); - mulsd(xmm0, xmm1); - - bind(L_2TAG_PACKET_9_0_2); - movsd(Address(rsp, 0), xmm0); - movsd(xmm0, Address(rsp, 112)); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_10_0_2); - - bind(L_2TAG_PACKET_8_0_2); - xorpd(xmm1, xmm1); - xorpd(xmm0, xmm0); - movl(eax, 49136); - pinsrw(xmm0, eax, 3); - divsd(xmm0, xmm1); - movl(edx, 2); - jmp(L_2TAG_PACKET_9_0_2); - - bind(L_2TAG_PACKET_4_0_2); - movdl(edx, xmm1); - psrlq(xmm1, 32); - movdl(ecx, xmm1); - orl(edx, ecx); - cmpl(edx, 0); - jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); - xorpd(xmm1, xmm1); - movl(eax, 18416); - pinsrw(xmm1, eax, 3); - mulsd(xmm0, xmm1); - movapd(xmm1, xmm0); - pextrw(eax, xmm0, 3); - por(xmm0, xmm2); - psllq(xmm0, 5); - movl(ecx, 18416); - psrlq(xmm0, 34); - rcpss(xmm0, xmm0); - psllq(xmm1, 12); - pshufd(xmm6, xmm5, 228); - psrlq(xmm1, 12); - jmp(L_2TAG_PACKET_1_0_2); - - bind(L_2TAG_PACKET_2_0_2); - movsd(Address(rsp, 24), xmm0); - fld_d(Address(rsp, 24)); - - bind(L_2TAG_PACKET_10_0_2); - movl(tmp, Address(rsp, 40)); -} - -#endif // !_LP64 - /******************************************************************************/ // ALGORITHM DESCRIPTION - POW() // --------------------- @@ -1351,7 +788,6 @@ void MacroAssembler::fast_log(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xm // /******************************************************************************/ -#ifdef _LP64 ALIGNED_(16) juint _HIGHSIGMASK[] = { 0x00000000UL, 0xfffff800UL, 0x00000000UL, 0xfffff800UL @@ -3109,1749 +2545,1401 @@ void MacroAssembler::fast_pow(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xm bind(B1_5); addq(rsp, 40); } -#endif // _LP64 -#ifndef _LP64 +/******************************************************************************/ +// ALGORITHM DESCRIPTION - SIN() +// --------------------- +// +// 1. RANGE REDUCTION +// +// We perform an initial range reduction from X to r with +// +// X =~= N * pi/32 + r +// +// so that |r| <= pi/64 + epsilon. We restrict inputs to those +// where |N| <= 932560. Beyond this, the range reduction is +// insufficiently accurate. For extremely small inputs, +// denormalization can occur internally, impacting performance. +// This means that the main path is actually only taken for +// 2^-252 <= |X| < 90112. +// +// To avoid branches, we perform the range reduction to full +// accuracy each time. +// +// X - N * (P_1 + P_2 + P_3) +// +// where P_1 and P_2 are 32-bit numbers (so multiplication by N +// is exact) and P_3 is a 53-bit number. Together, these +// approximate pi well enough for all cases in the restricted +// range. +// +// The main reduction sequence is: +// +// y = 32/pi * x +// N = integer(y) +// (computed by adding and subtracting off SHIFTER) +// +// m_1 = N * P_1 +// m_2 = N * P_2 +// r_1 = x - m_1 +// r = r_1 - m_2 +// (this r can be used for most of the calculation) +// +// c_1 = r_1 - r +// m_3 = N * P_3 +// c_2 = c_1 - m_2 +// c = c_2 - m_3 +// +// 2. MAIN ALGORITHM +// +// The algorithm uses a table lookup based on B = M * pi / 32 +// where M = N mod 64. The stored values are: +// sigma closest power of 2 to cos(B) +// C_hl 53-bit cos(B) - sigma +// S_hi + S_lo 2 * 53-bit sin(B) +// +// The computation is organized as follows: +// +// sin(B + r + c) = [sin(B) + sigma * r] + +// r * (cos(B) - sigma) + +// sin(B) * [cos(r + c) - 1] + +// cos(B) * [sin(r + c) - r] +// +// which is approximately: +// +// [S_hi + sigma * r] + +// C_hl * r + +// S_lo + S_hi * [(cos(r) - 1) - r * c] + +// (C_hl + sigma) * [(sin(r) - r) + c] +// +// and this is what is actually computed. We separate this sum +// into four parts: +// +// hi + med + pols + corr +// +// where +// +// hi = S_hi + sigma r +// med = C_hl * r +// pols = S_hi * (cos(r) - 1) + (C_hl + sigma) * (sin(r) - r) +// corr = S_lo + c * ((C_hl + sigma) - S_hi * r) +// +// 3. POLYNOMIAL +// +// The polynomial S_hi * (cos(r) - 1) + (C_hl + sigma) * +// (sin(r) - r) can be rearranged freely, since it is quite +// small, so we exploit parallelism to the fullest. +// +// psc4 = SC_4 * r_1 +// msc4 = psc4 * r +// r2 = r * r +// msc2 = SC_2 * r2 +// r4 = r2 * r2 +// psc3 = SC_3 + msc4 +// psc1 = SC_1 + msc2 +// msc3 = r4 * psc3 +// sincospols = psc1 + msc3 +// pols = sincospols * +// +// +// 4. CORRECTION TERM +// +// This is where the "c" component of the range reduction is +// taken into account; recall that just "r" is used for most of +// the calculation. +// +// -c = m_3 - c_2 +// -d = S_hi * r - (C_hl + sigma) +// corr = -c * -d + S_lo +// +// 5. COMPENSATED SUMMATIONS +// +// The two successive compensated summations add up the high +// and medium parts, leaving just the low parts to add up at +// the end. +// +// rs = sigma * r +// res_int = S_hi + rs +// k_0 = S_hi - res_int +// k_2 = k_0 + rs +// med = C_hl * r +// res_hi = res_int + med +// k_1 = res_int - res_hi +// k_3 = k_1 + med +// +// 6. FINAL SUMMATION +// +// We now add up all the small parts: +// +// res_lo = pols(hi) + pols(lo) + corr + k_1 + k_3 +// +// Now the overall result is just: +// +// res_hi + res_lo +// +// 7. SMALL ARGUMENTS +// +// If |x| < SNN (SNN meaning the smallest normal number), we +// simply perform 0.1111111 cdots 1111 * x. For SNN <= |x|, we +// do 2^-55 * (2^55 * x - x). +// +// Special cases: +// sin(NaN) = quiet NaN, and raise invalid exception +// sin(INF) = NaN and raise invalid exception +// sin(+/-0) = +/-0 +// +/******************************************************************************/ -ALIGNED_(16) juint _static_const_table_pow[] = +ALIGNED_(16) juint _ONEHALF[] = { - 0x00000000UL, 0xbfd61a00UL, 0x00000000UL, 0xbf5dabe1UL, 0xf8000000UL, - 0xffffffffUL, 0x00000000UL, 0xfffff800UL, 0x00000000UL, 0x3ff00000UL, - 0x00000000UL, 0x00000000UL, 0x20000000UL, 0x3feff00aUL, 0x96621f95UL, - 0x3e5b1856UL, 0xe0000000UL, 0x3fefe019UL, 0xe5916f9eUL, 0xbe325278UL, - 0x00000000UL, 0x3fefd02fUL, 0x859a1062UL, 0x3e595fb7UL, 0xc0000000UL, - 0x3fefc049UL, 0xb245f18fUL, 0xbe529c38UL, 0xe0000000UL, 0x3fefb069UL, - 0xad2880a7UL, 0xbe501230UL, 0x60000000UL, 0x3fefa08fUL, 0xc8e72420UL, - 0x3e597bd1UL, 0x80000000UL, 0x3fef90baUL, 0xc30c4500UL, 0xbe5d6c75UL, - 0xe0000000UL, 0x3fef80eaUL, 0x02c63f43UL, 0x3e2e1318UL, 0xc0000000UL, - 0x3fef7120UL, 0xb3d4ccccUL, 0xbe44c52aUL, 0x00000000UL, 0x3fef615cUL, - 0xdbd91397UL, 0xbe4e7d6cUL, 0xa0000000UL, 0x3fef519cUL, 0x65c5cd68UL, - 0xbe522dc8UL, 0xa0000000UL, 0x3fef41e2UL, 0x46d1306cUL, 0xbe5a840eUL, - 0xe0000000UL, 0x3fef322dUL, 0xd2980e94UL, 0x3e5071afUL, 0xa0000000UL, - 0x3fef227eUL, 0x773abadeUL, 0xbe5891e5UL, 0xa0000000UL, 0x3fef12d4UL, - 0xdc6bf46bUL, 0xbe5cccbeUL, 0xe0000000UL, 0x3fef032fUL, 0xbc7247faUL, - 0xbe2bab83UL, 0x80000000UL, 0x3feef390UL, 0xbcaa1e46UL, 0xbe53bb3bUL, - 0x60000000UL, 0x3feee3f6UL, 0x5f6c682dUL, 0xbe54c619UL, 0x80000000UL, - 0x3feed461UL, 0x5141e368UL, 0xbe4b6d86UL, 0xe0000000UL, 0x3feec4d1UL, - 0xec678f76UL, 0xbe369af6UL, 0x80000000UL, 0x3feeb547UL, 0x41301f55UL, - 0xbe2d4312UL, 0x60000000UL, 0x3feea5c2UL, 0x676da6bdUL, 0xbe4d8dd0UL, - 0x60000000UL, 0x3fee9642UL, 0x57a891c4UL, 0x3e51f991UL, 0xa0000000UL, - 0x3fee86c7UL, 0xe4eb491eUL, 0x3e579bf9UL, 0x20000000UL, 0x3fee7752UL, - 0xfddc4a2cUL, 0xbe3356e6UL, 0xc0000000UL, 0x3fee67e1UL, 0xd75b5bf1UL, - 0xbe449531UL, 0x80000000UL, 0x3fee5876UL, 0xbd423b8eUL, 0x3df54fe4UL, - 0x60000000UL, 0x3fee4910UL, 0x330e51b9UL, 0x3e54289cUL, 0x80000000UL, - 0x3fee39afUL, 0x8651a95fUL, 0xbe55aad6UL, 0xa0000000UL, 0x3fee2a53UL, - 0x5e98c708UL, 0xbe2fc4a9UL, 0xe0000000UL, 0x3fee1afcUL, 0x0989328dUL, - 0x3e23958cUL, 0x40000000UL, 0x3fee0babUL, 0xee642abdUL, 0xbe425dd8UL, - 0xa0000000UL, 0x3fedfc5eUL, 0xc394d236UL, 0x3e526362UL, 0x20000000UL, - 0x3feded17UL, 0xe104aa8eUL, 0x3e4ce247UL, 0xc0000000UL, 0x3fedddd4UL, - 0x265a9be4UL, 0xbe5bb77aUL, 0x40000000UL, 0x3fedce97UL, 0x0ecac52fUL, - 0x3e4a7cb1UL, 0xe0000000UL, 0x3fedbf5eUL, 0x124cb3b8UL, 0x3e257024UL, - 0x80000000UL, 0x3fedb02bUL, 0xe6d4febeUL, 0xbe2033eeUL, 0x20000000UL, - 0x3feda0fdUL, 0x39cca00eUL, 0xbe3ddabcUL, 0xc0000000UL, 0x3fed91d3UL, - 0xef8a552aUL, 0xbe543390UL, 0x40000000UL, 0x3fed82afUL, 0xb8e85204UL, - 0x3e513850UL, 0xe0000000UL, 0x3fed738fUL, 0x3d59fe08UL, 0xbe5db728UL, - 0x40000000UL, 0x3fed6475UL, 0x3aa7ead1UL, 0x3e58804bUL, 0xc0000000UL, - 0x3fed555fUL, 0xf8a35ba9UL, 0xbe5298b0UL, 0x00000000UL, 0x3fed464fUL, - 0x9a88dd15UL, 0x3e5a8cdbUL, 0x40000000UL, 0x3fed3743UL, 0xb0b0a190UL, - 0x3e598635UL, 0x80000000UL, 0x3fed283cUL, 0xe2113295UL, 0xbe5c1119UL, - 0x80000000UL, 0x3fed193aUL, 0xafbf1728UL, 0xbe492e9cUL, 0x60000000UL, - 0x3fed0a3dUL, 0xe4a4ccf3UL, 0x3e19b90eUL, 0x20000000UL, 0x3fecfb45UL, - 0xba3cbeb8UL, 0x3e406b50UL, 0xc0000000UL, 0x3fecec51UL, 0x110f7dddUL, - 0x3e0d6806UL, 0x40000000UL, 0x3fecdd63UL, 0x7dd7d508UL, 0xbe5a8943UL, - 0x80000000UL, 0x3fecce79UL, 0x9b60f271UL, 0xbe50676aUL, 0x80000000UL, - 0x3fecbf94UL, 0x0b9ad660UL, 0x3e59174fUL, 0x60000000UL, 0x3fecb0b4UL, - 0x00823d9cUL, 0x3e5bbf72UL, 0x20000000UL, 0x3feca1d9UL, 0x38a6ec89UL, - 0xbe4d38f9UL, 0x80000000UL, 0x3fec9302UL, 0x3a0b7d8eUL, 0x3e53dbfdUL, - 0xc0000000UL, 0x3fec8430UL, 0xc6826b34UL, 0xbe27c5c9UL, 0xc0000000UL, - 0x3fec7563UL, 0x0c706381UL, 0xbe593653UL, 0x60000000UL, 0x3fec669bUL, - 0x7df34ec7UL, 0x3e461ab5UL, 0xe0000000UL, 0x3fec57d7UL, 0x40e5e7e8UL, - 0xbe5c3daeUL, 0x00000000UL, 0x3fec4919UL, 0x5602770fUL, 0xbe55219dUL, - 0xc0000000UL, 0x3fec3a5eUL, 0xec7911ebUL, 0x3e5a5d25UL, 0x60000000UL, - 0x3fec2ba9UL, 0xb39ea225UL, 0xbe53c00bUL, 0x80000000UL, 0x3fec1cf8UL, - 0x967a212eUL, 0x3e5a8ddfUL, 0x60000000UL, 0x3fec0e4cUL, 0x580798bdUL, - 0x3e5f53abUL, 0x00000000UL, 0x3febffa5UL, 0xb8282df6UL, 0xbe46b874UL, - 0x20000000UL, 0x3febf102UL, 0xe33a6729UL, 0x3e54963fUL, 0x00000000UL, - 0x3febe264UL, 0x3b53e88aUL, 0xbe3adce1UL, 0x60000000UL, 0x3febd3caUL, - 0xc2585084UL, 0x3e5cde9fUL, 0x80000000UL, 0x3febc535UL, 0xa335c5eeUL, - 0xbe39fd9cUL, 0x20000000UL, 0x3febb6a5UL, 0x7325b04dUL, 0x3e42ba15UL, - 0x60000000UL, 0x3feba819UL, 0x1564540fUL, 0x3e3a9f35UL, 0x40000000UL, - 0x3feb9992UL, 0x83fff592UL, 0xbe5465ceUL, 0xa0000000UL, 0x3feb8b0fUL, - 0xb9da63d3UL, 0xbe4b1a0aUL, 0x80000000UL, 0x3feb7c91UL, 0x6d6f1ea4UL, - 0x3e557657UL, 0x00000000UL, 0x3feb6e18UL, 0x5e80a1bfUL, 0x3e4ddbb6UL, - 0x00000000UL, 0x3feb5fa3UL, 0x1c9eacb5UL, 0x3e592877UL, 0xa0000000UL, - 0x3feb5132UL, 0x6d40beb3UL, 0xbe51858cUL, 0xa0000000UL, 0x3feb42c6UL, - 0xd740c67bUL, 0x3e427ad2UL, 0x40000000UL, 0x3feb345fUL, 0xa3e0cceeUL, - 0xbe5c2fc4UL, 0x40000000UL, 0x3feb25fcUL, 0x8e752b50UL, 0xbe3da3c2UL, - 0xc0000000UL, 0x3feb179dUL, 0xa892e7deUL, 0x3e1fb481UL, 0xc0000000UL, - 0x3feb0943UL, 0x21ed71e9UL, 0xbe365206UL, 0x20000000UL, 0x3feafaeeUL, - 0x0e1380a3UL, 0x3e5c5b7bUL, 0x20000000UL, 0x3feaec9dUL, 0x3c3d640eUL, - 0xbe5dbbd0UL, 0x60000000UL, 0x3feade50UL, 0x8f97a715UL, 0x3e3a8ec5UL, - 0x20000000UL, 0x3fead008UL, 0x23ab2839UL, 0x3e2fe98aUL, 0x40000000UL, - 0x3feac1c4UL, 0xf4bbd50fUL, 0x3e54d8f6UL, 0xe0000000UL, 0x3feab384UL, - 0x14757c4dUL, 0xbe48774cUL, 0xc0000000UL, 0x3feaa549UL, 0x7c7b0eeaUL, - 0x3e5b51bbUL, 0x20000000UL, 0x3fea9713UL, 0xf56f7013UL, 0x3e386200UL, - 0xe0000000UL, 0x3fea88e0UL, 0xbe428ebeUL, 0xbe514af5UL, 0xe0000000UL, - 0x3fea7ab2UL, 0x8d0e4496UL, 0x3e4f9165UL, 0x60000000UL, 0x3fea6c89UL, - 0xdbacc5d5UL, 0xbe5c063bUL, 0x20000000UL, 0x3fea5e64UL, 0x3f19d970UL, - 0xbe5a0c8cUL, 0x20000000UL, 0x3fea5043UL, 0x09ea3e6bUL, 0x3e5065dcUL, - 0x80000000UL, 0x3fea4226UL, 0x78df246cUL, 0x3e5e05f6UL, 0x40000000UL, - 0x3fea340eUL, 0x4057d4a0UL, 0x3e431b2bUL, 0x40000000UL, 0x3fea25faUL, - 0x82867bb5UL, 0x3e4b76beUL, 0xa0000000UL, 0x3fea17eaUL, 0x9436f40aUL, - 0xbe5aad39UL, 0x20000000UL, 0x3fea09dfUL, 0x4b5253b3UL, 0x3e46380bUL, - 0x00000000UL, 0x3fe9fbd8UL, 0x8fc52466UL, 0xbe386f9bUL, 0x20000000UL, - 0x3fe9edd5UL, 0x22d3f344UL, 0xbe538347UL, 0x60000000UL, 0x3fe9dfd6UL, - 0x1ac33522UL, 0x3e5dbc53UL, 0x00000000UL, 0x3fe9d1dcUL, 0xeabdff1dUL, - 0x3e40fc0cUL, 0xe0000000UL, 0x3fe9c3e5UL, 0xafd30e73UL, 0xbe585e63UL, - 0xe0000000UL, 0x3fe9b5f3UL, 0xa52f226aUL, 0xbe43e8f9UL, 0x20000000UL, - 0x3fe9a806UL, 0xecb8698dUL, 0xbe515b36UL, 0x80000000UL, 0x3fe99a1cUL, - 0xf2b4e89dUL, 0x3e48b62bUL, 0x20000000UL, 0x3fe98c37UL, 0x7c9a88fbUL, - 0x3e44414cUL, 0x00000000UL, 0x3fe97e56UL, 0xda015741UL, 0xbe5d13baUL, - 0xe0000000UL, 0x3fe97078UL, 0x5fdace06UL, 0x3e51b947UL, 0x00000000UL, - 0x3fe962a0UL, 0x956ca094UL, 0x3e518785UL, 0x40000000UL, 0x3fe954cbUL, - 0x01164c1dUL, 0x3e5d5b57UL, 0xc0000000UL, 0x3fe946faUL, 0xe63b3767UL, - 0xbe4f84e7UL, 0x40000000UL, 0x3fe9392eUL, 0xe57cc2a9UL, 0x3e34eda3UL, - 0xe0000000UL, 0x3fe92b65UL, 0x8c75b544UL, 0x3e5766a0UL, 0xc0000000UL, - 0x3fe91da1UL, 0x37d1d087UL, 0xbe5e2ab1UL, 0x80000000UL, 0x3fe90fe1UL, - 0xa953dc20UL, 0x3e5fa1f3UL, 0x80000000UL, 0x3fe90225UL, 0xdbd3f369UL, - 0x3e47d6dbUL, 0xa0000000UL, 0x3fe8f46dUL, 0x1c9be989UL, 0xbe5e2b0aUL, - 0xa0000000UL, 0x3fe8e6b9UL, 0x3c93d76aUL, 0x3e5c8618UL, 0xe0000000UL, - 0x3fe8d909UL, 0x2182fc9aUL, 0xbe41aa9eUL, 0x20000000UL, 0x3fe8cb5eUL, - 0xe6b3539dUL, 0xbe530d19UL, 0x60000000UL, 0x3fe8bdb6UL, 0x49e58cc3UL, - 0xbe3bb374UL, 0xa0000000UL, 0x3fe8b012UL, 0xa7cfeb8fUL, 0x3e56c412UL, - 0x00000000UL, 0x3fe8a273UL, 0x8d52bc19UL, 0x3e1429b8UL, 0x60000000UL, - 0x3fe894d7UL, 0x4dc32c6cUL, 0xbe48604cUL, 0xc0000000UL, 0x3fe8873fUL, - 0x0c868e56UL, 0xbe564ee5UL, 0x00000000UL, 0x3fe879acUL, 0x56aee828UL, - 0x3e5e2fd8UL, 0x60000000UL, 0x3fe86c1cUL, 0x7ceab8ecUL, 0x3e493365UL, - 0xc0000000UL, 0x3fe85e90UL, 0x78d4dadcUL, 0xbe4f7f25UL, 0x00000000UL, - 0x3fe85109UL, 0x0ccd8280UL, 0x3e31e7a2UL, 0x40000000UL, 0x3fe84385UL, - 0x34ba4e15UL, 0x3e328077UL, 0x80000000UL, 0x3fe83605UL, 0xa670975aUL, - 0xbe53eee5UL, 0xa0000000UL, 0x3fe82889UL, 0xf61b77b2UL, 0xbe43a20aUL, - 0xa0000000UL, 0x3fe81b11UL, 0x13e6643bUL, 0x3e5e5fe5UL, 0xc0000000UL, - 0x3fe80d9dUL, 0x82cc94e8UL, 0xbe5ff1f9UL, 0xa0000000UL, 0x3fe8002dUL, - 0x8a0c9c5dUL, 0xbe42b0e7UL, 0x60000000UL, 0x3fe7f2c1UL, 0x22a16f01UL, - 0x3e5d9ea0UL, 0x20000000UL, 0x3fe7e559UL, 0xc38cd451UL, 0x3e506963UL, - 0xc0000000UL, 0x3fe7d7f4UL, 0x9902bc71UL, 0x3e4503d7UL, 0x40000000UL, - 0x3fe7ca94UL, 0xdef2a3c0UL, 0x3e3d98edUL, 0xa0000000UL, 0x3fe7bd37UL, - 0xed49abb0UL, 0x3e24c1ffUL, 0xe0000000UL, 0x3fe7afdeUL, 0xe3b0be70UL, - 0xbe40c467UL, 0x00000000UL, 0x3fe7a28aUL, 0xaf9f193cUL, 0xbe5dff6cUL, - 0xe0000000UL, 0x3fe79538UL, 0xb74cf6b6UL, 0xbe258ed0UL, 0xa0000000UL, - 0x3fe787ebUL, 0x1d9127c7UL, 0x3e345fb0UL, 0x40000000UL, 0x3fe77aa2UL, - 0x1028c21dUL, 0xbe4619bdUL, 0xa0000000UL, 0x3fe76d5cUL, 0x7cb0b5e4UL, - 0x3e40f1a2UL, 0xe0000000UL, 0x3fe7601aUL, 0x2b1bc4adUL, 0xbe32e8bbUL, - 0xe0000000UL, 0x3fe752dcUL, 0x6839f64eUL, 0x3e41f57bUL, 0xc0000000UL, - 0x3fe745a2UL, 0xc4121f7eUL, 0xbe52c40aUL, 0x60000000UL, 0x3fe7386cUL, - 0xd6852d72UL, 0xbe5c4e6bUL, 0xc0000000UL, 0x3fe72b39UL, 0x91d690f7UL, - 0xbe57f88fUL, 0xe0000000UL, 0x3fe71e0aUL, 0x627a2159UL, 0xbe4425d5UL, - 0xc0000000UL, 0x3fe710dfUL, 0x50a54033UL, 0x3e422b7eUL, 0x60000000UL, - 0x3fe703b8UL, 0x3b0b5f91UL, 0x3e5d3857UL, 0xe0000000UL, 0x3fe6f694UL, - 0x84d628a2UL, 0xbe51f090UL, 0x00000000UL, 0x3fe6e975UL, 0x306d8894UL, - 0xbe414d83UL, 0xe0000000UL, 0x3fe6dc58UL, 0x30bf24aaUL, 0xbe4650caUL, - 0x80000000UL, 0x3fe6cf40UL, 0xd4628d69UL, 0xbe5db007UL, 0xc0000000UL, - 0x3fe6c22bUL, 0xa2aae57bUL, 0xbe31d279UL, 0xc0000000UL, 0x3fe6b51aUL, - 0x860edf7eUL, 0xbe2d4c4aUL, 0x80000000UL, 0x3fe6a80dUL, 0xf3559341UL, - 0xbe5f7e98UL, 0xe0000000UL, 0x3fe69b03UL, 0xa885899eUL, 0xbe5c2011UL, - 0xe0000000UL, 0x3fe68dfdUL, 0x2bdc6d37UL, 0x3e224a82UL, 0xa0000000UL, - 0x3fe680fbUL, 0xc12ad1b9UL, 0xbe40cf56UL, 0x00000000UL, 0x3fe673fdUL, - 0x1bcdf659UL, 0xbdf52f2dUL, 0x00000000UL, 0x3fe66702UL, 0x5df10408UL, - 0x3e5663e0UL, 0xc0000000UL, 0x3fe65a0aUL, 0xa4070568UL, 0xbe40b12fUL, - 0x00000000UL, 0x3fe64d17UL, 0x71c54c47UL, 0x3e5f5e8bUL, 0x00000000UL, - 0x3fe64027UL, 0xbd4b7e83UL, 0x3e42ead6UL, 0xa0000000UL, 0x3fe6333aUL, - 0x61598bd2UL, 0xbe4c48d4UL, 0xc0000000UL, 0x3fe62651UL, 0x6f538d61UL, - 0x3e548401UL, 0xa0000000UL, 0x3fe6196cUL, 0x14344120UL, 0xbe529af6UL, - 0x00000000UL, 0x3fe60c8bUL, 0x5982c587UL, 0xbe3e1e4fUL, 0x00000000UL, - 0x3fe5ffadUL, 0xfe51d4eaUL, 0xbe4c897aUL, 0x80000000UL, 0x3fe5f2d2UL, - 0xfd46ebe1UL, 0x3e552e00UL, 0xa0000000UL, 0x3fe5e5fbUL, 0xa4695699UL, - 0x3e5ed471UL, 0x60000000UL, 0x3fe5d928UL, 0x80d118aeUL, 0x3e456b61UL, - 0xa0000000UL, 0x3fe5cc58UL, 0x304c330bUL, 0x3e54dc29UL, 0x80000000UL, - 0x3fe5bf8cUL, 0x0af2dedfUL, 0xbe3aa9bdUL, 0xe0000000UL, 0x3fe5b2c3UL, - 0x15fc9258UL, 0xbe479a37UL, 0xc0000000UL, 0x3fe5a5feUL, 0x9292c7eaUL, - 0x3e188650UL, 0x20000000UL, 0x3fe5993dUL, 0x33b4d380UL, 0x3e5d6d93UL, - 0x20000000UL, 0x3fe58c7fUL, 0x02fd16c7UL, 0x3e2fe961UL, 0xa0000000UL, - 0x3fe57fc4UL, 0x4a05edb6UL, 0xbe4d55b4UL, 0xa0000000UL, 0x3fe5730dUL, - 0x3d443abbUL, 0xbe5e6954UL, 0x00000000UL, 0x3fe5665aUL, 0x024acfeaUL, - 0x3e50e61bUL, 0x00000000UL, 0x3fe559aaUL, 0xcc9edd09UL, 0xbe325403UL, - 0x60000000UL, 0x3fe54cfdUL, 0x1fe26950UL, 0x3e5d500eUL, 0x60000000UL, - 0x3fe54054UL, 0x6c5ae164UL, 0xbe4a79b4UL, 0xc0000000UL, 0x3fe533aeUL, - 0x154b0287UL, 0xbe401571UL, 0xa0000000UL, 0x3fe5270cUL, 0x0673f401UL, - 0xbe56e56bUL, 0xe0000000UL, 0x3fe51a6dUL, 0x751b639cUL, 0x3e235269UL, - 0xa0000000UL, 0x3fe50dd2UL, 0x7c7b2bedUL, 0x3ddec887UL, 0xc0000000UL, - 0x3fe5013aUL, 0xafab4e17UL, 0x3e5e7575UL, 0x60000000UL, 0x3fe4f4a6UL, - 0x2e308668UL, 0x3e59aed6UL, 0x80000000UL, 0x3fe4e815UL, 0xf33e2a76UL, - 0xbe51f184UL, 0xe0000000UL, 0x3fe4db87UL, 0x839f3e3eUL, 0x3e57db01UL, - 0xc0000000UL, 0x3fe4cefdUL, 0xa9eda7bbUL, 0x3e535e0fUL, 0x00000000UL, - 0x3fe4c277UL, 0x2a8f66a5UL, 0x3e5ce451UL, 0xc0000000UL, 0x3fe4b5f3UL, - 0x05192456UL, 0xbe4e8518UL, 0xc0000000UL, 0x3fe4a973UL, 0x4aa7cd1dUL, - 0x3e46784aUL, 0x40000000UL, 0x3fe49cf7UL, 0x8e23025eUL, 0xbe5749f2UL, - 0x00000000UL, 0x3fe4907eUL, 0x18d30215UL, 0x3e360f39UL, 0x20000000UL, - 0x3fe48408UL, 0x63dcf2f3UL, 0x3e5e00feUL, 0xc0000000UL, 0x3fe47795UL, - 0x46182d09UL, 0xbe5173d9UL, 0xa0000000UL, 0x3fe46b26UL, 0x8f0e62aaUL, - 0xbe48f281UL, 0xe0000000UL, 0x3fe45ebaUL, 0x5775c40cUL, 0xbe56aad4UL, - 0x60000000UL, 0x3fe45252UL, 0x0fe25f69UL, 0x3e48bd71UL, 0x40000000UL, - 0x3fe445edUL, 0xe9989ec5UL, 0x3e590d97UL, 0x80000000UL, 0x3fe4398bUL, - 0xb3d9ffe3UL, 0x3e479dbcUL, 0x20000000UL, 0x3fe42d2dUL, 0x388e4d2eUL, - 0xbe5eed80UL, 0xe0000000UL, 0x3fe420d1UL, 0x6f797c18UL, 0x3e554b4cUL, - 0x20000000UL, 0x3fe4147aUL, 0x31048bb4UL, 0xbe5b1112UL, 0x80000000UL, - 0x3fe40825UL, 0x2efba4f9UL, 0x3e48ebc7UL, 0x40000000UL, 0x3fe3fbd4UL, - 0x50201119UL, 0x3e40b701UL, 0x40000000UL, 0x3fe3ef86UL, 0x0a4db32cUL, - 0x3e551de8UL, 0xa0000000UL, 0x3fe3e33bUL, 0x0c9c148bUL, 0xbe50c1f6UL, - 0x20000000UL, 0x3fe3d6f4UL, 0xc9129447UL, 0x3e533fa0UL, 0x00000000UL, - 0x3fe3cab0UL, 0xaae5b5a0UL, 0xbe22b68eUL, 0x20000000UL, 0x3fe3be6fUL, - 0x02305e8aUL, 0xbe54fc08UL, 0x60000000UL, 0x3fe3b231UL, 0x7f908258UL, - 0x3e57dc05UL, 0x00000000UL, 0x3fe3a5f7UL, 0x1a09af78UL, 0x3e08038bUL, - 0xe0000000UL, 0x3fe399bfUL, 0x490643c1UL, 0xbe5dbe42UL, 0xe0000000UL, - 0x3fe38d8bUL, 0x5e8ad724UL, 0xbe3c2b72UL, 0x20000000UL, 0x3fe3815bUL, - 0xc67196b6UL, 0x3e1713cfUL, 0xa0000000UL, 0x3fe3752dUL, 0x6182e429UL, - 0xbe3ec14cUL, 0x40000000UL, 0x3fe36903UL, 0xab6eb1aeUL, 0x3e5a2cc5UL, - 0x40000000UL, 0x3fe35cdcUL, 0xfe5dc064UL, 0xbe5c5878UL, 0x40000000UL, - 0x3fe350b8UL, 0x0ba6b9e4UL, 0x3e51619bUL, 0x80000000UL, 0x3fe34497UL, - 0x857761aaUL, 0x3e5fff53UL, 0x00000000UL, 0x3fe3387aUL, 0xf872d68cUL, - 0x3e484f4dUL, 0xa0000000UL, 0x3fe32c5fUL, 0x087e97c2UL, 0x3e52842eUL, - 0x80000000UL, 0x3fe32048UL, 0x73d6d0c0UL, 0xbe503edfUL, 0x80000000UL, - 0x3fe31434UL, 0x0c1456a1UL, 0xbe5f72adUL, 0xa0000000UL, 0x3fe30823UL, - 0x83a1a4d5UL, 0xbe5e65ccUL, 0xe0000000UL, 0x3fe2fc15UL, 0x855a7390UL, - 0xbe506438UL, 0x40000000UL, 0x3fe2f00bUL, 0xa2898287UL, 0x3e3d22a2UL, - 0xe0000000UL, 0x3fe2e403UL, 0x8b56f66fUL, 0xbe5aa5fdUL, 0x80000000UL, - 0x3fe2d7ffUL, 0x52db119aUL, 0x3e3a2e3dUL, 0x60000000UL, 0x3fe2cbfeUL, - 0xe2ddd4c0UL, 0xbe586469UL, 0x40000000UL, 0x3fe2c000UL, 0x6b01bf10UL, - 0x3e352b9dUL, 0x40000000UL, 0x3fe2b405UL, 0xb07a1cdfUL, 0x3e5c5cdaUL, - 0x80000000UL, 0x3fe2a80dUL, 0xc7b5f868UL, 0xbe5668b3UL, 0xc0000000UL, - 0x3fe29c18UL, 0x185edf62UL, 0xbe563d66UL, 0x00000000UL, 0x3fe29027UL, - 0xf729e1ccUL, 0x3e59a9a0UL, 0x80000000UL, 0x3fe28438UL, 0x6433c727UL, - 0xbe43cc89UL, 0x00000000UL, 0x3fe2784dUL, 0x41782631UL, 0xbe30750cUL, - 0xa0000000UL, 0x3fe26c64UL, 0x914911b7UL, 0xbe58290eUL, 0x40000000UL, - 0x3fe2607fUL, 0x3dcc73e1UL, 0xbe4269cdUL, 0x00000000UL, 0x3fe2549dUL, - 0x2751bf70UL, 0xbe5a6998UL, 0xc0000000UL, 0x3fe248bdUL, 0x4248b9fbUL, - 0xbe4ddb00UL, 0x80000000UL, 0x3fe23ce1UL, 0xf35cf82fUL, 0x3e561b71UL, - 0x60000000UL, 0x3fe23108UL, 0x8e481a2dUL, 0x3e518fb9UL, 0x60000000UL, - 0x3fe22532UL, 0x5ab96edcUL, 0xbe5fafc5UL, 0x40000000UL, 0x3fe2195fUL, - 0x80943911UL, 0xbe07f819UL, 0x40000000UL, 0x3fe20d8fUL, 0x386f2d6cUL, - 0xbe54ba8bUL, 0x40000000UL, 0x3fe201c2UL, 0xf29664acUL, 0xbe5eb815UL, - 0x20000000UL, 0x3fe1f5f8UL, 0x64f03390UL, 0x3e5e320cUL, 0x20000000UL, - 0x3fe1ea31UL, 0x747ff696UL, 0x3e5ef0a5UL, 0x40000000UL, 0x3fe1de6dUL, - 0x3e9ceb51UL, 0xbe5f8d27UL, 0x20000000UL, 0x3fe1d2acUL, 0x4ae0b55eUL, - 0x3e5faa21UL, 0x20000000UL, 0x3fe1c6eeUL, 0x28569a5eUL, 0x3e598a4fUL, - 0x20000000UL, 0x3fe1bb33UL, 0x54b33e07UL, 0x3e46130aUL, 0x20000000UL, - 0x3fe1af7bUL, 0x024f1078UL, 0xbe4dbf93UL, 0x00000000UL, 0x3fe1a3c6UL, - 0xb0783bfaUL, 0x3e419248UL, 0xe0000000UL, 0x3fe19813UL, 0x2f02b836UL, - 0x3e4e02b7UL, 0xc0000000UL, 0x3fe18c64UL, 0x28dec9d4UL, 0x3e09064fUL, - 0x80000000UL, 0x3fe180b8UL, 0x45cbf406UL, 0x3e5b1f46UL, 0x40000000UL, - 0x3fe1750fUL, 0x03d9964cUL, 0x3e5b0a79UL, 0x00000000UL, 0x3fe16969UL, - 0x8b5b882bUL, 0xbe238086UL, 0xa0000000UL, 0x3fe15dc5UL, 0x73bad6f8UL, - 0xbdf1fca4UL, 0x20000000UL, 0x3fe15225UL, 0x5385769cUL, 0x3e5e8d76UL, - 0xa0000000UL, 0x3fe14687UL, 0x1676dc6bUL, 0x3e571d08UL, 0x20000000UL, - 0x3fe13aedUL, 0xa8c41c7fUL, 0xbe598a25UL, 0x60000000UL, 0x3fe12f55UL, - 0xc4e1aaf0UL, 0x3e435277UL, 0xa0000000UL, 0x3fe123c0UL, 0x403638e1UL, - 0xbe21aa7cUL, 0xc0000000UL, 0x3fe1182eUL, 0x557a092bUL, 0xbdd0116bUL, - 0xc0000000UL, 0x3fe10c9fUL, 0x7d779f66UL, 0x3e4a61baUL, 0xc0000000UL, - 0x3fe10113UL, 0x2b09c645UL, 0xbe5d586eUL, 0x20000000UL, 0x3fe0ea04UL, - 0xea2cad46UL, 0x3e5aa97cUL, 0x20000000UL, 0x3fe0d300UL, 0x23190e54UL, - 0x3e50f1a7UL, 0xa0000000UL, 0x3fe0bc07UL, 0x1379a5a6UL, 0xbe51619dUL, - 0x60000000UL, 0x3fe0a51aUL, 0x926a3d4aUL, 0x3e5cf019UL, 0xa0000000UL, - 0x3fe08e38UL, 0xa8c24358UL, 0x3e35241eUL, 0x20000000UL, 0x3fe07762UL, - 0x24317e7aUL, 0x3e512cfaUL, 0x00000000UL, 0x3fe06097UL, 0xfd9cf274UL, - 0xbe55bef3UL, 0x00000000UL, 0x3fe049d7UL, 0x3689b49dUL, 0xbe36d26dUL, - 0x40000000UL, 0x3fe03322UL, 0xf72ef6c4UL, 0xbe54cd08UL, 0xa0000000UL, - 0x3fe01c78UL, 0x23702d2dUL, 0xbe5900bfUL, 0x00000000UL, 0x3fe005daUL, - 0x3f59c14cUL, 0x3e57d80bUL, 0x40000000UL, 0x3fdfde8dUL, 0xad67766dUL, - 0xbe57fad4UL, 0x40000000UL, 0x3fdfb17cUL, 0x644f4ae7UL, 0x3e1ee43bUL, - 0x40000000UL, 0x3fdf8481UL, 0x903234d2UL, 0x3e501a86UL, 0x40000000UL, - 0x3fdf579cUL, 0xafe9e509UL, 0xbe267c3eUL, 0x00000000UL, 0x3fdf2acdUL, - 0xb7dfda0bUL, 0xbe48149bUL, 0x40000000UL, 0x3fdefe13UL, 0x3b94305eUL, - 0x3e5f4ea7UL, 0x80000000UL, 0x3fded16fUL, 0x5d95da61UL, 0xbe55c198UL, - 0x00000000UL, 0x3fdea4e1UL, 0x406960c9UL, 0xbdd99a19UL, 0x00000000UL, - 0x3fde7868UL, 0xd22f3539UL, 0x3e470c78UL, 0x80000000UL, 0x3fde4c04UL, - 0x83eec535UL, 0xbe3e1232UL, 0x40000000UL, 0x3fde1fb6UL, 0x3dfbffcbUL, - 0xbe4b7d71UL, 0x40000000UL, 0x3fddf37dUL, 0x7e1be4e0UL, 0xbe5b8f8fUL, - 0x40000000UL, 0x3fddc759UL, 0x46dae887UL, 0xbe350458UL, 0x80000000UL, - 0x3fdd9b4aUL, 0xed6ecc49UL, 0xbe5f0045UL, 0x80000000UL, 0x3fdd6f50UL, - 0x2e9e883cUL, 0x3e2915daUL, 0x80000000UL, 0x3fdd436bUL, 0xf0bccb32UL, - 0x3e4a68c9UL, 0x80000000UL, 0x3fdd179bUL, 0x9bbfc779UL, 0xbe54a26aUL, - 0x00000000UL, 0x3fdcebe0UL, 0x7cea33abUL, 0x3e43c6b7UL, 0x40000000UL, - 0x3fdcc039UL, 0xe740fd06UL, 0x3e5526c2UL, 0x40000000UL, 0x3fdc94a7UL, - 0x9eadeb1aUL, 0xbe396d8dUL, 0xc0000000UL, 0x3fdc6929UL, 0xf0a8f95aUL, - 0xbe5c0ab2UL, 0x80000000UL, 0x3fdc3dc0UL, 0x6ee2693bUL, 0x3e0992e6UL, - 0xc0000000UL, 0x3fdc126bUL, 0x5ac6b581UL, 0xbe2834b6UL, 0x40000000UL, - 0x3fdbe72bUL, 0x8cc226ffUL, 0x3e3596a6UL, 0x00000000UL, 0x3fdbbbffUL, - 0xf92a74bbUL, 0x3e3c5813UL, 0x00000000UL, 0x3fdb90e7UL, 0x479664c0UL, - 0xbe50d644UL, 0x00000000UL, 0x3fdb65e3UL, 0x5004975bUL, 0xbe55258fUL, - 0x00000000UL, 0x3fdb3af3UL, 0xe4b23194UL, 0xbe588407UL, 0xc0000000UL, - 0x3fdb1016UL, 0xe65d4d0aUL, 0x3e527c26UL, 0x80000000UL, 0x3fdae54eUL, - 0x814fddd6UL, 0x3e5962a2UL, 0x40000000UL, 0x3fdaba9aUL, 0xe19d0913UL, - 0xbe562f4eUL, 0x80000000UL, 0x3fda8ff9UL, 0x43cfd006UL, 0xbe4cfdebUL, - 0x40000000UL, 0x3fda656cUL, 0x686f0a4eUL, 0x3e5e47a8UL, 0xc0000000UL, - 0x3fda3af2UL, 0x7200d410UL, 0x3e5e1199UL, 0xc0000000UL, 0x3fda108cUL, - 0xabd2266eUL, 0x3e5ee4d1UL, 0x40000000UL, 0x3fd9e63aUL, 0x396f8f2cUL, - 0x3e4dbffbUL, 0x00000000UL, 0x3fd9bbfbUL, 0xe32b25ddUL, 0x3e5c3a54UL, - 0x40000000UL, 0x3fd991cfUL, 0x431e4035UL, 0xbe457925UL, 0x80000000UL, - 0x3fd967b6UL, 0x7bed3dd3UL, 0x3e40c61dUL, 0x00000000UL, 0x3fd93db1UL, - 0xd7449365UL, 0x3e306419UL, 0x80000000UL, 0x3fd913beUL, 0x1746e791UL, - 0x3e56fcfcUL, 0x40000000UL, 0x3fd8e9dfUL, 0xf3a9028bUL, 0xbe5041b9UL, - 0xc0000000UL, 0x3fd8c012UL, 0x56840c50UL, 0xbe26e20aUL, 0x40000000UL, - 0x3fd89659UL, 0x19763102UL, 0xbe51f466UL, 0x80000000UL, 0x3fd86cb2UL, - 0x7032de7cUL, 0xbe4d298aUL, 0x80000000UL, 0x3fd8431eUL, 0xdeb39fabUL, - 0xbe4361ebUL, 0x40000000UL, 0x3fd8199dUL, 0x5d01cbe0UL, 0xbe5425b3UL, - 0x80000000UL, 0x3fd7f02eUL, 0x3ce99aa9UL, 0x3e146fa8UL, 0x80000000UL, - 0x3fd7c6d2UL, 0xd1a262b9UL, 0xbe5a1a69UL, 0xc0000000UL, 0x3fd79d88UL, - 0x8606c236UL, 0x3e423a08UL, 0x80000000UL, 0x3fd77451UL, 0x8fd1e1b7UL, - 0x3e5a6a63UL, 0xc0000000UL, 0x3fd74b2cUL, 0xe491456aUL, 0x3e42c1caUL, - 0x40000000UL, 0x3fd7221aUL, 0x4499a6d7UL, 0x3e36a69aUL, 0x00000000UL, - 0x3fd6f91aUL, 0x5237df94UL, 0xbe0f8f02UL, 0x00000000UL, 0x3fd6d02cUL, - 0xb6482c6eUL, 0xbe5abcf7UL, 0x00000000UL, 0x3fd6a750UL, 0x1919fd61UL, - 0xbe57ade2UL, 0x00000000UL, 0x3fd67e86UL, 0xaa7a994dUL, 0xbe3f3fbdUL, - 0x00000000UL, 0x3fd655ceUL, 0x67db014cUL, 0x3e33c550UL, 0x00000000UL, - 0x3fd62d28UL, 0xa82856b7UL, 0xbe1409d1UL, 0xc0000000UL, 0x3fd60493UL, - 0x1e6a300dUL, 0x3e55d899UL, 0x80000000UL, 0x3fd5dc11UL, 0x1222bd5cUL, - 0xbe35bfc0UL, 0xc0000000UL, 0x3fd5b3a0UL, 0x6e8dc2d3UL, 0x3e5d4d79UL, - 0x00000000UL, 0x3fd58b42UL, 0xe0e4ace6UL, 0xbe517303UL, 0x80000000UL, - 0x3fd562f4UL, 0xb306e0a8UL, 0x3e5edf0fUL, 0xc0000000UL, 0x3fd53ab8UL, - 0x6574bc54UL, 0x3e5ee859UL, 0x80000000UL, 0x3fd5128eUL, 0xea902207UL, - 0x3e5f6188UL, 0xc0000000UL, 0x3fd4ea75UL, 0x9f911d79UL, 0x3e511735UL, - 0x80000000UL, 0x3fd4c26eUL, 0xf9c77397UL, 0xbe5b1643UL, 0x40000000UL, - 0x3fd49a78UL, 0x15fc9258UL, 0x3e479a37UL, 0x80000000UL, 0x3fd47293UL, - 0xd5a04dd9UL, 0xbe426e56UL, 0xc0000000UL, 0x3fd44abfUL, 0xe04042f5UL, - 0x3e56f7c6UL, 0x40000000UL, 0x3fd422fdUL, 0x1d8bf2c8UL, 0x3e5d8810UL, - 0x00000000UL, 0x3fd3fb4cUL, 0x88a8ddeeUL, 0xbe311454UL, 0xc0000000UL, - 0x3fd3d3abUL, 0x3e3b5e47UL, 0xbe5d1b72UL, 0x40000000UL, 0x3fd3ac1cUL, - 0xc2ab5d59UL, 0x3e31b02bUL, 0xc0000000UL, 0x3fd3849dUL, 0xd4e34b9eUL, - 0x3e51cb2fUL, 0x40000000UL, 0x3fd35d30UL, 0x177204fbUL, 0xbe2b8cd7UL, - 0x80000000UL, 0x3fd335d3UL, 0xfcd38c82UL, 0xbe4356e1UL, 0x80000000UL, - 0x3fd30e87UL, 0x64f54accUL, 0xbe4e6224UL, 0x00000000UL, 0x3fd2e74cUL, - 0xaa7975d9UL, 0x3e5dc0feUL, 0x80000000UL, 0x3fd2c021UL, 0x516dab3fUL, - 0xbe50ffa3UL, 0x40000000UL, 0x3fd29907UL, 0x2bfb7313UL, 0x3e5674a2UL, - 0xc0000000UL, 0x3fd271fdUL, 0x0549fc99UL, 0x3e385d29UL, 0xc0000000UL, - 0x3fd24b04UL, 0x55b63073UL, 0xbe500c6dUL, 0x00000000UL, 0x3fd2241cUL, - 0x3f91953aUL, 0x3e389977UL, 0xc0000000UL, 0x3fd1fd43UL, 0xa1543f71UL, - 0xbe3487abUL, 0xc0000000UL, 0x3fd1d67bUL, 0x4ec8867cUL, 0x3df6a2dcUL, - 0x00000000UL, 0x3fd1afc4UL, 0x4328e3bbUL, 0x3e41d9c0UL, 0x80000000UL, - 0x3fd1891cUL, 0x2e1cda84UL, 0x3e3bdd87UL, 0x40000000UL, 0x3fd16285UL, - 0x4b5331aeUL, 0xbe53128eUL, 0x00000000UL, 0x3fd13bfeUL, 0xb9aec164UL, - 0xbe52ac98UL, 0xc0000000UL, 0x3fd11586UL, 0xd91e1316UL, 0xbe350630UL, - 0x80000000UL, 0x3fd0ef1fUL, 0x7cacc12cUL, 0x3e3f5219UL, 0x40000000UL, - 0x3fd0c8c8UL, 0xbce277b7UL, 0x3e3d30c0UL, 0x00000000UL, 0x3fd0a281UL, - 0x2a63447dUL, 0xbe541377UL, 0x80000000UL, 0x3fd07c49UL, 0xfac483b5UL, - 0xbe5772ecUL, 0xc0000000UL, 0x3fd05621UL, 0x36b8a570UL, 0xbe4fd4bdUL, - 0xc0000000UL, 0x3fd03009UL, 0xbae505f7UL, 0xbe450388UL, 0x80000000UL, - 0x3fd00a01UL, 0x3e35aeadUL, 0xbe5430fcUL, 0x80000000UL, 0x3fcfc811UL, - 0x707475acUL, 0x3e38806eUL, 0x80000000UL, 0x3fcf7c3fUL, 0xc91817fcUL, - 0xbe40cceaUL, 0x80000000UL, 0x3fcf308cUL, 0xae05d5e9UL, 0xbe4919b8UL, - 0x80000000UL, 0x3fcee4f8UL, 0xae6cc9e6UL, 0xbe530b94UL, 0x00000000UL, - 0x3fce9983UL, 0x1efe3e8eUL, 0x3e57747eUL, 0x00000000UL, 0x3fce4e2dUL, - 0xda78d9bfUL, 0xbe59a608UL, 0x00000000UL, 0x3fce02f5UL, 0x8abe2c2eUL, - 0x3e4a35adUL, 0x00000000UL, 0x3fcdb7dcUL, 0x1495450dUL, 0xbe0872ccUL, - 0x80000000UL, 0x3fcd6ce1UL, 0x86ee0ba0UL, 0xbe4f59a0UL, 0x00000000UL, - 0x3fcd2205UL, 0xe81ca888UL, 0x3e5402c3UL, 0x00000000UL, 0x3fccd747UL, - 0x3b4424b9UL, 0x3e5dfdc3UL, 0x80000000UL, 0x3fcc8ca7UL, 0xd305b56cUL, - 0x3e202da6UL, 0x00000000UL, 0x3fcc4226UL, 0x399a6910UL, 0xbe482a1cUL, - 0x80000000UL, 0x3fcbf7c2UL, 0x747f7938UL, 0xbe587372UL, 0x80000000UL, - 0x3fcbad7cUL, 0x6fc246a0UL, 0x3e50d83dUL, 0x00000000UL, 0x3fcb6355UL, - 0xee9e9be5UL, 0xbe5c35bdUL, 0x80000000UL, 0x3fcb194aUL, 0x8416c0bcUL, - 0x3e546d4fUL, 0x00000000UL, 0x3fcacf5eUL, 0x49f7f08fUL, 0x3e56da76UL, - 0x00000000UL, 0x3fca858fUL, 0x5dc30de2UL, 0x3e5f390cUL, 0x00000000UL, - 0x3fca3bdeUL, 0x950583b6UL, 0xbe5e4169UL, 0x80000000UL, 0x3fc9f249UL, - 0x33631553UL, 0x3e52aeb1UL, 0x00000000UL, 0x3fc9a8d3UL, 0xde8795a6UL, - 0xbe59a504UL, 0x00000000UL, 0x3fc95f79UL, 0x076bf41eUL, 0x3e5122feUL, - 0x80000000UL, 0x3fc9163cUL, 0x2914c8e7UL, 0x3e3dd064UL, 0x00000000UL, - 0x3fc8cd1dUL, 0x3a30eca3UL, 0xbe21b4aaUL, 0x80000000UL, 0x3fc8841aUL, - 0xb2a96650UL, 0xbe575444UL, 0x80000000UL, 0x3fc83b34UL, 0x2376c0cbUL, - 0xbe2a74c7UL, 0x80000000UL, 0x3fc7f26bUL, 0xd8a0b653UL, 0xbe5181b6UL, - 0x00000000UL, 0x3fc7a9bfUL, 0x32257882UL, 0xbe4a78b4UL, 0x00000000UL, - 0x3fc7612fUL, 0x1eee8bd9UL, 0xbe1bfe9dUL, 0x80000000UL, 0x3fc718bbUL, - 0x0c603cc4UL, 0x3e36fdc9UL, 0x80000000UL, 0x3fc6d064UL, 0x3728b8cfUL, - 0xbe1e542eUL, 0x80000000UL, 0x3fc68829UL, 0xc79a4067UL, 0x3e5c380fUL, - 0x00000000UL, 0x3fc6400bUL, 0xf69eac69UL, 0x3e550a84UL, 0x80000000UL, - 0x3fc5f808UL, 0xb7a780a4UL, 0x3e5d9224UL, 0x80000000UL, 0x3fc5b022UL, - 0xad9dfb1eUL, 0xbe55242fUL, 0x00000000UL, 0x3fc56858UL, 0x659b18beUL, - 0xbe4bfda3UL, 0x80000000UL, 0x3fc520a9UL, 0x66ee3631UL, 0xbe57d769UL, - 0x80000000UL, 0x3fc4d916UL, 0x1ec62819UL, 0x3e2427f7UL, 0x80000000UL, - 0x3fc4919fUL, 0xdec25369UL, 0xbe435431UL, 0x00000000UL, 0x3fc44a44UL, - 0xa8acfc4bUL, 0xbe3c62e8UL, 0x00000000UL, 0x3fc40304UL, 0xcf1d3eabUL, - 0xbdfba29fUL, 0x80000000UL, 0x3fc3bbdfUL, 0x79aba3eaUL, 0xbdf1b7c8UL, - 0x80000000UL, 0x3fc374d6UL, 0xb8d186daUL, 0xbe5130cfUL, 0x80000000UL, - 0x3fc32de8UL, 0x9d74f152UL, 0x3e2285b6UL, 0x00000000UL, 0x3fc2e716UL, - 0x50ae7ca9UL, 0xbe503920UL, 0x80000000UL, 0x3fc2a05eUL, 0x6caed92eUL, - 0xbe533924UL, 0x00000000UL, 0x3fc259c2UL, 0x9cb5034eUL, 0xbe510e31UL, - 0x80000000UL, 0x3fc21340UL, 0x12c4d378UL, 0xbe540b43UL, 0x80000000UL, - 0x3fc1ccd9UL, 0xcc418706UL, 0x3e59887aUL, 0x00000000UL, 0x3fc1868eUL, - 0x921f4106UL, 0xbe528e67UL, 0x80000000UL, 0x3fc1405cUL, 0x3969441eUL, - 0x3e5d8051UL, 0x00000000UL, 0x3fc0fa46UL, 0xd941ef5bUL, 0x3e5f9079UL, - 0x80000000UL, 0x3fc0b44aUL, 0x5a3e81b2UL, 0xbe567691UL, 0x00000000UL, - 0x3fc06e69UL, 0x9d66afe7UL, 0xbe4d43fbUL, 0x00000000UL, 0x3fc028a2UL, - 0x0a92a162UL, 0xbe52f394UL, 0x00000000UL, 0x3fbfc5eaUL, 0x209897e5UL, - 0x3e529e37UL, 0x00000000UL, 0x3fbf3ac5UL, 0x8458bd7bUL, 0x3e582831UL, - 0x00000000UL, 0x3fbeafd5UL, 0xb8d8b4b8UL, 0xbe486b4aUL, 0x00000000UL, - 0x3fbe2518UL, 0xe0a3b7b6UL, 0x3e5bafd2UL, 0x00000000UL, 0x3fbd9a90UL, - 0x2bf2710eUL, 0x3e383b2bUL, 0x00000000UL, 0x3fbd103cUL, 0x73eb6ab7UL, - 0xbe56d78dUL, 0x00000000UL, 0x3fbc861bUL, 0x32ceaff5UL, 0xbe32dc5aUL, - 0x00000000UL, 0x3fbbfc2eUL, 0xbee04cb7UL, 0xbe4a71a4UL, 0x00000000UL, - 0x3fbb7274UL, 0x35ae9577UL, 0x3e38142fUL, 0x00000000UL, 0x3fbae8eeUL, - 0xcbaddab4UL, 0xbe5490f0UL, 0x00000000UL, 0x3fba5f9aUL, 0x95ce1114UL, - 0x3e597c71UL, 0x00000000UL, 0x3fb9d67aUL, 0x6d7c0f78UL, 0x3e3abc2dUL, - 0x00000000UL, 0x3fb94d8dUL, 0x2841a782UL, 0xbe566cbcUL, 0x00000000UL, - 0x3fb8c4d2UL, 0x6ed429c6UL, 0xbe3cfff9UL, 0x00000000UL, 0x3fb83c4aUL, - 0xe4a49fbbUL, 0xbe552964UL, 0x00000000UL, 0x3fb7b3f4UL, 0x2193d81eUL, - 0xbe42fa72UL, 0x00000000UL, 0x3fb72bd0UL, 0xdd70c122UL, 0x3e527a8cUL, - 0x00000000UL, 0x3fb6a3dfUL, 0x03108a54UL, 0xbe450393UL, 0x00000000UL, - 0x3fb61c1fUL, 0x30ff7954UL, 0x3e565840UL, 0x00000000UL, 0x3fb59492UL, - 0xdedd460cUL, 0xbe5422b5UL, 0x00000000UL, 0x3fb50d36UL, 0x950f9f45UL, - 0xbe5313f6UL, 0x00000000UL, 0x3fb4860bUL, 0x582cdcb1UL, 0x3e506d39UL, - 0x00000000UL, 0x3fb3ff12UL, 0x7216d3a6UL, 0x3e4aa719UL, 0x00000000UL, - 0x3fb3784aUL, 0x57a423fdUL, 0x3e5a9b9fUL, 0x00000000UL, 0x3fb2f1b4UL, - 0x7a138b41UL, 0xbe50b418UL, 0x00000000UL, 0x3fb26b4eUL, 0x2fbfd7eaUL, - 0x3e23a53eUL, 0x00000000UL, 0x3fb1e519UL, 0x18913ccbUL, 0x3e465fc1UL, - 0x00000000UL, 0x3fb15f15UL, 0x7ea24e21UL, 0x3e042843UL, 0x00000000UL, - 0x3fb0d941UL, 0x7c6d9c77UL, 0x3e59f61eUL, 0x00000000UL, 0x3fb0539eUL, - 0x114efd44UL, 0x3e4ccab7UL, 0x00000000UL, 0x3faf9c56UL, 0x1777f657UL, - 0x3e552f65UL, 0x00000000UL, 0x3fae91d2UL, 0xc317b86aUL, 0xbe5a61e0UL, - 0x00000000UL, 0x3fad87acUL, 0xb7664efbUL, 0xbe41f64eUL, 0x00000000UL, - 0x3fac7de6UL, 0x5d3d03a9UL, 0x3e0807a0UL, 0x00000000UL, 0x3fab7480UL, - 0x743c38ebUL, 0xbe3726e1UL, 0x00000000UL, 0x3faa6b78UL, 0x06a253f1UL, - 0x3e5ad636UL, 0x00000000UL, 0x3fa962d0UL, 0xa35f541bUL, 0x3e5a187aUL, - 0x00000000UL, 0x3fa85a88UL, 0x4b86e446UL, 0xbe508150UL, 0x00000000UL, - 0x3fa7529cUL, 0x2589cacfUL, 0x3e52938aUL, 0x00000000UL, 0x3fa64b10UL, - 0xaf6b11f2UL, 0xbe3454cdUL, 0x00000000UL, 0x3fa543e2UL, 0x97506fefUL, - 0xbe5fdec5UL, 0x00000000UL, 0x3fa43d10UL, 0xe75f7dd9UL, 0xbe388dd3UL, - 0x00000000UL, 0x3fa3369cUL, 0xa4139632UL, 0xbdea5177UL, 0x00000000UL, - 0x3fa23086UL, 0x352d6f1eUL, 0xbe565ad6UL, 0x00000000UL, 0x3fa12accUL, - 0x77449eb7UL, 0xbe50d5c7UL, 0x00000000UL, 0x3fa0256eUL, 0x7478da78UL, - 0x3e404724UL, 0x00000000UL, 0x3f9e40dcUL, 0xf59cef7fUL, 0xbe539d0aUL, - 0x00000000UL, 0x3f9c3790UL, 0x1511d43cUL, 0x3e53c2c8UL, 0x00000000UL, - 0x3f9a2f00UL, 0x9b8bff3cUL, 0xbe43b3e1UL, 0x00000000UL, 0x3f982724UL, - 0xad1e22a5UL, 0x3e46f0bdUL, 0x00000000UL, 0x3f962000UL, 0x130d9356UL, - 0x3e475ba0UL, 0x00000000UL, 0x3f941994UL, 0x8f86f883UL, 0xbe513d0bUL, - 0x00000000UL, 0x3f9213dcUL, 0x914d0dc8UL, 0xbe534335UL, 0x00000000UL, - 0x3f900ed8UL, 0x2d73e5e7UL, 0xbe22ba75UL, 0x00000000UL, 0x3f8c1510UL, - 0xc5b7d70eUL, 0x3e599c5dUL, 0x00000000UL, 0x3f880de0UL, 0x8a27857eUL, - 0xbe3d28c8UL, 0x00000000UL, 0x3f840810UL, 0xda767328UL, 0x3e531b3dUL, - 0x00000000UL, 0x3f8003b0UL, 0x77bacaf3UL, 0xbe5f04e3UL, 0x00000000UL, - 0x3f780150UL, 0xdf4b0720UL, 0x3e5a8bffUL, 0x00000000UL, 0x3f6ffc40UL, - 0x34c48e71UL, 0xbe3fcd99UL, 0x00000000UL, 0x3f5ff6c0UL, 0x1ad218afUL, - 0xbe4c78a7UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x80000000UL, - 0x00000000UL, 0xfffff800UL, 0x00000000UL, 0xfffff800UL, 0x00000000UL, - 0x3ff72000UL, 0x161bb241UL, 0xbf5dabe1UL, 0x6dc96112UL, 0xbf836578UL, - 0xee241472UL, 0xbf9b0301UL, 0x9f95985aUL, 0xbfb528dbUL, 0xb3841d2aUL, - 0xbfd619b6UL, 0x518775e3UL, 0x3f9004f2UL, 0xac8349bbUL, 0x3fa76c9bUL, - 0x486ececcUL, 0x3fc4635eUL, 0x161bb241UL, 0xbf5dabe1UL, 0x9f95985aUL, - 0xbfb528dbUL, 0xf8b5787dUL, 0x3ef2531eUL, 0x486ececbUL, 0x3fc4635eUL, - 0x412055ccUL, 0xbdd61bb2UL, 0x00000000UL, 0xfffffff8UL, 0x00000000UL, - 0xffffffffUL, 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x3b700000UL, - 0xfa5abcbfUL, 0x3ff00b1aUL, 0xa7609f71UL, 0xbc84f6b2UL, 0xa9fb3335UL, - 0x3ff0163dUL, 0x9ab8cdb7UL, 0x3c9b6129UL, 0x143b0281UL, 0x3ff02168UL, - 0x0fc54eb6UL, 0xbc82bf31UL, 0x3e778061UL, 0x3ff02c9aUL, 0x535b085dUL, - 0xbc719083UL, 0x2e11bbccUL, 0x3ff037d4UL, 0xeeade11aUL, 0x3c656811UL, - 0xe86e7f85UL, 0x3ff04315UL, 0x1977c96eUL, 0xbc90a31cUL, 0x72f654b1UL, - 0x3ff04e5fUL, 0x3aa0d08cUL, 0x3c84c379UL, 0xd3158574UL, 0x3ff059b0UL, - 0xa475b465UL, 0x3c8d73e2UL, 0x0e3c1f89UL, 0x3ff0650aUL, 0x5799c397UL, - 0xbc95cb7bUL, 0x29ddf6deUL, 0x3ff0706bUL, 0xe2b13c27UL, 0xbc8c91dfUL, - 0x2b72a836UL, 0x3ff07bd4UL, 0x54458700UL, 0x3c832334UL, 0x18759bc8UL, - 0x3ff08745UL, 0x4bb284ffUL, 0x3c6186beUL, 0xf66607e0UL, 0x3ff092bdUL, - 0x800a3fd1UL, 0xbc968063UL, 0xcac6f383UL, 0x3ff09e3eUL, 0x18316136UL, - 0x3c914878UL, 0x9b1f3919UL, 0x3ff0a9c7UL, 0x873d1d38UL, 0x3c85d16cUL, - 0x6cf9890fUL, 0x3ff0b558UL, 0x4adc610bUL, 0x3c98a62eUL, 0x45e46c85UL, - 0x3ff0c0f1UL, 0x06d21cefUL, 0x3c94f989UL, 0x2b7247f7UL, 0x3ff0cc92UL, - 0x16e24f71UL, 0x3c901edcUL, 0x23395decUL, 0x3ff0d83bUL, 0xe43f316aUL, - 0xbc9bc14dUL, 0x32d3d1a2UL, 0x3ff0e3ecUL, 0x27c57b52UL, 0x3c403a17UL, - 0x5fdfa9c5UL, 0x3ff0efa5UL, 0xbc54021bUL, 0xbc949db9UL, 0xaffed31bUL, - 0x3ff0fb66UL, 0xc44ebd7bUL, 0xbc6b9bedUL, 0x28d7233eUL, 0x3ff10730UL, - 0x1692fdd5UL, 0x3c8d46ebUL, 0xd0125b51UL, 0x3ff11301UL, 0x39449b3aUL, - 0xbc96c510UL, 0xab5e2ab6UL, 0x3ff11edbUL, 0xf703fb72UL, 0xbc9ca454UL, - 0xc06c31ccUL, 0x3ff12abdUL, 0xb36ca5c7UL, 0xbc51b514UL, 0x14f204abUL, - 0x3ff136a8UL, 0xba48dcf0UL, 0xbc67108fUL, 0xaea92de0UL, 0x3ff1429aUL, - 0x9af1369eUL, 0xbc932fbfUL, 0x934f312eUL, 0x3ff14e95UL, 0x39bf44abUL, - 0xbc8b91e8UL, 0xc8a58e51UL, 0x3ff15a98UL, 0xb9eeab0aUL, 0x3c82406aUL, - 0x5471c3c2UL, 0x3ff166a4UL, 0x82ea1a32UL, 0x3c58f23bUL, 0x3c7d517bUL, - 0x3ff172b8UL, 0xb9d78a76UL, 0xbc819041UL, 0x8695bbc0UL, 0x3ff17ed4UL, - 0xe2ac5a64UL, 0x3c709e3fUL, 0x388c8deaUL, 0x3ff18af9UL, 0xd1970f6cUL, - 0xbc911023UL, 0x58375d2fUL, 0x3ff19726UL, 0x85f17e08UL, 0x3c94aaddUL, - 0xeb6fcb75UL, 0x3ff1a35bUL, 0x7b4968e4UL, 0x3c8e5b4cUL, 0xf8138a1cUL, - 0x3ff1af99UL, 0xa4b69280UL, 0x3c97bf85UL, 0x84045cd4UL, 0x3ff1bbe0UL, - 0x352ef607UL, 0xbc995386UL, 0x95281c6bUL, 0x3ff1c82fUL, 0x8010f8c9UL, - 0x3c900977UL, 0x3168b9aaUL, 0x3ff1d487UL, 0x00a2643cUL, 0x3c9e016eUL, - 0x5eb44027UL, 0x3ff1e0e7UL, 0x088cb6deUL, 0xbc96fdd8UL, 0x22fcd91dUL, - 0x3ff1ed50UL, 0x027bb78cUL, 0xbc91df98UL, 0x8438ce4dUL, 0x3ff1f9c1UL, - 0xa097af5cUL, 0xbc9bf524UL, 0x88628cd6UL, 0x3ff2063bUL, 0x814a8495UL, - 0x3c8dc775UL, 0x3578a819UL, 0x3ff212beUL, 0x2cfcaac9UL, 0x3c93592dUL, - 0x917ddc96UL, 0x3ff21f49UL, 0x9494a5eeUL, 0x3c82a97eUL, 0xa27912d1UL, - 0x3ff22bddUL, 0x5577d69fUL, 0x3c8d34fbUL, 0x6e756238UL, 0x3ff2387aUL, - 0xb6c70573UL, 0x3c99b07eUL, 0xfb82140aUL, 0x3ff2451fUL, 0x911ca996UL, - 0x3c8acfccUL, 0x4fb2a63fUL, 0x3ff251ceUL, 0xbef4f4a4UL, 0x3c8ac155UL, - 0x711ece75UL, 0x3ff25e85UL, 0x4ac31b2cUL, 0x3c93e1a2UL, 0x65e27cddUL, - 0x3ff26b45UL, 0x9940e9d9UL, 0x3c82bd33UL, 0x341ddf29UL, 0x3ff2780eUL, - 0x05f9e76cUL, 0x3c9e067cUL, 0xe1f56381UL, 0x3ff284dfUL, 0x8c3f0d7eUL, - 0xbc9a4c3aUL, 0x7591bb70UL, 0x3ff291baUL, 0x28401cbdUL, 0xbc82cc72UL, - 0xf51fdee1UL, 0x3ff29e9dUL, 0xafad1255UL, 0x3c8612e8UL, 0x66d10f13UL, - 0x3ff2ab8aUL, 0x191690a7UL, 0xbc995743UL, 0xd0dad990UL, 0x3ff2b87fUL, - 0xd6381aa4UL, 0xbc410adcUL, 0x39771b2fUL, 0x3ff2c57eUL, 0xa6eb5124UL, - 0xbc950145UL, 0xa6e4030bUL, 0x3ff2d285UL, 0x54db41d5UL, 0x3c900247UL, - 0x1f641589UL, 0x3ff2df96UL, 0xfbbce198UL, 0x3c9d16cfUL, 0xa93e2f56UL, - 0x3ff2ecafUL, 0x45d52383UL, 0x3c71ca0fUL, 0x4abd886bUL, 0x3ff2f9d2UL, - 0x532bda93UL, 0xbc653c55UL, 0x0a31b715UL, 0x3ff306feUL, 0xd23182e4UL, - 0x3c86f46aUL, 0xedeeb2fdUL, 0x3ff31432UL, 0xf3f3fcd1UL, 0x3c8959a3UL, - 0xfc4cd831UL, 0x3ff32170UL, 0x8e18047cUL, 0x3c8a9ce7UL, 0x3ba8ea32UL, - 0x3ff32eb8UL, 0x3cb4f318UL, 0xbc9c45e8UL, 0xb26416ffUL, 0x3ff33c08UL, - 0x843659a6UL, 0x3c932721UL, 0x66e3fa2dUL, 0x3ff34962UL, 0x930881a4UL, - 0xbc835a75UL, 0x5f929ff1UL, 0x3ff356c5UL, 0x5c4e4628UL, 0xbc8b5ceeUL, - 0xa2de883bUL, 0x3ff36431UL, 0xa06cb85eUL, 0xbc8c3144UL, 0x373aa9cbUL, - 0x3ff371a7UL, 0xbf42eae2UL, 0xbc963aeaUL, 0x231e754aUL, 0x3ff37f26UL, - 0x9eceb23cUL, 0xbc99f5caUL, 0x6d05d866UL, 0x3ff38caeUL, 0x3c9904bdUL, - 0xbc9e958dUL, 0x1b7140efUL, 0x3ff39a40UL, 0xfc8e2934UL, 0xbc99a9a5UL, - 0x34e59ff7UL, 0x3ff3a7dbUL, 0xd661f5e3UL, 0xbc75e436UL, 0xbfec6cf4UL, - 0x3ff3b57fUL, 0xe26fff18UL, 0x3c954c66UL, 0xc313a8e5UL, 0x3ff3c32dUL, - 0x375d29c3UL, 0xbc9efff8UL, 0x44ede173UL, 0x3ff3d0e5UL, 0x8c284c71UL, - 0x3c7fe8d0UL, 0x4c123422UL, 0x3ff3dea6UL, 0x11f09ebcUL, 0x3c8ada09UL, - 0xdf1c5175UL, 0x3ff3ec70UL, 0x7b8c9bcaUL, 0xbc8af663UL, 0x04ac801cUL, - 0x3ff3fa45UL, 0xf956f9f3UL, 0xbc97d023UL, 0xc367a024UL, 0x3ff40822UL, - 0xb6f4d048UL, 0x3c8bddf8UL, 0x21f72e2aUL, 0x3ff4160aUL, 0x1c309278UL, - 0xbc5ef369UL, 0x2709468aUL, 0x3ff423fbUL, 0xc0b314ddUL, 0xbc98462dUL, - 0xd950a897UL, 0x3ff431f5UL, 0xe35f7999UL, 0xbc81c7ddUL, 0x3f84b9d4UL, - 0x3ff43ffaUL, 0x9704c003UL, 0x3c8880beUL, 0x6061892dUL, 0x3ff44e08UL, - 0x04ef80d0UL, 0x3c489b7aUL, 0x42a7d232UL, 0x3ff45c20UL, 0x82fb1f8eUL, - 0xbc686419UL, 0xed1d0057UL, 0x3ff46a41UL, 0xd1648a76UL, 0x3c9c944bUL, - 0x668b3237UL, 0x3ff4786dUL, 0xed445733UL, 0xbc9c20f0UL, 0xb5c13cd0UL, - 0x3ff486a2UL, 0xb69062f0UL, 0x3c73c1a3UL, 0xe192aed2UL, 0x3ff494e1UL, - 0x5e499ea0UL, 0xbc83b289UL, 0xf0d7d3deUL, 0x3ff4a32aUL, 0xf3d1be56UL, - 0x3c99cb62UL, 0xea6db7d7UL, 0x3ff4b17dUL, 0x7f2897f0UL, 0xbc8125b8UL, - 0xd5362a27UL, 0x3ff4bfdaUL, 0xafec42e2UL, 0x3c7d4397UL, 0xb817c114UL, - 0x3ff4ce41UL, 0x690abd5dUL, 0x3c905e29UL, 0x99fddd0dUL, 0x3ff4dcb2UL, - 0xbc6a7833UL, 0x3c98ecdbUL, 0x81d8abffUL, 0x3ff4eb2dUL, 0x2e5d7a52UL, - 0xbc95257dUL, 0x769d2ca7UL, 0x3ff4f9b2UL, 0xd25957e3UL, 0xbc94b309UL, - 0x7f4531eeUL, 0x3ff50841UL, 0x49b7465fUL, 0x3c7a249bUL, 0xa2cf6642UL, - 0x3ff516daUL, 0x69bd93efUL, 0xbc8f7685UL, 0xe83f4eefUL, 0x3ff5257dUL, - 0x43efef71UL, 0xbc7c998dUL, 0x569d4f82UL, 0x3ff5342bUL, 0x1db13cadUL, - 0xbc807abeUL, 0xf4f6ad27UL, 0x3ff542e2UL, 0x192d5f7eUL, 0x3c87926dUL, - 0xca5d920fUL, 0x3ff551a4UL, 0xefede59bUL, 0xbc8d689cUL, 0xdde910d2UL, - 0x3ff56070UL, 0x168eebf0UL, 0xbc90fb6eUL, 0x36b527daUL, 0x3ff56f47UL, - 0x011d93adUL, 0x3c99bb2cUL, 0xdbe2c4cfUL, 0x3ff57e27UL, 0x8a57b9c4UL, - 0xbc90b98cUL, 0xd497c7fdUL, 0x3ff58d12UL, 0x5b9a1de8UL, 0x3c8295e1UL, - 0x27ff07ccUL, 0x3ff59c08UL, 0xe467e60fUL, 0xbc97e2ceUL, 0xdd485429UL, - 0x3ff5ab07UL, 0x054647adUL, 0x3c96324cUL, 0xfba87a03UL, 0x3ff5ba11UL, - 0x4c233e1aUL, 0xbc9b77a1UL, 0x8a5946b7UL, 0x3ff5c926UL, 0x816986a2UL, - 0x3c3c4b1bUL, 0x90998b93UL, 0x3ff5d845UL, 0xa8b45643UL, 0xbc9cd6a7UL, - 0x15ad2148UL, 0x3ff5e76fUL, 0x3080e65eUL, 0x3c9ba6f9UL, 0x20dceb71UL, - 0x3ff5f6a3UL, 0xe3cdcf92UL, 0xbc89eaddUL, 0xb976dc09UL, 0x3ff605e1UL, - 0x9b56de47UL, 0xbc93e242UL, 0xe6cdf6f4UL, 0x3ff6152aUL, 0x4ab84c27UL, - 0x3c9e4b3eUL, 0xb03a5585UL, 0x3ff6247eUL, 0x7e40b497UL, 0xbc9383c1UL, - 0x1d1929fdUL, 0x3ff633ddUL, 0xbeb964e5UL, 0x3c984710UL, 0x34ccc320UL, - 0x3ff64346UL, 0x759d8933UL, 0xbc8c483cUL, 0xfebc8fb7UL, 0x3ff652b9UL, - 0xc9a73e09UL, 0xbc9ae3d5UL, 0x82552225UL, 0x3ff66238UL, 0x87591c34UL, - 0xbc9bb609UL, 0xc70833f6UL, 0x3ff671c1UL, 0x586c6134UL, 0xbc8e8732UL, - 0xd44ca973UL, 0x3ff68155UL, 0x44f73e65UL, 0x3c6038aeUL, 0xb19e9538UL, - 0x3ff690f4UL, 0x9aeb445dUL, 0x3c8804bdUL, 0x667f3bcdUL, 0x3ff6a09eUL, - 0x13b26456UL, 0xbc9bdd34UL, 0xfa75173eUL, 0x3ff6b052UL, 0x2c9a9d0eUL, - 0x3c7a38f5UL, 0x750bdabfUL, 0x3ff6c012UL, 0x67ff0b0dUL, 0xbc728956UL, - 0xddd47645UL, 0x3ff6cfdcUL, 0xb6f17309UL, 0x3c9c7aa9UL, 0x3c651a2fUL, - 0x3ff6dfb2UL, 0x683c88abUL, 0xbc6bbe3aUL, 0x98593ae5UL, 0x3ff6ef92UL, - 0x9e1ac8b2UL, 0xbc90b974UL, 0xf9519484UL, 0x3ff6ff7dUL, 0x25860ef6UL, - 0xbc883c0fUL, 0x66f42e87UL, 0x3ff70f74UL, 0xd45aa65fUL, 0x3c59d644UL, - 0xe8ec5f74UL, 0x3ff71f75UL, 0x86887a99UL, 0xbc816e47UL, 0x86ead08aUL, - 0x3ff72f82UL, 0x2cd62c72UL, 0xbc920aa0UL, 0x48a58174UL, 0x3ff73f9aUL, - 0x6c65d53cUL, 0xbc90a8d9UL, 0x35d7cbfdUL, 0x3ff74fbdUL, 0x618a6e1cUL, - 0x3c9047fdUL, 0x564267c9UL, 0x3ff75febUL, 0x57316dd3UL, 0xbc902459UL, - 0xb1ab6e09UL, 0x3ff77024UL, 0x169147f8UL, 0x3c9b7877UL, 0x4fde5d3fUL, - 0x3ff78069UL, 0x0a02162dUL, 0x3c9866b8UL, 0x38ac1cf6UL, 0x3ff790b9UL, - 0x62aadd3eUL, 0x3c9349a8UL, 0x73eb0187UL, 0x3ff7a114UL, 0xee04992fUL, - 0xbc841577UL, 0x0976cfdbUL, 0x3ff7b17bUL, 0x8468dc88UL, 0xbc9bebb5UL, - 0x0130c132UL, 0x3ff7c1edUL, 0xd1164dd6UL, 0x3c9f124cUL, 0x62ff86f0UL, - 0x3ff7d26aUL, 0xfb72b8b4UL, 0x3c91bddbUL, 0x36cf4e62UL, 0x3ff7e2f3UL, - 0xba15797eUL, 0x3c705d02UL, 0x8491c491UL, 0x3ff7f387UL, 0xcf9311aeUL, - 0xbc807f11UL, 0x543e1a12UL, 0x3ff80427UL, 0x626d972bUL, 0xbc927c86UL, - 0xadd106d9UL, 0x3ff814d2UL, 0x0d151d4dUL, 0x3c946437UL, 0x994cce13UL, - 0x3ff82589UL, 0xd41532d8UL, 0xbc9d4c1dUL, 0x1eb941f7UL, 0x3ff8364cUL, - 0x31df2bd5UL, 0x3c999b9aUL, 0x4623c7adUL, 0x3ff8471aUL, 0xa341cdfbUL, - 0xbc88d684UL, 0x179f5b21UL, 0x3ff857f4UL, 0xf8b216d0UL, 0xbc5ba748UL, - 0x9b4492edUL, 0x3ff868d9UL, 0x9bd4f6baUL, 0xbc9fc6f8UL, 0xd931a436UL, - 0x3ff879caUL, 0xd2db47bdUL, 0x3c85d2d7UL, 0xd98a6699UL, 0x3ff88ac7UL, - 0xf37cb53aUL, 0x3c9994c2UL, 0xa478580fUL, 0x3ff89bd0UL, 0x4475202aUL, - 0x3c9d5395UL, 0x422aa0dbUL, 0x3ff8ace5UL, 0x56864b27UL, 0x3c96e9f1UL, - 0xbad61778UL, 0x3ff8be05UL, 0xfc43446eUL, 0x3c9ecb5eUL, 0x16b5448cUL, - 0x3ff8cf32UL, 0x32e9e3aaUL, 0xbc70d55eUL, 0x5e0866d9UL, 0x3ff8e06aUL, - 0x6fc9b2e6UL, 0xbc97114aUL, 0x99157736UL, 0x3ff8f1aeUL, 0xa2e3976cUL, - 0x3c85cc13UL, 0xd0282c8aUL, 0x3ff902feUL, 0x85fe3fd2UL, 0x3c9592caUL, - 0x0b91ffc6UL, 0x3ff9145bUL, 0x2e582524UL, 0xbc9dd679UL, 0x53aa2fe2UL, - 0x3ff925c3UL, 0xa639db7fUL, 0xbc83455fUL, 0xb0cdc5e5UL, 0x3ff93737UL, - 0x81b57ebcUL, 0xbc675fc7UL, 0x2b5f98e5UL, 0x3ff948b8UL, 0x797d2d99UL, - 0xbc8dc3d6UL, 0xcbc8520fUL, 0x3ff95a44UL, 0x96a5f039UL, 0xbc764b7cUL, - 0x9a7670b3UL, 0x3ff96bddUL, 0x7f19c896UL, 0xbc5ba596UL, 0x9fde4e50UL, - 0x3ff97d82UL, 0x7c1b85d1UL, 0xbc9d185bUL, 0xe47a22a2UL, 0x3ff98f33UL, - 0xa24c78ecUL, 0x3c7cabdaUL, 0x70ca07baUL, 0x3ff9a0f1UL, 0x91cee632UL, - 0xbc9173bdUL, 0x4d53fe0dUL, 0x3ff9b2bbUL, 0x4df6d518UL, 0xbc9dd84eUL, - 0x82a3f090UL, 0x3ff9c491UL, 0xb071f2beUL, 0x3c7c7c46UL, 0x194bb8d5UL, - 0x3ff9d674UL, 0xa3dd8233UL, 0xbc9516beUL, 0x19e32323UL, 0x3ff9e863UL, - 0x78e64c6eUL, 0x3c7824caUL, 0x8d07f29eUL, 0x3ff9fa5eUL, 0xaaf1faceUL, - 0xbc84a9ceUL, 0x7b5de565UL, 0x3ffa0c66UL, 0x5d1cd533UL, 0xbc935949UL, - 0xed8eb8bbUL, 0x3ffa1e7aUL, 0xee8be70eUL, 0x3c9c6618UL, 0xec4a2d33UL, - 0x3ffa309bUL, 0x7ddc36abUL, 0x3c96305cUL, 0x80460ad8UL, 0x3ffa42c9UL, - 0x589fb120UL, 0xbc9aa780UL, 0xb23e255dUL, 0x3ffa5503UL, 0xdb8d41e1UL, - 0xbc9d2f6eUL, 0x8af46052UL, 0x3ffa674aUL, 0x30670366UL, 0x3c650f56UL, - 0x1330b358UL, 0x3ffa799eUL, 0xcac563c7UL, 0x3c9bcb7eUL, 0x53c12e59UL, - 0x3ffa8bfeUL, 0xb2ba15a9UL, 0xbc94f867UL, 0x5579fdbfUL, 0x3ffa9e6bUL, - 0x0ef7fd31UL, 0x3c90fac9UL, 0x21356ebaUL, 0x3ffab0e5UL, 0xdae94545UL, - 0x3c889c31UL, 0xbfd3f37aUL, 0x3ffac36bUL, 0xcae76cd0UL, 0xbc8f9234UL, - 0x3a3c2774UL, 0x3ffad5ffUL, 0xb6b1b8e5UL, 0x3c97ef3bUL, 0x995ad3adUL, - 0x3ffae89fUL, 0x345dcc81UL, 0x3c97a1cdUL, 0xe622f2ffUL, 0x3ffafb4cUL, - 0x0f315ecdUL, 0xbc94b2fcUL, 0x298db666UL, 0x3ffb0e07UL, 0x4c80e425UL, - 0xbc9bdef5UL, 0x6c9a8952UL, 0x3ffb20ceUL, 0x4a0756ccUL, 0x3c94dd02UL, - 0xb84f15fbUL, 0x3ffb33a2UL, 0x3084d708UL, 0xbc62805eUL, 0x15b749b1UL, - 0x3ffb4684UL, 0xe9df7c90UL, 0xbc7f763dUL, 0x8de5593aUL, 0x3ffb5972UL, - 0xbbba6de3UL, 0xbc9c71dfUL, 0x29f1c52aUL, 0x3ffb6c6eUL, 0x52883f6eUL, - 0x3c92a8f3UL, 0xf2fb5e47UL, 0x3ffb7f76UL, 0x7e54ac3bUL, 0xbc75584fUL, - 0xf22749e4UL, 0x3ffb928cUL, 0x54cb65c6UL, 0xbc9b7216UL, 0x30a1064aUL, - 0x3ffba5b0UL, 0x0e54292eUL, 0xbc9efcd3UL, 0xb79a6f1fUL, 0x3ffbb8e0UL, - 0xc9696205UL, 0xbc3f52d1UL, 0x904bc1d2UL, 0x3ffbcc1eUL, 0x7a2d9e84UL, - 0x3c823dd0UL, 0xc3f3a207UL, 0x3ffbdf69UL, 0x60ea5b53UL, 0xbc3c2623UL, - 0x5bd71e09UL, 0x3ffbf2c2UL, 0x3f6b9c73UL, 0xbc9efdcaUL, 0x6141b33dUL, - 0x3ffc0628UL, 0xa1fbca34UL, 0xbc8d8a5aUL, 0xdd85529cUL, 0x3ffc199bUL, - 0x895048ddUL, 0x3c811065UL, 0xd9fa652cUL, 0x3ffc2d1cUL, 0x17c8a5d7UL, - 0xbc96e516UL, 0x5fffd07aUL, 0x3ffc40abUL, 0xe083c60aUL, 0x3c9b4537UL, - 0x78fafb22UL, 0x3ffc5447UL, 0x2493b5afUL, 0x3c912f07UL, 0x2e57d14bUL, - 0x3ffc67f1UL, 0xff483cadUL, 0x3c92884dUL, 0x8988c933UL, 0x3ffc7ba8UL, - 0xbe255559UL, 0xbc8e76bbUL, 0x9406e7b5UL, 0x3ffc8f6dUL, 0x48805c44UL, - 0x3c71acbcUL, 0x5751c4dbUL, 0x3ffca340UL, 0xd10d08f5UL, 0xbc87f2beUL, - 0xdcef9069UL, 0x3ffcb720UL, 0xd1e949dbUL, 0x3c7503cbUL, 0x2e6d1675UL, - 0x3ffccb0fUL, 0x86009092UL, 0xbc7d220fUL, 0x555dc3faUL, 0x3ffcdf0bUL, - 0x53829d72UL, 0xbc8dd83bUL, 0x5b5bab74UL, 0x3ffcf315UL, 0xb86dff57UL, - 0xbc9a08e9UL, 0x4a07897cUL, 0x3ffd072dUL, 0x43797a9cUL, 0xbc9cbc37UL, - 0x2b08c968UL, 0x3ffd1b53UL, 0x219a36eeUL, 0x3c955636UL, 0x080d89f2UL, - 0x3ffd2f87UL, 0x719d8578UL, 0xbc9d487bUL, 0xeacaa1d6UL, 0x3ffd43c8UL, - 0xbf5a1614UL, 0x3c93db53UL, 0xdcfba487UL, 0x3ffd5818UL, 0xd75b3707UL, - 0x3c82ed02UL, 0xe862e6d3UL, 0x3ffd6c76UL, 0x4a8165a0UL, 0x3c5fe87aUL, - 0x16c98398UL, 0x3ffd80e3UL, 0x8beddfe8UL, 0xbc911ec1UL, 0x71ff6075UL, - 0x3ffd955dUL, 0xbb9af6beUL, 0x3c9a052dUL, 0x03db3285UL, 0x3ffda9e6UL, - 0x696db532UL, 0x3c9c2300UL, 0xd63a8315UL, 0x3ffdbe7cUL, 0x926b8be4UL, - 0xbc9b76f1UL, 0xf301b460UL, 0x3ffdd321UL, 0x78f018c3UL, 0x3c92da57UL, - 0x641c0658UL, 0x3ffde7d5UL, 0x8e79ba8fUL, 0xbc9ca552UL, 0x337b9b5fUL, - 0x3ffdfc97UL, 0x4f184b5cUL, 0xbc91a5cdUL, 0x6b197d17UL, 0x3ffe1167UL, - 0xbd5c7f44UL, 0xbc72b529UL, 0x14f5a129UL, 0x3ffe2646UL, 0x817a1496UL, - 0xbc97b627UL, 0x3b16ee12UL, 0x3ffe3b33UL, 0x31fdc68bUL, 0xbc99f4a4UL, - 0xe78b3ff6UL, 0x3ffe502eUL, 0x80a9cc8fUL, 0x3c839e89UL, 0x24676d76UL, - 0x3ffe6539UL, 0x7522b735UL, 0xbc863ff8UL, 0xfbc74c83UL, 0x3ffe7a51UL, - 0xca0c8de2UL, 0x3c92d522UL, 0x77cdb740UL, 0x3ffe8f79UL, 0x80b054b1UL, - 0xbc910894UL, 0xa2a490daUL, 0x3ffea4afUL, 0x179c2893UL, 0xbc9e9c23UL, - 0x867cca6eUL, 0x3ffeb9f4UL, 0x2293e4f2UL, 0x3c94832fUL, 0x2d8e67f1UL, - 0x3ffecf48UL, 0xb411ad8cUL, 0xbc9c93f3UL, 0xa2188510UL, 0x3ffee4aaUL, - 0xa487568dUL, 0x3c91c68dUL, 0xee615a27UL, 0x3ffefa1bUL, 0x86a4b6b0UL, - 0x3c9dc7f4UL, 0x1cb6412aUL, 0x3fff0f9cUL, 0x65181d45UL, 0xbc932200UL, - 0x376bba97UL, 0x3fff252bUL, 0xbf0d8e43UL, 0x3c93a1a5UL, 0x48dd7274UL, - 0x3fff3ac9UL, 0x3ed837deUL, 0xbc795a5aUL, 0x5b6e4540UL, 0x3fff5076UL, - 0x2dd8a18bUL, 0x3c99d3e1UL, 0x798844f8UL, 0x3fff6632UL, 0x3539343eUL, - 0x3c9fa37bUL, 0xad9cbe14UL, 0x3fff7bfdUL, 0xd006350aUL, 0xbc9dbb12UL, - 0x02243c89UL, 0x3fff91d8UL, 0xa779f689UL, 0xbc612ea8UL, 0x819e90d8UL, - 0x3fffa7c1UL, 0xf3a5931eUL, 0x3c874853UL, 0x3692d514UL, 0x3fffbdbaUL, - 0x15098eb6UL, 0xbc796773UL, 0x2b8f71f1UL, 0x3fffd3c2UL, 0x966579e7UL, - 0x3c62eb74UL, 0x6b2a23d9UL, 0x3fffe9d9UL, 0x7442fde3UL, 0x3c74a603UL, - 0xe78a6731UL, 0x3f55d87fUL, 0xd704a0c0UL, 0x3fac6b08UL, 0x6fba4e77UL, - 0x3f83b2abUL, 0xff82c58fUL, 0x3fcebfbdUL, 0xfefa39efUL, 0x3fe62e42UL, - 0x00000000UL, 0x00000000UL, 0xfefa39efUL, 0x3fe62e42UL, 0xfefa39efUL, - 0xbfe62e42UL, 0xf8000000UL, 0xffffffffUL, 0xf8000000UL, 0xffffffffUL, - 0x00000000UL, 0x80000000UL, 0x00000000UL, 0x00000000UL - + 0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL }; -//registers, -// input: xmm0, xmm1 -// scratch: xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 -// eax, edx, ecx, ebx +ALIGNED_(16) juint _P_2[] = +{ + 0x1a600000UL, 0x3d90b461UL, 0x1a600000UL, 0x3d90b461UL +}; -// Code generated by Intel C compiler for LIBM library +ALIGNED_(16) juint _SC_4[] = +{ + 0xa556c734UL, 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL +}; -void MacroAssembler::fast_pow(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register tmp) { - Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; - Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; - Label L_2TAG_PACKET_8_0_2, L_2TAG_PACKET_9_0_2, L_2TAG_PACKET_10_0_2, L_2TAG_PACKET_11_0_2; - Label L_2TAG_PACKET_12_0_2, L_2TAG_PACKET_13_0_2, L_2TAG_PACKET_14_0_2, L_2TAG_PACKET_15_0_2; - Label L_2TAG_PACKET_16_0_2, L_2TAG_PACKET_17_0_2, L_2TAG_PACKET_18_0_2, L_2TAG_PACKET_19_0_2; - Label L_2TAG_PACKET_20_0_2, L_2TAG_PACKET_21_0_2, L_2TAG_PACKET_22_0_2, L_2TAG_PACKET_23_0_2; - Label L_2TAG_PACKET_24_0_2, L_2TAG_PACKET_25_0_2, L_2TAG_PACKET_26_0_2, L_2TAG_PACKET_27_0_2; - Label L_2TAG_PACKET_28_0_2, L_2TAG_PACKET_29_0_2, L_2TAG_PACKET_30_0_2, L_2TAG_PACKET_31_0_2; - Label L_2TAG_PACKET_32_0_2, L_2TAG_PACKET_33_0_2, L_2TAG_PACKET_34_0_2, L_2TAG_PACKET_35_0_2; - Label L_2TAG_PACKET_36_0_2, L_2TAG_PACKET_37_0_2, L_2TAG_PACKET_38_0_2, L_2TAG_PACKET_39_0_2; - Label L_2TAG_PACKET_40_0_2, L_2TAG_PACKET_41_0_2, L_2TAG_PACKET_42_0_2, L_2TAG_PACKET_43_0_2; - Label L_2TAG_PACKET_44_0_2, L_2TAG_PACKET_45_0_2, L_2TAG_PACKET_46_0_2, L_2TAG_PACKET_47_0_2; - Label L_2TAG_PACKET_48_0_2, L_2TAG_PACKET_49_0_2, L_2TAG_PACKET_50_0_2, L_2TAG_PACKET_51_0_2; - Label L_2TAG_PACKET_52_0_2, L_2TAG_PACKET_53_0_2, L_2TAG_PACKET_54_0_2, L_2TAG_PACKET_55_0_2; - Label L_2TAG_PACKET_56_0_2, L_2TAG_PACKET_57_0_2, L_2TAG_PACKET_58_0_2, start; +ALIGNED_(16) juint _Ctable[] = +{ + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, 0xbf73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, + 0xc0000000UL, 0xbc626d19UL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, + 0xbfa60beaUL, 0x2ed59f06UL, 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, + 0x00000000UL, 0x3ff00000UL, 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, + 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, 0x00000000UL, 0x3ff00000UL, + 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, 0x20000000UL, + 0x3c5e0d89UL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, 0xbfc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0x3ff00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, + 0x20000000UL, 0x3c68076aUL, 0x00000000UL, 0x3ff00000UL, 0x99fcef32UL, + 0x3fca8279UL, 0x667f3bcdUL, 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, + 0x00000000UL, 0x3fe00000UL, 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, + 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, 0x00000000UL, 0x3fe00000UL, + 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, 0xe0000000UL, + 0x3c39f630UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, 0xbf9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0x3fe00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0x3fed906bUL, + 0x20000000UL, 0x3c7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x76acf82dUL, + 0x3fa4a031UL, 0x56c62ddaUL, 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, + 0x00000000UL, 0x3fd00000UL, 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, + 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, 0x00000000UL, 0x3fd00000UL, + 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, 0x40000000UL, + 0xbc887df6UL, 0x00000000UL, 0x3fc00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x3ff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0x3fefd88dUL, + 0x40000000UL, 0xbc887df6UL, 0x00000000UL, 0xbfc00000UL, 0x0e5967d5UL, + 0x3fac1d1fUL, 0xcff75cb0UL, 0x3fef6297UL, 0x20000000UL, 0x3c756217UL, + 0x00000000UL, 0xbfd00000UL, 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, + 0x3fee9f41UL, 0xe0000000UL, 0x3c8760b1UL, 0x00000000UL, 0xbfd00000UL, + 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, 0x3fed906bUL, 0x20000000UL, + 0x3c7457e6UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, 0x3f9d4a2cUL, + 0xf180bdb1UL, 0x3fec38b2UL, 0x80000000UL, 0xbc76e0b1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0x3fea9b66UL, + 0xe0000000UL, 0x3c39f630UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, + 0xbfc133ccUL, 0x6b151741UL, 0x3fe8bc80UL, 0x20000000UL, 0xbc82c5e1UL, + 0x00000000UL, 0xbfe00000UL, 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, + 0x3fe6a09eUL, 0x20000000UL, 0xbc8bdd34UL, 0x00000000UL, 0xbfe00000UL, + 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, 0x3fe44cf3UL, 0x20000000UL, + 0x3c68076aUL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, 0x3fc59267UL, + 0x39ae68c8UL, 0x3fe1c73bUL, 0x20000000UL, 0x3c8b25ddUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0x3fde2b5dUL, + 0x20000000UL, 0x3c5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, + 0x3fb37ca1UL, 0xa6aea963UL, 0x3fd87de2UL, 0xe0000000UL, 0xbc672cedUL, + 0x00000000UL, 0xbff00000UL, 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, + 0x3fd29406UL, 0xa0000000UL, 0xbc75d28dUL, 0x00000000UL, 0xbff00000UL, + 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, 0x3fc8f8b8UL, 0xc0000000UL, + 0xbc626d19UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, 0x3f73b92eUL, + 0xbc29b42cUL, 0x3fb917a6UL, 0xe0000000UL, 0xbc3e2718UL, 0x00000000UL, + 0xbff00000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x176d6d31UL, + 0x3f73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0xbff00000UL, 0x011469fbUL, 0x3f93ad06UL, 0x3c69a60bUL, + 0xbfc8f8b8UL, 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0xbff00000UL, + 0x939d225aUL, 0x3fa60beaUL, 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, + 0x3c75d28dUL, 0x00000000UL, 0xbff00000UL, 0x866b95cfUL, 0x3fb37ca1UL, + 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, 0x3c672cedUL, 0x00000000UL, + 0xbff00000UL, 0x73fa1279UL, 0x3fbe3a68UL, 0x3806f63bUL, 0xbfde2b5dUL, + 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0xbff00000UL, 0x5bc57974UL, + 0x3fc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0xbff00000UL, 0x53aba2fdUL, 0x3fcd0dfeUL, 0x25091dd6UL, + 0xbfe44cf3UL, 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0xbff00000UL, + 0x99fcef32UL, 0xbfca8279UL, 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, + 0x3c8bdd34UL, 0x00000000UL, 0xbfe00000UL, 0x94247758UL, 0xbfc133ccUL, + 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, 0x3c82c5e1UL, 0x00000000UL, + 0xbfe00000UL, 0x9ae68c87UL, 0xbfac73b3UL, 0x290ea1a3UL, 0xbfea9b66UL, + 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0xbfe00000UL, 0x7f909c4eUL, + 0x3f9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0xbfe00000UL, 0x65455a75UL, 0x3fbe0875UL, 0xcf328d46UL, + 0xbfed906bUL, 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0xbfe00000UL, + 0x76acf82dUL, 0xbfa4a031UL, 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, + 0xbc8760b1UL, 0x00000000UL, 0xbfd00000UL, 0x0e5967d5UL, 0x3fac1d1fUL, + 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, 0xbc756217UL, 0x00000000UL, + 0xbfd00000UL, 0x0f592f50UL, 0x3f9ba165UL, 0xa3d12526UL, 0xbfefd88dUL, + 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0xbfc00000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0xbff00000UL, 0x00000000UL, 0x00000000UL, + 0x00000000UL, 0x00000000UL, 0x0f592f50UL, 0xbf9ba165UL, 0xa3d12526UL, + 0xbfefd88dUL, 0x40000000UL, 0x3c887df6UL, 0x00000000UL, 0x3fc00000UL, + 0x0e5967d5UL, 0xbfac1d1fUL, 0xcff75cb0UL, 0xbfef6297UL, 0x20000000UL, + 0xbc756217UL, 0x00000000UL, 0x3fd00000UL, 0x76acf82dUL, 0x3fa4a031UL, + 0x56c62ddaUL, 0xbfee9f41UL, 0xe0000000UL, 0xbc8760b1UL, 0x00000000UL, + 0x3fd00000UL, 0x65455a75UL, 0xbfbe0875UL, 0xcf328d46UL, 0xbfed906bUL, + 0x20000000UL, 0xbc7457e6UL, 0x00000000UL, 0x3fe00000UL, 0x7f909c4eUL, + 0xbf9d4a2cUL, 0xf180bdb1UL, 0xbfec38b2UL, 0x80000000UL, 0x3c76e0b1UL, + 0x00000000UL, 0x3fe00000UL, 0x9ae68c87UL, 0x3fac73b3UL, 0x290ea1a3UL, + 0xbfea9b66UL, 0xe0000000UL, 0xbc39f630UL, 0x00000000UL, 0x3fe00000UL, + 0x94247758UL, 0x3fc133ccUL, 0x6b151741UL, 0xbfe8bc80UL, 0x20000000UL, + 0x3c82c5e1UL, 0x00000000UL, 0x3fe00000UL, 0x99fcef32UL, 0x3fca8279UL, + 0x667f3bcdUL, 0xbfe6a09eUL, 0x20000000UL, 0x3c8bdd34UL, 0x00000000UL, + 0x3fe00000UL, 0x53aba2fdUL, 0xbfcd0dfeUL, 0x25091dd6UL, 0xbfe44cf3UL, + 0x20000000UL, 0xbc68076aUL, 0x00000000UL, 0x3ff00000UL, 0x5bc57974UL, + 0xbfc59267UL, 0x39ae68c8UL, 0xbfe1c73bUL, 0x20000000UL, 0xbc8b25ddUL, + 0x00000000UL, 0x3ff00000UL, 0x73fa1279UL, 0xbfbe3a68UL, 0x3806f63bUL, + 0xbfde2b5dUL, 0x20000000UL, 0xbc5e0d89UL, 0x00000000UL, 0x3ff00000UL, + 0x866b95cfUL, 0xbfb37ca1UL, 0xa6aea963UL, 0xbfd87de2UL, 0xe0000000UL, + 0x3c672cedUL, 0x00000000UL, 0x3ff00000UL, 0x939d225aUL, 0xbfa60beaUL, + 0x2ed59f06UL, 0xbfd29406UL, 0xa0000000UL, 0x3c75d28dUL, 0x00000000UL, + 0x3ff00000UL, 0x011469fbUL, 0xbf93ad06UL, 0x3c69a60bUL, 0xbfc8f8b8UL, + 0xc0000000UL, 0x3c626d19UL, 0x00000000UL, 0x3ff00000UL, 0x176d6d31UL, + 0xbf73b92eUL, 0xbc29b42cUL, 0xbfb917a6UL, 0xe0000000UL, 0x3c3e2718UL, + 0x00000000UL, 0x3ff00000UL +}; - assert_different_registers(tmp, eax, ecx, edx); +ALIGNED_(16) juint _SC_2[] = +{ + 0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL +}; - address static_const_table_pow = (address)_static_const_table_pow; +ALIGNED_(16) juint _SC_3[] = +{ + 0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL +}; + +ALIGNED_(16) juint _SC_1[] = +{ + 0x55555555UL, 0xbfc55555UL, 0x00000000UL, 0xbfe00000UL +}; + +ALIGNED_(16) juint _PI_INV_TABLE[] = +{ + 0x00000000UL, 0x00000000UL, 0xa2f9836eUL, 0x4e441529UL, 0xfc2757d1UL, + 0xf534ddc0UL, 0xdb629599UL, 0x3c439041UL, 0xfe5163abUL, 0xdebbc561UL, + 0xb7246e3aUL, 0x424dd2e0UL, 0x06492eeaUL, 0x09d1921cUL, 0xfe1deb1cUL, + 0xb129a73eUL, 0xe88235f5UL, 0x2ebb4484UL, 0xe99c7026UL, 0xb45f7e41UL, + 0x3991d639UL, 0x835339f4UL, 0x9c845f8bUL, 0xbdf9283bUL, 0x1ff897ffUL, + 0xde05980fUL, 0xef2f118bUL, 0x5a0a6d1fUL, 0x6d367ecfUL, 0x27cb09b7UL, + 0x4f463f66UL, 0x9e5fea2dUL, 0x7527bac7UL, 0xebe5f17bUL, 0x3d0739f7UL, + 0x8a5292eaUL, 0x6bfb5fb1UL, 0x1f8d5d08UL, 0x56033046UL, 0xfc7b6babUL, + 0xf0cfbc21UL +}; + +ALIGNED_(8) juint _PI_4[] = +{ + 0x40000000UL, 0x3fe921fbUL, 0x18469899UL, 0x3e64442dUL +}; + +ALIGNED_(8) juint _PI32INV[] = +{ + 0x6dc9c883UL, 0x40245f30UL +}; + +ALIGNED_(8) juint _SHIFTER[] = +{ + 0x00000000UL, 0x43380000UL +}; + +ALIGNED_(8) juint _SIGN_MASK[] = +{ + 0x00000000UL, 0x80000000UL +}; + +ALIGNED_(8) juint _P_3[] = +{ + 0x2e037073UL, 0x3b63198aUL +}; + +ALIGNED_(8) juint _ALL_ONES[] = +{ + 0xffffffffUL, 0x3fefffffUL +}; + +ALIGNED_(8) juint _TWO_POW_55[] = +{ + 0x00000000UL, 0x43600000UL +}; + +ALIGNED_(8) juint _TWO_POW_M55[] = +{ + 0x00000000UL, 0x3c800000ULL +}; + +ALIGNED_(8) juint _P_1[] = +{ + 0x54400000UL, 0x3fb921fbUL +}; + +ALIGNED_(8) juint _NEG_ZERO[] = +{ + 0x00000000UL, 0x80000000UL +}; + +void MacroAssembler::fast_sin(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ebx, Register ecx, Register edx, Register tmp1, Register tmp2, Register tmp3, Register tmp4) { + Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; + Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; + Label L_2TAG_PACKET_8_0_1, L_2TAG_PACKET_9_0_1, L_2TAG_PACKET_10_0_1, L_2TAG_PACKET_11_0_1; + Label L_2TAG_PACKET_13_0_1, L_2TAG_PACKET_14_0_1; + Label L_2TAG_PACKET_12_0_1, B1_1, B1_2, B1_4, start; + + assert_different_registers(tmp1, tmp2, tmp3, tmp4, eax, ebx, ecx, edx); + address ONEHALF = (address)_ONEHALF; + address P_2 = (address)_P_2; + address SC_4 = (address)_SC_4; + address Ctable = (address)_Ctable; + address SC_2 = (address)_SC_2; + address SC_3 = (address)_SC_3; + address SC_1 = (address)_SC_1; + address PI_INV_TABLE = (address)_PI_INV_TABLE; + address PI_4 = (address)_PI_4; + address PI32INV = (address)_PI32INV; + address SHIFTER = (address)_SHIFTER; + address SIGN_MASK = (address)_SIGN_MASK; + address P_3 = (address)_P_3; + address ALL_ONES = (address)_ALL_ONES; + address TWO_POW_55 = (address)_TWO_POW_55; + address TWO_POW_M55 = (address)_TWO_POW_M55; + address P_1 = (address)_P_1; + address NEG_ZERO = (address)_NEG_ZERO; bind(start); - subl(rsp, 120); - movl(Address(rsp, 64), tmp); - lea(tmp, ExternalAddress(static_const_table_pow)); - movsd(xmm0, Address(rsp, 128)); - movsd(xmm1, Address(rsp, 136)); - xorpd(xmm2, xmm2); - movl(eax, 16368); - pinsrw(xmm2, eax, 3); - movl(ecx, 1069088768); - movdl(xmm7, ecx); - movsd(Address(rsp, 16), xmm1); - xorpd(xmm1, xmm1); - movl(edx, 30704); - pinsrw(xmm1, edx, 3); + push(rbx); + subq(rsp, 16); movsd(Address(rsp, 8), xmm0); - movdqu(xmm3, xmm0); - movl(edx, 8192); - movdl(xmm4, edx); - movdqu(xmm6, Address(tmp, 8240)); - pextrw(eax, xmm0, 3); - por(xmm0, xmm2); - psllq(xmm0, 5); - movsd(xmm2, Address(tmp, 8256)); - psrlq(xmm0, 34); - movl(edx, eax); - andl(edx, 32752); - subl(edx, 16368); - movl(ecx, edx); - sarl(edx, 31); - addl(ecx, edx); - xorl(ecx, edx); - rcpss(xmm0, xmm0); - psllq(xmm3, 12); - addl(ecx, 16); - bsrl(ecx, ecx); - psrlq(xmm3, 12); - movl(Address(rsp, 24), rsi); - subl(eax, 16); - cmpl(eax, 32736); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); - movl(rsi, 0); - - bind(L_2TAG_PACKET_1_0_2); - mulss(xmm0, xmm7); - movl(edx, -1); - subl(ecx, 4); - shll(edx); - movdl(xmm5, edx); - por(xmm3, xmm1); - subl(eax, 16351); - cmpl(eax, 1); - jcc(Assembler::belowEqual, L_2TAG_PACKET_2_0_2); - paddd(xmm0, xmm4); - psllq(xmm5, 32); - movdl(edx, xmm0); - psllq(xmm0, 29); - pand(xmm5, xmm3); - - bind(L_2TAG_PACKET_3_0_2); - pand(xmm0, xmm6); - subsd(xmm3, xmm5); - subl(eax, 1); - sarl(eax, 4); - cvtsi2sdl(xmm7, eax); - mulpd(xmm5, xmm0); - - bind(L_2TAG_PACKET_4_0_2); - mulsd(xmm3, xmm0); - movdqu(xmm1, Address(tmp, 8272)); - subsd(xmm5, xmm2); - movdqu(xmm4, Address(tmp, 8288)); - movl(ecx, eax); - sarl(eax, 31); - addl(ecx, eax); - xorl(eax, ecx); - addl(eax, 1); - bsrl(eax, eax); - unpcklpd(xmm5, xmm3); - movdqu(xmm6, Address(tmp, 8304)); - addsd(xmm3, xmm5); - andl(edx, 16760832); - shrl(edx, 10); - addpd(xmm5, Address(tmp, edx, Address::times_1, -3616)); - movdqu(xmm0, Address(tmp, 8320)); - pshufd(xmm2, xmm3, 68); - mulsd(xmm3, xmm3); - mulpd(xmm1, xmm2); - mulpd(xmm4, xmm2); - addsd(xmm5, xmm7); - mulsd(xmm2, xmm3); - addpd(xmm6, xmm1); - mulsd(xmm3, xmm3); - addpd(xmm0, xmm4); - movsd(xmm1, Address(rsp, 16)); - movzwl(ecx, Address(rsp, 22)); - pshufd(xmm7, xmm5, 238); - movsd(xmm4, Address(tmp, 8368)); - mulpd(xmm6, xmm2); - pshufd(xmm3, xmm3, 68); - mulpd(xmm0, xmm2); - shll(eax, 4); - subl(eax, 15872); - andl(ecx, 32752); - addl(eax, ecx); - mulpd(xmm3, xmm6); - cmpl(eax, 624); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_5_0_2); - xorpd(xmm6, xmm6); - movl(edx, 17080); - pinsrw(xmm6, edx, 3); - movdqu(xmm2, xmm1); - pand(xmm4, xmm1); - subsd(xmm1, xmm4); - mulsd(xmm4, xmm5); - addsd(xmm0, xmm7); - mulsd(xmm1, xmm5); - movdqu(xmm7, xmm6); - addsd(xmm6, xmm4); - addpd(xmm3, xmm0); - movdl(edx, xmm6); - subsd(xmm6, xmm7); - pshufd(xmm0, xmm3, 238); - subsd(xmm4, xmm6); - addsd(xmm0, xmm3); - movl(ecx, edx); - andl(edx, 255); - addl(edx, edx); - movdqu(xmm5, Address(tmp, edx, Address::times_8, 8384)); - addsd(xmm4, xmm1); - mulsd(xmm2, xmm0); - movdqu(xmm7, Address(tmp, 12480)); - movdqu(xmm3, Address(tmp, 12496)); - shll(ecx, 12); - xorl(ecx, rsi); - andl(ecx, -1048576); - movdl(xmm6, ecx); - addsd(xmm2, xmm4); - movsd(xmm1, Address(tmp, 12512)); - pshufd(xmm0, xmm2, 68); - pshufd(xmm4, xmm2, 68); - mulpd(xmm0, xmm0); - movl(rsi, Address(rsp, 24)); - mulpd(xmm7, xmm4); - pshufd(xmm6, xmm6, 17); - mulsd(xmm1, xmm2); - mulsd(xmm0, xmm0); - paddd(xmm5, xmm6); - addpd(xmm3, xmm7); - mulsd(xmm1, xmm5); - pshufd(xmm6, xmm5, 238); - mulpd(xmm0, xmm3); - addsd(xmm1, xmm6); - pshufd(xmm3, xmm0, 238); - mulsd(xmm0, xmm5); - mulsd(xmm3, xmm5); - addsd(xmm0, xmm1); - addsd(xmm0, xmm3); - addsd(xmm0, xmm5); - movsd(Address(rsp, 0), xmm0); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); - - bind(L_2TAG_PACKET_7_0_2); - movsd(xmm0, Address(rsp, 128)); - movsd(xmm1, Address(rsp, 136)); - mulsd(xmm0, xmm1); - movsd(Address(rsp, 0), xmm0); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); - - bind(L_2TAG_PACKET_0_0_2); - addl(eax, 16); - movl(edx, 32752); - andl(edx, eax); - cmpl(edx, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_8_0_2); - testl(eax, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_9_0_2); - - bind(L_2TAG_PACKET_10_0_2); - movl(ecx, Address(rsp, 16)); - xorl(edx, edx); - testl(ecx, ecx); - movl(ecx, 1); - cmovl(Assembler::notEqual, edx, ecx); - orl(edx, Address(rsp, 20)); - cmpl(edx, 1072693248); - jcc(Assembler::equal, L_2TAG_PACKET_7_0_2); - movsd(xmm0, Address(rsp, 8)); - movsd(xmm3, Address(rsp, 8)); - movdl(edx, xmm3); - psrlq(xmm3, 32); - movdl(ecx, xmm3); - orl(edx, ecx); - cmpl(edx, 0); - jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); - xorpd(xmm3, xmm3); - movl(eax, 18416); - pinsrw(xmm3, eax, 3); - mulsd(xmm0, xmm3); - xorpd(xmm2, xmm2); - movl(eax, 16368); - pinsrw(xmm2, eax, 3); - movdqu(xmm3, xmm0); - pextrw(eax, xmm0, 3); - por(xmm0, xmm2); - movl(ecx, 18416); - psllq(xmm0, 5); - movsd(xmm2, Address(tmp, 8256)); - psrlq(xmm0, 34); - rcpss(xmm0, xmm0); - psllq(xmm3, 12); - movdqu(xmm6, Address(tmp, 8240)); - psrlq(xmm3, 12); - mulss(xmm0, xmm7); - movl(edx, -1024); - movdl(xmm5, edx); - por(xmm3, xmm1); - paddd(xmm0, xmm4); - psllq(xmm5, 32); - movdl(edx, xmm0); - psllq(xmm0, 29); - pand(xmm5, xmm3); - movl(rsi, 0); - pand(xmm0, xmm6); - subsd(xmm3, xmm5); - andl(eax, 32752); - subl(eax, 18416); - sarl(eax, 4); - cvtsi2sdl(xmm7, eax); - mulpd(xmm5, xmm0); - jmp(L_2TAG_PACKET_4_0_2); - - bind(L_2TAG_PACKET_12_0_2); - movl(ecx, Address(rsp, 16)); - xorl(edx, edx); - testl(ecx, ecx); - movl(ecx, 1); - cmovl(Assembler::notEqual, edx, ecx); - orl(edx, Address(rsp, 20)); - cmpl(edx, 1072693248); - jcc(Assembler::equal, L_2TAG_PACKET_7_0_2); - movsd(xmm0, Address(rsp, 8)); - movsd(xmm3, Address(rsp, 8)); - movdl(edx, xmm3); - psrlq(xmm3, 32); - movdl(ecx, xmm3); - orl(edx, ecx); - cmpl(edx, 0); - jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); - xorpd(xmm3, xmm3); - movl(eax, 18416); - pinsrw(xmm3, eax, 3); - mulsd(xmm0, xmm3); - xorpd(xmm2, xmm2); - movl(eax, 16368); - pinsrw(xmm2, eax, 3); - movdqu(xmm3, xmm0); - pextrw(eax, xmm0, 3); - por(xmm0, xmm2); - movl(ecx, 18416); - psllq(xmm0, 5); - movsd(xmm2, Address(tmp, 8256)); - psrlq(xmm0, 34); - rcpss(xmm0, xmm0); - psllq(xmm3, 12); - movdqu(xmm6, Address(tmp, 8240)); - psrlq(xmm3, 12); - mulss(xmm0, xmm7); - movl(edx, -1024); - movdl(xmm5, edx); - por(xmm3, xmm1); - paddd(xmm0, xmm4); - psllq(xmm5, 32); - movdl(edx, xmm0); - psllq(xmm0, 29); - pand(xmm5, xmm3); - movl(rsi, INT_MIN); - pand(xmm0, xmm6); - subsd(xmm3, xmm5); - andl(eax, 32752); - subl(eax, 18416); - sarl(eax, 4); - cvtsi2sdl(xmm7, eax); - mulpd(xmm5, xmm0); - jmp(L_2TAG_PACKET_4_0_2); - - bind(L_2TAG_PACKET_5_0_2); - cmpl(eax, 0); - jcc(Assembler::less, L_2TAG_PACKET_13_0_2); - cmpl(eax, 752); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_14_0_2); - - bind(L_2TAG_PACKET_15_0_2); - addsd(xmm0, xmm7); - movsd(xmm2, Address(tmp, 12544)); - addpd(xmm3, xmm0); - xorpd(xmm6, xmm6); - movl(eax, 17080); - pinsrw(xmm6, eax, 3); - pshufd(xmm0, xmm3, 238); - addsd(xmm0, xmm3); - movdqu(xmm3, xmm5); - addsd(xmm5, xmm0); - movdqu(xmm4, xmm2); - subsd(xmm3, xmm5); - movdqu(xmm7, xmm5); - pand(xmm5, xmm2); - movdqu(xmm2, xmm1); - pand(xmm4, xmm1); - subsd(xmm7, xmm5); - addsd(xmm0, xmm3); - subsd(xmm1, xmm4); - mulsd(xmm4, xmm5); - addsd(xmm0, xmm7); - mulsd(xmm2, xmm0); - movdqu(xmm7, xmm6); - mulsd(xmm1, xmm5); - addsd(xmm6, xmm4); - movdl(eax, xmm6); - subsd(xmm6, xmm7); - addsd(xmm2, xmm1); - movdqu(xmm7, Address(tmp, 12480)); - movdqu(xmm3, Address(tmp, 12496)); - subsd(xmm4, xmm6); - pextrw(edx, xmm6, 3); - movl(ecx, eax); - andl(eax, 255); - addl(eax, eax); - movdqu(xmm5, Address(tmp, eax, Address::times_8, 8384)); - addsd(xmm2, xmm4); - sarl(ecx, 8); - movl(eax, ecx); - sarl(ecx, 1); - subl(eax, ecx); - shll(ecx, 20); - xorl(ecx, rsi); - movdl(xmm6, ecx); - movsd(xmm1, Address(tmp, 12512)); - andl(edx, 32767); - cmpl(edx, 16529); - jcc(Assembler::above, L_2TAG_PACKET_14_0_2); - pshufd(xmm0, xmm2, 68); - pshufd(xmm4, xmm2, 68); - mulpd(xmm0, xmm0); - mulpd(xmm7, xmm4); - pshufd(xmm6, xmm6, 17); - mulsd(xmm1, xmm2); - mulsd(xmm0, xmm0); - paddd(xmm5, xmm6); - addpd(xmm3, xmm7); - mulsd(xmm1, xmm5); - pshufd(xmm6, xmm5, 238); - mulpd(xmm0, xmm3); - addsd(xmm1, xmm6); - pshufd(xmm3, xmm0, 238); - mulsd(xmm0, xmm5); - mulsd(xmm3, xmm5); - shll(eax, 4); - xorpd(xmm4, xmm4); - addl(eax, 16368); - pinsrw(xmm4, eax, 3); - addsd(xmm0, xmm1); - movl(rsi, Address(rsp, 24)); - addsd(xmm0, xmm3); - movdqu(xmm1, xmm0); - addsd(xmm0, xmm5); - mulsd(xmm0, xmm4); - pextrw(eax, xmm0, 3); - andl(eax, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_16_0_2); - cmpl(eax, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_17_0_2); - - bind(L_2TAG_PACKET_18_0_2); - movsd(Address(rsp, 0), xmm0); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); - - bind(L_2TAG_PACKET_8_0_2); - movsd(xmm1, Address(rsp, 16)); - movsd(xmm0, Address(rsp, 8)); - movdqu(xmm2, xmm0); - movdl(eax, xmm2); - psrlq(xmm2, 20); - movdl(edx, xmm2); - orl(eax, edx); - jcc(Assembler::equal, L_2TAG_PACKET_19_0_2); - addsd(xmm0, xmm0); - movdl(eax, xmm1); - psrlq(xmm1, 32); - movdl(edx, xmm1); - movl(ecx, edx); - addl(edx, edx); - orl(eax, edx); - jcc(Assembler::equal, L_2TAG_PACKET_20_0_2); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_20_0_2); - xorpd(xmm0, xmm0); - movl(eax, 16368); - pinsrw(xmm0, eax, 3); - movl(edx, 29); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_22_0_2); - movsd(xmm0, Address(rsp, 16)); - addpd(xmm0, xmm0); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_19_0_2); - movdl(eax, xmm1); - movdqu(xmm2, xmm1); - psrlq(xmm1, 32); - movdl(edx, xmm1); - movl(ecx, edx); - addl(edx, edx); - orl(eax, edx); - jcc(Assembler::equal, L_2TAG_PACKET_23_0_2); - pextrw(eax, xmm2, 3); - andl(eax, 32752); - cmpl(eax, 32752); - jcc(Assembler::notEqual, L_2TAG_PACKET_24_0_2); - movdl(eax, xmm2); - psrlq(xmm2, 20); - movdl(edx, xmm2); - orl(eax, edx); - jcc(Assembler::notEqual, L_2TAG_PACKET_22_0_2); - - bind(L_2TAG_PACKET_24_0_2); - pextrw(eax, xmm0, 3); - testl(eax, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_25_0_2); - testl(ecx, INT_MIN); - jcc(Assembler::notEqual, L_2TAG_PACKET_26_0_2); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_27_0_2); - movsd(xmm1, Address(rsp, 16)); - movdl(eax, xmm1); - testl(eax, 1); - jcc(Assembler::notEqual, L_2TAG_PACKET_28_0_2); - testl(eax, 2); - jcc(Assembler::notEqual, L_2TAG_PACKET_29_0_2); - jmp(L_2TAG_PACKET_28_0_2); - - bind(L_2TAG_PACKET_25_0_2); - shrl(ecx, 20); - andl(ecx, 2047); - cmpl(ecx, 1075); - jcc(Assembler::above, L_2TAG_PACKET_28_0_2); - jcc(Assembler::equal, L_2TAG_PACKET_30_0_2); - cmpl(ecx, 1074); - jcc(Assembler::above, L_2TAG_PACKET_27_0_2); - cmpl(ecx, 1023); - jcc(Assembler::below, L_2TAG_PACKET_28_0_2); - movsd(xmm1, Address(rsp, 16)); - movl(eax, 17208); - xorpd(xmm3, xmm3); - pinsrw(xmm3, eax, 3); - movdqu(xmm4, xmm3); - addsd(xmm3, xmm1); + movl(eax, Address(rsp, 12)); + movq(xmm1, ExternalAddress(PI32INV)); //0x6dc9c883UL, 0x40245f30UL + movq(xmm2, ExternalAddress(SHIFTER)); //0x00000000UL, 0x43380000UL + andl(eax, 2147418112); + subl(eax, 808452096); + cmpl(eax, 281346048); + jcc(Assembler::above, L_2TAG_PACKET_0_0_1); + mulsd(xmm1, xmm0); + movdqu(xmm5, ExternalAddress(ONEHALF)); //0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL + movq(xmm4, ExternalAddress(SIGN_MASK)); //0x00000000UL, 0x80000000UL + pand(xmm4, xmm0); + por(xmm5, xmm4); + addpd(xmm1, xmm5); + cvttsd2sil(edx, xmm1); + cvtsi2sdl(xmm1, edx); + movdqu(xmm6, ExternalAddress(P_2)); //0x1a600000UL, 0x3d90b461UL, 0x1a600000UL, 0x3d90b461UL + mov64(r8, 0x3fb921fb54400000); + movdq(xmm3, r8); + movdqu(xmm5, ExternalAddress(SC_4)); //0xa556c734UL, 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL + pshufd(xmm4, xmm0, 68); + mulsd(xmm3, xmm1); + movddup(xmm1, xmm1); + andl(edx, 63); + shll(edx, 5); + lea(rax, ExternalAddress(Ctable)); + addq(rax, rdx); + mulpd(xmm6, xmm1); + mulsd(xmm1, ExternalAddress(P_3)); //0x2e037073UL, 0x3b63198aUL subsd(xmm4, xmm3); - addsd(xmm1, xmm4); - pextrw(eax, xmm1, 3); - andl(eax, 32752); - jcc(Assembler::notEqual, L_2TAG_PACKET_28_0_2); - movdl(eax, xmm3); - andl(eax, 1); - jcc(Assembler::equal, L_2TAG_PACKET_28_0_2); - - bind(L_2TAG_PACKET_29_0_2); - movsd(xmm1, Address(rsp, 16)); - pextrw(eax, xmm1, 3); - andl(eax, 32768); - jcc(Assembler::equal, L_2TAG_PACKET_18_0_2); - xorpd(xmm0, xmm0); - movl(eax, 32768); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_28_0_2); - movsd(xmm1, Address(rsp, 16)); - pextrw(eax, xmm1, 3); - andl(eax, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_26_0_2); - - bind(L_2TAG_PACKET_31_0_2); - xorpd(xmm0, xmm0); - movl(eax, 32752); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_30_0_2); - movsd(xmm1, Address(rsp, 16)); - movdl(eax, xmm1); - andl(eax, 1); - jcc(Assembler::equal, L_2TAG_PACKET_28_0_2); - jmp(L_2TAG_PACKET_29_0_2); - - bind(L_2TAG_PACKET_32_0_2); - movdl(eax, xmm1); - psrlq(xmm1, 20); - movdl(edx, xmm1); - orl(eax, edx); - jcc(Assembler::equal, L_2TAG_PACKET_33_0_2); - movsd(xmm0, Address(rsp, 16)); - addsd(xmm0, xmm0); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_33_0_2); - movsd(xmm0, Address(rsp, 8)); - pextrw(eax, xmm0, 3); - cmpl(eax, 49136); - jcc(Assembler::notEqual, L_2TAG_PACKET_34_0_2); - movdl(ecx, xmm0); - psrlq(xmm0, 20); - movdl(edx, xmm0); - orl(ecx, edx); - jcc(Assembler::notEqual, L_2TAG_PACKET_34_0_2); - xorpd(xmm0, xmm0); - movl(eax, 32760); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_34_0_2); - movsd(xmm1, Address(rsp, 16)); - andl(eax, 32752); - subl(eax, 16368); - pextrw(edx, xmm1, 3); - xorpd(xmm0, xmm0); - xorl(eax, edx); - andl(eax, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_18_0_2); - movl(ecx, 32752); - pinsrw(xmm0, ecx, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_35_0_2); - movdl(eax, xmm1); - cmpl(edx, 17184); - jcc(Assembler::above, L_2TAG_PACKET_36_0_2); - testl(eax, 1); - jcc(Assembler::notEqual, L_2TAG_PACKET_37_0_2); - testl(eax, 2); - jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); - jmp(L_2TAG_PACKET_39_0_2); - - bind(L_2TAG_PACKET_36_0_2); - testl(eax, 1); - jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); - jmp(L_2TAG_PACKET_39_0_2); - - bind(L_2TAG_PACKET_9_0_2); - movsd(xmm2, Address(rsp, 8)); - movdl(eax, xmm2); - psrlq(xmm2, 31); - movdl(ecx, xmm2); - orl(eax, ecx); - jcc(Assembler::equal, L_2TAG_PACKET_11_0_2); - movsd(xmm1, Address(rsp, 16)); - pextrw(edx, xmm1, 3); - movdl(eax, xmm1); - movdqu(xmm2, xmm1); - psrlq(xmm2, 32); - movdl(ecx, xmm2); - addl(ecx, ecx); - orl(ecx, eax); - jcc(Assembler::equal, L_2TAG_PACKET_40_0_2); - andl(edx, 32752); - cmpl(edx, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_32_0_2); - cmpl(edx, 17200); - jcc(Assembler::above, L_2TAG_PACKET_38_0_2); - cmpl(edx, 17184); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_35_0_2); - cmpl(edx, 16368); - jcc(Assembler::below, L_2TAG_PACKET_37_0_2); - movl(eax, 17208); - xorpd(xmm2, xmm2); - pinsrw(xmm2, eax, 3); - movdqu(xmm4, xmm2); - addsd(xmm2, xmm1); - subsd(xmm4, xmm2); - addsd(xmm1, xmm4); - pextrw(eax, xmm1, 3); - andl(eax, 32767); - jcc(Assembler::notEqual, L_2TAG_PACKET_37_0_2); - movdl(eax, xmm2); - andl(eax, 1); - jcc(Assembler::equal, L_2TAG_PACKET_38_0_2); - - bind(L_2TAG_PACKET_39_0_2); - xorpd(xmm1, xmm1); - movl(edx, 30704); - pinsrw(xmm1, edx, 3); - movsd(xmm2, Address(tmp, 8256)); - movsd(xmm4, Address(rsp, 8)); - pextrw(eax, xmm4, 3); - movl(edx, 8192); - movdl(xmm4, edx); - andl(eax, 32767); - subl(eax, 16); - jcc(Assembler::less, L_2TAG_PACKET_12_0_2); - movl(edx, eax); - andl(edx, 32752); - subl(edx, 16368); - movl(ecx, edx); - sarl(edx, 31); - addl(ecx, edx); - xorl(ecx, edx); - addl(ecx, 16); - bsrl(ecx, ecx); - movl(rsi, INT_MIN); - jmp(L_2TAG_PACKET_1_0_2); - - bind(L_2TAG_PACKET_37_0_2); - xorpd(xmm1, xmm1); - movl(eax, 32752); - pinsrw(xmm1, eax, 3); - xorpd(xmm0, xmm0); - mulsd(xmm0, xmm1); - movl(edx, 28); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_38_0_2); - xorpd(xmm1, xmm1); - movl(edx, 30704); - pinsrw(xmm1, edx, 3); - movsd(xmm2, Address(tmp, 8256)); - movsd(xmm4, Address(rsp, 8)); - pextrw(eax, xmm4, 3); - movl(edx, 8192); - movdl(xmm4, edx); - andl(eax, 32767); - subl(eax, 16); - jcc(Assembler::less, L_2TAG_PACKET_10_0_2); - movl(edx, eax); - andl(edx, 32752); - subl(edx, 16368); - movl(ecx, edx); - sarl(edx, 31); - addl(ecx, edx); - xorl(ecx, edx); - addl(ecx, 16); - bsrl(ecx, ecx); - movl(rsi, 0); - jmp(L_2TAG_PACKET_1_0_2); - - bind(L_2TAG_PACKET_23_0_2); - xorpd(xmm0, xmm0); - movl(eax, 16368); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_26_0_2); - xorpd(xmm0, xmm0); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_13_0_2); - addl(eax, 384); - cmpl(eax, 0); - jcc(Assembler::less, L_2TAG_PACKET_41_0_2); - mulsd(xmm5, xmm1); - addsd(xmm0, xmm7); - shrl(rsi, 31); - addpd(xmm3, xmm0); - pshufd(xmm0, xmm3, 238); - addsd(xmm3, xmm0); - movsd(xmm4, Address(tmp, rsi, Address::times_8, 12528)); - mulsd(xmm1, xmm3); - xorpd(xmm0, xmm0); - movl(eax, 16368); - shll(rsi, 15); - orl(eax, rsi); - pinsrw(xmm0, eax, 3); - addsd(xmm5, xmm1); - movl(rsi, Address(rsp, 24)); - mulsd(xmm5, xmm4); - addsd(xmm0, xmm5); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_41_0_2); - movl(rsi, Address(rsp, 24)); - xorpd(xmm0, xmm0); - movl(eax, 16368); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_40_0_2); - xorpd(xmm0, xmm0); - movl(eax, 16368); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_42_0_2); - xorpd(xmm0, xmm0); - movl(eax, 16368); - pinsrw(xmm0, eax, 3); - movl(edx, 26); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_11_0_2); - movsd(xmm1, Address(rsp, 16)); - movdqu(xmm2, xmm1); - pextrw(eax, xmm1, 3); - andl(eax, 32752); - cmpl(eax, 32752); - jcc(Assembler::notEqual, L_2TAG_PACKET_43_0_2); - movdl(eax, xmm2); - psrlq(xmm2, 20); - movdl(edx, xmm2); - orl(eax, edx); - jcc(Assembler::notEqual, L_2TAG_PACKET_22_0_2); - - bind(L_2TAG_PACKET_43_0_2); - movdl(eax, xmm1); - psrlq(xmm1, 32); - movdl(edx, xmm1); - movl(ecx, edx); - addl(edx, edx); - orl(eax, edx); - jcc(Assembler::equal, L_2TAG_PACKET_42_0_2); - shrl(edx, 21); - cmpl(edx, 1075); - jcc(Assembler::above, L_2TAG_PACKET_44_0_2); - jcc(Assembler::equal, L_2TAG_PACKET_45_0_2); - cmpl(edx, 1023); - jcc(Assembler::below, L_2TAG_PACKET_44_0_2); - movsd(xmm1, Address(rsp, 16)); - movl(eax, 17208); - xorpd(xmm3, xmm3); - pinsrw(xmm3, eax, 3); - movdqu(xmm4, xmm3); - addsd(xmm3, xmm1); - subsd(xmm4, xmm3); - addsd(xmm1, xmm4); - pextrw(eax, xmm1, 3); - andl(eax, 32752); - jcc(Assembler::notEqual, L_2TAG_PACKET_44_0_2); - movdl(eax, xmm3); - andl(eax, 1); - jcc(Assembler::equal, L_2TAG_PACKET_44_0_2); - - bind(L_2TAG_PACKET_46_0_2); - movsd(xmm0, Address(rsp, 8)); - testl(ecx, INT_MIN); - jcc(Assembler::notEqual, L_2TAG_PACKET_47_0_2); - jmp(L_2TAG_PACKET_18_0_2); - - bind(L_2TAG_PACKET_45_0_2); - movsd(xmm1, Address(rsp, 16)); - movdl(eax, xmm1); - testl(eax, 1); - jcc(Assembler::notEqual, L_2TAG_PACKET_46_0_2); - - bind(L_2TAG_PACKET_44_0_2); - testl(ecx, INT_MIN); - jcc(Assembler::equal, L_2TAG_PACKET_26_0_2); - xorpd(xmm0, xmm0); - - bind(L_2TAG_PACKET_47_0_2); - movl(eax, 16368); - xorpd(xmm1, xmm1); - pinsrw(xmm1, eax, 3); - divsd(xmm1, xmm0); - movdqu(xmm0, xmm1); - movl(edx, 27); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_14_0_2); - movsd(xmm2, Address(rsp, 8)); - movsd(xmm6, Address(rsp, 16)); - pextrw(eax, xmm2, 3); - pextrw(edx, xmm6, 3); - movl(ecx, 32752); - andl(ecx, edx); - cmpl(ecx, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_48_0_2); - andl(eax, 32752); - subl(eax, 16368); - xorl(edx, eax); - testl(edx, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_49_0_2); - - bind(L_2TAG_PACKET_50_0_2); - movl(eax, 32736); - pinsrw(xmm0, eax, 3); - shrl(rsi, 16); - orl(eax, rsi); - pinsrw(xmm1, eax, 3); - movl(rsi, Address(rsp, 24)); - mulsd(xmm0, xmm1); - - bind(L_2TAG_PACKET_17_0_2); - movl(edx, 24); - - bind(L_2TAG_PACKET_21_0_2); - movsd(Address(rsp, 0), xmm0); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); - - bind(L_2TAG_PACKET_49_0_2); - movl(eax, 16); - pinsrw(xmm0, eax, 3); - mulsd(xmm0, xmm0); - testl(rsi, INT_MIN); - jcc(Assembler::equal, L_2TAG_PACKET_51_0_2); - movsd(xmm2, Address(tmp, 12560)); - xorpd(xmm0, xmm2); - - bind(L_2TAG_PACKET_51_0_2); - movl(rsi, Address(rsp, 24)); - movl(edx, 25); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_16_0_2); - pextrw(ecx, xmm5, 3); - pextrw(edx, xmm4, 3); - movl(eax, -1); - andl(ecx, 32752); - subl(ecx, 16368); - andl(edx, 32752); - addl(edx, ecx); - movl(ecx, -31); - sarl(edx, 4); - subl(ecx, edx); - jcc(Assembler::lessEqual, L_2TAG_PACKET_52_0_2); - cmpl(ecx, 20); - jcc(Assembler::above, L_2TAG_PACKET_53_0_2); - shll(eax); - - bind(L_2TAG_PACKET_52_0_2); - movdl(xmm0, eax); - psllq(xmm0, 32); - pand(xmm0, xmm5); - subsd(xmm5, xmm0); - addsd(xmm5, xmm1); - mulsd(xmm0, xmm4); - mulsd(xmm5, xmm4); - addsd(xmm0, xmm5); - - bind(L_2TAG_PACKET_53_0_2); - movl(edx, 25); - jmp(L_2TAG_PACKET_21_0_2); - - bind(L_2TAG_PACKET_2_0_2); - movzwl(ecx, Address(rsp, 22)); - movl(edx, INT_MIN); - movdl(xmm1, edx); - xorpd(xmm7, xmm7); - paddd(xmm0, xmm4); - psllq(xmm5, 32); - movdl(edx, xmm0); - psllq(xmm0, 29); - paddq(xmm1, xmm3); - pand(xmm5, xmm1); - andl(ecx, 32752); - cmpl(ecx, 16560); - jcc(Assembler::below, L_2TAG_PACKET_3_0_2); - pand(xmm0, xmm6); - subsd(xmm3, xmm5); - addl(eax, 16351); - shrl(eax, 4); - subl(eax, 1022); - cvtsi2sdl(xmm7, eax); + movq(xmm7, Address(rax, 8)); + subsd(xmm0, xmm3); + movddup(xmm3, xmm4); + subsd(xmm4, xmm6); + pshufd(xmm0, xmm0, 68); + movdqu(xmm2, Address(rax, 0)); mulpd(xmm5, xmm0); - movsd(xmm4, Address(tmp, 0)); - mulsd(xmm3, xmm0); - movsd(xmm6, Address(tmp, 0)); - subsd(xmm5, xmm2); - movsd(xmm1, Address(tmp, 8)); - pshufd(xmm2, xmm3, 68); - unpcklpd(xmm5, xmm3); - addsd(xmm3, xmm5); - movsd(xmm0, Address(tmp, 8)); - andl(edx, 16760832); - shrl(edx, 10); - addpd(xmm7, Address(tmp, edx, Address::times_1, -3616)); - mulsd(xmm4, xmm5); - mulsd(xmm0, xmm5); - mulsd(xmm6, xmm2); - mulsd(xmm1, xmm2); - movdqu(xmm2, xmm5); - mulsd(xmm4, xmm5); - addsd(xmm5, xmm0); - movdqu(xmm0, xmm7); + subpd(xmm0, xmm6); + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm6); + movdqu(xmm6, ExternalAddress(SC_2)); //0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL + subsd(xmm1, xmm3); + movq(xmm3, Address(rax, 24)); addsd(xmm2, xmm3); - addsd(xmm7, xmm5); - mulsd(xmm6, xmm2); - subsd(xmm0, xmm7); - movdqu(xmm2, xmm7); - addsd(xmm7, xmm4); - addsd(xmm0, xmm5); - subsd(xmm2, xmm7); - addsd(xmm4, xmm2); - pshufd(xmm2, xmm5, 238); - movdqu(xmm5, xmm7); - addsd(xmm7, xmm2); - addsd(xmm4, xmm0); - movdqu(xmm0, Address(tmp, 8272)); - subsd(xmm5, xmm7); - addsd(xmm6, xmm4); - movdqu(xmm4, xmm7); - addsd(xmm5, xmm2); - addsd(xmm7, xmm1); - movdqu(xmm2, Address(tmp, 8336)); - subsd(xmm4, xmm7); - addsd(xmm6, xmm5); - addsd(xmm4, xmm1); - pshufd(xmm5, xmm7, 238); - movdqu(xmm1, xmm7); - addsd(xmm7, xmm5); - subsd(xmm1, xmm7); - addsd(xmm1, xmm5); - movdqu(xmm5, Address(tmp, 8352)); - pshufd(xmm3, xmm3, 68); - addsd(xmm6, xmm4); - addsd(xmm6, xmm1); - movdqu(xmm1, Address(tmp, 8304)); - mulpd(xmm0, xmm3); - mulpd(xmm2, xmm3); - pshufd(xmm4, xmm3, 68); - mulpd(xmm3, xmm3); - addpd(xmm0, xmm1); - addpd(xmm5, xmm2); - mulsd(xmm4, xmm3); - movsd(xmm2, Address(tmp, 16)); - mulpd(xmm3, xmm3); - movsd(xmm1, Address(rsp, 16)); - movzwl(ecx, Address(rsp, 22)); - mulpd(xmm0, xmm4); - pextrw(eax, xmm7, 3); - mulpd(xmm5, xmm4); - mulpd(xmm0, xmm3); - movsd(xmm4, Address(tmp, 8376)); - pand(xmm2, xmm7); - addsd(xmm5, xmm6); subsd(xmm7, xmm2); - addpd(xmm5, xmm0); - andl(eax, 32752); - subl(eax, 16368); - andl(ecx, 32752); - cmpl(ecx, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_48_0_2); - addl(ecx, eax); - cmpl(ecx, 16576); - jcc(Assembler::aboveEqual, L_2TAG_PACKET_54_0_2); - pshufd(xmm0, xmm5, 238); - pand(xmm4, xmm1); - movdqu(xmm3, xmm1); - addsd(xmm5, xmm0); - subsd(xmm1, xmm4); - xorpd(xmm6, xmm6); - movl(edx, 17080); - pinsrw(xmm6, edx, 3); - addsd(xmm7, xmm5); - mulsd(xmm4, xmm2); - mulsd(xmm1, xmm2); - movdqu(xmm5, xmm6); - mulsd(xmm3, xmm7); - addsd(xmm6, xmm4); - addsd(xmm1, xmm3); - movdqu(xmm7, Address(tmp, 12480)); - movdl(edx, xmm6); - subsd(xmm6, xmm5); - movdqu(xmm3, Address(tmp, 12496)); - movsd(xmm2, Address(tmp, 12512)); - subsd(xmm4, xmm6); - movl(ecx, edx); - andl(edx, 255); - addl(edx, edx); - movdqu(xmm5, Address(tmp, edx, Address::times_8, 8384)); - addsd(xmm4, xmm1); - pextrw(edx, xmm6, 3); - shrl(ecx, 8); - movl(eax, ecx); - shrl(ecx, 1); - subl(eax, ecx); - shll(ecx, 20); - movdl(xmm6, ecx); - pshufd(xmm0, xmm4, 68); - pshufd(xmm1, xmm4, 68); - mulpd(xmm0, xmm0); - mulpd(xmm7, xmm1); - pshufd(xmm6, xmm6, 17); mulsd(xmm2, xmm4); - andl(edx, 32767); - cmpl(edx, 16529); - jcc(Assembler::above, L_2TAG_PACKET_14_0_2); - mulsd(xmm0, xmm0); - paddd(xmm5, xmm6); - addpd(xmm3, xmm7); - mulsd(xmm2, xmm5); - pshufd(xmm6, xmm5, 238); - mulpd(xmm0, xmm3); - addsd(xmm2, xmm6); - pshufd(xmm3, xmm0, 238); - addl(eax, 1023); - shll(eax, 20); - orl(eax, rsi); - movdl(xmm4, eax); - mulsd(xmm0, xmm5); - mulsd(xmm3, xmm5); - addsd(xmm0, xmm2); - psllq(xmm4, 32); - addsd(xmm0, xmm3); - movdqu(xmm1, xmm0); - addsd(xmm0, xmm5); - movl(rsi, Address(rsp, 24)); - mulsd(xmm0, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, ExternalAddress(SC_3)); //0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL + mulsd(xmm4, Address(rax, 0)); + addpd(xmm6, ExternalAddress(SC_1)); //0x55555555UL, 0xbfc55555UL, 0x00000000UL, 0xbfe00000UL + mulpd(xmm5, xmm0); + movdqu(xmm0, xmm3); + addsd(xmm3, Address(rax, 8)); + mulpd(xmm1, xmm7); + movdqu(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movq(xmm5, Address(rax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(rax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm5, xmm0); + addsd(xmm3, xmm7); + addsd(xmm1, xmm5); + addsd(xmm1, xmm3); + addsd(xmm1, xmm6); + unpckhpd(xmm6, xmm6); + movdqu(xmm0, xmm4); + addsd(xmm1, xmm6); + addsd(xmm0, xmm1); + jmp(B1_4); + + bind(L_2TAG_PACKET_0_0_1); + jcc(Assembler::greater, L_2TAG_PACKET_1_0_1); + shrl(eax, 20); + cmpl(eax, 3325); + jcc(Assembler::notEqual, L_2TAG_PACKET_2_0_1); + mulsd(xmm0, ExternalAddress(ALL_ONES)); //0xffffffffUL, 0x3fefffffUL + jmp(B1_4); + + bind(L_2TAG_PACKET_2_0_1); + movq(xmm3, ExternalAddress(TWO_POW_55)); //0x00000000UL, 0x43600000UL + mulsd(xmm3, xmm0); + subsd(xmm3, xmm0); + mulsd(xmm3, ExternalAddress(TWO_POW_M55)); //0x00000000UL, 0x3c800000UL + jmp(B1_4); + + bind(L_2TAG_PACKET_1_0_1); pextrw(eax, xmm0, 3); andl(eax, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_16_0_2); cmpl(eax, 32752); - jcc(Assembler::equal, L_2TAG_PACKET_17_0_2); + jcc(Assembler::equal, L_2TAG_PACKET_3_0_1); + pextrw(ecx, xmm0, 3); + andl(ecx, 32752); + subl(ecx, 16224); + shrl(ecx, 7); + andl(ecx, 65532); + lea(r11, ExternalAddress(PI_INV_TABLE)); + addq(rcx, r11); + movdq(rax, xmm0); + movl(r10, Address(rcx, 20)); + movl(r8, Address(rcx, 24)); + movl(edx, eax); + shrq(rax, 21); + orl(eax, INT_MIN); + shrl(eax, 11); + movl(r9, r10); + imulq(r10, rdx); + imulq(r9, rax); + imulq(r8, rax); + movl(rsi, Address(rcx, 16)); + movl(rdi, Address(rcx, 12)); + movl(r11, r10); + shrq(r10, 32); + addq(r9, r10); + addq(r11, r8); + movl(r8, r11); + shrq(r11, 32); + addq(r9, r11); + movl(r10, rsi); + imulq(rsi, rdx); + imulq(r10, rax); + movl(r11, rdi); + imulq(rdi, rdx); + movl(ebx, rsi); + shrq(rsi, 32); + addq(r9, rbx); + movl(ebx, r9); + shrq(r9, 32); + addq(r10, rsi); + addq(r10, r9); + shlq(rbx, 32); + orq(r8, rbx); + imulq(r11, rax); + movl(r9, Address(rcx, 8)); + movl(rsi, Address(rcx, 4)); + movl(ebx, rdi); + shrq(rdi, 32); + addq(r10, rbx); + movl(ebx, r10); + shrq(r10, 32); + addq(r11, rdi); + addq(r11, r10); + movq(rdi, r9); + imulq(r9, rdx); + imulq(rdi, rax); + movl(r10, r9); + shrq(r9, 32); + addq(r11, r10); + movl(r10, r11); + shrq(r11, 32); + addq(rdi, r9); + addq(rdi, r11); + movq(r9, rsi); + imulq(rsi, rdx); + imulq(r9, rax); + shlq(r10, 32); + orq(r10, rbx); + movl(eax, Address(rcx, 0)); + movl(r11, rsi); + shrq(rsi, 32); + addq(rdi, r11); + movl(r11, rdi); + shrq(rdi, 32); + addq(r9, rsi); + addq(r9, rdi); + imulq(rdx, rax); + pextrw(ebx, xmm0, 3); + lea(rdi, ExternalAddress(PI_INV_TABLE)); + subq(rcx, rdi); + addl(ecx, ecx); + addl(ecx, ecx); + addl(ecx, ecx); + addl(ecx, 19); + movl(rsi, 32768); + andl(rsi, ebx); + shrl(ebx, 4); + andl(ebx, 2047); + subl(ebx, 1023); + subl(ecx, ebx); + addq(r9, rdx); + movl(edx, ecx); + addl(edx, 32); + cmpl(ecx, 1); + jcc(Assembler::less, L_2TAG_PACKET_4_0_1); + negl(ecx); + addl(ecx, 29); + shll(r9); + movl(rdi, r9); + andl(r9, 536870911); + testl(r9, 268435456); + jcc(Assembler::notEqual, L_2TAG_PACKET_5_0_1); + shrl(r9); + movl(ebx, 0); + shlq(r9, 32); + orq(r9, r11); - bind(L_2TAG_PACKET_55_0_2); - movsd(Address(rsp, 0), xmm0); - fld_d(Address(rsp, 0)); - jmp(L_2TAG_PACKET_6_0_2); + bind(L_2TAG_PACKET_6_0_1); - bind(L_2TAG_PACKET_48_0_2); - movl(rsi, Address(rsp, 24)); + bind(L_2TAG_PACKET_7_0_1); - bind(L_2TAG_PACKET_56_0_2); - movsd(xmm0, Address(rsp, 8)); - movsd(xmm1, Address(rsp, 16)); - addsd(xmm1, xmm1); - xorpd(xmm2, xmm2); - movl(eax, 49136); - pinsrw(xmm2, eax, 3); - addsd(xmm2, xmm0); - pextrw(eax, xmm2, 3); - cmpl(eax, 0); - jcc(Assembler::notEqual, L_2TAG_PACKET_57_0_2); + cmpq(r9, 0); + jcc(Assembler::equal, L_2TAG_PACKET_8_0_1); + + bind(L_2TAG_PACKET_9_0_1); + bsrq(r11, r9); + movl(ecx, 29); + subl(ecx, r11); + jcc(Assembler::lessEqual, L_2TAG_PACKET_10_0_1); + shlq(r9); + movq(rax, r10); + shlq(r10); + addl(edx, ecx); + negl(ecx); + addl(ecx, 64); + shrq(rax); + shrq(r8); + orq(r9, rax); + orq(r10, r8); + + bind(L_2TAG_PACKET_11_0_1); + cvtsi2sdq(xmm0, r9); + shrq(r10, 1); + cvtsi2sdq(xmm3, r10); + xorpd(xmm4, xmm4); + shll(edx, 4); + negl(edx); + addl(edx, 16368); + orl(edx, rsi); + xorl(edx, ebx); + pinsrw(xmm4, edx, 3); + movq(xmm2, ExternalAddress(PI_4)); //0x40000000UL, 0x3fe921fbUL, 0x18469899UL, 0x3e64442dUL + movq(xmm6, ExternalAddress(8 + PI_4)); //0x3fe921fbUL, 0x18469899UL, 0x3e64442dUL + xorpd(xmm5, xmm5); + subl(edx, 1008); + pinsrw(xmm5, edx, 3); + mulsd(xmm0, xmm4); + shll(rsi, 16); + sarl(rsi, 31); + mulsd(xmm3, xmm5); + movdqu(xmm1, xmm0); + mulsd(xmm0, xmm2); + shrl(rdi, 29); + addsd(xmm1, xmm3); + mulsd(xmm3, xmm2); + addl(rdi, rsi); + xorl(rdi, rsi); + mulsd(xmm6, xmm1); + movl(eax, rdi); + addsd(xmm6, xmm3); + movdqu(xmm2, xmm0); + addsd(xmm0, xmm6); + subsd(xmm2, xmm0); + addsd(xmm6, xmm2); + + bind(L_2TAG_PACKET_12_0_1); + movq(xmm1, ExternalAddress(PI32INV)); //0x6dc9c883UL, 0x40245f30UL + mulsd(xmm1, xmm0); + movq(xmm5, ExternalAddress(ONEHALF)); //0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL + movq(xmm4, ExternalAddress(SIGN_MASK)); //0x00000000UL, 0x80000000UL + pand(xmm4, xmm0); + por(xmm5, xmm4); + addpd(xmm1, xmm5); + cvttsd2sil(edx, xmm1); + cvtsi2sdl(xmm1, edx); + movq(xmm3, ExternalAddress(P_1)); //0x54400000UL, 0x3fb921fbUL + movdqu(xmm2, ExternalAddress(P_2)); //0x1a600000UL, 0x3d90b461UL, 0x1a600000UL, 0x3d90b461UL + mulsd(xmm3, xmm1); + unpcklpd(xmm1, xmm1); + shll(eax, 3); + addl(edx, 1865216); + movdqu(xmm4, xmm0); + addl(edx, eax); + andl(edx, 63); + movdqu(xmm5, ExternalAddress(SC_4)); //0x54400000UL, 0x3fb921fbUL + lea(rax, ExternalAddress(Ctable)); + shll(edx, 5); + addq(rax, rdx); + mulpd(xmm2, xmm1); + subsd(xmm0, xmm3); + mulsd(xmm1, ExternalAddress(P_3)); //0x2e037073UL, 0x3b63198aUL + subsd(xmm4, xmm3); + movq(xmm7, Address(rax, 8)); + unpcklpd(xmm0, xmm0); + movdqu(xmm3, xmm4); + subsd(xmm4, xmm2); + mulpd(xmm5, xmm0); + subpd(xmm0, xmm2); + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm2); + movdqu(xmm2, Address(rax, 0)); + subsd(xmm1, xmm3); + movq(xmm3, Address(rax, 24)); + addsd(xmm2, xmm3); + subsd(xmm7, xmm2); + subsd(xmm1, xmm6); + movdqu(xmm6, ExternalAddress(SC_2)); //0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL + mulsd(xmm2, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, ExternalAddress(SC_3)); //0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL + mulsd(xmm4, Address(rax, 0)); + addpd(xmm6, ExternalAddress(SC_1)); //0x55555555UL, 0xbfc55555UL, 0x00000000UL, 0xbfe00000UL + mulpd(xmm5, xmm0); + movdqu(xmm0, xmm3); + addsd(xmm3, Address(rax, 8)); + mulpd(xmm1, xmm7); + movdqu(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movq(xmm5, Address(rax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(rax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm5, xmm0); + addsd(xmm3, xmm7); + addsd(xmm1, xmm5); + addsd(xmm1, xmm3); + addsd(xmm1, xmm6); + unpckhpd(xmm6, xmm6); + movdqu(xmm0, xmm4); + addsd(xmm1, xmm6); + addsd(xmm0, xmm1); + jmp(B1_4); + + bind(L_2TAG_PACKET_8_0_1); + addl(edx, 64); + movq(r9, r10); + movq(r10, r8); + movl(r8, 0); + cmpq(r9, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_9_0_1); + addl(edx, 64); + movq(r9, r10); + movq(r10, r8); + cmpq(r9, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_9_0_1); xorpd(xmm0, xmm0); - movl(eax, 32760); - pinsrw(xmm0, eax, 3); - jmp(L_2TAG_PACKET_18_0_2); + xorpd(xmm6, xmm6); + jmp(L_2TAG_PACKET_12_0_1); - bind(L_2TAG_PACKET_57_0_2); - movdl(edx, xmm1); - movdqu(xmm3, xmm1); - psrlq(xmm3, 20); - movdl(ecx, xmm3); - orl(ecx, edx); - jcc(Assembler::equal, L_2TAG_PACKET_58_0_2); - addsd(xmm1, xmm1); - movdqu(xmm0, xmm1); - jmp(L_2TAG_PACKET_18_0_2); + bind(L_2TAG_PACKET_10_0_1); + jcc(Assembler::equal, L_2TAG_PACKET_11_0_1); + negl(ecx); + shrq(r10); + movq(rax, r9); + shrq(r9); + subl(edx, ecx); + negl(ecx); + addl(ecx, 64); + shlq(rax); + orq(r10, rax); + jmp(L_2TAG_PACKET_11_0_1); - bind(L_2TAG_PACKET_58_0_2); - pextrw(eax, xmm0, 3); - andl(eax, 32752); - pextrw(edx, xmm1, 3); - xorpd(xmm0, xmm0); - subl(eax, 16368); - xorl(eax, edx); - testl(eax, 32768); - jcc(Assembler::notEqual, L_2TAG_PACKET_18_0_2); - movl(edx, 32752); - pinsrw(xmm0, edx, 3); - jmp(L_2TAG_PACKET_18_0_2); + bind(L_2TAG_PACKET_4_0_1); + negl(ecx); + shlq(r9, 32); + orq(r9, r11); + shlq(r9); + movq(rdi, r9); + testl(r9, INT_MIN); + jcc(Assembler::notEqual, L_2TAG_PACKET_13_0_1); + shrl(r9); + movl(ebx, 0); + shrq(rdi, 3); + jmp(L_2TAG_PACKET_7_0_1); - bind(L_2TAG_PACKET_54_0_2); - pextrw(eax, xmm1, 3); - pextrw(ecx, xmm2, 3); - xorl(eax, ecx); - testl(eax, 32768); - jcc(Assembler::equal, L_2TAG_PACKET_50_0_2); - jmp(L_2TAG_PACKET_49_0_2); + bind(L_2TAG_PACKET_5_0_1); + shrl(r9); + movl(ebx, 536870912); + shrl(ebx); + shlq(r9, 32); + orq(r9, r11); + shlq(rbx, 32); + addl(rdi, 536870912); + movl(rcx, 0); + movl(r11, 0); + subq(rcx, r8); + sbbq(r11, r10); + sbbq(rbx, r9); + movq(r8, rcx); + movq(r10, r11); + movq(r9, rbx); + movl(ebx, 32768); + jmp(L_2TAG_PACKET_6_0_1); - bind(L_2TAG_PACKET_6_0_2); - movl(tmp, Address(rsp, 64)); + bind(L_2TAG_PACKET_13_0_1); + shrl(r9); + mov64(rbx, 0x100000000); + shrq(rbx); + movl(rcx, 0); + movl(r11, 0); + subq(rcx, r8); + sbbq(r11, r10); + sbbq(rbx, r9); + movq(r8, rcx); + movq(r10, r11); + movq(r9, rbx); + movl(ebx, 32768); + shrq(rdi, 3); + addl(rdi, 536870912); + jmp(L_2TAG_PACKET_7_0_1); + bind(L_2TAG_PACKET_3_0_1); + movq(xmm0, Address(rsp, 8)); + mulsd(xmm0, ExternalAddress(NEG_ZERO)); //0x00000000UL, 0x80000000UL + movq(Address(rsp, 0), xmm0); + + bind(L_2TAG_PACKET_14_0_1); + + bind(B1_4); + addq(rsp, 16); + pop(rbx); } -#endif // !_LP64 +/******************************************************************************/ +// ALGORITHM DESCRIPTION - COS() +// --------------------- +// +// 1. RANGE REDUCTION +// +// We perform an initial range reduction from X to r with +// +// X =~= N * pi/32 + r +// +// so that |r| <= pi/64 + epsilon. We restrict inputs to those +// where |N| <= 932560. Beyond this, the range reduction is +// insufficiently accurate. For extremely small inputs, +// denormalization can occur internally, impacting performance. +// This means that the main path is actually only taken for +// 2^-252 <= |X| < 90112. +// +// To avoid branches, we perform the range reduction to full +// accuracy each time. +// +// X - N * (P_1 + P_2 + P_3) +// +// where P_1 and P_2 are 32-bit numbers (so multiplication by N +// is exact) and P_3 is a 53-bit number. Together, these +// approximate pi well enough for all cases in the restricted +// range. +// +// The main reduction sequence is: +// +// y = 32/pi * x +// N = integer(y) +// (computed by adding and subtracting off SHIFTER) +// +// m_1 = N * P_1 +// m_2 = N * P_2 +// r_1 = x - m_1 +// r = r_1 - m_2 +// (this r can be used for most of the calculation) +// +// c_1 = r_1 - r +// m_3 = N * P_3 +// c_2 = c_1 - m_2 +// c = c_2 - m_3 +// +// 2. MAIN ALGORITHM +// +// The algorithm uses a table lookup based on B = M * pi / 32 +// where M = N mod 64. The stored values are: +// sigma closest power of 2 to cos(B) +// C_hl 53-bit cos(B) - sigma +// S_hi + S_lo 2 * 53-bit sin(B) +// +// The computation is organized as follows: +// +// sin(B + r + c) = [sin(B) + sigma * r] + +// r * (cos(B) - sigma) + +// sin(B) * [cos(r + c) - 1] + +// cos(B) * [sin(r + c) - r] +// +// which is approximately: +// +// [S_hi + sigma * r] + +// C_hl * r + +// S_lo + S_hi * [(cos(r) - 1) - r * c] + +// (C_hl + sigma) * [(sin(r) - r) + c] +// +// and this is what is actually computed. We separate this sum +// into four parts: +// +// hi + med + pols + corr +// +// where +// +// hi = S_hi + sigma r +// med = C_hl * r +// pols = S_hi * (cos(r) - 1) + (C_hl + sigma) * (sin(r) - r) +// corr = S_lo + c * ((C_hl + sigma) - S_hi * r) +// +// 3. POLYNOMIAL +// +// The polynomial S_hi * (cos(r) - 1) + (C_hl + sigma) * +// (sin(r) - r) can be rearranged freely, since it is quite +// small, so we exploit parallelism to the fullest. +// +// psc4 = SC_4 * r_1 +// msc4 = psc4 * r +// r2 = r * r +// msc2 = SC_2 * r2 +// r4 = r2 * r2 +// psc3 = SC_3 + msc4 +// psc1 = SC_1 + msc2 +// msc3 = r4 * psc3 +// sincospols = psc1 + msc3 +// pols = sincospols * +// +// +// 4. CORRECTION TERM +// +// This is where the "c" component of the range reduction is +// taken into account; recall that just "r" is used for most of +// the calculation. +// +// -c = m_3 - c_2 +// -d = S_hi * r - (C_hl + sigma) +// corr = -c * -d + S_lo +// +// 5. COMPENSATED SUMMATIONS +// +// The two successive compensated summations add up the high +// and medium parts, leaving just the low parts to add up at +// the end. +// +// rs = sigma * r +// res_int = S_hi + rs +// k_0 = S_hi - res_int +// k_2 = k_0 + rs +// med = C_hl * r +// res_hi = res_int + med +// k_1 = res_int - res_hi +// k_3 = k_1 + med +// +// 6. FINAL SUMMATION +// +// We now add up all the small parts: +// +// res_lo = pols(hi) + pols(lo) + corr + k_1 + k_3 +// +// Now the overall result is just: +// +// res_hi + res_lo +// +// 7. SMALL ARGUMENTS +// +// Inputs with |X| < 2^-252 are treated specially as +// 1 - |x|. +// +// Special cases: +// cos(NaN) = quiet NaN, and raise invalid exception +// cos(INF) = NaN and raise invalid exception +// cos(0) = 1 +// +/******************************************************************************/ + +ALIGNED_(8) juint _ONE[] = +{ + 0x00000000UL, 0x3ff00000UL +}; + +void MacroAssembler::fast_cos(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register eax, Register ecx, Register edx, Register r8, Register r9, Register r10, Register r11) { + Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; + Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; + Label L_2TAG_PACKET_8_0_1, L_2TAG_PACKET_9_0_1, L_2TAG_PACKET_10_0_1, L_2TAG_PACKET_11_0_1; + Label L_2TAG_PACKET_12_0_1, L_2TAG_PACKET_13_0_1, B1_2, B1_3, B1_4, B1_5, start; + + assert_different_registers(r8, r9, r10, r11, eax, ecx, edx); + + address ONEHALF = (address)_ONEHALF; + address P_2 = (address)_P_2; + address SC_4 = (address)_SC_4; + address Ctable = (address)_Ctable; + address SC_2 = (address)_SC_2; + address SC_3 = (address)_SC_3; + address SC_1 = (address)_SC_1; + address PI_INV_TABLE = (address)_PI_INV_TABLE; + address PI_4 = (address)_PI_4; + address PI32INV = (address)_PI32INV; + address SIGN_MASK = (address)_SIGN_MASK; + address P_1 = (address)_P_1; + address P_3 = (address)_P_3; + address ONE = (address)_ONE; + address NEG_ZERO = (address)_NEG_ZERO; + + bind(start); + push(rbx); + subq(rsp, 16); + movsd(Address(rsp, 8), xmm0); + + bind(B1_2); + movl(eax, Address(rsp, 12)); + movq(xmm1, ExternalAddress(PI32INV)); //0x6dc9c883UL, 0x40245f30UL + andl(eax, 2147418112); + subl(eax, 808452096); + cmpl(eax, 281346048); + jcc(Assembler::above, L_2TAG_PACKET_0_0_1); + mulsd(xmm1, xmm0); + movdqu(xmm5, ExternalAddress(ONEHALF)); //0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL + movq(xmm4, ExternalAddress(SIGN_MASK)); //0x00000000UL, 0x80000000UL + pand(xmm4, xmm0); + por(xmm5, xmm4); + addpd(xmm1, xmm5); + cvttsd2sil(edx, xmm1); + cvtsi2sdl(xmm1, edx); + movdqu(xmm2, ExternalAddress(P_2)); //0x1a600000UL, 0x3d90b461UL, 0x1a600000UL, 0x3d90b461UL + movq(xmm3, ExternalAddress(P_1)); //0x54400000UL, 0x3fb921fbUL + mulsd(xmm3, xmm1); + unpcklpd(xmm1, xmm1); + addq(rdx, 1865232); + movdqu(xmm4, xmm0); + andq(rdx, 63); + movdqu(xmm5, ExternalAddress(SC_4)); //0xa556c734UL, 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL + lea(rax, ExternalAddress(Ctable)); + shlq(rdx, 5); + addq(rax, rdx); + mulpd(xmm2, xmm1); + subsd(xmm0, xmm3); + mulsd(xmm1, ExternalAddress(P_3)); //0x2e037073UL, 0x3b63198aUL + subsd(xmm4, xmm3); + movq(xmm7, Address(rax, 8)); + unpcklpd(xmm0, xmm0); + movdqu(xmm3, xmm4); + subsd(xmm4, xmm2); + mulpd(xmm5, xmm0); + subpd(xmm0, xmm2); + movdqu(xmm6, ExternalAddress(SC_2)); //0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm2); + movdqu(xmm2, Address(rax, 0)); + subsd(xmm1, xmm3); + movq(xmm3, Address(rax, 24)); + addsd(xmm2, xmm3); + subsd(xmm7, xmm2); + mulsd(xmm2, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, ExternalAddress(SC_3)); //0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL + mulsd(xmm4, Address(rax, 0)); + addpd(xmm6, ExternalAddress(SC_1)); //0x55555555UL, 0xbfc55555UL, 0x00000000UL, 0xbfe00000UL + mulpd(xmm5, xmm0); + movdqu(xmm0, xmm3); + addsd(xmm3, Address(rax, 8)); + mulpd(xmm1, xmm7); + movdqu(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movq(xmm5, Address(rax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(rax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm0, xmm5); + addsd(xmm3, xmm7); + addsd(xmm0, xmm1); + addsd(xmm0, xmm3); + addsd(xmm0, xmm6); + unpckhpd(xmm6, xmm6); + addsd(xmm0, xmm6); + addsd(xmm0, xmm4); + jmp(B1_4); + + bind(L_2TAG_PACKET_0_0_1); + jcc(Assembler::greater, L_2TAG_PACKET_1_0_1); + pextrw(eax, xmm0, 3); + andl(eax, 32767); + pinsrw(xmm0, eax, 3); + movq(xmm1, ExternalAddress(ONE)); //0x00000000UL, 0x3ff00000UL + subsd(xmm1, xmm0); + movdqu(xmm0, xmm1); + jmp(B1_4); + + bind(L_2TAG_PACKET_1_0_1); + pextrw(eax, xmm0, 3); + andl(eax, 32752); + cmpl(eax, 32752); + jcc(Assembler::equal, L_2TAG_PACKET_2_0_1); + pextrw(ecx, xmm0, 3); + andl(ecx, 32752); + subl(ecx, 16224); + shrl(ecx, 7); + andl(ecx, 65532); + lea(r11, ExternalAddress(PI_INV_TABLE)); + addq(rcx, r11); + movdq(rax, xmm0); + movl(r10, Address(rcx, 20)); + movl(r8, Address(rcx, 24)); + movl(edx, eax); + shrq(rax, 21); + orl(eax, INT_MIN); + shrl(eax, 11); + movl(r9, r10); + imulq(r10, rdx); + imulq(r9, rax); + imulq(r8, rax); + movl(rsi, Address(rcx, 16)); + movl(rdi, Address(rcx, 12)); + movl(r11, r10); + shrq(r10, 32); + addq(r9, r10); + addq(r11, r8); + movl(r8, r11); + shrq(r11, 32); + addq(r9, r11); + movl(r10, rsi); + imulq(rsi, rdx); + imulq(r10, rax); + movl(r11, rdi); + imulq(rdi, rdx); + movl(rbx, rsi); + shrq(rsi, 32); + addq(r9, rbx); + movl(rbx, r9); + shrq(r9, 32); + addq(r10, rsi); + addq(r10, r9); + shlq(rbx, 32); + orq(r8, rbx); + imulq(r11, rax); + movl(r9, Address(rcx, 8)); + movl(rsi, Address(rcx, 4)); + movl(rbx, rdi); + shrq(rdi, 32); + addq(r10, rbx); + movl(rbx, r10); + shrq(r10, 32); + addq(r11, rdi); + addq(r11, r10); + movq(rdi, r9); + imulq(r9, rdx); + imulq(rdi, rax); + movl(r10, r9); + shrq(r9, 32); + addq(r11, r10); + movl(r10, r11); + shrq(r11, 32); + addq(rdi, r9); + addq(rdi, r11); + movq(r9, rsi); + imulq(rsi, rdx); + imulq(r9, rax); + shlq(r10, 32); + orq(r10, rbx); + movl(eax, Address(rcx, 0)); + movl(r11, rsi); + shrq(rsi, 32); + addq(rdi, r11); + movl(r11, rdi); + shrq(rdi, 32); + addq(r9, rsi); + addq(r9, rdi); + imulq(rdx, rax); + pextrw(rbx, xmm0, 3); + lea(rdi, ExternalAddress(PI_INV_TABLE)); + subq(rcx, rdi); + addl(ecx, ecx); + addl(ecx, ecx); + addl(ecx, ecx); + addl(ecx, 19); + movl(rsi, 32768); + andl(rsi, rbx); + shrl(rbx, 4); + andl(rbx, 2047); + subl(rbx, 1023); + subl(ecx, rbx); + addq(r9, rdx); + movl(edx, ecx); + addl(edx, 32); + cmpl(ecx, 1); + jcc(Assembler::less, L_2TAG_PACKET_3_0_1); + negl(ecx); + addl(ecx, 29); + shll(r9); + movl(rdi, r9); + andl(r9, 536870911); + testl(r9, 268435456); + jcc(Assembler::notEqual, L_2TAG_PACKET_4_0_1); + shrl(r9); + movl(rbx, 0); + shlq(r9, 32); + orq(r9, r11); + + bind(L_2TAG_PACKET_5_0_1); + + bind(L_2TAG_PACKET_6_0_1); + cmpq(r9, 0); + jcc(Assembler::equal, L_2TAG_PACKET_7_0_1); + + bind(L_2TAG_PACKET_8_0_1); + bsrq(r11, r9); + movl(ecx, 29); + subl(ecx, r11); + jcc(Assembler::lessEqual, L_2TAG_PACKET_9_0_1); + shlq(r9); + movq(rax, r10); + shlq(r10); + addl(edx, ecx); + negl(ecx); + addl(ecx, 64); + shrq(rax); + shrq(r8); + orq(r9, rax); + orq(r10, r8); + + bind(L_2TAG_PACKET_10_0_1); + cvtsi2sdq(xmm0, r9); + shrq(r10, 1); + cvtsi2sdq(xmm3, r10); + xorpd(xmm4, xmm4); + shll(edx, 4); + negl(edx); + addl(edx, 16368); + orl(edx, rsi); + xorl(edx, rbx); + pinsrw(xmm4, edx, 3); + movq(xmm2, ExternalAddress(PI_4)); //0x40000000UL, 0x3fe921fbUL, 0x18469899UL, 0x3e64442dUL + movq(xmm6, ExternalAddress(8 + PI_4)); //0x3fe921fbUL, 0x18469899UL, 0x3e64442dUL + xorpd(xmm5, xmm5); + subl(edx, 1008); + pinsrw(xmm5, edx, 3); + mulsd(xmm0, xmm4); + shll(rsi, 16); + sarl(rsi, 31); + mulsd(xmm3, xmm5); + movdqu(xmm1, xmm0); + mulsd(xmm0, xmm2); + shrl(rdi, 29); + addsd(xmm1, xmm3); + mulsd(xmm3, xmm2); + addl(rdi, rsi); + xorl(rdi, rsi); + mulsd(xmm6, xmm1); + movl(eax, rdi); + addsd(xmm6, xmm3); + movdqu(xmm2, xmm0); + addsd(xmm0, xmm6); + subsd(xmm2, xmm0); + addsd(xmm6, xmm2); + + bind(L_2TAG_PACKET_11_0_1); + movq(xmm1, ExternalAddress(PI32INV)); //0x6dc9c883UL, 0x40245f30UL + mulsd(xmm1, xmm0); + movq(xmm5, ExternalAddress(ONEHALF)); //0x00000000UL, 0x3fe00000UL, 0x00000000UL, 0x3fe00000UL + movq(xmm4, ExternalAddress(SIGN_MASK)); //0x00000000UL, 0x80000000UL + pand(xmm4, xmm0); + por(xmm5, xmm4); + addpd(xmm1, xmm5); + cvttsd2siq(rdx, xmm1); + cvtsi2sdq(xmm1, rdx); + movq(xmm3, ExternalAddress(P_1)); //0x54400000UL, 0x3fb921fbUL + movdqu(xmm2, ExternalAddress(P_2)); //0x1a600000UL, 0x3d90b461UL, 0x1a600000UL, 0x3d90b461UL + mulsd(xmm3, xmm1); + unpcklpd(xmm1, xmm1); + shll(eax, 3); + addl(edx, 1865232); + movdqu(xmm4, xmm0); + addl(edx, eax); + andl(edx, 63); + movdqu(xmm5, ExternalAddress(SC_4)); //0xa556c734UL, 0x3ec71de3UL, 0x1a01a01aUL, 0x3efa01a0UL + lea(rax, ExternalAddress(Ctable)); + shll(edx, 5); + addq(rax, rdx); + mulpd(xmm2, xmm1); + subsd(xmm0, xmm3); + mulsd(xmm1, ExternalAddress(P_3)); //0x2e037073UL, 0x3b63198aUL + subsd(xmm4, xmm3); + movq(xmm7, Address(rax, 8)); + unpcklpd(xmm0, xmm0); + movdqu(xmm3, xmm4); + subsd(xmm4, xmm2); + mulpd(xmm5, xmm0); + subpd(xmm0, xmm2); + mulsd(xmm7, xmm4); + subsd(xmm3, xmm4); + mulpd(xmm5, xmm0); + mulpd(xmm0, xmm0); + subsd(xmm3, xmm2); + movdqu(xmm2, Address(rax, 0)); + subsd(xmm1, xmm3); + movq(xmm3, Address(rax, 24)); + addsd(xmm2, xmm3); + subsd(xmm7, xmm2); + subsd(xmm1, xmm6); + movdqu(xmm6, ExternalAddress(SC_2)); //0x11111111UL, 0x3f811111UL, 0x55555555UL, 0x3fa55555UL + mulsd(xmm2, xmm4); + mulpd(xmm6, xmm0); + mulsd(xmm3, xmm4); + mulpd(xmm2, xmm0); + mulpd(xmm0, xmm0); + addpd(xmm5, ExternalAddress(SC_3)); //0x1a01a01aUL, 0xbf2a01a0UL, 0x16c16c17UL, 0xbf56c16cUL + mulsd(xmm4, Address(rax, 0)); + addpd(xmm6, ExternalAddress(SC_1)); //0x55555555UL, 0xbfc55555UL, 0x00000000UL, 0xbfe00000UL + mulpd(xmm5, xmm0); + movdqu(xmm0, xmm3); + addsd(xmm3, Address(rax, 8)); + mulpd(xmm1, xmm7); + movdqu(xmm7, xmm4); + addsd(xmm4, xmm3); + addpd(xmm6, xmm5); + movq(xmm5, Address(rax, 8)); + subsd(xmm5, xmm3); + subsd(xmm3, xmm4); + addsd(xmm1, Address(rax, 16)); + mulpd(xmm6, xmm2); + addsd(xmm5, xmm0); + addsd(xmm3, xmm7); + addsd(xmm1, xmm5); + addsd(xmm1, xmm3); + addsd(xmm1, xmm6); + unpckhpd(xmm6, xmm6); + movdqu(xmm0, xmm4); + addsd(xmm1, xmm6); + addsd(xmm0, xmm1); + jmp(B1_4); + + bind(L_2TAG_PACKET_7_0_1); + addl(edx, 64); + movq(r9, r10); + movq(r10, r8); + movl(r8, 0); + cmpq(r9, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_8_0_1); + addl(edx, 64); + movq(r9, r10); + movq(r10, r8); + cmpq(r9, 0); + jcc(Assembler::notEqual, L_2TAG_PACKET_8_0_1); + xorpd(xmm0, xmm0); + xorpd(xmm6, xmm6); + jmp(L_2TAG_PACKET_11_0_1); + + bind(L_2TAG_PACKET_9_0_1); + jcc(Assembler::equal, L_2TAG_PACKET_10_0_1); + negl(ecx); + shrq(r10); + movq(rax, r9); + shrq(r9); + subl(edx, ecx); + negl(ecx); + addl(ecx, 64); + shlq(rax); + orq(r10, rax); + jmp(L_2TAG_PACKET_10_0_1); + bind(L_2TAG_PACKET_3_0_1); + negl(ecx); + shlq(r9, 32); + orq(r9, r11); + shlq(r9); + movq(rdi, r9); + testl(r9, INT_MIN); + jcc(Assembler::notEqual, L_2TAG_PACKET_12_0_1); + shrl(r9); + movl(rbx, 0); + shrq(rdi, 3); + jmp(L_2TAG_PACKET_6_0_1); + + bind(L_2TAG_PACKET_4_0_1); + shrl(r9); + movl(rbx, 536870912); + shrl(rbx); + shlq(r9, 32); + orq(r9, r11); + shlq(rbx, 32); + addl(rdi, 536870912); + movl(rcx, 0); + movl(r11, 0); + subq(rcx, r8); + sbbq(r11, r10); + sbbq(rbx, r9); + movq(r8, rcx); + movq(r10, r11); + movq(r9, rbx); + movl(rbx, 32768); + jmp(L_2TAG_PACKET_5_0_1); + + bind(L_2TAG_PACKET_12_0_1); + shrl(r9); + mov64(rbx, 0x100000000); + shrq(rbx); + movl(rcx, 0); + movl(r11, 0); + subq(rcx, r8); + sbbq(r11, r10); + sbbq(rbx, r9); + movq(r8, rcx); + movq(r10, r11); + movq(r9, rbx); + movl(rbx, 32768); + shrq(rdi, 3); + addl(rdi, 536870912); + jmp(L_2TAG_PACKET_6_0_1); + + bind(L_2TAG_PACKET_2_0_1); + movsd(xmm0, Address(rsp, 8)); + mulsd(xmm0, ExternalAddress(NEG_ZERO)); //0x00000000UL, 0x80000000UL + movq(Address(rsp, 0), xmm0); + + bind(L_2TAG_PACKET_13_0_1); + + bind(B1_4); + addq(rsp, 16); + pop(rbx); +} diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp index 288863524b9..ce7f8c02f5a 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp @@ -972,6 +972,15 @@ void MacroAssembler::addss(XMMRegister dst, AddressLiteral src) { } } +void MacroAssembler::addpd(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + Assembler::addpd(dst, as_Address(src)); + } else { + lea(rscratch1, src); + Assembler::addpd(dst, Address(rscratch1, 0)); + } +} + void MacroAssembler::align(int modulus) { align(modulus, offset()); } @@ -5417,7 +5426,7 @@ Register MacroAssembler::tlab_refill(Label& retry, Label& try_eden, Label& slow_case) { Register top = rax; - Register t1 = rcx; + Register t1 = rcx; // object size Register t2 = rsi; Register thread_reg = NOT_LP64(rdi) LP64_ONLY(r15_thread); assert_different_registers(top, thread_reg, t1, t2, /* preserve: */ rbx, rdx); @@ -5513,12 +5522,76 @@ Register MacroAssembler::tlab_refill(Label& retry, addptr(top, t1); subptr(top, (int32_t)ThreadLocalAllocBuffer::alignment_reserve_in_bytes()); movptr(Address(thread_reg, in_bytes(JavaThread::tlab_end_offset())), top); + + if (ZeroTLAB) { + // This is a fast TLAB refill, therefore the GC is not notified of it. + // So compiled code must fill the new TLAB with zeroes. + movptr(top, Address(thread_reg, in_bytes(JavaThread::tlab_start_offset()))); + zero_memory(top, t1, 0, t2); + } + verify_tlab(); jmp(retry); return thread_reg; // for use by caller } +// Preserves the contents of address, destroys the contents length_in_bytes and temp. +void MacroAssembler::zero_memory(Register address, Register length_in_bytes, int offset_in_bytes, Register temp) { + assert(address != length_in_bytes && address != temp && temp != length_in_bytes, "registers must be different"); + assert((offset_in_bytes & (BytesPerWord - 1)) == 0, "offset must be a multiple of BytesPerWord"); + Label done; + + testptr(length_in_bytes, length_in_bytes); + jcc(Assembler::zero, done); + + // initialize topmost word, divide index by 2, check if odd and test if zero + // note: for the remaining code to work, index must be a multiple of BytesPerWord +#ifdef ASSERT + { + Label L; + testptr(length_in_bytes, BytesPerWord - 1); + jcc(Assembler::zero, L); + stop("length must be a multiple of BytesPerWord"); + bind(L); + } +#endif + Register index = length_in_bytes; + xorptr(temp, temp); // use _zero reg to clear memory (shorter code) + if (UseIncDec) { + shrptr(index, 3); // divide by 8/16 and set carry flag if bit 2 was set + } else { + shrptr(index, 2); // use 2 instructions to avoid partial flag stall + shrptr(index, 1); + } +#ifndef _LP64 + // index could have not been a multiple of 8 (i.e., bit 2 was set) + { + Label even; + // note: if index was a multiple of 8, then it cannot + // be 0 now otherwise it must have been 0 before + // => if it is even, we don't need to check for 0 again + jcc(Assembler::carryClear, even); + // clear topmost word (no jump would be needed if conditional assignment worked here) + movptr(Address(address, index, Address::times_8, offset_in_bytes - 0*BytesPerWord), temp); + // index could be 0 now, must check again + jcc(Assembler::zero, done); + bind(even); + } +#endif // !_LP64 + // initialize remaining object fields: index is a multiple of 2 now + { + Label loop; + bind(loop); + movptr(Address(address, index, Address::times_8, offset_in_bytes - 1*BytesPerWord), temp); + NOT_LP64(movptr(Address(address, index, Address::times_8, offset_in_bytes - 2*BytesPerWord), temp);) + decrement(index); + jcc(Assembler::notZero, loop); + } + + bind(done); +} + void MacroAssembler::incr_allocated_bytes(Register thread, Register var_size_in_bytes, int con_size_in_bytes, @@ -5730,34 +5803,22 @@ void MacroAssembler::trigfunc(char trig, int num_fpu_regs_in_use) { } Label slow_case, done; + if (trig == 't') { + ExternalAddress pi4_adr = (address)&pi_4; + if (reachable(pi4_adr)) { + // x ?<= pi/4 + fld_d(pi4_adr); + fld_s(1); // Stack: X PI/4 X + fabs(); // Stack: |X| PI/4 X + fcmp(tmp); + jcc(Assembler::above, slow_case); - ExternalAddress pi4_adr = (address)&pi_4; - if (reachable(pi4_adr)) { - // x ?<= pi/4 - fld_d(pi4_adr); - fld_s(1); // Stack: X PI/4 X - fabs(); // Stack: |X| PI/4 X - fcmp(tmp); - jcc(Assembler::above, slow_case); - - // fastest case: -pi/4 <= x <= pi/4 - switch(trig) { - case 's': - fsin(); - break; - case 'c': - fcos(); - break; - case 't': + // fastest case: -pi/4 <= x <= pi/4 ftan(); - break; - default: - assert(false, "bad intrinsic"); - break; - } - jmp(done); - } + jmp(done); + } + } // slow case: runtime call bind(slow_case); @@ -5790,7 +5851,6 @@ void MacroAssembler::trigfunc(char trig, int num_fpu_regs_in_use) { } } - // Look up the method for a megamorphic invokeinterface call. // The target method is determined by . // The receiver klass is in recv_klass. diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp index bb5782820f0..95931ad2b24 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp @@ -522,6 +522,8 @@ class MacroAssembler: public Assembler { Label& slow_case // continuation point if fast allocation fails ); Register tlab_refill(Label& retry_tlab, Label& try_eden, Label& slow_case); // returns TLS address + void zero_memory(Register address, Register length_in_bytes, int offset_in_bytes, Register temp); + void incr_allocated_bytes(Register thread, Register var_size_in_bytes, int con_size_in_bytes, Register t1 = noreg); @@ -865,6 +867,7 @@ class MacroAssembler: public Assembler { void andpd(XMMRegister dst, Address src) { Assembler::andpd(dst, src); } void andpd(XMMRegister dst, AddressLiteral src); + void andpd(XMMRegister dst, XMMRegister src) { Assembler::andpd(dst, src); } void andps(XMMRegister dst, XMMRegister src) { Assembler::andps(dst, src); } void andps(XMMRegister dst, Address src) { Assembler::andps(dst, src); } @@ -900,10 +903,6 @@ class MacroAssembler: public Assembler { void ldmxcsr(Address src) { Assembler::ldmxcsr(src); } void ldmxcsr(AddressLiteral src); - // compute pow(x,y) and exp(x) with x86 instructions. Don't cover - // all corner cases and may result in NaN and require fallback to a - // runtime call. - void fast_pow(); void fast_exp(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register rax, Register rcx, Register rdx, Register tmp); @@ -911,11 +910,32 @@ class MacroAssembler: public Assembler { void fast_log(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register rax, Register rcx, Register rdx, Register tmp1 LP64_ONLY(COMMA Register tmp2)); + void fast_pow(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, Register rax, Register rcx, Register rdx NOT_LP64(COMMA Register tmp) LP64_ONLY(COMMA Register tmp1) LP64_ONLY(COMMA Register tmp2) LP64_ONLY(COMMA Register tmp3) LP64_ONLY(COMMA Register tmp4)); + void fast_sin(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, + XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, + Register rax, Register rbx LP64_ONLY(COMMA Register rcx), Register rdx + LP64_ONLY(COMMA Register tmp1) LP64_ONLY(COMMA Register tmp2) + LP64_ONLY(COMMA Register tmp3) LP64_ONLY(COMMA Register tmp4)); + + void fast_cos(XMMRegister xmm0, XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, + XMMRegister xmm4, XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, + Register rax, Register rcx, Register rdx NOT_LP64(COMMA Register tmp) + LP64_ONLY(COMMA Register r8) LP64_ONLY(COMMA Register r9) + LP64_ONLY(COMMA Register r10) LP64_ONLY(COMMA Register r11)); + +#ifndef _LP64 + void libm_sincos_huge(XMMRegister xmm0, XMMRegister xmm1, Register eax, Register ecx, + Register edx, Register ebx, Register esi, Register edi, + Register ebp, Register esp); + void libm_reduce_pi04l(Register eax, Register ecx, Register edx, Register ebx, + Register esi, Register edi, Register ebp, Register esp); +#endif + void increase_precision(); void restore_precision(); @@ -944,6 +964,10 @@ public: void addss(XMMRegister dst, Address src) { Assembler::addss(dst, src); } void addss(XMMRegister dst, AddressLiteral src); + void addpd(XMMRegister dst, XMMRegister src) { Assembler::addpd(dst, src); } + void addpd(XMMRegister dst, Address src) { Assembler::addpd(dst, src); } + void addpd(XMMRegister dst, AddressLiteral src); + void divsd(XMMRegister dst, XMMRegister src) { Assembler::divsd(dst, src); } void divsd(XMMRegister dst, Address src) { Assembler::divsd(dst, src); } void divsd(XMMRegister dst, AddressLiteral src); diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp index 5ac5593f1c9..894e4cb3319 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp @@ -2102,22 +2102,6 @@ class StubGenerator: public StubCodeGenerator { __ flog10(); __ ret(0); } - { - StubCodeMark mark(this, "StubRoutines", "sin"); - StubRoutines::_intrinsic_sin = (double (*)(double)) __ pc(); - - __ fld_d(Address(rsp, 4)); - __ trigfunc('s'); - __ ret(0); - } - { - StubCodeMark mark(this, "StubRoutines", "cos"); - StubRoutines::_intrinsic_cos = (double (*)(double)) __ pc(); - - __ fld_d(Address(rsp, 4)); - __ trigfunc('c'); - __ ret(0); - } { StubCodeMark mark(this, "StubRoutines", "tan"); StubRoutines::_intrinsic_tan = (double (*)(double)) __ pc(); @@ -2802,8 +2786,7 @@ class StubGenerator: public StubCodeGenerator { const Register to = rdx; // destination array address const Register key = rcx; // key array address const Register counter = rdi; // counter byte array initialized from initvector array address - - // and left with the results of the last encryption block + // and updated with the incremented counter in the end const Register len_reg = rbx; const Register pos = rax; @@ -2829,10 +2812,7 @@ class StubGenerator: public StubCodeGenerator { __ movptr(from , from_param); __ movptr(to , to_param); - //__ movptr(key, key_param); - //__ movptr(counter, rvec_param); __ movptr(len_reg , len_param); - //__ movptr(pos, 0); // Use the partially used encrpyted counter from last invocation Label L_exit_preLoop, L_preLoop_start; @@ -3007,8 +2987,8 @@ class StubGenerator: public StubCodeGenerator { __ subptr(len_reg, AESBlockSize); __ jmp(L_singleBlockLoopTop[k]); - __ BIND(L_processTail_insr[k]); - __ addptr(pos, len_reg); + __ BIND(L_processTail_insr[k]); // Process the tail part of the input array + __ addptr(pos, len_reg); // 1. Insert bytes from src array into xmm_from0 register __ testptr(len_reg, 8); __ jcc(Assembler::zero, L_processTail_4_insr[k]); __ subptr(pos,8); @@ -3035,11 +3015,11 @@ class StubGenerator: public StubCodeGenerator { __ BIND(L_processTail_exit_insr[k]); __ movptr(saved_encCounter_start, saved_counter_param); - __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); - __ pxor(xmm_result0, xmm_from0); + __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); // 2. Perform pxor of the encrypted counter and plaintext Bytes. + __ pxor(xmm_result0, xmm_from0); // Also the encrypted counter is saved for next invocation. __ testptr(len_reg, 8); - __ jcc(Assembler::zero, L_processTail_4_extr[k]); + __ jcc(Assembler::zero, L_processTail_4_extr[k]); // 3. Extract bytes from xmm_result0 into the dest. array __ pextrd(Address(to, pos), xmm_result0, 0); __ pextrd(Address(to, pos, Address::times_1, 4), xmm_result0, 1); __ psrldq(xmm_result0, 8); @@ -3445,6 +3425,76 @@ class StubGenerator: public StubCodeGenerator { } + address generate_libm_reduce_pi04l() { + address start = __ pc(); + + BLOCK_COMMENT("Entry:"); + __ libm_reduce_pi04l(rax, rcx, rdx, rbx, rsi, rdi, rbp, rsp); + + return start; + + } + + address generate_libm_sin_cos_huge() { + address start = __ pc(); + + const XMMRegister x0 = xmm0; + const XMMRegister x1 = xmm1; + + BLOCK_COMMENT("Entry:"); + __ libm_sincos_huge(x0, x1, rax, rcx, rdx, rbx, rsi, rdi, rbp, rsp); + + return start; + + } + + address generate_libmSin() { + address start = __ pc(); + + const XMMRegister x0 = xmm0; + const XMMRegister x1 = xmm1; + const XMMRegister x2 = xmm2; + const XMMRegister x3 = xmm3; + + const XMMRegister x4 = xmm4; + const XMMRegister x5 = xmm5; + const XMMRegister x6 = xmm6; + const XMMRegister x7 = xmm7; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ fast_sin(x0, x1, x2, x3, x4, x5, x6, x7, rax, rbx, rdx); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + + } + + address generate_libmCos() { + address start = __ pc(); + + const XMMRegister x0 = xmm0; + const XMMRegister x1 = xmm1; + const XMMRegister x2 = xmm2; + const XMMRegister x3 = xmm3; + + const XMMRegister x4 = xmm4; + const XMMRegister x5 = xmm5; + const XMMRegister x6 = xmm6; + const XMMRegister x7 = xmm7; + + const Register tmp = rbx; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ fast_cos(x0, x1, x2, x3, x4, x5, x6, x7, rax, rcx, rdx, tmp); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + + } // Safefetch stubs. void generate_safefetch(const char* name, int size, address* entry, @@ -3673,6 +3723,16 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_dexp = generate_libmExp(); StubRoutines::_dlog = generate_libmLog(); StubRoutines::_dpow = generate_libmPow(); + if (UseLibmSinIntrinsic || UseLibmCosIntrinsic) { + StubRoutines::_dlibm_reduce_pi04l = generate_libm_reduce_pi04l(); + StubRoutines::_dlibm_sin_cos_huge = generate_libm_sin_cos_huge(); + } + if (UseLibmSinIntrinsic) { + StubRoutines::_dsin = generate_libmSin(); + } + if (UseLibmCosIntrinsic) { + StubRoutines::_dcos = generate_libmCos(); + } } } diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp index b3eb330ac9b..db518d804db 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp @@ -2986,32 +2986,6 @@ class StubGenerator: public StubCodeGenerator { __ addq(rsp, 8); __ ret(0); } - { - StubCodeMark mark(this, "StubRoutines", "sin"); - StubRoutines::_intrinsic_sin = (double (*)(double)) __ pc(); - - __ subq(rsp, 8); - __ movdbl(Address(rsp, 0), xmm0); - __ fld_d(Address(rsp, 0)); - __ trigfunc('s'); - __ fstp_d(Address(rsp, 0)); - __ movdbl(xmm0, Address(rsp, 0)); - __ addq(rsp, 8); - __ ret(0); - } - { - StubCodeMark mark(this, "StubRoutines", "cos"); - StubRoutines::_intrinsic_cos = (double (*)(double)) __ pc(); - - __ subq(rsp, 8); - __ movdbl(Address(rsp, 0), xmm0); - __ fld_d(Address(rsp, 0)); - __ trigfunc('c'); - __ fstp_d(Address(rsp, 0)); - __ movdbl(xmm0, Address(rsp, 0)); - __ addq(rsp, 8); - __ ret(0); - } { StubCodeMark mark(this, "StubRoutines", "tan"); StubRoutines::_intrinsic_tan = (double (*)(double)) __ pc(); @@ -3752,7 +3726,7 @@ class StubGenerator: public StubCodeGenerator { const Register to = c_rarg1; // destination array address const Register key = c_rarg2; // key array address const Register counter = c_rarg3; // counter byte array initialized from counter array address - // and left with the results of the last encryption block + // and updated with the incremented counter in the end #ifndef _WIN64 const Register len_reg = c_rarg4; const Register saved_encCounter_start = c_rarg5; @@ -3967,8 +3941,8 @@ class StubGenerator: public StubCodeGenerator { __ addptr(pos, AESBlockSize); __ subptr(len_reg, AESBlockSize); __ jmp(L_singleBlockLoopTop[k]); - __ BIND(L_processTail_insr[k]); - __ addptr(pos, len_reg); + __ BIND(L_processTail_insr[k]); // Process the tail part of the input array + __ addptr(pos, len_reg); // 1. Insert bytes from src array into xmm_from0 register __ testptr(len_reg, 8); __ jcc(Assembler::zero, L_processTail_4_insr[k]); __ subptr(pos,8); @@ -3993,11 +3967,11 @@ class StubGenerator: public StubCodeGenerator { __ pinsrb(xmm_from0, Address(from, pos), 0); __ BIND(L_processTail_exit_insr[k]); - __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); - __ pxor(xmm_result0, xmm_from0); + __ movdqu(Address(saved_encCounter_start, 0), xmm_result0); // 2. Perform pxor of the encrypted counter and plaintext Bytes. + __ pxor(xmm_result0, xmm_from0); // Also the encrypted counter is saved for next invocation. __ testptr(len_reg, 8); - __ jcc(Assembler::zero, L_processTail_4_extr[k]); + __ jcc(Assembler::zero, L_processTail_4_extr[k]); // 3. Extract bytes from xmm_result0 into the dest. array __ pextrq(Address(to, pos), xmm_result0, 0); __ psrldq(xmm_result0, 8); __ addptr(pos, 8); @@ -4640,6 +4614,92 @@ class StubGenerator: public StubCodeGenerator { #endif __ fast_pow(x0, x1, x2, x3, x4, x5, x6, x7, rax, rcx, rdx, tmp1, tmp2, tmp3, tmp4); +#ifdef _WIN64 + // restore xmm regs belonging to calling function + __ movdqu(xmm6, Address(rsp, 0)); + __ movdqu(xmm7, Address(rsp, 2 * wordSize)); + __ addptr(rsp, 4 * wordSize); +#endif + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + + } + + address generate_libmSin() { + address start = __ pc(); + + const XMMRegister x0 = xmm0; + const XMMRegister x1 = xmm1; + const XMMRegister x2 = xmm2; + const XMMRegister x3 = xmm3; + + const XMMRegister x4 = xmm4; + const XMMRegister x5 = xmm5; + const XMMRegister x6 = xmm6; + const XMMRegister x7 = xmm7; + + const Register tmp1 = r8; + const Register tmp2 = r9; + const Register tmp3 = r10; + const Register tmp4 = r11; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + +#ifdef _WIN64 + // save the xmm registers which must be preserved 6-7 + __ subptr(rsp, 4 * wordSize); + __ movdqu(Address(rsp, 0), xmm6); + __ movdqu(Address(rsp, 2 * wordSize), xmm7); +#endif + __ fast_sin(x0, x1, x2, x3, x4, x5, x6, x7, rax, rbx, rcx, rdx, tmp1, tmp2, tmp3, tmp4); + +#ifdef _WIN64 + // restore xmm regs belonging to calling function + __ movdqu(xmm6, Address(rsp, 0)); + __ movdqu(xmm7, Address(rsp, 2 * wordSize)); + __ addptr(rsp, 4 * wordSize); +#endif + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + + } + + address generate_libmCos() { + address start = __ pc(); + + const XMMRegister x0 = xmm0; + const XMMRegister x1 = xmm1; + const XMMRegister x2 = xmm2; + const XMMRegister x3 = xmm3; + + const XMMRegister x4 = xmm4; + const XMMRegister x5 = xmm5; + const XMMRegister x6 = xmm6; + const XMMRegister x7 = xmm7; + + const Register tmp1 = r8; + const Register tmp2 = r9; + const Register tmp3 = r10; + const Register tmp4 = r11; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + +#ifdef _WIN64 + // save the xmm registers which must be preserved 6-7 + __ subptr(rsp, 4 * wordSize); + __ movdqu(Address(rsp, 0), xmm6); + __ movdqu(Address(rsp, 2 * wordSize), xmm7); +#endif + __ fast_cos(x0, x1, x2, x3, x4, x5, x6, x7, rax, rcx, rdx, tmp1, tmp2, tmp3, tmp4); + #ifdef _WIN64 // restore xmm regs belonging to calling function __ movdqu(xmm6, Address(rsp, 0)); @@ -4849,6 +4909,12 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_dexp = generate_libmExp(); StubRoutines::_dlog = generate_libmLog(); StubRoutines::_dpow = generate_libmPow(); + if (UseLibmSinIntrinsic) { + StubRoutines::_dsin = generate_libmSin(); + } + if (UseLibmCosIntrinsic) { + StubRoutines::_dcos = generate_libmCos(); + } } } diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp index c1ea0223389..17f76e8f876 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp @@ -30,7 +30,7 @@ // extend it. enum platform_dependent_constants { - code_size1 = 9000, // simply increase if too small (assembler will crash if too small) + code_size1 = 20000, // simply increase if too small (assembler will crash if too small) code_size2 = 33800 // simply increase if too small (assembler will crash if too small) }; diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp index 6f39276dc05..b4750ff4f90 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp @@ -32,7 +32,7 @@ static bool returns_to_call_stub(address return_pc) { return return_pc == _call_stub_return_address; } enum platform_dependent_constants { - code_size1 = 19000, // simply increase if too small (assembler will crash if too small) + code_size1 = 30000, // simply increase if too small (assembler will crash if too small) code_size2 = 35000 // simply increase if too small (assembler will crash if too small) }; diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index d31b0e5acd2..8756b51e899 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -671,7 +671,7 @@ void VM_Version::get_processor_features() { } // --AES-CTR ends-- } - } else if (UseAES || UseAESIntrinsics) { + } else if (UseAES || UseAESIntrinsics || UseAESCTRIntrinsics) { if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { warning("AES instructions are not available on this CPU"); FLAG_SET_DEFAULT(UseAES, false); @@ -707,16 +707,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); } - if (UseAESIntrinsics) { - if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - UseAESCTRIntrinsics = true; - } - } else if (UseAESCTRIntrinsics) { - if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) - warning("AES/CTR intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } - if (supports_sse4_2()) { if (FLAG_IS_DEFAULT(UseCRC32CIntrinsics)) { UseCRC32CIntrinsics = true; diff --git a/hotspot/src/cpu/x86/vm/x86.ad b/hotspot/src/cpu/x86/vm/x86.ad index 28d25cca76b..a316b914a52 100644 --- a/hotspot/src/cpu/x86/vm/x86.ad +++ b/hotspot/src/cpu/x86/vm/x86.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -1711,6 +1711,14 @@ const bool Matcher::match_rule_supported(int opcode) { if (UseAVX < 1 || UseAVX > 2) ret_value = false; break; + case Op_StrIndexOf: + if (!UseSSE42Intrinsics) + ret_value = false; + break; + case Op_StrIndexOfChar: + if (!(UseSSE > 4)) + ret_value = false; + break; } return ret_value; // Per default match rules are supported. diff --git a/hotspot/src/cpu/x86/vm/x86_32.ad b/hotspot/src/cpu/x86/vm/x86_32.ad index cebd468acb6..f684cd5b5cd 100644 --- a/hotspot/src/cpu/x86/vm/x86_32.ad +++ b/hotspot/src/cpu/x86/vm/x86_32.ad @@ -968,14 +968,15 @@ static int vec_stack_to_stack_helper(CodeBuffer *cbuf, bool do_size, int src_off case Op_VecS: calc_size = 3+src_offset_size + 3+dst_offset_size; break; - case Op_VecD: + case Op_VecD: { calc_size = 3+src_offset_size + 3+dst_offset_size; - src_offset += 4; - dst_offset += 4; - src_offset_size = (src_offset == 0) ? 0 : ((src_offset < 0x80) ? 1 : 4); - dst_offset_size = (dst_offset == 0) ? 0 : ((dst_offset < 0x80) ? 1 : 4); + int tmp_src_offset = src_offset + 4; + int tmp_dst_offset = dst_offset + 4; + src_offset_size = (tmp_src_offset == 0) ? 0 : ((tmp_src_offset < 0x80) ? 1 : 4); + dst_offset_size = (tmp_dst_offset == 0) ? 0 : ((tmp_dst_offset < 0x80) ? 1 : 4); calc_size += 3+src_offset_size + 3+dst_offset_size; break; + } case Op_VecX: case Op_VecY: case Op_VecZ: @@ -1009,6 +1010,7 @@ static int vec_stack_to_stack_helper(CodeBuffer *cbuf, bool do_size, int src_off __ vmovdqu(xmm0, Address(rsp, src_offset)); __ vmovdqu(Address(rsp, dst_offset), xmm0); __ vmovdqu(xmm0, Address(rsp, -32)); + break; case Op_VecZ: __ evmovdqul(Address(rsp, -64), xmm0, 2); __ evmovdqul(xmm0, Address(rsp, src_offset), 2); @@ -1019,7 +1021,7 @@ static int vec_stack_to_stack_helper(CodeBuffer *cbuf, bool do_size, int src_off ShouldNotReachHere(); } int size = __ offset() - offset; - assert(size == calc_size, "incorrect size calculattion"); + assert(size == calc_size, "incorrect size calculation"); return size; #ifndef PRODUCT } else if (!do_size) { @@ -1049,6 +1051,7 @@ static int vec_stack_to_stack_helper(CodeBuffer *cbuf, bool do_size, int src_off "vmovdqu [rsp + #%d], xmm0\n\t" "vmovdqu xmm0, [rsp - #32]", src_offset, dst_offset); + break; case Op_VecZ: st->print("vmovdqu [rsp - #64], xmm0\t# 512-bit mem-mem spill\n\t" "vmovdqu xmm0, [rsp + #%d]\n\t" @@ -9791,48 +9794,6 @@ instruct modD_reg(regD dst, regD src0, regD src1, eAXRegI rax, eFlagsReg cr) %{ ins_pipe( pipe_slow ); %} -instruct sinDPR_reg(regDPR1 dst, regDPR1 src) %{ - predicate (UseSSE<=1); - match(Set dst (SinD src)); - ins_cost(1800); - format %{ "DSIN $dst" %} - opcode(0xD9, 0xFE); - ins_encode( OpcP, OpcS ); - ins_pipe( pipe_slow ); -%} - -instruct sinD_reg(regD dst, eFlagsReg cr) %{ - predicate (UseSSE>=2); - match(Set dst (SinD dst)); - effect(KILL cr); // Push_{Src|Result}D() uses "{SUB|ADD} ESP,8" - ins_cost(1800); - format %{ "DSIN $dst" %} - opcode(0xD9, 0xFE); - ins_encode( Push_SrcD(dst), OpcP, OpcS, Push_ResultD(dst) ); - ins_pipe( pipe_slow ); -%} - -instruct cosDPR_reg(regDPR1 dst, regDPR1 src) %{ - predicate (UseSSE<=1); - match(Set dst (CosD src)); - ins_cost(1800); - format %{ "DCOS $dst" %} - opcode(0xD9, 0xFF); - ins_encode( OpcP, OpcS ); - ins_pipe( pipe_slow ); -%} - -instruct cosD_reg(regD dst, eFlagsReg cr) %{ - predicate (UseSSE>=2); - match(Set dst (CosD dst)); - effect(KILL cr); // Push_{Src|Result}D() uses "{SUB|ADD} ESP,8" - ins_cost(1800); - format %{ "DCOS $dst" %} - opcode(0xD9, 0xFF); - ins_encode( Push_SrcD(dst), OpcP, OpcS, Push_ResultD(dst) ); - ins_pipe( pipe_slow ); -%} - instruct tanDPR_reg(regDPR1 dst, regDPR1 src) %{ predicate (UseSSE<=1); match(Set dst(TanD src)); diff --git a/hotspot/src/cpu/x86/vm/x86_64.ad b/hotspot/src/cpu/x86/vm/x86_64.ad index 9def843e07e..455354834ea 100644 --- a/hotspot/src/cpu/x86/vm/x86_64.ad +++ b/hotspot/src/cpu/x86/vm/x86_64.ad @@ -1079,6 +1079,7 @@ static void vec_stack_to_stack_helper(CodeBuffer *cbuf, int src_offset, __ vmovdqu(xmm0, Address(rsp, src_offset)); __ vmovdqu(Address(rsp, dst_offset), xmm0); __ vmovdqu(xmm0, Address(rsp, -32)); + break; case Op_VecZ: __ evmovdqul(Address(rsp, -64), xmm0, 2); __ evmovdqul(xmm0, Address(rsp, src_offset), 2); @@ -9819,24 +9820,6 @@ instruct cmpD_imm(rRegI dst, regD src, immD con, rFlagsReg cr) %{ %} // -----------Trig and Trancendental Instructions------------------------------ -instruct cosD_reg(regD dst) %{ - match(Set dst (CosD dst)); - - format %{ "dcos $dst\n\t" %} - opcode(0xD9, 0xFF); - ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); - ins_pipe( pipe_slow ); -%} - -instruct sinD_reg(regD dst) %{ - match(Set dst (SinD dst)); - - format %{ "dsin $dst\n\t" %} - opcode(0xD9, 0xFE); - ins_encode( Push_SrcXD(dst), OpcP, OpcS, Push_ResultXD(dst) ); - ins_pipe( pipe_slow ); -%} - instruct tanD_reg(regD dst) %{ match(Set dst (TanD dst)); diff --git a/hotspot/src/jdk.vm.ci/share/classes/META-INF/services/jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory b/hotspot/src/jdk.vm.ci/share/classes/META-INF/services/jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory new file mode 100644 index 00000000000..fe3a4573058 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/META-INF/services/jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory @@ -0,0 +1,3 @@ +jdk.vm.ci.hotspot.aarch64.AArch64HotSpotJVMCIBackendFactory +jdk.vm.ci.hotspot.amd64.AMD64HotSpotJVMCIBackendFactory +jdk.vm.ci.hotspot.sparc.SPARCHotSpotJVMCIBackendFactory diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CallingConvention.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CallingConvention.java index 6161619548b..240b4be1423 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CallingConvention.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CallingConvention.java @@ -34,35 +34,9 @@ import jdk.vm.ci.meta.Value; public class CallingConvention { /** - * Constants denoting the type of a call for which a calling convention is requested. + * Marker interface denoting the type of a call for which a calling convention is requested. */ - public enum Type { - /** - * A request for the outgoing argument locations at a call site to Java code. - */ - JavaCall(true), - - /** - * A request for the incoming argument locations. - */ - JavaCallee(false), - - /** - * A request for the outgoing argument locations at a call site to external native code that - * complies with the platform ABI. - */ - NativeCall(true); - - /** - * Determines if this is a request for the outgoing argument locations at a call site. - */ - public final boolean out; - - public static final Type[] VALUES = values(); - - private Type(boolean out) { - this.out = out; - } + public interface Type { } /** diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeCacheProvider.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeCacheProvider.java index e4f3cae3bee..7e836d22f21 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeCacheProvider.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeCacheProvider.java @@ -22,12 +22,8 @@ */ package jdk.vm.ci.code; -import jdk.vm.ci.code.CompilationResult.Call; -import jdk.vm.ci.code.CompilationResult.DataPatch; -import jdk.vm.ci.code.CompilationResult.Mark; -import jdk.vm.ci.code.DataSection.Data; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.code.site.Call; +import jdk.vm.ci.code.site.Mark; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.SpeculationLog; @@ -41,7 +37,7 @@ public interface CodeCacheProvider { * default implementation of the method. * * @param method a method implemented by the installed code - * @param compResult the compilation result to be added + * @param compiledCode the compiled code to be added * @param log the speculation log to be used * @param installedCode a predefined {@link InstalledCode} object to use as a reference to the * installed code. If {@code null}, a new {@link InstalledCode} object will be @@ -49,8 +45,8 @@ public interface CodeCacheProvider { * @return a reference to the ready-to-run code * @throws BailoutException if the code installation failed */ - default InstalledCode addCode(ResolvedJavaMethod method, CompilationResult compResult, SpeculationLog log, InstalledCode installedCode) { - return installCode(new CompilationRequest(method), compResult, installedCode, log, false); + default InstalledCode addCode(ResolvedJavaMethod method, CompiledCode compiledCode, SpeculationLog log, InstalledCode installedCode) { + return installCode(method, compiledCode, installedCode, log, false); } /** @@ -59,21 +55,20 @@ public interface CodeCacheProvider { * * @param method a method implemented by the installed code and for which the installed code * becomes the default implementation - * @param compResult the compilation result to be added + * @param compiledCode the compiled code to be added * @return a reference to the ready-to-run code * @throws BailoutException if the code installation failed */ - default InstalledCode setDefaultCode(ResolvedJavaMethod method, CompilationResult compResult) { - return installCode(new CompilationRequest(method), compResult, null, null, true); + default InstalledCode setDefaultCode(ResolvedJavaMethod method, CompiledCode compiledCode) { + return installCode(method, compiledCode, null, null, true); } /** * Installs code based on a given compilation result. * - * @param compRequest details of the method compiled to produce {@code compResult} or - * {@code null} if the input to {@code compResult} was not a - * {@link ResolvedJavaMethod} - * @param compResult the compilation result to be added + * @param method the method compiled to produce {@code compiledCode} or {@code null} if the + * input to {@code compResult} was not a {@link ResolvedJavaMethod} + * @param compiledCode the compiled code to be added * @param installedCode a pre-allocated {@link InstalledCode} object to use as a reference to * the installed code. If {@code null}, a new {@link InstalledCode} object will be * created. @@ -85,7 +80,7 @@ public interface CodeCacheProvider { * @return a reference to the compiled and ready-to-run installed code * @throws BailoutException if the code installation failed */ - InstalledCode installCode(CompilationRequest compRequest, CompilationResult compResult, InstalledCode installedCode, SpeculationLog log, boolean isDefault); + InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault); /** * Invalidates {@code installedCode} such that {@link InvalidInstalledCodeException} will be @@ -121,19 +116,6 @@ public interface CodeCacheProvider { */ int getMinimumOutgoingSize(); - /** - * Determines if a {@link DataPatch} should be created for a given primitive constant that is - * part of a {@link CompilationResult}. A data patch is always created for an object constant. - */ - boolean needsDataPatch(JavaConstant constant); - - /** - * Create a {@link Data} item for one or more {@link Constant Constants}, that can be used in a - * {@link DataPatch}. If more than one {@link Constant} is given, then they are tightly packed - * into a single {@link Data} item. - */ - Data createDataItem(Constant... constants); - /** * Gets a description of the target architecture. */ diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeUtil.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeUtil.java index a138a30dca2..9cde2e53bee 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeUtil.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CodeUtil.java @@ -417,7 +417,7 @@ public class CodeUtil { /** * Create a calling convention from a {@link ResolvedJavaMethod}. */ - public static CallingConvention getCallingConvention(CodeCacheProvider codeCache, CallingConvention.Type type, ResolvedJavaMethod method, boolean stackOnly) { + public static CallingConvention getCallingConvention(CodeCacheProvider codeCache, CallingConvention.Type type, ResolvedJavaMethod method) { Signature sig = method.getSignature(); JavaType retType = sig.getReturnType(null); int sigCount = sig.getParameterCount(false); @@ -434,6 +434,6 @@ public class CodeUtil { } RegisterConfig registerConfig = codeCache.getRegisterConfig(); - return registerConfig.getCallingConvention(type, retType, argTypes, codeCache.getTarget(), stackOnly); + return registerConfig.getCallingConvention(type, retType, argTypes, codeCache.getTarget()); } } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationRequestResult.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationRequestResult.java new file mode 100644 index 00000000000..fcde020b7d9 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationRequestResult.java @@ -0,0 +1,71 @@ +/* + * 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.vm.ci.code; + +/** + * Simple class to provide information about the result of a compile request. + */ +public final class CompilationRequestResult { + + /** + * A user readable description of the failure. + */ + private final String failureMessage; + + /** + * Whether this is a transient failure where retrying would help. + */ + private final boolean retry; + + /** + * Number of bytecodes inlined into the compilation, exclusive of the bytecodes in the root + * method. + */ + private final int inlinedBytecodes; + + private CompilationRequestResult(String failureMessage, boolean retry, int inlinedBytecodes) { + this.failureMessage = failureMessage; + this.retry = retry; + this.inlinedBytecodes = inlinedBytecodes; + } + + public static CompilationRequestResult success(int inlinedBytecodes) { + return new CompilationRequestResult(null, true, inlinedBytecodes); + } + + public static CompilationRequestResult failure(String failureMessage, boolean retry) { + return new CompilationRequestResult(failureMessage, retry, 0); + } + + public String getFailureMessage() { + return failureMessage; + } + + public boolean getRetry() { + return retry; + } + + public int getInlinedBytecodes() { + return inlinedBytecodes; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationResult.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationResult.java deleted file mode 100644 index b8c5ff4843a..00000000000 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompilationResult.java +++ /dev/null @@ -1,1078 +0,0 @@ -/* - * Copyright (c) 2009, 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. - */ -package jdk.vm.ci.code; - -import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; -import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import jdk.vm.ci.meta.Assumptions.Assumption; -import jdk.vm.ci.meta.InvokeTarget; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.MetaUtil; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.VMConstant; - -/** - * Represents the output from compiling a method, including the compiled machine code, associated - * data and references, relocation information, deoptimization information, etc. - */ -public class CompilationResult { - - /** - * Represents a code position with associated additional information. - */ - public abstract static class Site { - - /** - * The position (or offset) of this site with respect to the start of the target method. - */ - public final int pcOffset; - - public Site(int pos) { - this.pcOffset = pos; - } - - @Override - public final int hashCode() { - throw new UnsupportedOperationException("hashCode"); - } - - @Override - public String toString() { - return identityHashCodeString(this); - } - - @Override - public abstract boolean equals(Object obj); - } - - /** - * Represents an infopoint with associated debug info. Note that safepoints are also infopoints. - */ - public static class Infopoint extends Site implements Comparable { - - public final DebugInfo debugInfo; - - public final InfopointReason reason; - - public Infopoint(int pcOffset, DebugInfo debugInfo, InfopointReason reason) { - super(pcOffset); - this.debugInfo = debugInfo; - this.reason = reason; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(pcOffset); - sb.append("[]"); - appendDebugInfo(sb, debugInfo); - return sb.toString(); - } - - @Override - public int compareTo(Infopoint o) { - if (pcOffset < o.pcOffset) { - return -1; - } else if (pcOffset > o.pcOffset) { - return 1; - } - return this.reason.compareTo(o.reason); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj.getClass() == getClass()) { - Infopoint that = (Infopoint) obj; - if (this.pcOffset == that.pcOffset && Objects.equals(this.debugInfo, that.debugInfo) && Objects.equals(this.reason, that.reason)) { - return true; - } - } - return false; - } - } - - public enum MetaSpaceAccessType { - Move, - Store, // store only works for compressed oops (memory <- 32bit value). Compressed oops is - // not supported using AOT. TODO: Look at HotSpotStoreConstantOp - Compare; // HotSpotCompareMemoryConstantOp, HotSpotCompareConstantOp - - private MetaSpaceAccessType() { - } - } - - /** - * Represents a meta space pointer access in the code. - */ - public static final class MetaSpaceAccess extends Infopoint { - - /** - * Metaspace reference. - */ - public final Object reference; // Object here is a HotSpotResolvedObjectType or a - // HotSpotMetaSpaceConstant - - public final MetaSpaceAccessType type; - - /** - * Instruction size. - */ - public final int instructionSize; - - public MetaSpaceAccess(Object reference, int instructionSize, MetaSpaceAccessType type, int pcOffset, DebugInfo debugInfo) { - super(pcOffset, debugInfo, InfopointReason.METASPACE_ACCESS); - this.type = type; - this.reference = reference; - this.instructionSize = instructionSize; - } - } - - /** - * Represents a call in the code. - */ - public static final class Call extends Infopoint { - - /** - * The target of the call. - */ - public final InvokeTarget target; - - /** - * The size of the call instruction. - */ - public final int size; - - /** - * Specifies if this call is direct or indirect. A direct call has an immediate operand - * encoding the absolute or relative (to the call itself) address of the target. An indirect - * call has a register or memory operand specifying the target address of the call. - */ - public final boolean direct; - - public Call(InvokeTarget target, int pcOffset, int size, boolean direct, DebugInfo debugInfo) { - super(pcOffset, debugInfo, InfopointReason.CALL); - this.size = size; - this.target = target; - this.direct = direct; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof Call && super.equals(obj)) { - Call that = (Call) obj; - if (this.size == that.size && this.direct == that.direct && Objects.equals(this.target, that.target)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(pcOffset); - sb.append('['); - sb.append(target); - sb.append(']'); - - if (debugInfo != null) { - appendDebugInfo(sb, debugInfo); - } - - return sb.toString(); - } - } - - /** - * Represents some external data that is referenced by the code. - */ - public abstract static class Reference { - - @Override - public abstract int hashCode(); - - @Override - public abstract boolean equals(Object obj); - } - - public static final class ConstantReference extends Reference { - - private final VMConstant constant; - - public ConstantReference(VMConstant constant) { - this.constant = constant; - } - - public VMConstant getConstant() { - return constant; - } - - @Override - public String toString() { - return constant.toString(); - } - - @Override - public int hashCode() { - return constant.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ConstantReference) { - ConstantReference that = (ConstantReference) obj; - return Objects.equals(this.constant, that.constant); - } - return false; - } - } - - public static final class DataSectionReference extends Reference { - - private boolean initialized; - private int offset; - - public DataSectionReference() { - // will be set after the data section layout is fixed - offset = 0xDEADDEAD; - } - - public int getOffset() { - assert initialized; - - return offset; - } - - public void setOffset(int offset) { - assert !initialized; - initialized = true; - - this.offset = offset; - } - - @Override - public int hashCode() { - return offset; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DataSectionReference) { - DataSectionReference that = (DataSectionReference) obj; - return this.offset == that.offset; - } - return false; - } - - @Override - public String toString() { - if (initialized) { - return String.format("DataSection[0x%x]", offset); - } else { - return "DataSection[?]"; - } - } - } - - /** - * Represents a code site that references some data. The associated data can be either a - * {@link DataSectionReference reference} to the data section, or it may be an inlined - * {@link JavaConstant} that needs to be patched. - */ - public static final class DataPatch extends Site { - - public Reference reference; - public Object note; - - public DataPatch(int pcOffset, Reference reference) { - super(pcOffset); - this.reference = reference; - this.note = null; - } - - public DataPatch(int pcOffset, Reference reference, Object note) { - super(pcOffset); - this.reference = reference; - this.note = note; - } - - @Override - public String toString() { - if (note != null) { - return String.format("%d[, note: %s]", pcOffset, reference.toString(), note.toString()); - } else { - return String.format("%d[]", pcOffset, reference.toString()); - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DataPatch) { - DataPatch that = (DataPatch) obj; - if (this.pcOffset == that.pcOffset && Objects.equals(this.reference, that.reference) && Objects.equals(this.note, that.note)) { - return true; - } - } - return false; - } - } - - /** - * Provides extra information about instructions or data at specific positions in - * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to - * enhance a disassembly of the code. - */ - public abstract static class CodeAnnotation { - - public final int position; - - public CodeAnnotation(int position) { - this.position = position; - } - - @Override - public final int hashCode() { - throw new UnsupportedOperationException("hashCode"); - } - - @Override - public String toString() { - return identityHashCodeString(this); - } - - @Override - public abstract boolean equals(Object obj); - } - - /** - * A string comment about one or more instructions at a specific position in the code. - */ - public static final class CodeComment extends CodeAnnotation { - - public final String value; - - public CodeComment(int position, String comment) { - super(position); - this.value = comment; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof CodeComment) { - CodeComment that = (CodeComment) obj; - if (this.position == that.position && this.value.equals(that.value)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "@" + position + ": " + value; - } - } - - /** - * Describes a table of signed offsets embedded in the code. The offsets are relative to the - * starting address of the table. This type of table maybe generated when translating a - * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch} - * JVM instruction). - * - * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} - * inclusive. - */ - public static final class JumpTable extends CodeAnnotation { - - /** - * The low value in the key range (inclusive). - */ - public final int low; - - /** - * The high value in the key range (inclusive). - */ - public final int high; - - /** - * The size (in bytes) of each table entry. - */ - public final int entrySize; - - public JumpTable(int position, int low, int high, int entrySize) { - super(position); - this.low = low; - this.high = high; - this.entrySize = entrySize; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof JumpTable) { - JumpTable that = (JumpTable) obj; - if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) { - return true; - } - } - return false; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]"; - } - } - - /** - * Represents exception handler information for a specific code position. It includes the catch - * code position as well as the caught exception type. - */ - public static final class ExceptionHandler extends Site { - - public final int handlerPos; - - ExceptionHandler(int pcOffset, int handlerPos) { - super(pcOffset); - this.handlerPos = handlerPos; - } - - @Override - public String toString() { - return String.format("%d[]", pcOffset, handlerPos); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ExceptionHandler) { - ExceptionHandler that = (ExceptionHandler) obj; - if (this.pcOffset == that.pcOffset && this.handlerPos == that.handlerPos) { - return true; - } - } - return false; - } - } - - /** - * Represents a mark in the machine code that can be used by the runtime for its own purposes. A - * mark can reference other marks. - */ - public static final class Mark extends Site { - - public final Object id; - - public Mark(int pcOffset, Object id) { - super(pcOffset); - this.id = id; - } - - @Override - public String toString() { - if (id == null) { - return String.format("%d[]", pcOffset); - } else if (id instanceof Integer) { - return String.format("%d[]", pcOffset, Integer.toHexString((Integer) id)); - } else { - return String.format("%d[]", pcOffset, id.toString()); - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof Mark) { - Mark that = (Mark) obj; - if (this.pcOffset == that.pcOffset && Objects.equals(this.id, that.id)) { - return true; - } - } - return false; - } - } - - /** - * Specifies whether this compilation is a {@code +ImmutableCode} {@code +GeneratePIC} - * compilation. - */ - private final boolean isImmutablePIC; - - private boolean closed; - - private int entryBCI = -1; - - private final DataSection dataSection = new DataSection(); - - private final List infopoints = new ArrayList<>(); - private final List dataPatches = new ArrayList<>(); - private final List exceptionHandlers = new ArrayList<>(); - private final List marks = new ArrayList<>(); - - private int totalFrameSize = -1; - private int customStackAreaOffset = -1; - - private final String name; - - /** - * The buffer containing the emitted machine code. - */ - private byte[] targetCode; - - /** - * The leading number of bytes in {@link #targetCode} containing the emitted machine code. - */ - private int targetCodeSize; - - private ArrayList annotations; - - private Assumption[] assumptions; - - /** - * The list of the methods whose bytecodes were used as input to the compilation. If - * {@code null}, then the compilation did not record method dependencies. Otherwise, the first - * element of this array is the root method of the compilation. - */ - private ResolvedJavaMethod[] methods; - - private int bytecodeSize; - - private boolean hasUnsafeAccess; - - public CompilationResult() { - this(null); - } - - public CompilationResult(String name) { - this.name = name; - this.isImmutablePIC = false; - } - - public CompilationResult(boolean isImmutablePIC) { - this.name = null; - this.isImmutablePIC = isImmutablePIC; - } - - @Override - public int hashCode() { - // CompilationResult instances should not be used as hash map keys - throw new UnsupportedOperationException("hashCode"); - } - - @Override - public String toString() { - if (methods != null) { - return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]"; - } - return identityHashCodeString(this); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj.getClass() == getClass()) { - CompilationResult that = (CompilationResult) obj; - // @formatter:off - if (this.entryBCI == that.entryBCI && - this.customStackAreaOffset == that.customStackAreaOffset && - this.totalFrameSize == that.totalFrameSize && - this.targetCodeSize == that.targetCodeSize && - Objects.equals(this.name, that.name) && - Objects.equals(this.annotations, that.annotations) && - Objects.equals(this.dataSection, that.dataSection) && - Objects.equals(this.exceptionHandlers, that.exceptionHandlers) && - Objects.equals(this.dataPatches, that.dataPatches) && - Objects.equals(this.infopoints, that.infopoints) && - Objects.equals(this.marks, that.marks) && - Arrays.equals(this.assumptions, that.assumptions) && - Arrays.equals(targetCode, that.targetCode)) { - return true; - } - // @formatter:on - } - return false; - } - - /** - * @return true is this is a {@code +ImmutableCode} {@code +GeneratePIC} compilation, false - * otherwise. - */ - public boolean isImmutablePIC() { - return isImmutablePIC; - } - - /** - * @return the entryBCI - */ - public int getEntryBCI() { - return entryBCI; - } - - /** - * @param entryBCI the entryBCI to set - */ - public void setEntryBCI(int entryBCI) { - checkOpen(); - this.entryBCI = entryBCI; - } - - /** - * Sets the assumptions made during compilation. - */ - public void setAssumptions(Assumption[] assumptions) { - checkOpen(); - this.assumptions = assumptions; - } - - /** - * Gets the assumptions made during compilation. - * - * The caller must not modify the contents of the returned array. - */ - public Assumption[] getAssumptions() { - return assumptions; - } - - /** - * Sets the methods whose bytecodes were used as input to the compilation. - * - * @param rootMethod the root method of the compilation - * @param inlinedMethods the methods inlined during compilation - */ - public void setMethods(ResolvedJavaMethod rootMethod, Collection inlinedMethods) { - checkOpen(); - assert rootMethod != null; - assert inlinedMethods != null; - if (inlinedMethods.contains(rootMethod)) { - methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]); - for (int i = 0; i < methods.length; i++) { - if (methods[i].equals(rootMethod)) { - if (i != 0) { - ResolvedJavaMethod tmp = methods[0]; - methods[0] = methods[i]; - methods[i] = tmp; - } - break; - } - } - } else { - methods = new ResolvedJavaMethod[1 + inlinedMethods.size()]; - methods[0] = rootMethod; - int i = 1; - for (ResolvedJavaMethod m : inlinedMethods) { - methods[i++] = m; - } - } - } - - /** - * Gets the methods whose bytecodes were used as input to the compilation. - * - * The caller must not modify the contents of the returned array. - * - * @return {@code null} if the compilation did not record method dependencies otherwise the - * methods whose bytecodes were used as input to the compilation with the first element - * being the root method of the compilation - */ - public ResolvedJavaMethod[] getMethods() { - return methods; - } - - public void setBytecodeSize(int bytecodeSize) { - checkOpen(); - this.bytecodeSize = bytecodeSize; - } - - public int getBytecodeSize() { - return bytecodeSize; - } - - public DataSection getDataSection() { - return dataSection; - } - - /** - * The total frame size of the method in bytes. This includes the return address pushed onto the - * stack, if any. - * - * @return the frame size - */ - public int getTotalFrameSize() { - assert totalFrameSize != -1 : "frame size not yet initialized!"; - return totalFrameSize; - } - - /** - * Sets the total frame size in bytes. This includes the return address pushed onto the stack, - * if any. - * - * @param size the size of the frame in bytes - */ - public void setTotalFrameSize(int size) { - checkOpen(); - totalFrameSize = size; - } - - /** - * Sets the machine that has been generated by the compiler. - * - * @param code the machine code generated - * @param size the size of the machine code - */ - public void setTargetCode(byte[] code, int size) { - checkOpen(); - targetCode = code; - targetCodeSize = size; - } - - /** - * Records a data patch in the code section. The data patch can refer to something in the - * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined - * constant}. - * - * @param codePos The position in the code that needs to be patched. - * @param ref The reference that should be inserted in the code. - */ - public void recordDataPatch(int codePos, Reference ref) { - checkOpen(); - assert codePos >= 0 && ref != null; - dataPatches.add(new DataPatch(codePos, ref)); - } - - /** - * Records a data patch in the code section. The data patch can refer to something in the - * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined - * constant}. - * - * @param codePos The position in the code that needs to be patched. - * @param ref The reference that should be inserted in the code. - * @param note The note attached to data patch for use by post-processing tools - */ - public void recordDataPatchWithNote(int codePos, Reference ref, Object note) { - assert codePos >= 0 && ref != null; - dataPatches.add(new DataPatch(codePos, ref, note)); - } - - /** - * Records metaspace access. - */ - public void recordMetaspaceAccess(Object reference, int instructionSize, MetaSpaceAccessType type, int codePos, DebugInfo debugInfo) { - final MetaSpaceAccess metaspace = new MetaSpaceAccess(reference, instructionSize, type, codePos, debugInfo); - addInfopoint(metaspace); - } - - /** - * Records a call in the code array. - * - * @param codePos the position of the call in the code array - * @param size the size of the call instruction - * @param target the being called - * @param debugInfo the debug info for the call - * @param direct specifies if this is a {@linkplain Call#direct direct} call - */ - public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) { - checkOpen(); - final Call call = new Call(target, codePos, size, direct, debugInfo); - addInfopoint(call); - } - - /** - * Records an exception handler for this method. - * - * @param codePos the position in the code that is covered by the handler - * @param handlerPos the position of the handler - */ - public void recordExceptionHandler(int codePos, int handlerPos) { - checkOpen(); - assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos); - exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos)); - } - - /** - * Validate if the exception handler for codePos already exists and handlerPos is different. - * - * @param codePos - * @param handlerPos - * @return true if the validation is successful - */ - private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) { - ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos); - return exHandler == null || exHandler.handlerPos == handlerPos; - } - - /** - * Returns the first ExceptionHandler which matches codePos. - * - * @param codePos position to search for - * @return first matching ExceptionHandler - */ - private ExceptionHandler getExceptionHandlerForCodePos(int codePos) { - for (ExceptionHandler h : exceptionHandlers) { - if (h.pcOffset == codePos) { - return h; - } - } - return null; - } - - /** - * Records an infopoint in the code array. - * - * @param codePos the position of the infopoint in the code array - * @param debugInfo the debug info for the infopoint - */ - public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) { - addInfopoint(new Infopoint(codePos, debugInfo, reason)); - } - - /** - * Records a custom infopoint in the code section. - * - * Compiler implementations can use this method to record non-standard infopoints, which are not - * handled by dedicated methods like {@link #recordCall}. - * - * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint} - */ - public void addInfopoint(Infopoint infopoint) { - checkOpen(); - infopoints.add(infopoint); - } - - /** - * Records an instruction mark within this method. - * - * @param codePos the position in the code that is covered by the handler - * @param markId the identifier for this mark - */ - public Mark recordMark(int codePos, Object markId) { - checkOpen(); - Mark mark = new Mark(codePos, markId); - marks.add(mark); - return mark; - } - - /** - * Offset in bytes for the custom stack area (relative to sp). - * - * @return the offset in bytes - */ - public int getCustomStackAreaOffset() { - return customStackAreaOffset; - } - - /** - * @see #getCustomStackAreaOffset() - * @param offset - */ - public void setCustomStackAreaOffset(int offset) { - checkOpen(); - customStackAreaOffset = offset; - } - - /** - * @return the machine code generated for this method - */ - public byte[] getTargetCode() { - return targetCode; - } - - /** - * @return the size of the machine code generated for this method - */ - public int getTargetCodeSize() { - return targetCodeSize; - } - - /** - * @return the code annotations or {@code null} if there are none - */ - public List getAnnotations() { - if (annotations == null) { - return Collections.emptyList(); - } - return annotations; - } - - public void addAnnotation(CodeAnnotation annotation) { - checkOpen(); - assert annotation != null; - if (annotations == null) { - annotations = new ArrayList<>(); - } - annotations.add(annotation); - } - - private static void appendDebugInfo(StringBuilder sb, DebugInfo info) { - if (info != null) { - ReferenceMap refMap = info.getReferenceMap(); - if (refMap != null) { - sb.append(refMap.toString()); - sb.append(']'); - } - RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo(); - if (calleeSaveInfo != null) { - sb.append(" callee-save-info["); - String sep = ""; - for (Map.Entry e : calleeSaveInfo.registersToSlots(true).entrySet()) { - sb.append(sep).append(e.getKey()).append("->").append(e.getValue()); - sep = ", "; - } - sb.append(']'); - } - BytecodePosition codePos = info.getBytecodePosition(); - if (codePos != null) { - MetaUtil.appendLocation(sb.append(" "), codePos.getMethod(), codePos.getBCI()); - if (info.hasFrame()) { - sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack); - if (info.frame().numLocks > 0) { - sb.append(" #locks=").append(info.frame().numLocks); - } - } - } - } - } - - /** - * @return the list of infopoints, sorted by {@link Site#pcOffset} - */ - public List getInfopoints() { - if (infopoints.isEmpty()) { - return emptyList(); - } - return unmodifiableList(infopoints); - } - - /** - * @return the list of data references - */ - public List getDataPatches() { - if (dataPatches.isEmpty()) { - return emptyList(); - } - return unmodifiableList(dataPatches); - } - - /** - * @return the list of exception handlers - */ - public List getExceptionHandlers() { - if (exceptionHandlers.isEmpty()) { - return emptyList(); - } - return unmodifiableList(exceptionHandlers); - } - - /** - * @return the list of marks - */ - public List getMarks() { - if (marks.isEmpty()) { - return emptyList(); - } - return unmodifiableList(marks); - } - - public String getName() { - return name; - } - - public void setHasUnsafeAccess(boolean hasUnsafeAccess) { - checkOpen(); - this.hasUnsafeAccess = hasUnsafeAccess; - } - - public boolean hasUnsafeAccess() { - return hasUnsafeAccess; - } - - /** - * Clears the information in this object pertaining to generating code. That is, the - * {@linkplain #getMarks() marks}, {@linkplain #getInfopoints() infopoints}, - * {@linkplain #getExceptionHandlers() exception handlers}, {@linkplain #getDataPatches() data - * patches} and {@linkplain #getAnnotations() annotations} recorded in this object are cleared. - */ - public void resetForEmittingCode() { - checkOpen(); - infopoints.clear(); - dataPatches.clear(); - exceptionHandlers.clear(); - marks.clear(); - dataSection.clear(); - if (annotations != null) { - annotations.clear(); - } - } - - private void checkOpen() { - if (closed) { - throw new IllegalStateException(); - } - } - - /** - * Closes this compilation result to future updates. - */ - public void close() { - if (closed) { - throw new IllegalStateException("Cannot re-close compilation result " + this); - } - dataSection.close(); - closed = true; - } -} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompiledCode.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompiledCode.java new file mode 100644 index 00000000000..214bcbfaafe --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/CompiledCode.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code; + +/** + * The output from compiling a method. + */ +public interface CompiledCode { +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/DataSection.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/DataSection.java deleted file mode 100644 index c397cd1c0fb..00000000000 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/DataSection.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2014, 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. - */ -package jdk.vm.ci.code; - -import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Objects; -import java.util.function.Consumer; - -import jdk.vm.ci.code.CompilationResult.DataPatch; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; -import jdk.vm.ci.code.DataSection.Data; -import jdk.vm.ci.meta.SerializableConstant; - -public final class DataSection implements Iterable { - - @FunctionalInterface - public interface DataBuilder { - - void emit(ByteBuffer buffer, Consumer patch); - - static DataBuilder raw(byte[] data) { - return (buffer, patch) -> buffer.put(data); - } - - static DataBuilder serializable(SerializableConstant c) { - return (buffer, patch) -> c.serialize(buffer); - } - - static DataBuilder zero(int size) { - switch (size) { - case 1: - return (buffer, patch) -> buffer.put((byte) 0); - case 2: - return (buffer, patch) -> buffer.putShort((short) 0); - case 4: - return (buffer, patch) -> buffer.putInt(0); - case 8: - return (buffer, patch) -> buffer.putLong(0L); - default: - return (buffer, patch) -> { - int rest = size; - while (rest > 8) { - buffer.putLong(0L); - rest -= 8; - } - while (rest > 0) { - buffer.put((byte) 0); - rest--; - } - }; - } - } - } - - public static final class Data { - - private int alignment; - private final int size; - private final DataBuilder builder; - - private DataSectionReference ref; - - public Data(int alignment, int size, DataBuilder builder) { - this.alignment = alignment; - this.size = size; - this.builder = builder; - - // initialized in DataSection.insertData(Data) - ref = null; - } - - public void updateAlignment(int newAlignment) { - if (newAlignment == alignment) { - return; - } - alignment = lcm(alignment, newAlignment); - } - - public int getAlignment() { - return alignment; - } - - public int getSize() { - return size; - } - - public DataBuilder getBuilder() { - return builder; - } - - @Override - public int hashCode() { - // Data instances should not be used as hash map keys - throw new UnsupportedOperationException("hashCode"); - } - - @Override - public String toString() { - return identityHashCodeString(this); - } - - @Override - public boolean equals(Object obj) { - assert ref != null; - if (obj == this) { - return true; - } - if (obj instanceof Data) { - Data that = (Data) obj; - if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) { - return true; - } - } - return false; - } - } - - private final ArrayList dataItems = new ArrayList<>(); - - private boolean closed; - private int sectionAlignment; - private int sectionSize; - - @Override - public int hashCode() { - // DataSection instances should not be used as hash map keys - throw new UnsupportedOperationException("hashCode"); - } - - @Override - public String toString() { - return identityHashCodeString(this); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DataSection) { - DataSection that = (DataSection) obj; - if (this.closed == that.closed && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) { - return true; - } - } - return false; - } - - /** - * Inserts a {@link Data} item into the data section. If the item is already in the data - * section, the same {@link DataSectionReference} is returned. - * - * @param data the {@link Data} item to be inserted - * @return a unique {@link DataSectionReference} identifying the {@link Data} item - */ - public DataSectionReference insertData(Data data) { - checkOpen(); - synchronized (data) { - if (data.ref == null) { - data.ref = new DataSectionReference(); - dataItems.add(data); - } - return data.ref; - } - } - - /** - * Transfers all {@link Data} from the provided other {@link DataSection} to this - * {@link DataSection}, and empties the other section. - */ - public void addAll(DataSection other) { - checkOpen(); - other.checkOpen(); - - for (Data data : other.dataItems) { - assert data.ref != null; - dataItems.add(data); - } - other.dataItems.clear(); - } - - /** - * Determines if this object has been {@link #close() closed}. - */ - public boolean closed() { - return closed; - } - - /** - * Computes the layout of the data section and closes this object to further updates. - * - * This must be called exactly once. - */ - void close() { - checkOpen(); - closed = true; - - // simple heuristic: put items with larger alignment requirement first - dataItems.sort((a, b) -> a.alignment - b.alignment); - - int position = 0; - int alignment = 1; - for (Data d : dataItems) { - alignment = lcm(alignment, d.alignment); - position = align(position, d.alignment); - - d.ref.setOffset(position); - position += d.size; - } - - sectionAlignment = alignment; - sectionSize = position; - } - - /** - * Gets the size of the data section. - * - * This must only be called once this object has been {@linkplain #closed() closed}. - */ - public int getSectionSize() { - checkClosed(); - return sectionSize; - } - - /** - * Gets the minimum alignment requirement of the data section. - * - * This must only be called once this object has been {@linkplain #closed() closed}. - */ - public int getSectionAlignment() { - checkClosed(); - return sectionAlignment; - } - - /** - * Builds the data section into a given buffer. - * - * This must only be called once this object has been {@linkplain #closed() closed}. - * - * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must - * hold at least {@link #getSectionSize()} bytes. - * @param patch a {@link Consumer} to receive {@link DataPatch data patches} for relocations in - * the data section - */ - public void buildDataSection(ByteBuffer buffer, Consumer patch) { - checkClosed(); - for (Data d : dataItems) { - buffer.position(d.ref.getOffset()); - d.builder.emit(buffer, patch); - } - } - - public Data findData(DataSectionReference ref) { - for (Data d : dataItems) { - if (d.ref == ref) { - return d; - } - } - return null; - } - - public Iterator iterator() { - return dataItems.iterator(); - } - - public static int lcm(int x, int y) { - if (x == 0) { - return y; - } else if (y == 0) { - return x; - } - - int a = Math.max(x, y); - int b = Math.min(x, y); - while (b > 0) { - int tmp = a % b; - a = b; - b = tmp; - } - - int gcd = a; - return x * y / gcd; - } - - private static int align(int position, int alignment) { - return ((position + alignment - 1) / alignment) * alignment; - } - - private void checkClosed() { - if (!closed) { - throw new IllegalStateException(); - } - } - - private void checkOpen() { - if (closed) { - throw new IllegalStateException(); - } - } - - public void clear() { - checkOpen(); - this.dataItems.clear(); - this.sectionAlignment = 0; - this.sectionSize = 0; - } -} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/RegisterConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/RegisterConfig.java index f942b723dbc..4a6d44c4e51 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/RegisterConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/RegisterConfig.java @@ -57,9 +57,8 @@ public interface RegisterConfig { * @param returnType the return type (can be null for methods returning {@code void}) * @param parameterTypes the types of the arguments of the call * @param target the target platform - * @param stackOnly ignore registers */ - CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly); + CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target); /** * Gets the ordered set of registers that are can be used to pass parameters according to a diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/package-info.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/package-info.java index 63f1ea5df74..8183f89b22d 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/package-info.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/package-info.java @@ -1,26 +1,29 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR - * REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 only, as published by the Free Software Foundation. + * This code is 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 + * 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. + * 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. + * 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 that defines the interface between a Java application that wants to install code and the runtime. * The runtime provides in implementation of the {@link jdk.vm.ci.code.CodeCacheProvider} interface. - * The method {@link jdk.vm.ci.code.CodeCacheProvider#addCode(jdk.vm.ci.meta.ResolvedJavaMethod, CompilationResult, jdk.vm.ci.meta.SpeculationLog, InstalledCode)} + * The method {@link jdk.vm.ci.code.CodeCacheProvider#addCode(jdk.vm.ci.meta.ResolvedJavaMethod, CompiledCode, jdk.vm.ci.meta.SpeculationLog, InstalledCode)} * can be used to install code. */ package jdk.vm.ci.code; diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Call.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Call.java new file mode 100644 index 00000000000..c323b055a35 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Call.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import java.util.Objects; + +import jdk.vm.ci.code.DebugInfo; +import jdk.vm.ci.meta.InvokeTarget; + +/** + * Represents a call in the code. + */ +public final class Call extends Infopoint { + + /** + * The target of the call. + */ + public final InvokeTarget target; + + /** + * The size of the call instruction. + */ + public final int size; + + /** + * Specifies if this call is direct or indirect. A direct call has an immediate operand encoding + * the absolute or relative (to the call itself) address of the target. An indirect call has a + * register or memory operand specifying the target address of the call. + */ + public final boolean direct; + + public Call(InvokeTarget target, int pcOffset, int size, boolean direct, DebugInfo debugInfo) { + super(pcOffset, debugInfo, InfopointReason.CALL); + this.size = size; + this.target = target; + this.direct = direct; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Call && super.equals(obj)) { + Call that = (Call) obj; + if (this.size == that.size && this.direct == that.direct && Objects.equals(this.target, that.target)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(pcOffset); + sb.append('['); + sb.append(target); + sb.append(']'); + + if (debugInfo != null) { + appendDebugInfo(sb, debugInfo); + } + + return sb.toString(); + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ConstantReference.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ConstantReference.java new file mode 100644 index 00000000000..59f0a1e6a55 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ConstantReference.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import java.util.Objects; + +import jdk.vm.ci.meta.VMConstant; + +public final class ConstantReference extends Reference { + + private final VMConstant constant; + + public ConstantReference(VMConstant constant) { + this.constant = constant; + } + + public VMConstant getConstant() { + return constant; + } + + @Override + public String toString() { + return constant.toString(); + } + + @Override + public int hashCode() { + return constant.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ConstantReference) { + ConstantReference that = (ConstantReference) obj; + return Objects.equals(this.constant, that.constant); + } + return false; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataPatch.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataPatch.java new file mode 100644 index 00000000000..45941aac9a0 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataPatch.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import java.util.Objects; + +import jdk.vm.ci.meta.JavaConstant; + +/** + * Represents a code site that references some data. The associated data can be either a + * {@link DataSectionReference reference} to the data section, or it may be an inlined + * {@link JavaConstant} that needs to be patched. + */ +public final class DataPatch extends Site { + + public Reference reference; + public Object note; + + public DataPatch(int pcOffset, Reference reference) { + super(pcOffset); + this.reference = reference; + this.note = null; + } + + public DataPatch(int pcOffset, Reference reference, Object note) { + super(pcOffset); + this.reference = reference; + this.note = note; + } + + @Override + public String toString() { + if (note != null) { + return String.format("%d[, note: %s]", pcOffset, reference.toString(), note.toString()); + } else { + return String.format("%d[]", pcOffset, reference.toString()); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DataPatch) { + DataPatch that = (DataPatch) obj; + if (this.pcOffset == that.pcOffset && Objects.equals(this.reference, that.reference) && Objects.equals(this.note, that.note)) { + return true; + } + } + return false; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataSectionReference.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataSectionReference.java new file mode 100644 index 00000000000..5602c99d4cb --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/DataSectionReference.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +public final class DataSectionReference extends Reference { + + private boolean initialized; + private int offset; + + public DataSectionReference() { + // will be set after the data section layout is fixed + offset = 0xDEADDEAD; + } + + public int getOffset() { + assert initialized; + + return offset; + } + + public void setOffset(int offset) { + assert !initialized; + initialized = true; + + this.offset = offset; + } + + @Override + public int hashCode() { + return offset; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DataSectionReference) { + DataSectionReference that = (DataSectionReference) obj; + return this.offset == that.offset; + } + return false; + } + + @Override + public String toString() { + if (initialized) { + return String.format("DataSection[0x%x]", offset); + } else { + return "DataSection[?]"; + } + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ExceptionHandler.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ExceptionHandler.java new file mode 100644 index 00000000000..3f9f73f3867 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/ExceptionHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +/** + * Represents exception handler information for a specific code position. It includes the catch code + * position as well as the caught exception type. + */ +public final class ExceptionHandler extends Site { + + public final int handlerPos; + + public ExceptionHandler(int pcOffset, int handlerPos) { + super(pcOffset); + this.handlerPos = handlerPos; + } + + @Override + public String toString() { + return String.format("%d[]", pcOffset, handlerPos); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ExceptionHandler) { + ExceptionHandler that = (ExceptionHandler) obj; + if (this.pcOffset == that.pcOffset && this.handlerPos == that.handlerPos) { + return true; + } + } + return false; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Infopoint.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Infopoint.java new file mode 100644 index 00000000000..c59c006a680 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Infopoint.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import java.util.Map; +import java.util.Objects; + +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.code.DebugInfo; +import jdk.vm.ci.code.ReferenceMap; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterSaveLayout; +import jdk.vm.ci.meta.MetaUtil; + +/** + * Represents an infopoint with associated debug info. Note that safepoints are also infopoints. + */ +public class Infopoint extends Site implements Comparable { + + public final DebugInfo debugInfo; + + public final InfopointReason reason; + + public Infopoint(int pcOffset, DebugInfo debugInfo, InfopointReason reason) { + super(pcOffset); + this.debugInfo = debugInfo; + this.reason = reason; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(pcOffset); + sb.append("[]"); + appendDebugInfo(sb, debugInfo); + return sb.toString(); + } + + @Override + public int compareTo(Infopoint o) { + if (pcOffset < o.pcOffset) { + return -1; + } else if (pcOffset > o.pcOffset) { + return 1; + } + return this.reason.compareTo(o.reason); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj.getClass() == getClass()) { + Infopoint that = (Infopoint) obj; + if (this.pcOffset == that.pcOffset && Objects.equals(this.debugInfo, that.debugInfo) && Objects.equals(this.reason, that.reason)) { + return true; + } + } + return false; + } + + protected static void appendDebugInfo(StringBuilder sb, DebugInfo info) { + if (info != null) { + ReferenceMap refMap = info.getReferenceMap(); + if (refMap != null) { + sb.append(refMap.toString()); + sb.append(']'); + } + RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo(); + if (calleeSaveInfo != null) { + sb.append(" callee-save-info["); + String sep = ""; + for (Map.Entry e : calleeSaveInfo.registersToSlots(true).entrySet()) { + sb.append(sep).append(e.getKey()).append("->").append(e.getValue()); + sep = ", "; + } + sb.append(']'); + } + BytecodePosition codePos = info.getBytecodePosition(); + if (codePos != null) { + MetaUtil.appendLocation(sb.append(" "), codePos.getMethod(), codePos.getBCI()); + if (info.hasFrame()) { + sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack); + if (info.frame().numLocks > 0) { + sb.append(" #locks=").append(info.frame().numLocks); + } + } + } + } + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/InfopointReason.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/InfopointReason.java similarity index 91% rename from hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/InfopointReason.java rename to hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/InfopointReason.java index 317ed96ce7e..6509dbdf69d 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/InfopointReason.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/InfopointReason.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.vm.ci.code; +package jdk.vm.ci.code.site; /** * A reason for infopoint insertion. diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Mark.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Mark.java new file mode 100644 index 00000000000..29de0c01334 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Mark.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import java.util.Objects; + +/** + * Represents a mark in the machine code that can be used by the runtime for its own purposes. A + * mark can reference other marks. + */ +public final class Mark extends Site { + + public final Object id; + + public Mark(int pcOffset, Object id) { + super(pcOffset); + this.id = id; + } + + @Override + public String toString() { + if (id == null) { + return String.format("%d[]", pcOffset); + } else if (id instanceof Integer) { + return String.format("%d[]", pcOffset, Integer.toHexString((Integer) id)); + } else { + return String.format("%d[]", pcOffset, id.toString()); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Mark) { + Mark that = (Mark) obj; + if (this.pcOffset == that.pcOffset && Objects.equals(this.id, that.id)) { + return true; + } + } + return false; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Reference.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Reference.java new file mode 100644 index 00000000000..601d4158a63 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Reference.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +/** + * Represents some external data that is referenced by the code. + */ +public abstract class Reference { + + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object obj); +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Site.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Site.java new file mode 100644 index 00000000000..dbf76c58be0 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/site/Site.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.code.site; + +import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; + +/** + * Represents a code position with associated additional information. + */ +public abstract class Site { + + /** + * The position (or offset) of this site with respect to the start of the target method. + */ + public final int pcOffset; + + public Site(int pos) { + this.pcOffset = pos; + } + + @Override + public final int hashCode() { + throw new UnsupportedOperationException("hashCode"); + } + + @Override + public String toString() { + return identityHashCodeString(this); + } + + @Override + public abstract boolean equals(Object obj); +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java index 6981820018b..79caafca05a 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java @@ -41,9 +41,7 @@ import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.inittimer.InitTimer; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.runtime.JVMCIBackend; -import jdk.vm.ci.service.ServiceProvider; -@ServiceProvider(HotSpotJVMCIBackendFactory.class) public class AArch64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFactory { protected EnumSet computeFeatures(@SuppressWarnings("unused") HotSpotVMConfig config) { @@ -124,8 +122,7 @@ public class AArch64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFac } } - protected JVMCIBackend createBackend(HotSpotMetaAccessProvider metaAccess, HotSpotCodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, - StackIntrospection stackIntrospection) { + protected JVMCIBackend createBackend(HotSpotMetaAccessProvider metaAccess, HotSpotCodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, StackIntrospection stackIntrospection) { return new JVMCIBackend(metaAccess, codeCache, constantReflection, stackIntrospection); } } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java index 9590d2fcda4..aafee14a145 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.aarch64/src/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotRegisterConfig.java @@ -62,6 +62,7 @@ import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCallingConventionType; import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; @@ -96,6 +97,7 @@ public class AArch64HotSpotRegisterConfig implements RegisterConfig { return allocatable.clone(); } + @Override public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) { ArrayList list = new ArrayList<>(); for (Register reg : registers) { @@ -191,16 +193,19 @@ public class AArch64HotSpotRegisterConfig implements RegisterConfig { } @Override - public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly) { - if (type == Type.NativeCall) { - return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; + if (type == HotSpotCallingConventionType.NativeCall) { + return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, hotspotType, target); } // On x64, parameter locations are the same whether viewed // from the caller or callee perspective - return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, hotspotType, target); } + @Override public Register[] getCallingConventionRegisters(Type type, JavaKind kind) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; switch (kind) { case Boolean: case Byte: @@ -209,7 +214,7 @@ public class AArch64HotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - return type == Type.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters; + return hotspotType == HotSpotCallingConventionType.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters; case Float: case Double: return simdParameterRegisters; @@ -218,7 +223,7 @@ public class AArch64HotSpotRegisterConfig implements RegisterConfig { } } - private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, Type type, TargetDescription target, boolean stackOnly) { + private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, HotSpotCallingConventionType type, TargetDescription target) { AllocatableValue[] locations = new AllocatableValue[parameterTypes.length]; int currentGeneral = 0; @@ -236,14 +241,14 @@ public class AArch64HotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - if (!stackOnly && currentGeneral < generalParameterRegisters.length) { + if (currentGeneral < generalParameterRegisters.length) { Register register = generalParameterRegisters[currentGeneral++]; locations[i] = register.asValue(target.getLIRKind(kind)); } break; case Float: case Double: - if (!stackOnly && currentSIMD < simdParameterRegisters.length) { + if (currentSIMD < simdParameterRegisters.length) { Register register = simdParameterRegisters[currentSIMD++]; locations[i] = register.asValue(target.getLIRKind(kind)); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java index fb6eba20f92..69a31604560 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java @@ -41,9 +41,7 @@ import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.inittimer.InitTimer; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.runtime.JVMCIBackend; -import jdk.vm.ci.service.ServiceProvider; -@ServiceProvider(HotSpotJVMCIBackendFactory.class) public class AMD64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFactory { protected EnumSet computeFeatures(HotSpotVMConfig config) { diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java index 876263c75ef..1baf7368a96 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.amd64/src/jdk/vm/ci/hotspot/amd64/AMD64HotSpotRegisterConfig.java @@ -56,6 +56,7 @@ import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCallingConventionType; import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; @@ -90,6 +91,7 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { return allocatable.clone(); } + @Override public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) { ArrayList list = new ArrayList<>(); for (Register reg : registers) { @@ -175,6 +177,7 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { return callerSaved; } + @Override public Register[] getCalleeSaveRegisters() { return null; } @@ -190,16 +193,19 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { } @Override - public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly) { - if (type == Type.NativeCall) { - return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; + if (type == HotSpotCallingConventionType.NativeCall) { + return callingConvention(nativeGeneralParameterRegisters, returnType, parameterTypes, hotspotType, target); } // On x64, parameter locations are the same whether viewed // from the caller or callee perspective - return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + return callingConvention(javaGeneralParameterRegisters, returnType, parameterTypes, hotspotType, target); } + @Override public Register[] getCallingConventionRegisters(Type type, JavaKind kind) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; switch (kind) { case Boolean: case Byte: @@ -208,7 +214,7 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - return type == Type.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters; + return hotspotType == HotSpotCallingConventionType.NativeCall ? nativeGeneralParameterRegisters : javaGeneralParameterRegisters; case Float: case Double: return xmmParameterRegisters; @@ -217,12 +223,12 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { } } - private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, Type type, TargetDescription target, boolean stackOnly) { + private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, HotSpotCallingConventionType type, TargetDescription target) { AllocatableValue[] locations = new AllocatableValue[parameterTypes.length]; int currentGeneral = 0; int currentXMM = 0; - int currentStackOffset = type == Type.NativeCall && needsNativeStackHomeSpace ? generalParameterRegisters.length * target.wordSize : 0; + int currentStackOffset = type == HotSpotCallingConventionType.NativeCall && needsNativeStackHomeSpace ? generalParameterRegisters.length * target.wordSize : 0; for (int i = 0; i < parameterTypes.length; i++) { final JavaKind kind = parameterTypes[i].getJavaKind().getStackKind(); @@ -235,14 +241,14 @@ public class AMD64HotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - if (!stackOnly && currentGeneral < generalParameterRegisters.length) { + if (currentGeneral < generalParameterRegisters.length) { Register register = generalParameterRegisters[currentGeneral++]; locations[i] = register.asValue(target.getLIRKind(kind)); } break; case Float: case Double: - if (!stackOnly && currentXMM < xmmParameterRegisters.length) { + if (currentXMM < xmmParameterRegisters.length) { Register register = xmmParameterRegisters[currentXMM++]; locations[i] = register.asValue(target.getLIRKind(kind)); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotJVMCIBackendFactory.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotJVMCIBackendFactory.java index f0f0b05190e..703167d99e3 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotJVMCIBackendFactory.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotJVMCIBackendFactory.java @@ -39,11 +39,9 @@ import jdk.vm.ci.hotspot.HotSpotStackIntrospection; import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.inittimer.InitTimer; import jdk.vm.ci.runtime.JVMCIBackend; -import jdk.vm.ci.service.ServiceProvider; import jdk.vm.ci.sparc.SPARC; import jdk.vm.ci.sparc.SPARC.CPUFeature; -@ServiceProvider(HotSpotJVMCIBackendFactory.class) public class SPARCHotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFactory { protected TargetDescription createTarget(HotSpotVMConfig config) { diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java index 58dfcdc3e55..c2c198e8348 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot.sparc/src/jdk/vm/ci/hotspot/sparc/SPARCHotSpotRegisterConfig.java @@ -22,9 +22,6 @@ */ package jdk.vm.ci.hotspot.sparc; -import static jdk.vm.ci.code.CallingConvention.Type.JavaCall; -import static jdk.vm.ci.code.CallingConvention.Type.JavaCallee; -import static jdk.vm.ci.code.CallingConvention.Type.NativeCall; import static jdk.vm.ci.meta.JavaKind.Void; import static jdk.vm.ci.meta.Value.ILLEGAL; import static jdk.vm.ci.sparc.SPARC.REGISTER_SAFE_AREA_SIZE; @@ -81,6 +78,7 @@ import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCallingConventionType; import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaKind; @@ -107,6 +105,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { return allocatable.clone(); } + @Override public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) { ArrayList list = new ArrayList<>(); for (Register reg : registers) { @@ -200,17 +199,20 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { } @Override - public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target, boolean stackOnly) { - if (type == JavaCall || type == NativeCall) { - return callingConvention(cpuCallerParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + public CallingConvention getCallingConvention(Type type, JavaType returnType, JavaType[] parameterTypes, TargetDescription target) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; + if (type == HotSpotCallingConventionType.JavaCall || type == HotSpotCallingConventionType.NativeCall) { + return callingConvention(cpuCallerParameterRegisters, returnType, parameterTypes, hotspotType, target); } - if (type == JavaCallee) { - return callingConvention(cpuCalleeParameterRegisters, returnType, parameterTypes, type, target, stackOnly); + if (type == HotSpotCallingConventionType.JavaCallee) { + return callingConvention(cpuCalleeParameterRegisters, returnType, parameterTypes, hotspotType, target); } throw JVMCIError.shouldNotReachHere(); } + @Override public Register[] getCallingConventionRegisters(Type type, JavaKind kind) { + HotSpotCallingConventionType hotspotType = (HotSpotCallingConventionType) type; switch (kind) { case Boolean: case Byte: @@ -219,7 +221,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - return type == Type.JavaCallee ? cpuCalleeParameterRegisters : cpuCallerParameterRegisters; + return hotspotType == HotSpotCallingConventionType.JavaCallee ? cpuCalleeParameterRegisters : cpuCallerParameterRegisters; case Double: case Float: return fpuFloatParameterRegisters; @@ -228,7 +230,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { } } - private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, Type type, TargetDescription target, boolean stackOnly) { + private CallingConvention callingConvention(Register[] generalParameterRegisters, JavaType returnType, JavaType[] parameterTypes, HotSpotCallingConventionType type, TargetDescription target) { AllocatableValue[] locations = new AllocatableValue[parameterTypes.length]; int currentGeneral = 0; @@ -246,13 +248,13 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - if (!stackOnly && currentGeneral < generalParameterRegisters.length) { + if (currentGeneral < generalParameterRegisters.length) { Register register = generalParameterRegisters[currentGeneral++]; locations[i] = register.asValue(target.getLIRKind(kind)); } break; case Double: - if (!stackOnly && currentFloating < fpuFloatParameterRegisters.length) { + if (currentFloating < fpuFloatParameterRegisters.length) { if (currentFloating % 2 != 0) { // Make register number even to be a double reg currentFloating++; @@ -263,7 +265,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { } break; case Float: - if (!stackOnly && currentFloating < fpuFloatParameterRegisters.length) { + if (currentFloating < fpuFloatParameterRegisters.length) { Register register = fpuFloatParameterRegisters[currentFloating++]; locations[i] = register.asValue(target.getLIRKind(kind)); } @@ -287,7 +289,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { AllocatableValue returnLocation = returnKind == Void ? ILLEGAL : getReturnRegister(returnKind, type).asValue(target.getLIRKind(returnKind.getStackKind())); int outArgSpillArea; - if (type == NativeCall && addNativeRegisterArgumentSlots) { + if (type == HotSpotCallingConventionType.NativeCall && addNativeRegisterArgumentSlots) { // Space for native callee which may spill our outgoing arguments outArgSpillArea = Math.min(locations.length, generalParameterRegisters.length) * target.wordSize; } else { @@ -302,10 +304,10 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { @Override public Register getReturnRegister(JavaKind kind) { - return getReturnRegister(kind, JavaCallee); + return getReturnRegister(kind, HotSpotCallingConventionType.JavaCallee); } - private static Register getReturnRegister(JavaKind kind, Type type) { + private static Register getReturnRegister(JavaKind kind, HotSpotCallingConventionType type) { switch (kind) { case Boolean: case Byte: @@ -314,7 +316,7 @@ public class SPARCHotSpotRegisterConfig implements RegisterConfig { case Int: case Long: case Object: - return type == JavaCallee ? i0 : o0; + return type == HotSpotCallingConventionType.JavaCallee ? i0 : o0; case Float: return f0; case Double: diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java index 4f4e7e4e9b9..ef3fd7cf1c5 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java @@ -29,6 +29,7 @@ import static jdk.vm.ci.inittimer.InitTimer.timer; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.code.InvalidInstalledCodeException; import jdk.vm.ci.code.TargetDescription; @@ -316,21 +317,6 @@ final class CompilerToVM { public native int getMetadata(TargetDescription target, HotSpotCompiledCode compiledCode, HotSpotMetaData metaData); - /** - * Notifies the VM of statistics for a completed compilation. - * - * @param id the identifier of the compilation - * @param method the method compiled - * @param osr specifies if the compilation was for on-stack-replacement - * @param processedBytecodes the number of bytecodes processed during the compilation, including - * the bytecodes of all inlined methods - * @param time the amount time spent compiling {@code method} - * @param timeUnitsPerSecond the granularity of the units for the {@code time} value - * @param installedCode the nmethod installed as a result of the compilation - */ - synchronized native void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethodImpl method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond, - InstalledCode installedCode); - /** * Resets all compilation statistics. */ @@ -604,4 +590,14 @@ final class CompilerToVM { * @throws IllegalArgumentException if an out of range position is given */ native int methodDataProfileDataSize(long metaspaceMethodData, int position); + + /** + * Return the amount of native stack required for the interpreter frames represented by + * {@code frame}. This is used when emitting the stack banging code to ensure that there is + * enough space for the frames during deoptimization. + * + * @param frame + * @return the number of bytes required for deoptimization of this frame state + */ + native int interpreterFrameSize(BytecodeFrame frame); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCallingConventionType.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCallingConventionType.java new file mode 100644 index 00000000000..357a4a23050 --- /dev/null +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCallingConventionType.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.hotspot; + +import jdk.vm.ci.code.CallingConvention; +import jdk.vm.ci.code.CallingConvention.Type; + +public enum HotSpotCallingConventionType implements CallingConvention.Type { + /** + * A request for the outgoing argument locations at a call site to Java code. + */ + JavaCall(true), + + /** + * A request for the incoming argument locations. + */ + JavaCallee(false), + + /** + * A request for the outgoing argument locations at a call site to external native code that + * complies with the platform ABI. + */ + NativeCall(true); + + /** + * Determines if this is a request for the outgoing argument locations at a call site. + */ + public final boolean out; + + public static final Type[] VALUES = values(); + + private HotSpotCallingConventionType(boolean out) { + this.out = out; + } +} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java index 6f9770a5c2d..a01a304d2a8 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,30 +22,19 @@ */ package jdk.vm.ci.hotspot; -import static jdk.vm.ci.hotspot.HotSpotCompressedNullConstant.COMPRESSED_NULL; - import java.lang.reflect.Field; import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationRequest; -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.Call; -import jdk.vm.ci.code.CompilationResult.ConstantReference; -import jdk.vm.ci.code.CompilationResult.DataPatch; -import jdk.vm.ci.code.CompilationResult.Mark; -import jdk.vm.ci.code.DataSection; -import jdk.vm.ci.code.DataSection.Data; -import jdk.vm.ci.code.DataSection.DataBuilder; +import jdk.vm.ci.code.CompiledCode; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.SerializableConstant; +import jdk.vm.ci.code.site.Call; +import jdk.vm.ci.code.site.Mark; +import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.SpeculationLog; -import jdk.vm.ci.meta.VMConstant; /** * HotSpot implementation of {@link CodeCacheProvider}. @@ -113,41 +102,25 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider { return runtime.getConfig().runtimeCallStackSize; } - private InstalledCode logOrDump(InstalledCode installedCode, CompilationResult compResult) { - ((HotSpotJVMCIRuntime) runtime).notifyInstall(this, installedCode, compResult); + private InstalledCode logOrDump(InstalledCode installedCode, CompiledCode compiledCode) { + ((HotSpotJVMCIRuntime) runtime).notifyInstall(this, installedCode, compiledCode); return installedCode; } - public InstalledCode installCode(CompilationRequest compRequest, CompilationResult compResult, InstalledCode installedCode, SpeculationLog log, boolean isDefault) { - HotSpotResolvedJavaMethod method = compRequest != null ? (HotSpotResolvedJavaMethod) compRequest.getMethod() : null; + public InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault) { InstalledCode resultInstalledCode; if (installedCode == null) { if (method == null) { // Must be a stub - resultInstalledCode = new HotSpotRuntimeStub(compResult.getName()); + resultInstalledCode = new HotSpotRuntimeStub(((HotSpotCompiledCode) compiledCode).getName()); } else { - resultInstalledCode = new HotSpotNmethod(method, compResult.getName(), isDefault); + resultInstalledCode = new HotSpotNmethod((HotSpotResolvedJavaMethod) method, ((HotSpotCompiledCode) compiledCode).getName(), isDefault); } } else { resultInstalledCode = installedCode; } - HotSpotCompiledCode compiledCode; - if (method != null) { - final int id; - final long jvmciEnv; - if (compRequest instanceof HotSpotCompilationRequest) { - HotSpotCompilationRequest hsCompRequest = (HotSpotCompilationRequest) compRequest; - id = hsCompRequest.getId(); - jvmciEnv = hsCompRequest.getJvmciEnv(); - } else { - id = method.allocateCompileId(compRequest.getEntryBCI()); - jvmciEnv = 0L; - } - compiledCode = new HotSpotCompiledNmethod(method, compResult, id, jvmciEnv); - } else { - compiledCode = new HotSpotCompiledCode(compResult); - } - int result = runtime.getCompilerToVM().installCode(target, compiledCode, resultInstalledCode, (HotSpotSpeculationLog) log); + + int result = runtime.getCompilerToVM().installCode(target, (HotSpotCompiledCode) compiledCode, resultInstalledCode, (HotSpotSpeculationLog) log); if (result != config.codeInstallResultOk) { String resultDesc = config.getCodeInstallResultDescription(result); if (compiledCode instanceof HotSpotCompiledNmethod) { @@ -163,87 +136,16 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider { } throw new BailoutException(result != config.codeInstallResultDependenciesFailed, msg); } else { - throw new BailoutException("Error installing %s: %s", compResult.getName(), resultDesc); + throw new BailoutException("Error installing %s: %s", ((HotSpotCompiledCode) compiledCode).getName(), resultDesc); } } - return logOrDump(resultInstalledCode, compResult); + return logOrDump(resultInstalledCode, compiledCode); } public void invalidateInstalledCode(InstalledCode installedCode) { runtime.getCompilerToVM().invalidateInstalledCode(installedCode); } - public boolean needsDataPatch(JavaConstant constant) { - return constant instanceof HotSpotMetaspaceConstant; - } - - private Data createSingleDataItem(Constant constant) { - int size; - DataBuilder builder; - if (constant instanceof VMConstant) { - VMConstant vmConstant = (VMConstant) constant; - boolean compressed; - if (constant instanceof HotSpotConstant) { - HotSpotConstant c = (HotSpotConstant) vmConstant; - compressed = c.isCompressed(); - } else { - throw new JVMCIError(String.valueOf(constant)); - } - - size = compressed ? 4 : target.wordSize; - if (size == 4) { - builder = (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference(vmConstant))); - buffer.putInt(0xDEADDEAD); - }; - } else { - assert size == 8; - builder = (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference(vmConstant))); - buffer.putLong(0xDEADDEADDEADDEADL); - }; - } - } else if (JavaConstant.isNull(constant)) { - boolean compressed = COMPRESSED_NULL.equals(constant); - size = compressed ? 4 : target.wordSize; - builder = DataBuilder.zero(size); - } else if (constant instanceof SerializableConstant) { - SerializableConstant s = (SerializableConstant) constant; - size = s.getSerializedSize(); - builder = DataBuilder.serializable(s); - } else { - throw new JVMCIError(String.valueOf(constant)); - } - - return new Data(size, size, builder); - } - - public Data createDataItem(Constant... constants) { - assert constants.length > 0; - if (constants.length == 1) { - return createSingleDataItem(constants[0]); - } else { - DataBuilder[] builders = new DataBuilder[constants.length]; - int size = 0; - int alignment = 1; - for (int i = 0; i < constants.length; i++) { - Data data = createSingleDataItem(constants[i]); - - assert size % data.getAlignment() == 0 : "invalid alignment in packed constants"; - alignment = DataSection.lcm(alignment, data.getAlignment()); - - builders[i] = data.getBuilder(); - size += data.getSize(); - } - DataBuilder ret = (buffer, patches) -> { - for (DataBuilder b : builders) { - b.emit(buffer, patches); - } - }; - return new Data(alignment, size, ret); - } - } - @Override public TargetDescription getTarget() { return target; @@ -268,20 +170,8 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider { return runtime.getCompilerToVM().shouldDebugNonSafepoints(); } - /** - * Notifies the VM of statistics for a completed compilation. - * - * @param id the identifier of the compilation - * @param method the method compiled - * @param osr specifies if the compilation was for on-stack-replacement - * @param processedBytecodes the number of bytecodes processed during the compilation, including - * the bytecodes of all inlined methods - * @param time the amount time spent compiling {@code method} - * @param timeUnitsPerSecond the granularity of the units for the {@code time} value - * @param installedCode the nmethod installed as a result of the compilation - */ - public void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethod method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond, InstalledCode installedCode) { - runtime.getCompilerToVM().notifyCompilationStatistics(id, (HotSpotResolvedJavaMethodImpl) method, osr, processedBytecodes, time, timeUnitsPerSecond, installedCode); + public int interpreterFrameSize(BytecodeFrame pos) { + return runtime.getCompilerToVM().interpreterFrameSize(pos); } /** diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java index 27236ad771d..5605cdb429d 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,62 +22,86 @@ */ package jdk.vm.ci.hotspot; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; -import java.util.stream.Stream.Builder; - import jdk.vm.ci.code.BytecodeFrame; -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.CodeAnnotation; -import jdk.vm.ci.code.CompilationResult.CodeComment; -import jdk.vm.ci.code.CompilationResult.DataPatch; -import jdk.vm.ci.code.CompilationResult.ExceptionHandler; -import jdk.vm.ci.code.CompilationResult.Infopoint; -import jdk.vm.ci.code.CompilationResult.JumpTable; -import jdk.vm.ci.code.CompilationResult.Mark; -import jdk.vm.ci.code.CompilationResult.Site; -import jdk.vm.ci.code.DataSection; -import jdk.vm.ci.code.InfopointReason; -import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.code.CompiledCode; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.Site; import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.ResolvedJavaMethod; /** - * A {@link CompilationResult} with additional HotSpot-specific information required for installing - * the code in HotSpot's code cache. + * A {@link CompiledCode} with additional HotSpot-specific information required for installing the + * code in HotSpot's code cache. */ -public class HotSpotCompiledCode { +public class HotSpotCompiledCode implements CompiledCode { - public final String name; - public final Site[] sites; - public final ExceptionHandler[] exceptionHandlers; - public final Comment[] comments; - public final Assumption[] assumptions; + /** + * The name of this compilation unit. + */ + protected final String name; - public final byte[] targetCode; - public final int targetCodeSize; + /** + * The buffer containing the emitted machine code. + */ + protected final byte[] targetCode; - public final byte[] dataSection; - public final int dataSectionAlignment; - public final DataPatch[] dataSectionPatches; - public final boolean isImmutablePIC; + /** + * The leading number of bytes in {@link #targetCode} containing the emitted machine code. + */ + protected final int targetCodeSize; - public final int totalFrameSize; - public final int customStackAreaOffset; + /** + * A list of code annotations describing special sites in {@link #targetCode}. + */ + protected final Site[] sites; + + /** + * A list of {@link Assumption} this code relies on. + */ + protected final Assumption[] assumptions; /** * The list of the methods whose bytecodes were used as input to the compilation. If * {@code null}, then the compilation did not record method dependencies. Otherwise, the first * element of this array is the root method of the compilation. */ - public final ResolvedJavaMethod[] methods; + protected final ResolvedJavaMethod[] methods; + + /** + * A list of comments that will be included in code dumps. + */ + protected final Comment[] comments; + + /** + * The data section containing serialized constants for the emitted machine code. + */ + protected final byte[] dataSection; + + /** + * The minimum alignment of the data section. + */ + protected final int dataSectionAlignment; + + /** + * A list of relocations in the {@link #dataSection}. + */ + protected final DataPatch[] dataSectionPatches; + + /** + * A flag determining whether this code is immutable and position independent. + */ + protected final boolean isImmutablePIC; + + /** + * The total size of the stack frame of this compiled method. + */ + protected final int totalFrameSize; + + /** + * Offset in bytes for the custom stack area (relative to sp). + */ + protected final int customStackAreaOffset; public static class Comment { @@ -90,59 +114,38 @@ public class HotSpotCompiledCode { } } - public HotSpotCompiledCode(CompilationResult compResult) { - name = compResult.getName(); - sites = getSortedSites(compResult); - if (compResult.getExceptionHandlers().isEmpty()) { - exceptionHandlers = null; - } else { - exceptionHandlers = compResult.getExceptionHandlers().toArray(new ExceptionHandler[compResult.getExceptionHandlers().size()]); - } - List annotations = compResult.getAnnotations(); - comments = new Comment[annotations.size()]; - if (!annotations.isEmpty()) { - for (int i = 0; i < comments.length; i++) { - CodeAnnotation annotation = annotations.get(i); - String text; - if (annotation instanceof CodeComment) { - CodeComment codeComment = (CodeComment) annotation; - text = codeComment.value; - } else if (annotation instanceof JumpTable) { - JumpTable jumpTable = (JumpTable) annotation; - text = "JumpTable [" + jumpTable.low + " .. " + jumpTable.high + "]"; - } else { - text = annotation.toString(); - } - comments[i] = new Comment(annotation.position, text); - } - } - assumptions = compResult.getAssumptions(); + public HotSpotCompiledCode(String name, byte[] targetCode, int targetCodeSize, Site[] sites, Assumption[] assumptions, ResolvedJavaMethod[] methods, Comment[] comments, byte[] dataSection, + int dataSectionAlignment, DataPatch[] dataSectionPatches, boolean isImmutablePIC, int totalFrameSize, int customStackAreaOffset) { + this.name = name; + this.targetCode = targetCode; + this.targetCodeSize = targetCodeSize; + this.sites = sites; + this.assumptions = assumptions; + this.methods = methods; + + this.comments = comments; + this.dataSection = dataSection; + this.dataSectionAlignment = dataSectionAlignment; + this.dataSectionPatches = dataSectionPatches; + this.isImmutablePIC = isImmutablePIC; + this.totalFrameSize = totalFrameSize; + this.customStackAreaOffset = customStackAreaOffset; + assert validateFrames(); + } - targetCode = compResult.getTargetCode(); - targetCodeSize = compResult.getTargetCodeSize(); + public String getName() { + return name; + } - DataSection data = compResult.getDataSection(); - dataSection = new byte[data.getSectionSize()]; - - ByteBuffer buffer = ByteBuffer.wrap(dataSection).order(ByteOrder.nativeOrder()); - Builder patchBuilder = Stream.builder(); - data.buildDataSection(buffer, patchBuilder); - - dataSectionAlignment = data.getSectionAlignment(); - dataSectionPatches = patchBuilder.build().toArray(len -> new DataPatch[len]); - - isImmutablePIC = compResult.isImmutablePIC(); - - totalFrameSize = compResult.getTotalFrameSize(); - customStackAreaOffset = compResult.getCustomStackAreaOffset(); - - methods = compResult.getMethods(); + @Override + public String toString() { + return name; } /** - * Ensure that all the frames passed into HotSpot are properly formatted with an empty or - * illegal slot following double word slots. + * Ensure that all the frames passed into the VM are properly formatted with an empty or illegal + * slot following double word slots. */ private boolean validateFrames() { for (Site site : sites) { @@ -156,117 +159,4 @@ public class HotSpotCompiledCode { } return true; } - - static class SiteComparator implements Comparator { - - /** - * Defines an order for sorting {@link Infopoint}s based on their - * {@linkplain Infopoint#reason reasons}. This is used to choose which infopoint to preserve - * when multiple infopoints collide on the same PC offset. A negative order value implies a - * non-optional infopoint (i.e., must be preserved). Non-optional infopoints must not - * collide. - */ - static final Map HOTSPOT_INFOPOINT_SORT_ORDER = new EnumMap<>(InfopointReason.class); - static { - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.SAFEPOINT, -4); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.CALL, -3); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.IMPLICIT_EXCEPTION, -2); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.METASPACE_ACCESS, 1); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.METHOD_START, 2); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.METHOD_END, 3); - HOTSPOT_INFOPOINT_SORT_ORDER.put(InfopointReason.BYTECODE_POSITION, 4); - } - - static int ord(Infopoint info) { - return HOTSPOT_INFOPOINT_SORT_ORDER.get(info.reason); - } - - static int checkCollision(Infopoint i1, Infopoint i2) { - int o1 = ord(i1); - int o2 = ord(i2); - if (o1 < 0 && o2 < 0) { - throw new JVMCIError("Non-optional infopoints cannot collide: %s and %s", i1, i2); - } - return o1 - o2; - } - - /** - * Records whether any two {@link Infopoint}s had the same {@link Infopoint#pcOffset}. - */ - boolean sawCollidingInfopoints; - - public int compare(Site s1, Site s2) { - if (s1.pcOffset == s2.pcOffset) { - // Marks must come first since patching a call site - // may need to know the mark denoting the call type - // (see uses of CodeInstaller::_next_call_type). - boolean s1IsMark = s1 instanceof Mark; - boolean s2IsMark = s2 instanceof Mark; - if (s1IsMark != s2IsMark) { - return s1IsMark ? -1 : 1; - } - - // Infopoints must group together so put them after - // other Site types. - boolean s1IsInfopoint = s1 instanceof Infopoint; - boolean s2IsInfopoint = s2 instanceof Infopoint; - if (s1IsInfopoint != s2IsInfopoint) { - return s1IsInfopoint ? 1 : -1; - } - - if (s1IsInfopoint) { - sawCollidingInfopoints = true; - return checkCollision((Infopoint) s1, (Infopoint) s2); - } - } - return s1.pcOffset - s2.pcOffset; - } - } - - /** - * HotSpot expects sites to be presented in ascending order of PC (see - * {@code DebugInformationRecorder::add_new_pc_offset}). In addition, it expects - * {@link Infopoint} PCs to be unique. - */ - private static Site[] getSortedSites(CompilationResult target) { - List[] lists = new List[]{target.getInfopoints(), target.getDataPatches(), target.getMarks()}; - int count = 0; - for (List list : lists) { - count += list.size(); - } - Site[] result = new Site[count]; - int pos = 0; - for (List list : lists) { - for (Object elem : list) { - result[pos++] = (Site) elem; - } - } - SiteComparator c = new SiteComparator(); - Arrays.sort(result, c); - if (c.sawCollidingInfopoints) { - Infopoint lastInfopoint = null; - List copy = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - if (result[i] instanceof Infopoint) { - Infopoint info = (Infopoint) result[i]; - if (lastInfopoint == null || lastInfopoint.pcOffset != info.pcOffset) { - lastInfopoint = info; - copy.add(info); - } else { - // Omit this colliding infopoint - assert lastInfopoint.reason.compareTo(info.reason) <= 0; - } - } else { - copy.add(result[i]); - } - } - result = copy.toArray(new Site[copy.size()]); - } - return result; - } - - @Override - public String toString() { - return name; - } } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java index e81f35b5e43..a2038a57341 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledNmethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,28 +22,31 @@ */ package jdk.vm.ci.hotspot; -import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Site; import jdk.vm.ci.inittimer.SuppressFBWarnings; +import jdk.vm.ci.meta.Assumptions.Assumption; +import jdk.vm.ci.meta.ResolvedJavaMethod; /** * {@link HotSpotCompiledCode} destined for installation as an nmethod. */ public final class HotSpotCompiledNmethod extends HotSpotCompiledCode { - public final HotSpotResolvedJavaMethod method; - public final int entryBCI; + protected final HotSpotResolvedJavaMethod method; + protected final int entryBCI; /** * Compilation identifier. */ - public final int id; + protected final int id; /** * Address of a native {@code JVMCIEnv} object or 0L if no such object exists. */ - public final long jvmciEnv; + protected final long jvmciEnv; - public final boolean hasUnsafeAccess; + protected final boolean hasUnsafeAccess; /** * May be set by VM if code installation fails. It will describe in more detail why installation @@ -51,13 +54,15 @@ public final class HotSpotCompiledNmethod extends HotSpotCompiledCode { */ @SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "set by the VM") private String installationFailureMessage; - public HotSpotCompiledNmethod(HotSpotResolvedJavaMethod method, CompilationResult compResult, int id, long jvmciEnv) { - super(compResult); + public HotSpotCompiledNmethod(String name, byte[] targetCode, int targetCodeSize, Site[] sites, Assumption[] assumptions, ResolvedJavaMethod[] methods, Comment[] comments, byte[] dataSection, + int dataSectionAlignment, DataPatch[] dataSectionPatches, boolean isImmutablePIC, int totalFrameSize, int customStackAreaOffset, HotSpotResolvedJavaMethod method, int entryBCI, + int id, long jvmciEnv, boolean hasUnsafeAccess) { + super(name, targetCode, targetCodeSize, sites, assumptions, methods, comments, dataSection, dataSectionAlignment, dataSectionPatches, isImmutablePIC, totalFrameSize, customStackAreaOffset); this.method = method; - this.entryBCI = compResult.getEntryBCI(); + this.entryBCI = entryBCI; this.id = id; this.jvmciEnv = jvmciEnv; - this.hasUnsafeAccess = compResult.hasUnsafeAccess(); + this.hasUnsafeAccess = hasUnsafeAccess; } @Override diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java index ffe53825947..3b720feb38c 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java @@ -27,6 +27,8 @@ import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale; import java.lang.reflect.Array; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; @@ -42,11 +44,6 @@ import jdk.vm.ci.meta.ResolvedJavaType; */ public class HotSpotConstantReflectionProvider implements ConstantReflectionProvider, HotSpotProxified { - /** - * Determines whether to treat {@code final} fields with default values as constant. - */ - private static final boolean TrustFinalDefaultFields = HotSpotJVMCIRuntime.getBooleanProperty("TrustFinalDefaultFields", true); - protected final HotSpotJVMCIRuntimeProvider runtime; protected final HotSpotMethodHandleAccessProvider methodHandleAccess; protected final HotSpotMemoryAccessProviderImpl memoryAccess; @@ -249,14 +246,14 @@ public class HotSpotConstantReflectionProvider implements ConstantReflectionProv * Determines if a value read from a {@code final} instance field is considered constant. The * implementation in {@link HotSpotConstantReflectionProvider} returns true if {@code value} is * not the {@link JavaConstant#isDefaultForKind default value} for its kind or if - * {@link #TrustFinalDefaultFields} is true. + * {@link Option#TrustFinalDefaultFields} is true. * * @param value a value read from a {@code final} instance field * @param receiverClass the {@link Object#getClass() class} of object from which the * {@code value} was read */ protected boolean isFinalInstanceFieldValueConstant(JavaConstant value, Class receiverClass) { - return !value.isDefaultForKind() || TrustFinalDefaultFields; + return !value.isDefaultForKind() || Option.TrustFinalDefaultFields.getBoolean(); } /** @@ -359,4 +356,18 @@ public class HotSpotConstantReflectionProvider implements ConstantReflectionProv } return dimensions; } + + @Override + public JavaConstant asJavaClass(ResolvedJavaType type) { + return HotSpotObjectConstantImpl.forObject(((HotSpotResolvedJavaType) type).mirror()); + } + + @Override + public Constant asObjectHub(ResolvedJavaType type) { + if (type instanceof HotSpotResolvedObjectType) { + return ((HotSpotResolvedObjectType) type).klass(); + } else { + throw JVMCIError.unimplemented(); + } + } } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java index 1e242413b63..b1b486721e8 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java @@ -23,17 +23,18 @@ package jdk.vm.ci.hotspot; import jdk.vm.ci.code.CompilationRequest; +import jdk.vm.ci.code.CompilationRequestResult; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.runtime.JVMCICompiler; import jdk.vm.ci.runtime.JVMCICompilerFactory; import jdk.vm.ci.runtime.JVMCIRuntime; -import jdk.vm.ci.service.Services; +import jdk.vm.ci.services.Services; final class HotSpotJVMCICompilerConfig { private static class DummyCompilerFactory implements JVMCICompilerFactory, JVMCICompiler { - public void compileMethod(CompilationRequest request) { + public CompilationRequestResult compileMethod(CompilationRequest request) { throw new JVMCIError("no JVMCI compiler selected"); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 4077c0f40f4..88711cc3cb8 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ import static jdk.vm.ci.inittimer.InitTimer.timer; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -37,7 +38,8 @@ import java.util.Objects; import java.util.TreeMap; import jdk.vm.ci.code.Architecture; -import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.CompilationRequestResult; +import jdk.vm.ci.code.CompiledCode; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.inittimer.InitTimer; @@ -48,7 +50,7 @@ import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.runtime.JVMCI; import jdk.vm.ci.runtime.JVMCIBackend; import jdk.vm.ci.runtime.JVMCICompiler; -import jdk.vm.ci.service.Services; +import jdk.vm.ci.services.Services; import jdk.internal.misc.VM; //JaCoCo Exclude @@ -85,19 +87,95 @@ public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, H } /** - * Gets a boolean value based on a system property {@linkplain VM#getSavedProperty(String) - * saved} at system initialization time. The property name is prefixed with "{@code jvmci.}". - * - * @param name the name of the system property to derive a boolean value from using - * {@link Boolean#parseBoolean(String)} - * @param def the value to return if there is no system property corresponding to {@code name} + * A list of all supported JVMCI options. */ - public static boolean getBooleanProperty(String name, boolean def) { - String value = VM.getSavedProperty("jvmci." + name); - if (value == null) { - return def; + public enum Option { + ImplicitStableValues(boolean.class, true, "Mark well-known stable fields as such."), + // Note: The following one is not used (see InitTimer.ENABLED). + InitTimer(boolean.class, false, "Specifies if initialization timing is enabled."), + PrintConfig(boolean.class, false, "Prints all HotSpotVMConfig fields."), + PrintFlags(boolean.class, false, "Prints all JVMCI flags and exits."), + ShowFlags(boolean.class, false, "Prints all JVMCI flags and continues."), + TraceMethodDataFilter(String.class, null, ""), + TrustFinalDefaultFields(boolean.class, true, "Determines whether to treat final fields with default values as constant."); + + /** + * The prefix for system properties that are JVMCI options. + */ + private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci."; + + /** + * Marker for uninitialized flags. + */ + private static final String UNINITIALIZED = "UNINITIALIZED"; + + private final Class type; + private Object value; + private final Object defaultValue; + private boolean isDefault; + private final String help; + + private Option(Class type, Object defaultValue, String help) { + assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name(); + this.type = type; + this.value = UNINITIALIZED; + this.defaultValue = defaultValue; + this.help = help; + } + + private Object getValue() { + if (value == UNINITIALIZED) { + String propertyValue = VM.getSavedProperty(JVMCI_OPTION_PROPERTY_PREFIX + name()); + if (propertyValue == null) { + this.value = defaultValue; + this.isDefault = true; + } else { + if (type == boolean.class) { + this.value = Boolean.parseBoolean(propertyValue); + } else if (type == String.class) { + this.value = propertyValue; + } else { + throw new JVMCIError("Unexpected option type " + type); + } + this.isDefault = false; + } + // Saved properties should not be interned - let's be sure + assert value != UNINITIALIZED; + } + return value; + } + + /** + * Returns the option's value as boolean. + * + * @return option's value + */ + public boolean getBoolean() { + return (boolean) getValue(); + } + + /** + * Returns the option's value as String. + * + * @return option's value + */ + public String getString() { + return (String) getValue(); + } + + /** + * Prints all option flags to {@code out}. + * + * @param out stream to print to + */ + public static void printFlags(PrintStream out) { + out.println("[List of JVMCI options]"); + for (Option option : values()) { + Object value = option.getValue(); + String assign = option.isDefault ? ":=" : " ="; + out.printf("%9s %-40s %s %-14s %s%n", option.type.getSimpleName(), option, assign, value, option.help); + } } - return Boolean.parseBoolean(value); } public static HotSpotJVMCIBackendFactory findFactory(String architecture) { @@ -164,7 +242,16 @@ public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, H } metaAccessContext = context; - if (Boolean.valueOf(System.getProperty("jvmci.printconfig"))) { + boolean printFlags = Option.PrintFlags.getBoolean(); + boolean showFlags = Option.ShowFlags.getBoolean(); + if (printFlags || showFlags) { + Option.printFlags(System.out); + if (printFlags) { + System.exit(0); + } + } + + if (Option.PrintConfig.getBoolean()) { printConfig(config, compilerToVm); } @@ -241,8 +328,10 @@ public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, H * Called from the VM. */ @SuppressWarnings({"unused"}) - private void compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) { - getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id)); + private CompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) { + CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, jvmciEnv, id)); + assert result != null : "compileMethod must always return something"; + return result; } /** @@ -262,11 +351,11 @@ public final class HotSpotJVMCIRuntime implements HotSpotJVMCIRuntimeProvider, H * * @param hotSpotCodeCacheProvider * @param installedCode - * @param compResult + * @param compiledCode */ - void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompilationResult compResult) { + void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { for (HotSpotVMEventListener vmEventListener : vmEventListeners) { - vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compResult); + vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode); } } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaFieldImpl.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaFieldImpl.java index c6f4dabc0fb..f516412fa6e 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaFieldImpl.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaFieldImpl.java @@ -29,6 +29,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.LocationIdentity; import jdk.vm.ci.meta.MetaAccessProvider; @@ -41,11 +42,6 @@ import jdk.vm.ci.meta.ResolvedJavaType; */ class HotSpotResolvedJavaFieldImpl implements HotSpotResolvedJavaField, HotSpotProxified { - /** - * Mark well-known stable fields as such. - */ - private static final boolean ImplicitStableValues = HotSpotJVMCIRuntime.getBooleanProperty("ImplicitStableValues", true); - private final HotSpotResolvedObjectTypeImpl holder; private final String name; private JavaType type; @@ -198,7 +194,7 @@ class HotSpotResolvedJavaFieldImpl implements HotSpotResolvedJavaField, HotSpotP return true; } assert getAnnotation(Stable.class) == null; - if (ImplicitStableValues && isImplicitStableField()) { + if (Option.ImplicitStableValues.getBoolean() && isImplicitStableField()) { return true; } return false; diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java index 0708b377366..65b58610165 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.Map; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.DefaultProfilingInfo; @@ -417,8 +418,6 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp return false; } - private static final String TraceMethodDataFilter = System.getProperty("jvmci.traceMethodDataFilter"); - @Override public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) { ProfilingInfo info; @@ -427,7 +426,8 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp long metaspaceMethodData = UNSAFE.getAddress(metaspaceMethod + config().methodDataOffset); if (metaspaceMethodData != 0) { methodData = new HotSpotMethodData(metaspaceMethodData, this); - if (TraceMethodDataFilter != null && this.format("%H.%n").contains(TraceMethodDataFilter)) { + String methodDataFilter = Option.TraceMethodDataFilter.getString(); + if (methodDataFilter != null && this.format("%H.%n").contains(methodDataFilter)) { System.out.println("Raw method data for " + this.format("%H.%n(%p)") + ":"); System.out.println(methodData.toString()); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java index 0dc87859b3e..27b95d9e55e 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java @@ -303,16 +303,6 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem return isLeaf() ? this : null; } - @Override - public JavaConstant getJavaClass() { - return HotSpotObjectConstantImpl.forObject(mirror()); - } - - @Override - public Constant getObjectHub() { - return klass(); - } - @Override public AssumptionResult hasFinalizableSubclass() { assert !isArray(); diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java index 7a8f3e8a633..b8039190175 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java @@ -109,16 +109,6 @@ public final class HotSpotResolvedPrimitiveType extends HotSpotResolvedJavaType return null; } - @Override - public JavaConstant getObjectHub() { - throw JVMCIError.unimplemented(); - } - - @Override - public JavaConstant getJavaClass() { - throw JVMCIError.unimplemented(); - } - @Override public AssumptionResult hasFinalizableSubclass() { return new AssumptionResult<>(false); diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java index ad1e6df6085..e3ec2ccd264 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -973,11 +973,12 @@ public class HotSpotVMConfig { @HotSpotVMFlag(name = "UseBlockZeroing", archs = {"sparc"}) @Stable public boolean useBlockZeroing; @HotSpotVMFlag(name = "BlockZeroingLowLimit", archs = {"sparc"}) @Stable public int blockZeroingLowLimit; - // offsets, ... @HotSpotVMFlag(name = "StackShadowPages") @Stable public int stackShadowPages; @HotSpotVMFlag(name = "UseStackBanging") @Stable public boolean useStackBanging; @HotSpotVMConstant(name = "STACK_BIAS") @Stable public int stackBias; + @HotSpotVMField(name = "CompilerToVM::Data::vm_page_size", type = "int", get = HotSpotVMField.Type.VALUE) @Stable public int vmPageSize; + // offsets, ... @HotSpotVMField(name = "oopDesc::_mark", type = "markOop", get = HotSpotVMField.Type.OFFSET) @Stable public int markOffset; @HotSpotVMField(name = "oopDesc::_metadata._klass", type = "Klass*", get = HotSpotVMField.Type.OFFSET) @Stable public int hubOffset; diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMEventListener.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMEventListener.java index c9d3db7ef09..c14a32dbffc 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMEventListener.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ */ package jdk.vm.ci.hotspot; -import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.CompiledCode; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.meta.JVMCIMetaAccessContext; import jdk.vm.ci.meta.ResolvedJavaType; @@ -40,9 +40,9 @@ public interface HotSpotVMEventListener { * * @param hotSpotCodeCacheProvider * @param installedCode - * @param compResult + * @param compiledCode */ - default void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompilationResult compResult) { + default void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) { } /** diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.inittimer/src/jdk/vm/ci/inittimer/InitTimer.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.inittimer/src/jdk/vm/ci/inittimer/InitTimer.java index 921b537bfb1..01fad3d82be 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.inittimer/src/jdk/vm/ci/inittimer/InitTimer.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.inittimer/src/jdk/vm/ci/inittimer/InitTimer.java @@ -65,9 +65,10 @@ public final class InitTimer implements AutoCloseable { } /** - * Specifies if initialization timing is enabled. + * Specifies if initialization timing is enabled. Note: This property cannot use + * {@code HotSpotJVMCIRuntime.Option} since that class is not visible from this package. */ - private static final boolean ENABLED = Boolean.getBoolean("jvmci.inittimer") || Boolean.getBoolean("jvmci.runtime.TimeInit"); + private static final boolean ENABLED = Boolean.getBoolean("jvmci.InitTimer"); public static final AtomicInteger nesting = ENABLED ? new AtomicInteger() : null; public static final String SPACES = " "; diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ConstantReflectionProvider.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ConstantReflectionProvider.java index 33cc2a27ebc..f6e2cbb6a7e 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ConstantReflectionProvider.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ConstantReflectionProvider.java @@ -160,4 +160,15 @@ public interface ConstantReflectionProvider { * Gets raw memory access. */ MemoryAccessProvider getMemoryAccessProvider(); + + /** + * Gets the runtime representation of the {@link Class} object of this type. + */ + JavaConstant asJavaClass(ResolvedJavaType type); + + /** + * Gets the runtime representation of the "hub" of this type--that is, the closest part of the + * type representation which is typically stored in the object header. + */ + Constant asObjectHub(ResolvedJavaType type); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java index d46a0fc3dce..3649c267f6b 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java @@ -33,17 +33,6 @@ import jdk.vm.ci.meta.Assumptions.AssumptionResult; * . */ public interface ResolvedJavaType extends JavaType, ModifiersProvider { - /** - * Gets the runtime representation of the Java class object of this type. - */ - JavaConstant getJavaClass(); - - /** - * Gets the runtime representation of the "hub" of this type--that is, the closest part of the - * type representation which is typically stored in the object header. - */ - Constant getObjectHub(); - /** * Checks whether this type has a finalizer method. * diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.runtime/src/jdk/vm/ci/runtime/JVMCICompiler.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.runtime/src/jdk/vm/ci/runtime/JVMCICompiler.java index 72a1b53b8f9..8aa5df1bf8c 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.runtime/src/jdk/vm/ci/runtime/JVMCICompiler.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.runtime/src/jdk/vm/ci/runtime/JVMCICompiler.java @@ -23,6 +23,7 @@ package jdk.vm.ci.runtime; import jdk.vm.ci.code.CompilationRequest; +import jdk.vm.ci.code.CompilationRequestResult; public interface JVMCICompiler { int INVOCATION_ENTRY_BCI = -1; @@ -31,5 +32,5 @@ public interface JVMCICompiler { * Services a compilation request. This object should compile the method to machine code and * install it in the code cache if the compilation is successful. */ - void compileMethod(CompilationRequest request); + CompilationRequestResult compileMethod(CompilationRequest request); } diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/META-INF/services/javax.annotation.processing.Processor b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 72506df9cb8..00000000000 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -jdk.vm.ci.service.processor.ServiceProviderProcessor diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/jdk/vm/ci/service/processor/ServiceProviderProcessor.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/jdk/vm/ci/service/processor/ServiceProviderProcessor.java deleted file mode 100644 index d8aea467257..00000000000 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service.processor/src/jdk/vm/ci/service/processor/ServiceProviderProcessor.java +++ /dev/null @@ -1,119 +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. - * - * 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.vm.ci.service.processor; - -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.MirroredTypeException; -import javax.lang.model.type.TypeMirror; -import javax.tools.Diagnostic.Kind; -import javax.tools.FileObject; -import javax.tools.StandardLocation; - -import jdk.vm.ci.service.ServiceProvider; - -@SupportedAnnotationTypes("jdk.vm.ci.service.ServiceProvider") -public class ServiceProviderProcessor extends AbstractProcessor { - - private final Set processed = new HashSet<>(); - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - private boolean verifyAnnotation(TypeMirror serviceInterface, TypeElement serviceProvider) { - if (!processingEnv.getTypeUtils().isSubtype(serviceProvider.asType(), serviceInterface)) { - String msg = String.format("Service provider class %s must implement service interface %s", serviceProvider.getSimpleName(), serviceInterface); - processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider); - return false; - } - - return true; - } - - private void processElement(TypeElement serviceProvider) { - if (processed.contains(serviceProvider)) { - return; - } - - processed.add(serviceProvider); - ServiceProvider annotation = serviceProvider.getAnnotation(ServiceProvider.class); - if (annotation != null) { - try { - annotation.value(); - } catch (MirroredTypeException ex) { - TypeMirror serviceInterface = ex.getTypeMirror(); - if (verifyAnnotation(serviceInterface, serviceProvider)) { - String interfaceName = ex.getTypeMirror().toString(); - createProviderFile(serviceProvider, interfaceName); - } - } - } - } - - private void createProviderFile(TypeElement serviceProvider, String interfaceName) { - if (serviceProvider.getNestingKind().isNested()) { - // This is a simplifying constraint that means we don't have to - // processed the qualified name to insert '$' characters at - // the relevant positions. - String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName()); - processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider); - return; - } - - String filename = "META-INF/jvmci.providers/" + serviceProvider.getQualifiedName(); - try { - FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, serviceProvider); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); - writer.println(interfaceName); - writer.close(); - } catch (IOException e) { - processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), serviceProvider); - } - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - - for (Element element : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) { - assert element.getKind().isClass(); - processElement((TypeElement) element); - } - - return true; - } -} diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/.checkstyle_checks.xml b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/.checkstyle_checks.xml deleted file mode 100644 index 003d8f7dcb5..00000000000 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/.checkstyle_checks.xml +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/src/jdk/vm/ci/service/Services.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/Services.java similarity index 99% rename from hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/src/jdk/vm/ci/service/Services.java rename to hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/Services.java index 9a6263a9b99..be4a1cc1afa 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.service/src/jdk/vm/ci/service/Services.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/Services.java @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.vm.ci.service; +package jdk.vm.ci.services; import java.util.Formatter; import java.util.Iterator; diff --git a/hotspot/src/os/aix/vm/attachListener_aix.cpp b/hotspot/src/os/aix/vm/attachListener_aix.cpp index 95f5c871028..cb56efc26f7 100644 --- a/hotspot/src/os/aix/vm/attachListener_aix.cpp +++ b/hotspot/src/os/aix/vm/attachListener_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/c1_globals_aix.hpp b/hotspot/src/os/aix/vm/c1_globals_aix.hpp index 45b57f71954..0d4affd2bab 100644 --- a/hotspot/src/os/aix/vm/c1_globals_aix.hpp +++ b/hotspot/src/os/aix/vm/c1_globals_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/c2_globals_aix.hpp b/hotspot/src/os/aix/vm/c2_globals_aix.hpp index ea63015a513..b42a7051fea 100644 --- a/hotspot/src/os/aix/vm/c2_globals_aix.hpp +++ b/hotspot/src/os/aix/vm/c2_globals_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/decoder_aix.hpp b/hotspot/src/os/aix/vm/decoder_aix.hpp index 6158179c81b..73840ab87e2 100644 --- a/hotspot/src/os/aix/vm/decoder_aix.hpp +++ b/hotspot/src/os/aix/vm/decoder_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 SAP AG. All rights reserved. + * Copyright (c) 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/globals_aix.hpp b/hotspot/src/os/aix/vm/globals_aix.hpp index 51612fae7a6..bfd244eb6fc 100644 --- a/hotspot/src/os/aix/vm/globals_aix.hpp +++ b/hotspot/src/os/aix/vm/globals_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/interfaceSupport_aix.hpp b/hotspot/src/os/aix/vm/interfaceSupport_aix.hpp index 8f32db7b810..a8bb618e287 100644 --- a/hotspot/src/os/aix/vm/interfaceSupport_aix.hpp +++ b/hotspot/src/os/aix/vm/interfaceSupport_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/jsig.c b/hotspot/src/os/aix/vm/jsig.c index d39fbe311eb..001255ab365 100644 --- a/hotspot/src/os/aix/vm/jsig.c +++ b/hotspot/src/os/aix/vm/jsig.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/jvm_aix.cpp b/hotspot/src/os/aix/vm/jvm_aix.cpp index 7a9fb8969b8..d6e9038c4c7 100644 --- a/hotspot/src/os/aix/vm/jvm_aix.cpp +++ b/hotspot/src/os/aix/vm/jvm_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/jvm_aix.h b/hotspot/src/os/aix/vm/jvm_aix.h index 75feef49dc1..e79d2e638fd 100644 --- a/hotspot/src/os/aix/vm/jvm_aix.h +++ b/hotspot/src/os/aix/vm/jvm_aix.h @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libo4.cpp b/hotspot/src/os/aix/vm/libo4.cpp index d47f70eb8b8..c0c8299aedd 100644 --- a/hotspot/src/os/aix/vm/libo4.cpp +++ b/hotspot/src/os/aix/vm/libo4.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libo4.hpp b/hotspot/src/os/aix/vm/libo4.hpp index 13bc1856adb..fb920071dfc 100644 --- a/hotspot/src/os/aix/vm/libo4.hpp +++ b/hotspot/src/os/aix/vm/libo4.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libodm_aix.cpp b/hotspot/src/os/aix/vm/libodm_aix.cpp index e39a97d9f77..af998b84086 100644 --- a/hotspot/src/os/aix/vm/libodm_aix.cpp +++ b/hotspot/src/os/aix/vm/libodm_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015, 2015 SAP AG. All rights reserved. + * Copyright (c) 2015, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libodm_aix.hpp b/hotspot/src/os/aix/vm/libodm_aix.hpp index 908bb2686a4..920b58f746f 100644 --- a/hotspot/src/os/aix/vm/libodm_aix.hpp +++ b/hotspot/src/os/aix/vm/libodm_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015, 2015 SAP AG. All rights reserved. + * Copyright (c) 2015, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libperfstat_aix.cpp b/hotspot/src/os/aix/vm/libperfstat_aix.cpp index afd577ffcbc..388b4052c9e 100644 --- a/hotspot/src/os/aix/vm/libperfstat_aix.cpp +++ b/hotspot/src/os/aix/vm/libperfstat_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/libperfstat_aix.hpp b/hotspot/src/os/aix/vm/libperfstat_aix.hpp index c0da6709061..164f486a2dc 100644 --- a/hotspot/src/os/aix/vm/libperfstat_aix.hpp +++ b/hotspot/src/os/aix/vm/libperfstat_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/loadlib_aix.cpp b/hotspot/src/os/aix/vm/loadlib_aix.cpp index 53e4891442b..8373cc2162e 100644 --- a/hotspot/src/os/aix/vm/loadlib_aix.cpp +++ b/hotspot/src/os/aix/vm/loadlib_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/loadlib_aix.hpp b/hotspot/src/os/aix/vm/loadlib_aix.hpp index b619141cd54..0f5b1489719 100644 --- a/hotspot/src/os/aix/vm/loadlib_aix.hpp +++ b/hotspot/src/os/aix/vm/loadlib_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/misc_aix.cpp b/hotspot/src/os/aix/vm/misc_aix.cpp index 52a26c0f592..f0ccdfbcbab 100644 --- a/hotspot/src/os/aix/vm/misc_aix.cpp +++ b/hotspot/src/os/aix/vm/misc_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 SAP AG. All rights reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/misc_aix.hpp b/hotspot/src/os/aix/vm/misc_aix.hpp index 38d9383cfa2..fb5fc4c526a 100644 --- a/hotspot/src/os/aix/vm/misc_aix.hpp +++ b/hotspot/src/os/aix/vm/misc_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/mutex_aix.inline.hpp b/hotspot/src/os/aix/vm/mutex_aix.inline.hpp index 82ae899e14d..ee59295dcba 100644 --- a/hotspot/src/os/aix/vm/mutex_aix.inline.hpp +++ b/hotspot/src/os/aix/vm/mutex_aix.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/osThread_aix.cpp b/hotspot/src/os/aix/vm/osThread_aix.cpp index c2b1f69b291..0b13454ffef 100644 --- a/hotspot/src/os/aix/vm/osThread_aix.cpp +++ b/hotspot/src/os/aix/vm/osThread_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/osThread_aix.hpp b/hotspot/src/os/aix/vm/osThread_aix.hpp index 73d2a336e25..405b2a4b103 100644 --- a/hotspot/src/os/aix/vm/osThread_aix.hpp +++ b/hotspot/src/os/aix/vm/osThread_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/os_aix.cpp b/hotspot/src/os/aix/vm/os_aix.cpp index a3f52f36ea9..f63a789b89c 100644 --- a/hotspot/src/os/aix/vm/os_aix.cpp +++ b/hotspot/src/os/aix/vm/os_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/os_aix.hpp b/hotspot/src/os/aix/vm/os_aix.hpp index b718d04c52e..cc4337070c8 100644 --- a/hotspot/src/os/aix/vm/os_aix.hpp +++ b/hotspot/src/os/aix/vm/os_aix.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013, 2015 SAP AG. All rights reserved. + * Copyright (c) 2013, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/os_aix.inline.hpp b/hotspot/src/os/aix/vm/os_aix.inline.hpp index a12c5d4f424..11c0c3fc5d3 100644 --- a/hotspot/src/os/aix/vm/os_aix.inline.hpp +++ b/hotspot/src/os/aix/vm/os_aix.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/perfMemory_aix.cpp b/hotspot/src/os/aix/vm/perfMemory_aix.cpp index 8b8431daf9e..2604a03dae8 100644 --- a/hotspot/src/os/aix/vm/perfMemory_aix.cpp +++ b/hotspot/src/os/aix/vm/perfMemory_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/porting_aix.cpp b/hotspot/src/os/aix/vm/porting_aix.cpp index 40078547dc1..c0d54241eaa 100644 --- a/hotspot/src/os/aix/vm/porting_aix.cpp +++ b/hotspot/src/os/aix/vm/porting_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/porting_aix.hpp b/hotspot/src/os/aix/vm/porting_aix.hpp index 196f93fa06f..8da037cc187 100644 --- a/hotspot/src/os/aix/vm/porting_aix.hpp +++ b/hotspot/src/os/aix/vm/porting_aix.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os/aix/vm/threadCritical_aix.cpp b/hotspot/src/os/aix/vm/threadCritical_aix.cpp index f2d651ff7e8..a5d893ba9f1 100644 --- a/hotspot/src/os/aix/vm/threadCritical_aix.cpp +++ b/hotspot/src/os/aix/vm/threadCritical_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp b/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp index d3afeeb399d..5cbb383f4b8 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014, SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp b/hotspot/src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp index d705c869a16..c730ed269f8 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp b/hotspot/src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp index 71eb6ac9536..0c791f15772 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014, SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp b/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp index b447b3fd7be..f109a111047 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp b/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp index 3bda955a8a8..569445ed284 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp b/hotspot/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp index 85c62b6640f..bcbb30d7b92 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp b/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp index 96a09b2787f..1ac366126db 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp b/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp index 2ca7c861cc9..ca649130a7d 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp b/hotspot/src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp index 7a29d8351b0..1dbcb8a7c93 100644 --- a/hotspot/src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp +++ b/hotspot/src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp b/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp index d9e1f5d84e0..c40ce1fbe4e 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014, SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp b/hotspot/src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp index 50174b66d1c..1d995a9ee4c 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp b/hotspot/src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp index 9e414dcd805..3def90fefe2 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014, SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp b/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp index f9401d13c9b..b7318c22c08 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2015 SAP AG. All rights reserved. + * Copyright (c) 2012, 2015 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp b/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp index 26bfc14273e..73937ddac4d 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp b/hotspot/src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp index c3b880f87ac..41d8445382c 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp b/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp index 92661222b07..895fb25d3fa 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp b/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp index 237070ec1fe..5caf4f33c16 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp b/hotspot/src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp index d947186ec86..a0adde047d2 100644 --- a/hotspot/src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp +++ b/hotspot/src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/share/tools/hsdis/hsdis.c b/hotspot/src/share/tools/hsdis/hsdis.c index 3d038f1ecd9..8706163981c 100644 --- a/hotspot/src/share/tools/hsdis/hsdis.c +++ b/hotspot/src/share/tools/hsdis/hsdis.c @@ -125,15 +125,15 @@ decode_instructions(void* start_pv, void* end_pv, event_callback_t event_callback_arg, void* event_stream_arg, printf_callback_t printf_callback_arg, void* printf_stream_arg, const char* options) { - decode_instructions_virtual((uintptr_t)start_pv, - (uintptr_t)end_pv, - (unsigned char*)start_pv, - (uintptr_t)end_pv - (uintptr_t)start_pv, - event_callback_arg, - event_stream_arg, - printf_callback_arg, - printf_stream_arg, - options, false); + return decode_instructions_virtual((uintptr_t)start_pv, + (uintptr_t)end_pv, + (unsigned char*)start_pv, + (uintptr_t)end_pv - (uintptr_t)start_pv, + event_callback_arg, + event_stream_arg, + printf_callback_arg, + printf_stream_arg, + options, false); } static void* decode(struct hsdis_app_data* app_data, const char* options) { @@ -212,6 +212,7 @@ static const char* format_insn_close(const char* close, case dis_condjsr: type = "condjsr"; break; case dis_dref: type = "dref"; break; case dis_dref2: type = "dref2"; break; + case dis_noninsn: type = "noninsn"; break; } strcpy(buf, close); diff --git a/hotspot/src/share/vm/adlc/formssel.cpp b/hotspot/src/share/vm/adlc/formssel.cpp index 4e5fe365e52..d408c904c6c 100644 --- a/hotspot/src/share/vm/adlc/formssel.cpp +++ b/hotspot/src/share/vm/adlc/formssel.cpp @@ -4010,7 +4010,6 @@ int MatchRule::is_expensive() const { if( _rChild ) { const char *opType = _rChild->_opType; if( strcmp(opType,"AtanD")==0 || - strcmp(opType,"CosD")==0 || strcmp(opType,"DivD")==0 || strcmp(opType,"DivF")==0 || strcmp(opType,"DivI")==0 || @@ -4018,7 +4017,6 @@ int MatchRule::is_expensive() const { strcmp(opType,"ModD")==0 || strcmp(opType,"ModF")==0 || strcmp(opType,"ModI")==0 || - strcmp(opType,"SinD")==0 || strcmp(opType,"SqrtD")==0 || strcmp(opType,"TanD")==0 || strcmp(opType,"ConvD2F")==0 || diff --git a/hotspot/src/share/vm/c1/c1_Compilation.cpp b/hotspot/src/share/vm/c1/c1_Compilation.cpp index c8db6252c85..1a102cabe8b 100644 --- a/hotspot/src/share/vm/c1/c1_Compilation.cpp +++ b/hotspot/src/share/vm/c1/c1_Compilation.cpp @@ -420,8 +420,7 @@ void Compilation::install_code(int frame_size) { implicit_exception_table(), compiler(), has_unsafe_access(), - SharedRuntime::is_wide_vector(max_vector_size()), - directive() + SharedRuntime::is_wide_vector(max_vector_size()) ); } diff --git a/hotspot/src/share/vm/c1/c1_Compilation.hpp b/hotspot/src/share/vm/c1/c1_Compilation.hpp index f9f6eca889c..3bc07da9f7d 100644 --- a/hotspot/src/share/vm/c1/c1_Compilation.hpp +++ b/hotspot/src/share/vm/c1/c1_Compilation.hpp @@ -28,6 +28,7 @@ #include "ci/ciEnv.hpp" #include "ci/ciMethodData.hpp" #include "code/exceptionHandlerTable.hpp" +#include "compiler/compilerDirectives.hpp" #include "memory/resourceArea.hpp" #include "runtime/deoptimization.hpp" diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp index 952ce683db7..b2766898d71 100644 --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp @@ -1748,10 +1748,6 @@ void GraphBuilder::invoke(Bytecodes::Code code) { const Bytecodes::Code bc_raw = stream()->cur_bc_raw(); assert(declared_signature != NULL, "cannot be null"); - if (!C1PatchInvokeDynamic && Bytecodes::has_optional_appendix(bc_raw) && !will_link) { - BAILOUT("unlinked call site (C1PatchInvokeDynamic is off)"); - } - // we have to make sure the argument size (incl. the receiver) // is correct for compilation (the call would fail later during // linkage anyway) - was bug (gri 7/28/99) @@ -1803,8 +1799,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) { // Push appendix argument (MethodType, CallSite, etc.), if one. bool patch_for_appendix = false; int patching_appendix_arg = 0; - if (C1PatchInvokeDynamic && - (Bytecodes::has_optional_appendix(bc_raw) && (!will_link || PatchALot))) { + if (Bytecodes::has_optional_appendix(bc_raw) && (!will_link || PatchALot)) { Value arg = append(new Constant(new ObjectConstant(compilation()->env()->unloaded_ciinstance()), copy_state_before())); apush(arg); patch_for_appendix = true; diff --git a/hotspot/src/share/vm/c1/c1_LIR.cpp b/hotspot/src/share/vm/c1/c1_LIR.cpp index 8397df6a048..6077c282472 100644 --- a/hotspot/src/share/vm/c1/c1_LIR.cpp +++ b/hotspot/src/share/vm/c1/c1_LIR.cpp @@ -729,8 +729,6 @@ void LIR_OpVisitState::visit(LIR_Op* op) { case lir_tan: - case lir_sin: - case lir_cos: case lir_log10: { assert(op->as_Op2() != NULL, "must be"); LIR_Op2* op2 = (LIR_Op2*)op; @@ -1740,8 +1738,6 @@ const char * LIR_Op::name() const { case lir_rem: s = "rem"; break; case lir_abs: s = "abs"; break; case lir_sqrt: s = "sqrt"; break; - case lir_sin: s = "sin"; break; - case lir_cos: s = "cos"; break; case lir_tan: s = "tan"; break; case lir_log10: s = "log10"; break; case lir_logic_and: s = "logic_and"; break; diff --git a/hotspot/src/share/vm/c1/c1_LIR.hpp b/hotspot/src/share/vm/c1/c1_LIR.hpp index ade1fbd31cc..c5e52dec265 100644 --- a/hotspot/src/share/vm/c1/c1_LIR.hpp +++ b/hotspot/src/share/vm/c1/c1_LIR.hpp @@ -958,8 +958,6 @@ enum LIR_Code { , lir_rem , lir_sqrt , lir_abs - , lir_sin - , lir_cos , lir_tan , lir_log10 , lir_logic_and @@ -2194,8 +2192,6 @@ class LIR_List: public CompilationResourceObj { void abs (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_abs , from, tmp, to)); } void sqrt(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_sqrt, from, tmp, to)); } void log10 (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log10, from, LIR_OprFact::illegalOpr, to, tmp)); } - void sin (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_sin , from, tmp1, to, tmp2)); } - void cos (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_cos , from, tmp1, to, tmp2)); } void tan (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_tan , from, tmp1, to, tmp2)); } void add (LIR_Opr left, LIR_Opr right, LIR_Opr res) { append(new LIR_Op2(lir_add, left, right, res)); } diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp index 4d016513ee7..7f33a2c4d22 100644 --- a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp @@ -736,9 +736,7 @@ void LIR_Assembler::emit_op2(LIR_Op2* op) { case lir_abs: case lir_sqrt: - case lir_sin: case lir_tan: - case lir_cos: case lir_log10: intrinsic_op(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op); break; diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp index f474b00d20b..cda969cb4b8 100644 --- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp +++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp @@ -6599,8 +6599,6 @@ void LinearScanStatistic::collect(LinearScan* allocator) { case lir_div_strictfp: case lir_rem: case lir_sqrt: - case lir_sin: - case lir_cos: case lir_abs: case lir_log10: case lir_logic_and: diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp index 5d308d1b476..92c721575d0 100644 --- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp @@ -321,6 +321,8 @@ const char* Runtime1::name_for_address(address entry) { FUNCTION_CASE(entry, StubRoutines::dexp()); FUNCTION_CASE(entry, StubRoutines::dlog()); FUNCTION_CASE(entry, StubRoutines::dpow()); + FUNCTION_CASE(entry, StubRoutines::dsin()); + FUNCTION_CASE(entry, StubRoutines::dcos()); #undef FUNCTION_CASE @@ -955,16 +957,19 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i constantPoolHandle pool(thread, caller_method->constants()); int index = bytecode.index(); LinkResolver::resolve_invoke(info, Handle(), pool, index, bc, CHECK); - appendix = info.resolved_appendix(); switch (bc) { case Bytecodes::_invokehandle: { int cache_index = ConstantPool::decode_cpcache_index(index, true); assert(cache_index >= 0 && cache_index < pool->cache()->length(), "unexpected cache index"); - pool->cache()->entry_at(cache_index)->set_method_handle(pool, info); + ConstantPoolCacheEntry* cpce = pool->cache()->entry_at(cache_index); + cpce->set_method_handle(pool, info); + appendix = cpce->appendix_if_resolved(pool); // just in case somebody already resolved the entry break; } case Bytecodes::_invokedynamic: { - pool->invokedynamic_cp_cache_entry_at(index)->set_dynamic_call(pool, info); + ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); + cpce->set_dynamic_call(pool, info); + appendix = cpce->appendix_if_resolved(pool); // just in case somebody already resolved the entry break; } default: fatal("unexpected bytecode for load_appendix_patching_id"); @@ -1031,6 +1036,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i address copy_buff = stub_location - *byte_skip - *byte_count; address being_initialized_entry = stub_location - *being_initialized_entry_offset; if (TracePatching) { + ttyLocker ttyl; tty->print_cr(" Patching %s at bci %d at address " INTPTR_FORMAT " (%s)", Bytecodes::name(code), bci, p2i(instr_pc), (stub_id == Runtime1::access_field_patching_id) ? "field" : "klass"); nmethod* caller_code = CodeCache::find_nmethod(caller_frame.pc()); diff --git a/hotspot/src/share/vm/c1/c1_globals.hpp b/hotspot/src/share/vm/c1/c1_globals.hpp index 3cdcfc2ed38..ad6548e4928 100644 --- a/hotspot/src/share/vm/c1/c1_globals.hpp +++ b/hotspot/src/share/vm/c1/c1_globals.hpp @@ -341,9 +341,6 @@ develop(bool, PrintCFGToFile, false, \ "print control flow graph to a separate file during compilation") \ \ - diagnostic(bool, C1PatchInvokeDynamic, true, \ - "Patch invokedynamic appendix not known at compile time") \ - \ // Read default values for c1 globals C1_FLAGS(DECLARE_DEVELOPER_FLAG, \ diff --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp index b3c4eddded6..2775c6776f5 100644 --- a/hotspot/src/share/vm/ci/ciEnv.cpp +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -38,7 +38,6 @@ #include "code/scopeDesc.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileLog.hpp" -#include "compiler/compilerDirectives.hpp" #include "compiler/disassembler.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "interpreter/linkResolver.hpp" @@ -959,7 +958,6 @@ void ciEnv::register_method(ciMethod* target, AbstractCompiler* compiler, bool has_unsafe_access, bool has_wide_vectors, - DirectiveSet* directives, RTMState rtm_state) { VM_ENTRY_MARK; nmethod* nm = NULL; @@ -1041,14 +1039,6 @@ void ciEnv::register_method(ciMethod* target, code_buffer->free_blob(); if (nm != NULL) { - bool printnmethods = directives->PrintAssemblyOption || directives->PrintNMethodsOption; - if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) { - nm->print_nmethod(printnmethods); - } - if (directives->PrintAssemblyOption) { - Disassembler::decode(nm); - } - nm->set_has_unsafe_access(has_unsafe_access); nm->set_has_wide_vectors(has_wide_vectors); #if INCLUDE_RTM_OPT diff --git a/hotspot/src/share/vm/ci/ciEnv.hpp b/hotspot/src/share/vm/ci/ciEnv.hpp index b1a95bd38f6..cd7138d33b2 100644 --- a/hotspot/src/share/vm/ci/ciEnv.hpp +++ b/hotspot/src/share/vm/ci/ciEnv.hpp @@ -32,11 +32,9 @@ #include "code/dependencies.hpp" #include "code/exceptionHandlerTable.hpp" #include "compiler/oopMap.hpp" -#include "compiler/compilerDirectives.hpp" #include "runtime/thread.hpp" class CompileTask; -class DirectiveSet; // ciEnv // @@ -372,7 +370,6 @@ public: AbstractCompiler* compiler, bool has_unsafe_access, bool has_wide_vectors, - DirectiveSet* directives, RTMState rtm_state = NoRTM); diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp index b0d3234af6e..d55d272842d 100644 --- a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp @@ -174,7 +174,6 @@ public: return 2; } } - bool has_default_methods() { assert(is_loaded(), "must be loaded"); return _has_default_methods; @@ -261,6 +260,11 @@ public: return NULL; } + bool can_be_instantiated() { + assert(is_loaded(), "must be loaded"); + return !is_interface() && !is_abstract(); + } + // Dump the current state of this klass for compilation replay. virtual void dump_replay_data(outputStream* out); }; diff --git a/hotspot/src/share/vm/ci/ciTypeFlow.cpp b/hotspot/src/share/vm/ci/ciTypeFlow.cpp index a1675d17a14..2edfa7d4abe 100644 --- a/hotspot/src/share/vm/ci/ciTypeFlow.cpp +++ b/hotspot/src/share/vm/ci/ciTypeFlow.cpp @@ -2930,7 +2930,7 @@ bool ciTypeFlow::is_dominated_by(int bci, int dom_bci) { } // Dominated[i] is true if block i is dominated by dom_block - int num_blocks = _methodBlocks->num_blocks(); + int num_blocks = block_count(); bool* dominated = NEW_RESOURCE_ARRAY(bool, num_blocks); for (int i = 0; i < num_blocks; ++i) { dominated[i] = true; diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index 60834c3855d..2df6a47088c 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -1784,6 +1784,20 @@ void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st) } } +/** + * Print the throwable stack trace by calling the Java method java.lang.Throwable.printStackTrace(). + */ +void java_lang_Throwable::java_printStackTrace(Handle throwable, TRAPS) { + assert(throwable->is_a(SystemDictionary::Throwable_klass()), "Throwable instance expected"); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, + throwable, + KlassHandle(THREAD, SystemDictionary::Throwable_klass()), + vmSymbols::printStackTrace_name(), + vmSymbols::void_method_signature(), + THREAD); +} + void java_lang_Throwable::fill_in_stack_trace(Handle throwable, const methodHandle& method, TRAPS) { if (!StackTraceInThrowable) return; ResourceMark rm(THREAD); diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index f16428234c9..9f7c2d1233a 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -509,6 +509,7 @@ class java_lang_Throwable: AllStatic { // Printing static void print(Handle throwable, outputStream* st); static void print_stack_trace(Handle throwable, outputStream* st); + static void java_printStackTrace(Handle throwable, TRAPS); // Debugging friend class JavaClasses; }; diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index 2e7ab518471..2a61613e910 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -2384,6 +2384,7 @@ static methodHandle unpack_method_and_appendix(Handle mname, oop appendix = appendix_box->obj_at(0); if (TraceMethodHandles) { #ifndef PRODUCT + ttyLocker ttyl; tty->print("Linked method=" INTPTR_FORMAT ": ", p2i(m)); m->print(); if (appendix != NULL) { tty->print("appendix = "); appendix->print(); } diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp index a0a50cec45d..d90326e9aa5 100644 --- a/hotspot/src/share/vm/code/codeBlob.cpp +++ b/hotspot/src/share/vm/code/codeBlob.cpp @@ -181,6 +181,11 @@ const ImmutableOopMap* CodeBlob::oop_map_for_return_address(address return_addre return oop_maps()->find_map_at_offset((intptr_t) return_address - (intptr_t) code_begin()); } +void CodeBlob::print_code() { + HandleMark hm; + ResourceMark m; + Disassembler::decode(this, tty); +} //---------------------------------------------------------------------------------------------------- // Implementation of BufferBlob diff --git a/hotspot/src/share/vm/code/codeBlob.hpp b/hotspot/src/share/vm/code/codeBlob.hpp index a4a4cab7c8b..4a45f6b96f4 100644 --- a/hotspot/src/share/vm/code/codeBlob.hpp +++ b/hotspot/src/share/vm/code/codeBlob.hpp @@ -196,6 +196,7 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC { void print() const { print_on(tty); } virtual void print_on(outputStream* st) const; virtual void print_value_on(outputStream* st) const; + void print_code(); // Deal with Disassembler, VTune, Forte, JvmtiExport, MemoryService. static void trace_new_stub(CodeBlob* blob, const char* name1, const char* name2 = ""); diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp index 544a55663f1..eeaa485602f 100644 --- a/hotspot/src/share/vm/code/nmethod.cpp +++ b/hotspot/src/share/vm/code/nmethod.cpp @@ -2642,6 +2642,7 @@ address nmethod::continuation_for_implicit_exception(address pc) { ResourceMark rm(thread); CodeBlob* cb = CodeCache::find_blob(pc); assert(cb != NULL && cb == this, ""); + ttyLocker ttyl; tty->print_cr("implicit exception happened at " INTPTR_FORMAT, p2i(pc)); print(); method()->print_codes(); @@ -2963,13 +2964,6 @@ void nmethod::print() const { nul_chk_table_size()); } -void nmethod::print_code() { - HandleMark hm; - ResourceMark m; - Disassembler::decode(this); -} - - #ifndef PRODUCT void nmethod::print_scopes() { diff --git a/hotspot/src/share/vm/code/nmethod.hpp b/hotspot/src/share/vm/code/nmethod.hpp index f38ed2edb36..b736fab13ed 100644 --- a/hotspot/src/share/vm/code/nmethod.hpp +++ b/hotspot/src/share/vm/code/nmethod.hpp @@ -704,7 +704,6 @@ public: // printing support void print() const; - void print_code(); void print_relocations() PRODUCT_RETURN; void print_pcs() PRODUCT_RETURN; void print_scopes() PRODUCT_RETURN; diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index 5e7d182fbad..497c0568754 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -227,6 +227,11 @@ bool compileBroker_init() { CompileTaskWrapper::CompileTaskWrapper(CompileTask* task) { CompilerThread* thread = CompilerThread::current(); thread->set_task(task); +#if INCLUDE_JVMCI + if (task->is_blocking() && CompileBroker::compiler(task->comp_level())->is_jvmci()) { + task->set_jvmci_compiler_thread(thread); + } +#endif CompileLog* log = thread->log(); if (log != NULL) task->log_task_start(log); } @@ -245,10 +250,12 @@ CompileTaskWrapper::~CompileTaskWrapper() { MutexLocker notifier(task->lock(), thread); task->mark_complete(); #if INCLUDE_JVMCI - if (CompileBroker::compiler(task->comp_level())->is_jvmci() && - !task->has_waiter()) { - // The waiting thread timed out and thus did not free the task. - free_task = true; + if (CompileBroker::compiler(task->comp_level())->is_jvmci()) { + if (!task->has_waiter()) { + // The waiting thread timed out and thus did not free the task. + free_task = true; + } + task->set_jvmci_compiler_thread(NULL); } #endif if (!free_task) { @@ -1332,11 +1339,56 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue, return new_task; } -// 1 second should be long enough to complete most JVMCI compilations -// and not too long to stall a blocking JVMCI compilation that -// is trying to acquire a lock held by the app thread that submitted the -// compilation. -static const long BLOCKING_JVMCI_COMPILATION_TIMEOUT = 1000; +#if INCLUDE_JVMCI +// The number of milliseconds to wait before checking if the +// JVMCI compiler thread is blocked. +static const long BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE = 500; + +// The number of successive times the above check is allowed to +// see a blocked JVMCI compiler thread before unblocking the +// thread waiting for the compilation to finish. +static const int BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS = 5; + +/** + * Waits for a JVMCI compiler to complete a given task. This thread + * waits until either the task completes or it sees the JVMCI compiler + * thread is blocked for N consecutive milliseconds where N is + * BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE * + * BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS. + * + * @return true if this thread needs to free/recycle the task + */ +bool CompileBroker::wait_for_jvmci_completion(CompileTask* task, JavaThread* thread) { + MutexLocker waiter(task->lock(), thread); + int consecutively_blocked = 0; + while (task->lock()->wait(!Mutex::_no_safepoint_check_flag, BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE)) { + CompilerThread* jvmci_compiler_thread = task->jvmci_compiler_thread(); + if (jvmci_compiler_thread != NULL) { + JavaThreadState state; + { + // A JVMCI compiler thread should not disappear at this point + // but let's be extra safe. + MutexLocker mu(Threads_lock, thread); + state = jvmci_compiler_thread->thread_state(); + } + if (state == _thread_blocked) { + if (++consecutively_blocked == BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS) { + if (PrintCompilation) { + task->print(tty, "wait for blocking compilation timed out"); + } + break; + } + } else { + consecutively_blocked = 0; + } + } else { + // Still waiting on JVMCI compiler queue + } + } + task->clear_waiter(); + return task->is_complete(); +} +#endif /** * Wait for the compilation task to complete. @@ -1356,16 +1408,7 @@ void CompileBroker::wait_for_completion(CompileTask* task) { bool free_task; #if INCLUDE_JVMCI if (compiler(task->comp_level())->is_jvmci()) { - MutexLocker waiter(task->lock(), thread); - // No need to check if compilation has completed - just - // rely on the time out. The JVMCI compiler thread will - // recycle the CompileTask. - task->lock()->wait(!Mutex::_no_safepoint_check_flag, BLOCKING_JVMCI_COMPILATION_TIMEOUT); - // If the compilation completes while has_waiter is true then - // this thread is responsible for freeing the task. Otherwise - // the compiler thread will free the task. - task->clear_waiter(); - free_task = task->is_complete(); + free_task = wait_for_jvmci_completion(task, thread); } else #endif { @@ -1755,6 +1798,8 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { push_jni_handle_block(); Method* target_handle = task->method(); int compilable = ciEnv::MethodCompilable; + const char* failure_reason = NULL; + const char* retry_message = NULL; AbstractCompiler *comp = compiler(task_level); int system_dictionary_modification_counter; @@ -1774,10 +1819,16 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { jvmci->compile_method(method, osr_bci, &env); post_compile(thread, task, event, task->code() != NULL, NULL); + + failure_reason = env.failure_reason(); + if (!env.retryable()) { + retry_message = "not retryable"; + compilable = ciEnv::MethodCompilable_not_at_tier; + } + } else #endif // INCLUDE_JVMCI { - NoHandleMark nhm; ThreadToNativeFromVM ttn(thread); @@ -1825,31 +1876,45 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { compilable = ci_env.compilable(); if (ci_env.failing()) { - task->set_failure_reason(ci_env.failure_reason()); - ci_env.report_failure(ci_env.failure_reason()); - const char* retry_message = ci_env.retry_message(); - if (_compilation_log != NULL) { - _compilation_log->log_failure(thread, task, ci_env.failure_reason(), retry_message); - } - if (PrintCompilation) { - FormatBufferResource msg = retry_message != NULL ? - FormatBufferResource("COMPILE SKIPPED: %s (%s)", ci_env.failure_reason(), retry_message) : - FormatBufferResource("COMPILE SKIPPED: %s", ci_env.failure_reason()); - task->print(tty, msg); - } + failure_reason = ci_env.failure_reason(); + retry_message = ci_env.retry_message(); + ci_env.report_failure(failure_reason); } post_compile(thread, task, event, !ci_env.failing(), &ci_env); } - DirectivesStack::release(directive); + // Remove the JNI handle block after the ciEnv destructor has run in + // the previous block. pop_jni_handle_block(); + if (failure_reason != NULL) { + task->set_failure_reason(failure_reason); + if (_compilation_log != NULL) { + _compilation_log->log_failure(thread, task, failure_reason, retry_message); + } + if (PrintCompilation) { + FormatBufferResource msg = retry_message != NULL ? + FormatBufferResource("COMPILE SKIPPED: %s (%s)", failure_reason, retry_message) : + FormatBufferResource("COMPILE SKIPPED: %s", failure_reason); + task->print(tty, msg); + } + } + methodHandle method(thread, task->method()); DTRACE_METHOD_COMPILE_END_PROBE(method, compiler_name(task_level), task->is_success()); collect_statistics(thread, time, task); + bool printnmethods = directive->PrintAssemblyOption || directive->PrintNMethodsOption; + if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) { + nmethod* nm = task->code(); + if (nm != NULL) { + nm->print_nmethod(printnmethods); + } + } + DirectivesStack::release(directive); + if (PrintCompilation && PrintCompilation2) { tty->print("%7d ", (int) tty->time_stamp().milliseconds()); // print timestamp tty->print("%4d ", compile_id); // print compilation number diff --git a/hotspot/src/share/vm/compiler/compileBroker.hpp b/hotspot/src/share/vm/compiler/compileBroker.hpp index 448f1f8897d..d74646b8855 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.hpp +++ b/hotspot/src/share/vm/compiler/compileBroker.hpp @@ -233,6 +233,9 @@ class CompileBroker: AllStatic { const char* comment, bool blocking); static void wait_for_completion(CompileTask* task); +#if INCLUDE_JVMCI + static bool wait_for_jvmci_completion(CompileTask* task, JavaThread* thread); +#endif static void invoke_compiler_on_method(CompileTask* task); static void post_compile(CompilerThread* thread, CompileTask* task, EventCompilation& event, bool success, ciEnv* ci_env); diff --git a/hotspot/src/share/vm/compiler/compileTask.cpp b/hotspot/src/share/vm/compiler/compileTask.cpp index a0efba91365..a59aa55c5c4 100644 --- a/hotspot/src/share/vm/compiler/compileTask.cpp +++ b/hotspot/src/share/vm/compiler/compileTask.cpp @@ -91,6 +91,7 @@ void CompileTask::initialize(int compile_id, _osr_bci = osr_bci; _is_blocking = is_blocking; JVMCI_ONLY(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();) + JVMCI_ONLY(_jvmci_compiler_thread = NULL;) _comp_level = comp_level; _num_inlined_bytecodes = 0; diff --git a/hotspot/src/share/vm/compiler/compileTask.hpp b/hotspot/src/share/vm/compiler/compileTask.hpp index 17f77b34dee..92f74dc569b 100644 --- a/hotspot/src/share/vm/compiler/compileTask.hpp +++ b/hotspot/src/share/vm/compiler/compileTask.hpp @@ -56,6 +56,8 @@ class CompileTask : public CHeapObj { bool _is_blocking; #if INCLUDE_JVMCI bool _has_waiter; + // Compiler thread for a blocking JVMCI compilation + CompilerThread* _jvmci_compiler_thread; #endif int _comp_level; int _num_inlined_bytecodes; @@ -92,6 +94,12 @@ class CompileTask : public CHeapObj { #if INCLUDE_JVMCI bool has_waiter() const { return _has_waiter; } void clear_waiter() { _has_waiter = false; } + CompilerThread* jvmci_compiler_thread() const { return _jvmci_compiler_thread; } + void set_jvmci_compiler_thread(CompilerThread* t) { + assert(is_blocking(), "must be"); + assert((t == NULL) != (_jvmci_compiler_thread == NULL), "must be"); + _jvmci_compiler_thread = t; + } #endif nmethodLocker* code_handle() const { return _code_handle; } diff --git a/hotspot/src/share/vm/compiler/compilerDirectives.cpp b/hotspot/src/share/vm/compiler/compilerDirectives.cpp index ba35617fc27..28b09f6cb61 100644 --- a/hotspot/src/share/vm/compiler/compilerDirectives.cpp +++ b/hotspot/src/share/vm/compiler/compilerDirectives.cpp @@ -164,20 +164,15 @@ int CompilerDirectives::refcount() { DirectiveSet* CompilerDirectives::get_for(AbstractCompiler *comp) { assert(DirectivesStack_lock->owned_by_self(), ""); - inc_refcount(); // The compiling thread is responsible to decrement this when finished. if (comp == NULL) { // Xint return _c1_store; - } else if (comp->is_c2()) { + } else if (comp->is_c2()) { return _c2_store; - } else if (comp->is_c1()) { + } else { + // use c1_store as default + assert(comp->is_c1() || comp->is_jvmci() || comp->is_shark(), ""); return _c1_store; - } else if (comp->is_shark()) { - return NULL; - } else if (comp->is_jvmci()) { - return NULL; } - ShouldNotReachHere(); - return NULL; } // In the list of disabled intrinsics, the ID of the disabled intrinsics can separated: @@ -459,6 +454,7 @@ DirectiveSet* DirectivesStack::getDefaultDirective(AbstractCompiler* comp) { MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); assert(_bottom != NULL, "Must never be empty"); + _bottom->inc_refcount(); return _bottom->get_for(comp); } @@ -521,12 +517,13 @@ void DirectivesStack::print(outputStream* st) { } void DirectivesStack::release(DirectiveSet* set) { + assert(set != NULL, "Never NULL"); MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); if (set->is_exclusive_copy()) { // Old CompilecCmmands forced us to create an exclusive copy delete set; } else { - assert(set->directive() != NULL, ""); + assert(set->directive() != NULL, "Never NULL"); release(set->directive()); } } @@ -553,26 +550,18 @@ DirectiveSet* DirectivesStack::getMatchingDirective(methodHandle method, Abstrac while (dir != NULL) { if (dir->is_default_directive() || dir->match(method)) { match = dir->get_for(comp); - if (match == NULL) { - // temporary workaround for compilers without directives. - if (dir->is_default_directive()) { - // default dir is always enabled - // match c1 store - it contains all common flags even if C1 is unavailable - match = dir->_c1_store; - break; - } - } else { - if (match->EnableOption) { - // The directiveSet for this compile is also enabled -> success - break; - } + assert(match != NULL, "Consistency"); + if (match->EnableOption) { + // The directiveSet for this compile is also enabled -> success + dir->inc_refcount(); + break; } } dir = dir->next(); } } - guarantee(match != NULL, "There should always be a default directive that matches"); + // Check for legacy compile commands update, without DirectivesStack_lock return match->compilecommand_compatibility_init(method); } diff --git a/hotspot/src/share/vm/compiler/disassembler.cpp b/hotspot/src/share/vm/compiler/disassembler.cpp index 4b15e7c6301..58859784479 100644 --- a/hotspot/src/share/vm/compiler/disassembler.cpp +++ b/hotspot/src/share/vm/compiler/disassembler.cpp @@ -513,6 +513,7 @@ address decode_env::decode_instructions(address start, address end) { void Disassembler::decode(CodeBlob* cb, outputStream* st) { + ttyLocker ttyl; if (!load_library()) return; if (cb->is_nmethod()) { decode((nmethod*)cb, st); @@ -526,12 +527,14 @@ void Disassembler::decode(CodeBlob* cb, outputStream* st) { } void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c) { + ttyLocker ttyl; if (!load_library()) return; decode_env env(CodeCache::find_blob_unsafe(start), st, c); env.decode_instructions(start, end); } void Disassembler::decode(nmethod* nm, outputStream* st) { + ttyLocker ttyl; if (!load_library()) return; decode_env env(nm, st); env.output()->print_cr("----------------------------------------------------------------------"); diff --git a/hotspot/src/share/vm/gc/shared/collectedHeap.cpp b/hotspot/src/share/vm/gc/shared/collectedHeap.cpp index b4055f67e57..8feb5d819b7 100644 --- a/hotspot/src/share/vm/gc/shared/collectedHeap.cpp +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -571,10 +571,10 @@ void CollectedHeap::resize_all_tlabs() { } } -void CollectedHeap::full_gc_dump(GCTimer* timer, const char* when) { - if (HeapDumpBeforeFullGC || HeapDumpAfterFullGC) { +void CollectedHeap::full_gc_dump(GCTimer* timer, bool before) { + if ((HeapDumpBeforeFullGC && before) || (HeapDumpAfterFullGC && !before)) { GCIdMarkAndRestore gc_id_mark; - FormatBuffer<> title("Heap Dump (%s full gc)", when); + FormatBuffer<> title("Heap Dump (%s full gc)", before ? "before" : "after"); GCTraceTime(Info, gc) tm(title.buffer(), timer); HeapDumper::dump_heap(); } @@ -582,7 +582,8 @@ void CollectedHeap::full_gc_dump(GCTimer* timer, const char* when) { if (log.is_trace()) { ResourceMark rm; GCIdMarkAndRestore gc_id_mark; - FormatBuffer<> title("Class Histogram (%s full gc)", when); + FormatBuffer<> title("Class Histogram (%s full gc)", + before ? "before" : "after"); GCTraceTime(Trace, gc, classhisto) tm(title.buffer(), timer); VM_GC_HeapInspection inspector(log.trace_stream(), false /* ! full gc */); inspector.doit(); @@ -590,11 +591,11 @@ void CollectedHeap::full_gc_dump(GCTimer* timer, const char* when) { } void CollectedHeap::pre_full_gc_dump(GCTimer* timer) { - full_gc_dump(timer, "before"); + full_gc_dump(timer, true); } void CollectedHeap::post_full_gc_dump(GCTimer* timer) { - full_gc_dump(timer, "after"); + full_gc_dump(timer, false); } void CollectedHeap::initialize_reserved_region(HeapWord *start, HeapWord *end) { diff --git a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp index 7b959e994b8..613397b02d6 100644 --- a/hotspot/src/share/vm/gc/shared/collectedHeap.hpp +++ b/hotspot/src/share/vm/gc/shared/collectedHeap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -525,7 +525,7 @@ class CollectedHeap : public CHeapObj { // Generate any dumps preceding or following a full gc private: - void full_gc_dump(GCTimer* timer, const char* when); + void full_gc_dump(GCTimer* timer, bool before); public: void pre_full_gc_dump(GCTimer* timer); void post_full_gc_dump(GCTimer* timer); diff --git a/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.cpp b/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.cpp index 43389fb4613..d2b688ffc5b 100644 --- a/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.cpp +++ b/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.cpp @@ -105,7 +105,7 @@ void ThreadLocalAllocBuffer::accumulate_statistics() { // an illusion of a contiguous Eden and optionally retires the tlab. // Waste accounting should be done in caller as appropriate; see, // for example, clear_before_allocation(). -void ThreadLocalAllocBuffer::make_parsable(bool retire) { +void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) { if (end() != NULL) { invariants(); @@ -113,7 +113,7 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire) { myThread()->incr_allocated_bytes(used_bytes()); } - CollectedHeap::fill_with_object(top(), hard_end(), retire); + CollectedHeap::fill_with_object(top(), hard_end(), retire && zap); if (retire || ZeroTLAB) { // "Reset" the TLAB set_start(NULL); diff --git a/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.hpp b/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.hpp index eca56006510..2af19576191 100644 --- a/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.hpp +++ b/hotspot/src/share/vm/gc/shared/threadLocalAllocBuffer.hpp @@ -145,8 +145,8 @@ public: // Initialization at startup static void startup_initialization(); - // Make an in-use tlab parsable, optionally also retiring it. - void make_parsable(bool retire); + // Make an in-use tlab parsable, optionally retiring and/or zapping it. + void make_parsable(bool retire, bool zap = true); // Retire in-use tlab before allocation of a new tlab void clear_before_allocation(); diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp index 86b6c22822f..b6d780963dc 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2012, 2014 SAP SE. 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 diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp index 914492f32d9..166390d8d6b 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -1247,6 +1247,7 @@ void SignatureHandlerLibrary::add(const methodHandle& method) { } else { // debugging suppport if (PrintSignatureHandlers && (handler != Interpreter::slow_signature_handler())) { + ttyLocker ttyl; tty->cr(); tty->print_cr("argument handler #%d for: %s %s (fingerprint = " UINT64_FORMAT ", %d bytes generated)", _handlers->length(), diff --git a/hotspot/src/share/vm/interpreter/linkResolver.cpp b/hotspot/src/share/vm/interpreter/linkResolver.cpp index 71f3beb4387..7e3c60b185d 100644 --- a/hotspot/src/share/vm/interpreter/linkResolver.cpp +++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp @@ -449,6 +449,7 @@ methodHandle LinkResolver::lookup_polymorphic_method( assert(result->intrinsic_id() != vmIntrinsics::_invokeGeneric, "wrong place to find this"); assert(basic_signature == result->signature(), "predict the result signature"); if (TraceMethodHandles) { + ttyLocker ttyl; tty->print("lookup_polymorphic_method => intrinsic "); result->print_on(tty); } @@ -481,6 +482,7 @@ methodHandle LinkResolver::lookup_polymorphic_method( &method_type, CHECK_NULL); if (TraceMethodHandles) { + ttyLocker ttyl; tty->print("lookup_polymorphic_method => (via Java) "); result->print_on(tty); tty->print(" lookup_polymorphic_method => appendix = "); @@ -1609,10 +1611,11 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan } if (TraceMethodHandles) { - ResourceMark rm(THREAD); - tty->print_cr("resolve_invokedynamic #%d %s %s", + ResourceMark rm(THREAD); + tty->print_cr("resolve_invokedynamic #%d %s %s in %s", ConstantPool::decode_invokedynamic_index(index), - method_name->as_C_string(), method_signature->as_C_string()); + method_name->as_C_string(), method_signature->as_C_string(), + current_klass->name()->as_C_string()); tty->print(" BSM info: "); bootstrap_specifier->print(); } diff --git a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp index 7adb8e3fdfb..bd56e38c08e 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -489,7 +489,6 @@ JVMCIEnv::CodeInstallResult CodeInstaller::gather_metadata(Handle target, Handle if (result != JVMCIEnv::ok) { return result; } - process_exception_handlers(); _debug_recorder->pcs_size(); // ehm, create the sentinel record @@ -523,7 +522,6 @@ JVMCIEnv::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, Hand if (result != JVMCIEnv::ok) { return result; } - process_exception_handlers(); int stack_slots = _total_frame_size / HeapWordSize; // conversion to words @@ -574,7 +572,6 @@ void CodeInstaller::initialize_fields(oop target, oop compiled_code, TRAPS) { _parameter_count = 0; } _sites_handle = JNIHandles::make_local(HotSpotCompiledCode::sites(compiled_code)); - _exception_handlers_handle = JNIHandles::make_local(HotSpotCompiledCode::exceptionHandlers(compiled_code)); _code_handle = JNIHandles::make_local(HotSpotCompiledCode::targetCode(compiled_code)); _code_size = HotSpotCompiledCode::targetCodeSize(compiled_code); @@ -608,8 +605,8 @@ int CodeInstaller::estimate_stubs_size(TRAPS) { objArrayOop sites = this->sites(); for (int i = 0; i < sites->length(); i++) { oop site = sites->obj_at(i); - if (site != NULL && site->is_a(CompilationResult_Mark::klass())) { - oop id_obj = CompilationResult_Mark::id(site); + if (site != NULL && site->is_a(site_Mark::klass())) { + oop id_obj = site_Mark::id(site); if (id_obj != NULL) { if (!java_lang_boxing_object::is_instance(id_obj, T_INT)) { JVMCI_ERROR_0("expected Integer id, got %s", id_obj->klass()->signature_name()); @@ -669,18 +666,18 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer, if (patch.is_null()) { THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); } - Handle reference = CompilationResult_DataPatch::reference(patch); + Handle reference = site_DataPatch::reference(patch); if (reference.is_null()) { THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); } - if (!reference->is_a(CompilationResult_ConstantReference::klass())) { + if (!reference->is_a(site_ConstantReference::klass())) { JVMCI_ERROR_OK("invalid patch in data section: %s", reference->klass()->signature_name()); } - Handle constant = CompilationResult_ConstantReference::constant(reference); + Handle constant = site_ConstantReference::constant(reference); if (constant.is_null()) { THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); } - address dest = _constants->start() + CompilationResult_Site::pcOffset(patch); + address dest = _constants->start() + site_Site::pcOffset(patch); if (constant->is_a(HotSpotMetaspaceConstantImpl::klass())) { if (HotSpotMetaspaceConstantImpl::compressed(constant)) { #ifdef _LP64 @@ -716,27 +713,30 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer, THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); } - jint pc_offset = CompilationResult_Site::pcOffset(site); + jint pc_offset = site_Site::pcOffset(site); - if (site->is_a(CompilationResult_Call::klass())) { + if (site->is_a(site_Call::klass())) { TRACE_jvmci_4("call at %i", pc_offset); site_Call(buffer, pc_offset, site, CHECK_OK); - } else if (site->is_a(CompilationResult_Infopoint::klass())) { + } else if (site->is_a(site_Infopoint::klass())) { // three reasons for infopoints denote actual safepoints - oop reason = CompilationResult_Infopoint::reason(site); - if (InfopointReason::SAFEPOINT() == reason || InfopointReason::CALL() == reason || InfopointReason::IMPLICIT_EXCEPTION() == reason) { + oop reason = site_Infopoint::reason(site); + if (site_InfopointReason::SAFEPOINT() == reason || site_InfopointReason::CALL() == reason || site_InfopointReason::IMPLICIT_EXCEPTION() == reason) { TRACE_jvmci_4("safepoint at %i", pc_offset); site_Safepoint(buffer, pc_offset, site, CHECK_OK); } else { TRACE_jvmci_4("infopoint at %i", pc_offset); site_Infopoint(buffer, pc_offset, site, CHECK_OK); } - } else if (site->is_a(CompilationResult_DataPatch::klass())) { + } else if (site->is_a(site_DataPatch::klass())) { TRACE_jvmci_4("datapatch at %i", pc_offset); site_DataPatch(buffer, pc_offset, site, CHECK_OK); - } else if (site->is_a(CompilationResult_Mark::klass())) { + } else if (site->is_a(site_Mark::klass())) { TRACE_jvmci_4("mark at %i", pc_offset); site_Mark(buffer, pc_offset, site, CHECK_OK); + } else if (site->is_a(site_ExceptionHandler::klass())) { + TRACE_jvmci_4("exceptionhandler at %i", pc_offset); + site_ExceptionHandler(pc_offset, site); } else { JVMCI_ERROR_OK("unexpected site subclass: %s", site->klass()->signature_name()); } @@ -802,21 +802,14 @@ void CodeInstaller::assumption_CallSiteTargetValue(Handle assumption) { _dependencies->assert_call_site_target_value(callSite(), methodHandle()); } -void CodeInstaller::process_exception_handlers() { - if (exception_handlers() != NULL) { - objArrayOop handlers = exception_handlers(); - for (int i = 0; i < handlers->length(); i++) { - oop exc = handlers->obj_at(i); - jint pc_offset = CompilationResult_Site::pcOffset(exc); - jint handler_offset = CompilationResult_ExceptionHandler::handlerPos(exc); +void CodeInstaller::site_ExceptionHandler(jint pc_offset, Handle exc) { + jint handler_offset = site_ExceptionHandler::handlerPos(exc); - // Subtable header - _exception_handler_table.add_entry(HandlerTableEntry(1, pc_offset, 0)); + // Subtable header + _exception_handler_table.add_entry(HandlerTableEntry(1, pc_offset, 0)); - // Subtable entry - _exception_handler_table.add_entry(HandlerTableEntry(-1, handler_offset, 0)); - } - } + // Subtable entry + _exception_handler_table.add_entry(HandlerTableEntry(-1, handler_offset, 0)); } // If deoptimization happens, the interpreter should reexecute these bytecodes. @@ -988,7 +981,7 @@ void CodeInstaller::record_scope(jint pc_offset, Handle position, ScopeMode scop } void CodeInstaller::site_Safepoint(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { - Handle debug_info = CompilationResult_Infopoint::debugInfo(site); + Handle debug_info = site_Infopoint::debugInfo(site); if (debug_info.is_null()) { JVMCI_ERROR("debug info expected at safepoint at %i", pc_offset); } @@ -1002,7 +995,7 @@ void CodeInstaller::site_Safepoint(CodeBuffer& buffer, jint pc_offset, Handle si } void CodeInstaller::site_Infopoint(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { - Handle debug_info = CompilationResult_Infopoint::debugInfo(site); + Handle debug_info = site_Infopoint::debugInfo(site); if (debug_info.is_null()) { JVMCI_ERROR("debug info expected at infopoint at %i", pc_offset); } @@ -1017,7 +1010,7 @@ void CodeInstaller::site_Infopoint(CodeBuffer& buffer, jint pc_offset, Handle si } void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { - Handle target = CompilationResult_Call::target(site); + Handle target = site_Call::target(site); InstanceKlass* target_klass = InstanceKlass::cast(target->klass()); Handle hotspot_method; // JavaMethod @@ -1029,7 +1022,7 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, T hotspot_method = target; } - Handle debug_info = CompilationResult_Call::debugInfo(site); + Handle debug_info = site_Call::debugInfo(site); assert(hotspot_method.not_null() ^ foreign_call.not_null(), "Call site needs exactly one type"); @@ -1066,11 +1059,11 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, T } void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { - Handle reference = CompilationResult_DataPatch::reference(site); + Handle reference = site_DataPatch::reference(site); if (reference.is_null()) { THROW(vmSymbols::java_lang_NullPointerException()); - } else if (reference->is_a(CompilationResult_ConstantReference::klass())) { - Handle constant = CompilationResult_ConstantReference::constant(reference); + } else if (reference->is_a(site_ConstantReference::klass())) { + Handle constant = site_ConstantReference::constant(reference); if (constant.is_null()) { THROW(vmSymbols::java_lang_NullPointerException()); } else if (constant->is_a(HotSpotObjectConstantImpl::klass())) { @@ -1080,8 +1073,8 @@ void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle si } else { JVMCI_ERROR("unknown constant type in data patch: %s", constant->klass()->signature_name()); } - } else if (reference->is_a(CompilationResult_DataSectionReference::klass())) { - int data_offset = CompilationResult_DataSectionReference::offset(reference); + } else if (reference->is_a(site_DataSectionReference::klass())) { + int data_offset = site_DataSectionReference::offset(reference); if (0 <= data_offset && data_offset < _constants_size) { pd_patch_DataSectionReference(pc_offset, data_offset); } else { @@ -1093,7 +1086,7 @@ void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle si } void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { - Handle id_obj = CompilationResult_Mark::id(site); + Handle id_obj = site_Mark::id(site); if (id_obj.not_null()) { if (!java_lang_boxing_object::is_instance(id_obj(), T_INT)) { diff --git a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp index 1157759d0ed..ccf6a6d836f 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,7 +120,6 @@ private: jobject _data_section_handle; jobject _data_section_patches_handle; jobject _sites_handle; - jobject _exception_handlers_handle; CodeOffsets _offsets; jobject _code_handle; @@ -166,7 +165,6 @@ private: arrayOop code() { return (arrayOop) JNIHandles::resolve(_code_handle); } arrayOop data_section() { return (arrayOop) JNIHandles::resolve(_data_section_handle); } objArrayOop data_section_patches() { return (objArrayOop) JNIHandles::resolve(_data_section_patches_handle); } - objArrayOop exception_handlers() { return (objArrayOop) JNIHandles::resolve(_exception_handlers_handle); } #ifndef PRODUCT objArrayOop comments() { return (objArrayOop) JNIHandles::resolve(_comments_handle); } #endif @@ -196,7 +194,7 @@ protected: narrowKlass record_narrow_metadata_reference(Handle constant, TRAPS); #endif - // extract the fields of the CompilationResult + // extract the fields of the HotSpotCompiledCode void initialize_fields(oop target, oop target_method, TRAPS); void initialize_dependencies(oop target_method, OopRecorder* oop_recorder, TRAPS); @@ -216,6 +214,7 @@ protected: void site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); void site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); void site_Mark(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); + void site_ExceptionHandler(jint pc_offset, Handle site); OopMap* create_oop_map(Handle debug_info, TRAPS); @@ -235,7 +234,6 @@ protected: GrowableArray* record_virtual_objects(Handle debug_info, TRAPS); - void process_exception_handlers(); int estimateStubSpace(int static_call_stubs); }; diff --git a/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp b/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp index 8e5a2ff86f7..b9da75cde0d 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp @@ -134,7 +134,6 @@ void JVMCICompiler::compile_method(const methodHandle& method, int entry_bci, JV JVMCIRuntime::initialize_well_known_classes(CHECK_ABORT); HandleMark hm; - ResourceMark rm; Handle receiver = JVMCIRuntime::get_HotSpotJVMCIRuntime(CHECK_ABORT); JavaValue method_result(T_OBJECT); @@ -143,8 +142,8 @@ void JVMCICompiler::compile_method(const methodHandle& method, int entry_bci, JV JavaCalls::call_static(&method_result, SystemDictionary::HotSpotResolvedJavaMethodImpl_klass(), vmSymbols::fromMetaspace_name(), vmSymbols::method_fromMetaspace_signature(), &args, THREAD); + JavaValue result(T_OBJECT); if (!HAS_PENDING_EXCEPTION) { - JavaValue result(T_VOID); JavaCallArguments args; args.push_oop(receiver); args.push_oop((oop)method_result.get_jobject()); @@ -162,15 +161,27 @@ void JVMCICompiler::compile_method(const methodHandle& method, int entry_bci, JV Handle exception(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; - { - ttyLocker ttyl; - java_lang_Throwable::print_stack_trace(exception, tty); - } + java_lang_Throwable::java_printStackTrace(exception, THREAD); - // Something went wrong so disable compilation at this level - method->set_not_compilable(CompLevel_full_optimization); + env->set_failure("exception throw", false); } else { - _methodsCompiled++; + oop result_object = (oop) result.get_jobject(); + if (result_object != NULL) { + oop failure_message = CompilationRequestResult::failureMessage(result_object); + if (failure_message != NULL) { + const char* failure_reason = java_lang_String::as_utf8_string(failure_message); + env->set_failure(failure_reason, CompilationRequestResult::retry(result_object) != 0); + } else { + if (env->task()->code() == NULL) { + env->set_failure("no nmethod produced", true); + } else { + env->task()->set_num_inlined_bytecodes(CompilationRequestResult::inlinedBytecodes(result_object)); + _methodsCompiled++; + } + } + } else { + assert(false, "JVMCICompiler.compileMethod should always return non-null"); + } } } @@ -181,11 +192,7 @@ void JVMCICompiler::abort_on_pending_exception(Handle exception, const char* mes Thread* THREAD = Thread::current(); CLEAR_PENDING_EXCEPTION; - { - ttyLocker ttyl; - tty->print_raw_cr(message); - java_lang_Throwable::print_stack_trace(exception, tty); - } + java_lang_Throwable::java_printStackTrace(exception, THREAD); // Give other aborting threads to also print their stack traces. // This can be very useful when debugging class initialization diff --git a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp index cdb3ded801c..a444c909e9e 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp @@ -150,6 +150,8 @@ HeapWord** CompilerToVM::Data::_heap_top_addr; jbyte* CompilerToVM::Data::cardtable_start_address; int CompilerToVM::Data::cardtable_shift; +int CompilerToVM::Data::vm_page_size; + void CompilerToVM::Data::initialize() { Klass_vtable_start_offset = in_bytes(Klass::vtable_start_offset()); Klass_vtable_length_offset = in_bytes(Klass::vtable_length_offset()); @@ -199,6 +201,8 @@ void CompilerToVM::Data::initialize() { ShouldNotReachHere(); break; } + + vm_page_size = os::vm_page_size(); } /** @@ -831,30 +835,6 @@ C2V_VMENTRY(jint, getMetadata, (JNIEnv *jniEnv, jobject, jobject target, jobject return result; C2V_END -C2V_VMENTRY(void, notifyCompilationStatistics, (JNIEnv *jniEnv, jobject, jint id, jobject hotspot_method, jboolean osr, jint processedBytecodes, jlong time, jlong timeUnitsPerSecond, jobject installed_code)) - JVMCICompiler* compiler = JVMCICompiler::instance(CHECK); - CompilerStatistics* stats = compiler->stats(); - - elapsedTimer timer = elapsedTimer(time, timeUnitsPerSecond); - if (osr) { - stats->_osr.update(timer, processedBytecodes); - } else { - stats->_standard.update(timer, processedBytecodes); - } - Handle installed_code_handle = JNIHandles::resolve(installed_code); - if (installed_code_handle->is_a(HotSpotInstalledCode::klass())) { - stats->_nmethods_size += HotSpotInstalledCode::size(installed_code_handle); - stats->_nmethods_code_size += HotSpotInstalledCode::codeSize(installed_code_handle); - } - - if (CITimeEach) { - methodHandle method = CompilerToVM::asMethod(hotspot_method); - float bytes_per_sec = 1.0 * processedBytecodes / timer.seconds(); - tty->print_cr("%3d seconds: %f bytes/sec: %f (bytes %d)", - id, timer.seconds(), bytes_per_sec, processedBytecodes); - } -C2V_END - C2V_VMENTRY(void, resetCompilationStatistics, (JNIEnv *jniEnv, jobject)) JVMCICompiler* compiler = JVMCICompiler::instance(CHECK); CompilerStatistics* stats = compiler->stats(); @@ -894,10 +874,8 @@ C2V_VMENTRY(jobject, disassembleCodeBlob, (JNIEnv *jniEnv, jobject, jobject inst if (!nm->is_alive()) { return NULL; } - Disassembler::decode(nm, &st); - } else { - Disassembler::decode(cb, &st); } + Disassembler::decode(cb, &st); if (st.size() <= 0) { return NULL; } @@ -1387,6 +1365,42 @@ C2V_VMENTRY(int, methodDataProfileDataSize, (JNIEnv*, jobject, jlong metaspace_m THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), err_msg("Invalid profile data position %d", position)); C2V_END +C2V_VMENTRY(int, interpreterFrameSize, (JNIEnv*, jobject, jobject bytecode_frame_handle)) + if (bytecode_frame_handle == NULL) { + THROW_0(vmSymbols::java_lang_NullPointerException()); + } + + oop top_bytecode_frame = JNIHandles::resolve_non_null(bytecode_frame_handle); + oop bytecode_frame = top_bytecode_frame; + int size = 0; + int callee_parameters = 0; + int callee_locals = 0; + Method* method = getMethodFromHotSpotMethod(BytecodePosition::method(bytecode_frame)); + int extra_args = method->max_stack() - BytecodeFrame::numStack(bytecode_frame); + + while (bytecode_frame != NULL) { + int locks = BytecodeFrame::numLocks(bytecode_frame); + int temps = BytecodeFrame::numStack(bytecode_frame); + bool is_top_frame = (bytecode_frame == top_bytecode_frame); + Method* method = getMethodFromHotSpotMethod(BytecodePosition::method(bytecode_frame)); + + int frame_size = BytesPerWord * Interpreter::size_activation(method->max_stack(), + temps + callee_parameters, + extra_args, + locks, + callee_parameters, + callee_locals, + is_top_frame); + size += frame_size; + + callee_parameters = method->size_of_parameters(); + callee_locals = method->max_locals(); + extra_args = 0; + bytecode_frame = BytecodePosition::caller(bytecode_frame); + } + return size + Deoptimization::last_frame_adjust(0, callee_locals) * BytesPerWord; +C2V_END + #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f)) @@ -1397,6 +1411,7 @@ C2V_END #define STACK_TRACE_ELEMENT "Ljava/lang/StackTraceElement;" #define INSTALLED_CODE "Ljdk/vm/ci/code/InstalledCode;" #define TARGET_DESCRIPTION "Ljdk/vm/ci/code/TargetDescription;" +#define BYTECODE_FRAME "Ljdk/vm/ci/code/BytecodeFrame;" #define RESOLVED_METHOD "Ljdk/vm/ci/meta/ResolvedJavaMethod;" #define HS_RESOLVED_METHOD "Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;" #define HS_RESOLVED_KLASS "Ljdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl;" @@ -1446,7 +1461,6 @@ JNINativeMethod CompilerToVM::methods[] = { {CC"initializeConfiguration", CC"("HS_CONFIG")J", FN_PTR(initializeConfiguration)}, {CC"installCode", CC"("TARGET_DESCRIPTION HS_COMPILED_CODE INSTALLED_CODE HS_SPECULATION_LOG")I", FN_PTR(installCode)}, {CC"getMetadata", CC"("TARGET_DESCRIPTION HS_COMPILED_CODE HS_METADATA")I", FN_PTR(getMetadata)}, - {CC"notifyCompilationStatistics", CC"(I"HS_RESOLVED_METHOD"ZIJJ"INSTALLED_CODE")V", FN_PTR(notifyCompilationStatistics)}, {CC"resetCompilationStatistics", CC"()V", FN_PTR(resetCompilationStatistics)}, {CC"disassembleCodeBlob", CC"("INSTALLED_CODE")"STRING, FN_PTR(disassembleCodeBlob)}, {CC"executeInstalledCode", CC"(["OBJECT INSTALLED_CODE")"OBJECT, FN_PTR(executeInstalledCode)}, @@ -1467,6 +1481,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC"writeDebugOutput", CC"([BII)V", FN_PTR(writeDebugOutput)}, {CC"flushDebugOutput", CC"()V", FN_PTR(flushDebugOutput)}, {CC"methodDataProfileDataSize", CC"(JI)I", FN_PTR(methodDataProfileDataSize)}, + {CC"interpreterFrameSize", CC"("BYTECODE_FRAME")I", FN_PTR(interpreterFrameSize)}, }; int CompilerToVM::methods_count() { diff --git a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.hpp b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.hpp index 6ad1f3264a0..9ec78fd7358 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.hpp @@ -63,6 +63,8 @@ class CompilerToVM { static jbyte* cardtable_start_address; static int cardtable_shift; + static int vm_page_size; + public: static void initialize(); }; diff --git a/hotspot/src/share/vm/jvmci/jvmciEnv.cpp b/hotspot/src/share/vm/jvmci/jvmciEnv.cpp index 4142f790c6c..edf2d21fdd0 100644 --- a/hotspot/src/share/vm/jvmci/jvmciEnv.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciEnv.cpp @@ -48,16 +48,17 @@ #include "jvmci/jvmciRuntime.hpp" #include "jvmci/jvmciJavaClasses.hpp" -JVMCIEnv::JVMCIEnv(CompileTask* task, int system_dictionary_modification_counter) { - _task = task; - _system_dictionary_modification_counter = system_dictionary_modification_counter; - { - // Get Jvmti capabilities under lock to get consistent values. - MutexLocker mu(JvmtiThreadState_lock); - _jvmti_can_hotswap_or_post_breakpoint = JvmtiExport::can_hotswap_or_post_breakpoint(); - _jvmti_can_access_local_variables = JvmtiExport::can_access_local_variables(); - _jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions(); - } +JVMCIEnv::JVMCIEnv(CompileTask* task, int system_dictionary_modification_counter): + _task(task), + _system_dictionary_modification_counter(system_dictionary_modification_counter), + _failure_reason(NULL), + _retryable(true) +{ + // Get Jvmti capabilities under lock to get consistent values. + MutexLocker mu(JvmtiThreadState_lock); + _jvmti_can_hotswap_or_post_breakpoint = JvmtiExport::can_hotswap_or_post_breakpoint(); + _jvmti_can_access_local_variables = JvmtiExport::can_access_local_variables(); + _jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions(); } // ------------------------------------------------------------------ @@ -534,7 +535,9 @@ JVMCIEnv::CodeInstallResult JVMCIEnv::register_method( // Record successful registration. // (Put nm into the task handle *before* publishing to the Java heap.) CompileTask* task = env == NULL ? NULL : env->task(); - if (task != NULL) task->set_code(nm); + if (task != NULL) { + task->set_code(nm); + } if (installed_code->is_a(HotSpotNmethod::klass()) && HotSpotNmethod::isDefault(installed_code())) { if (entry_bci == InvocationEntryBci) { diff --git a/hotspot/src/share/vm/jvmci/jvmciEnv.hpp b/hotspot/src/share/vm/jvmci/jvmciEnv.hpp index 2bc95d3308c..833074326bf 100644 --- a/hotspot/src/share/vm/jvmci/jvmciEnv.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciEnv.hpp @@ -100,6 +100,10 @@ private: CompileTask* _task; int _system_dictionary_modification_counter; + // Compilation result values + const char* _failure_reason; + bool _retryable; + // Cache JVMTI state bool _jvmti_can_hotswap_or_post_breakpoint; bool _jvmti_can_access_local_variables; @@ -141,6 +145,14 @@ private: public: CompileTask* task() { return _task; } + const char* failure_reason() { return _failure_reason; } + bool retryable() { return _retryable; } + + void set_failure(const char* reason, bool retryable) { + _failure_reason = reason; + _retryable = retryable; + } + // Register the result of a compilation. static JVMCIEnv::CodeInstallResult register_method( const methodHandle& target, diff --git a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp index 0765e0edf24..083f726d170 100644 --- a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp @@ -80,19 +80,18 @@ class JVMCIJavaClasses : AllStatic { end_class \ start_class(HotSpotCompiledCode) \ oop_field(HotSpotCompiledCode, name, "Ljava/lang/String;") \ - objArrayOop_field(HotSpotCompiledCode, sites, "[Ljdk/vm/ci/code/CompilationResult$Site;") \ - objArrayOop_field(HotSpotCompiledCode, exceptionHandlers, "[Ljdk/vm/ci/code/CompilationResult$ExceptionHandler;") \ - objArrayOop_field(HotSpotCompiledCode, comments, "[Ljdk/vm/ci/hotspot/HotSpotCompiledCode$Comment;") \ - objArrayOop_field(HotSpotCompiledCode, assumptions, "[Ljdk/vm/ci/meta/Assumptions$Assumption;") \ typeArrayOop_field(HotSpotCompiledCode, targetCode, "[B") \ int_field(HotSpotCompiledCode, targetCodeSize) \ + objArrayOop_field(HotSpotCompiledCode, sites, "[Ljdk/vm/ci/code/site/Site;") \ + objArrayOop_field(HotSpotCompiledCode, assumptions, "[Ljdk/vm/ci/meta/Assumptions$Assumption;") \ + objArrayOop_field(HotSpotCompiledCode, methods, "[Ljdk/vm/ci/meta/ResolvedJavaMethod;") \ + objArrayOop_field(HotSpotCompiledCode, comments, "[Ljdk/vm/ci/hotspot/HotSpotCompiledCode$Comment;") \ typeArrayOop_field(HotSpotCompiledCode, dataSection, "[B") \ int_field(HotSpotCompiledCode, dataSectionAlignment) \ - objArrayOop_field(HotSpotCompiledCode, dataSectionPatches, "[Ljdk/vm/ci/code/CompilationResult$DataPatch;") \ + objArrayOop_field(HotSpotCompiledCode, dataSectionPatches, "[Ljdk/vm/ci/code/site/DataPatch;") \ boolean_field(HotSpotCompiledCode, isImmutablePIC) \ int_field(HotSpotCompiledCode, totalFrameSize) \ int_field(HotSpotCompiledCode, customStackAreaOffset) \ - objArrayOop_field(HotSpotCompiledCode, methods, "[Ljdk/vm/ci/meta/ResolvedJavaMethod;") \ end_class \ start_class(HotSpotCompiledCode_Comment) \ oop_field(HotSpotCompiledCode_Comment, text, "Ljava/lang/String;") \ @@ -132,36 +131,41 @@ class JVMCIJavaClasses : AllStatic { oop_field(Assumptions_CallSiteTargetValue, callSite, "Ljava/lang/invoke/CallSite;") \ oop_field(Assumptions_CallSiteTargetValue, methodHandle, "Ljava/lang/invoke/MethodHandle;") \ end_class \ - start_class(CompilationResult_Site) \ - int_field(CompilationResult_Site, pcOffset) \ + start_class(site_Site) \ + int_field(site_Site, pcOffset) \ end_class \ - start_class(CompilationResult_Call) \ - oop_field(CompilationResult_Call, target, "Ljdk/vm/ci/meta/InvokeTarget;") \ - oop_field(CompilationResult_Call, debugInfo, "Ljdk/vm/ci/code/DebugInfo;") \ + start_class(site_Call) \ + oop_field(site_Call, target, "Ljdk/vm/ci/meta/InvokeTarget;") \ + oop_field(site_Call, debugInfo, "Ljdk/vm/ci/code/DebugInfo;") \ end_class \ - start_class(CompilationResult_DataPatch) \ - oop_field(CompilationResult_DataPatch, reference, "Ljdk/vm/ci/code/CompilationResult$Reference;") \ + start_class(site_DataPatch) \ + oop_field(site_DataPatch, reference, "Ljdk/vm/ci/code/site/Reference;") \ end_class \ - start_class(CompilationResult_ConstantReference) \ - oop_field(CompilationResult_ConstantReference, constant, "Ljdk/vm/ci/meta/VMConstant;") \ + start_class(site_ConstantReference) \ + oop_field(site_ConstantReference, constant, "Ljdk/vm/ci/meta/VMConstant;") \ end_class \ - start_class(CompilationResult_DataSectionReference) \ - int_field(CompilationResult_DataSectionReference, offset) \ + start_class(site_DataSectionReference) \ + int_field(site_DataSectionReference, offset) \ end_class \ - start_class(InfopointReason) \ - static_oop_field(InfopointReason, SAFEPOINT, "Ljdk/vm/ci/code/InfopointReason;") \ - static_oop_field(InfopointReason, CALL, "Ljdk/vm/ci/code/InfopointReason;") \ - static_oop_field(InfopointReason, IMPLICIT_EXCEPTION, "Ljdk/vm/ci/code/InfopointReason;") \ + start_class(site_InfopointReason) \ + static_oop_field(site_InfopointReason, SAFEPOINT, "Ljdk/vm/ci/code/site/InfopointReason;") \ + static_oop_field(site_InfopointReason, CALL, "Ljdk/vm/ci/code/site/InfopointReason;") \ + static_oop_field(site_InfopointReason, IMPLICIT_EXCEPTION, "Ljdk/vm/ci/code/site/InfopointReason;") \ end_class \ - start_class(CompilationResult_Infopoint) \ - oop_field(CompilationResult_Infopoint, debugInfo, "Ljdk/vm/ci/code/DebugInfo;") \ - oop_field(CompilationResult_Infopoint, reason, "Ljdk/vm/ci/code/InfopointReason;") \ + start_class(site_Infopoint) \ + oop_field(site_Infopoint, debugInfo, "Ljdk/vm/ci/code/DebugInfo;") \ + oop_field(site_Infopoint, reason, "Ljdk/vm/ci/code/site/InfopointReason;") \ end_class \ - start_class(CompilationResult_ExceptionHandler) \ - int_field(CompilationResult_ExceptionHandler, handlerPos) \ + start_class(site_ExceptionHandler) \ + int_field(site_ExceptionHandler, handlerPos) \ end_class \ - start_class(CompilationResult_Mark) \ - oop_field(CompilationResult_Mark, id, "Ljava/lang/Object;") \ + start_class(site_Mark) \ + oop_field(site_Mark, id, "Ljava/lang/Object;") \ + end_class \ + start_class(CompilationRequestResult) \ + oop_field(CompilationRequestResult, failureMessage, "Ljava/lang/String;") \ + boolean_field(CompilationRequestResult, retry) \ + int_field(CompilationRequestResult, inlinedBytecodes) \ end_class \ start_class(DebugInfo) \ oop_field(DebugInfo, bytecodePosition, "Ljdk/vm/ci/code/BytecodePosition;") \ @@ -289,7 +293,7 @@ class JVMCIJavaClasses : AllStatic { long_field(HotSpotConstantPool, metaspaceConstantPool) \ end_class \ start_class(HotSpotJVMCIRuntime) \ - objArrayOop_field(HotSpotJVMCIRuntime, trivialPrefixes, "[Ljava/lang/String;") \ + objArrayOop_field(HotSpotJVMCIRuntime, trivialPrefixes, "[Ljava/lang/String;") \ end_class \ /* end*/ diff --git a/hotspot/src/share/vm/jvmci/systemDictionary_jvmci.hpp b/hotspot/src/share/vm/jvmci/systemDictionary_jvmci.hpp index 58701527f0f..864539f6654 100644 --- a/hotspot/src/share/vm/jvmci/systemDictionary_jvmci.hpp +++ b/hotspot/src/share/vm/jvmci/systemDictionary_jvmci.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,15 +60,7 @@ do_klass(DebugInfo_klass, jdk_vm_ci_code_DebugInfo, Jvmci) \ do_klass(RegisterSaveLayout_klass, jdk_vm_ci_code_RegisterSaveLayout, Jvmci) \ do_klass(BytecodeFrame_klass, jdk_vm_ci_code_BytecodeFrame, Jvmci) \ - do_klass(CompilationResult_Call_klass, jdk_vm_ci_code_CompilationResult_Call, Jvmci) \ - do_klass(CompilationResult_ConstantReference_klass, jdk_vm_ci_code_CompilationResult_ConstantReference, Jvmci) \ - do_klass(CompilationResult_DataPatch_klass, jdk_vm_ci_code_CompilationResult_DataPatch, Jvmci) \ - do_klass(CompilationResult_DataSectionReference_klass, jdk_vm_ci_code_CompilationResult_DataSectionReference, Jvmci) \ - do_klass(CompilationResult_ExceptionHandler_klass, jdk_vm_ci_code_CompilationResult_ExceptionHandler, Jvmci) \ - do_klass(CompilationResult_Mark_klass, jdk_vm_ci_code_CompilationResult_Mark, Jvmci) \ - do_klass(CompilationResult_Infopoint_klass, jdk_vm_ci_code_CompilationResult_Infopoint, Jvmci) \ - do_klass(CompilationResult_Site_klass, jdk_vm_ci_code_CompilationResult_Site, Jvmci) \ - do_klass(InfopointReason_klass, jdk_vm_ci_code_InfopointReason, Jvmci) \ + do_klass(CompilationRequestResult_klass, jdk_vm_ci_code_CompilationRequestResult, Jvmci) \ do_klass(InstalledCode_klass, jdk_vm_ci_code_InstalledCode, Jvmci) \ do_klass(code_Location_klass, jdk_vm_ci_code_Location, Jvmci) \ do_klass(code_Register_klass, jdk_vm_ci_code_Register, Jvmci) \ @@ -76,6 +68,15 @@ do_klass(StackSlot_klass, jdk_vm_ci_code_StackSlot, Jvmci) \ do_klass(StackLockValue_klass, jdk_vm_ci_code_StackLockValue, Jvmci) \ do_klass(VirtualObject_klass, jdk_vm_ci_code_VirtualObject, Jvmci) \ + do_klass(site_Call_klass, jdk_vm_ci_code_site_Call, Jvmci) \ + do_klass(site_ConstantReference_klass, jdk_vm_ci_code_site_ConstantReference, Jvmci) \ + do_klass(site_DataPatch_klass, jdk_vm_ci_code_site_DataPatch, Jvmci) \ + do_klass(site_DataSectionReference_klass, jdk_vm_ci_code_site_DataSectionReference, Jvmci) \ + do_klass(site_ExceptionHandler_klass, jdk_vm_ci_code_site_ExceptionHandler, Jvmci) \ + do_klass(site_Mark_klass, jdk_vm_ci_code_site_Mark, Jvmci) \ + do_klass(site_Infopoint_klass, jdk_vm_ci_code_site_Infopoint, Jvmci) \ + do_klass(site_Site_klass, jdk_vm_ci_code_site_Site, Jvmci) \ + do_klass(site_InfopointReason_klass, jdk_vm_ci_code_site_InfopointReason, Jvmci) \ do_klass(JavaConstant_klass, jdk_vm_ci_meta_JavaConstant, Jvmci) \ do_klass(PrimitiveConstant_klass, jdk_vm_ci_meta_PrimitiveConstant, Jvmci) \ do_klass(RawConstant_klass, jdk_vm_ci_meta_RawConstant, Jvmci) \ diff --git a/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp b/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp index 0d6e4639853..20dc5c6eb5e 100644 --- a/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp +++ b/hotspot/src/share/vm/jvmci/vmStructs_jvmci.cpp @@ -74,6 +74,8 @@ static_field(CompilerToVM::Data, cardtable_start_address, jbyte*) \ static_field(CompilerToVM::Data, cardtable_shift, int) \ \ + static_field(CompilerToVM::Data, vm_page_size, int) \ + \ static_field(Abstract_VM_Version, _features, uint64_t) \ \ nonstatic_field(Array, _length, int) \ diff --git a/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp b/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp index 8d8af6bd79a..f7aada3b81b 100644 --- a/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp +++ b/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,31 +64,32 @@ template(jdk_vm_ci_meta_Assumptions_ConcreteMethod, "jdk/vm/ci/meta/Assumptions$ConcreteMethod") \ template(jdk_vm_ci_meta_Assumptions_CallSiteTargetValue, "jdk/vm/ci/meta/Assumptions$CallSiteTargetValue") \ template(jdk_vm_ci_code_Architecture, "jdk/vm/ci/code/Architecture") \ - template(jdk_vm_ci_code_TargetDescription, "jdk/vm/ci/code/TargetDescription") \ - template(jdk_vm_ci_code_CompilationResult_Call, "jdk/vm/ci/code/CompilationResult$Call") \ - template(jdk_vm_ci_code_CompilationResult_ConstantReference, "jdk/vm/ci/code/CompilationResult$ConstantReference") \ - template(jdk_vm_ci_code_CompilationResult_DataPatch, "jdk/vm/ci/code/CompilationResult$DataPatch") \ - template(jdk_vm_ci_code_CompilationResult_DataSectionReference, "jdk/vm/ci/code/CompilationResult$DataSectionReference") \ - template(jdk_vm_ci_code_CompilationResult_ExceptionHandler, "jdk/vm/ci/code/CompilationResult$ExceptionHandler") \ - template(jdk_vm_ci_code_CompilationResult_Mark, "jdk/vm/ci/code/CompilationResult$Mark") \ - template(jdk_vm_ci_code_CompilationResult_Infopoint, "jdk/vm/ci/code/CompilationResult$Infopoint") \ - template(jdk_vm_ci_code_CompilationResult_Site, "jdk/vm/ci/code/CompilationResult$Site") \ - template(jdk_vm_ci_code_InfopointReason, "jdk/vm/ci/code/InfopointReason") \ - template(jdk_vm_ci_code_InstalledCode, "jdk/vm/ci/code/InstalledCode") \ template(jdk_vm_ci_code_BytecodeFrame, "jdk/vm/ci/code/BytecodeFrame") \ template(jdk_vm_ci_code_BytecodePosition, "jdk/vm/ci/code/BytecodePosition") \ + template(jdk_vm_ci_code_CompilationRequestResult, "jdk/vm/ci/code/CompilationRequestResult") \ template(jdk_vm_ci_code_DebugInfo, "jdk/vm/ci/code/DebugInfo") \ + template(jdk_vm_ci_code_InstalledCode, "jdk/vm/ci/code/InstalledCode") \ template(jdk_vm_ci_code_Location, "jdk/vm/ci/code/Location") \ template(jdk_vm_ci_code_Register, "jdk/vm/ci/code/Register") \ template(jdk_vm_ci_code_RegisterValue, "jdk/vm/ci/code/RegisterValue") \ template(jdk_vm_ci_code_StackSlot, "jdk/vm/ci/code/StackSlot") \ template(jdk_vm_ci_code_StackLockValue, "jdk/vm/ci/code/StackLockValue") \ + template(jdk_vm_ci_code_TargetDescription, "jdk/vm/ci/code/TargetDescription") \ template(jdk_vm_ci_code_VirtualObject, "jdk/vm/ci/code/VirtualObject") \ template(jdk_vm_ci_code_RegisterSaveLayout, "jdk/vm/ci/code/RegisterSaveLayout") \ template(jdk_vm_ci_code_InvalidInstalledCodeException, "jdk/vm/ci/code/InvalidInstalledCodeException") \ + template(jdk_vm_ci_code_site_Call, "jdk/vm/ci/code/site/Call") \ + template(jdk_vm_ci_code_site_ConstantReference, "jdk/vm/ci/code/site/ConstantReference") \ + template(jdk_vm_ci_code_site_DataPatch, "jdk/vm/ci/code/site/DataPatch") \ + template(jdk_vm_ci_code_site_DataSectionReference, "jdk/vm/ci/code/site/DataSectionReference") \ + template(jdk_vm_ci_code_site_ExceptionHandler, "jdk/vm/ci/code/site/ExceptionHandler") \ + template(jdk_vm_ci_code_site_Mark, "jdk/vm/ci/code/site/Mark") \ + template(jdk_vm_ci_code_site_Infopoint, "jdk/vm/ci/code/site/Infopoint") \ + template(jdk_vm_ci_code_site_Site, "jdk/vm/ci/code/site/Site") \ + template(jdk_vm_ci_code_site_InfopointReason, "jdk/vm/ci/code/site/InfopointReason") \ template(jdk_vm_ci_common_JVMCIError, "jdk/vm/ci/common/JVMCIError") \ template(compileMethod_name, "compileMethod") \ - template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)V") \ + template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)Ljdk/vm/ci/code/CompilationRequestResult;") \ template(fromMetaspace_name, "fromMetaspace") \ template(method_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;") \ template(constantPool_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotConstantPool;") \ diff --git a/hotspot/src/share/vm/memory/heap.cpp b/hotspot/src/share/vm/memory/heap.cpp index 1910ed49e18..7afab964a7f 100644 --- a/hotspot/src/share/vm/memory/heap.cpp +++ b/hotspot/src/share/vm/memory/heap.cpp @@ -149,6 +149,10 @@ bool CodeHeap::expand_by(size_t size) { // expand _memory space size_t dm = align_to_page_size(_memory.committed_size() + size) - _memory.committed_size(); if (dm > 0) { + // Use at least the available uncommitted space if 'size' is larger + if (_memory.uncommitted_size() != 0 && dm > _memory.uncommitted_size()) { + dm = _memory.uncommitted_size(); + } char* base = _memory.low() + _memory.committed_size(); if (!_memory.expand_by(dm)) return false; on_code_mapping(base, dm); diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp index 9218e2a439a..1e9b0b649e9 100644 --- a/hotspot/src/share/vm/oops/cpCache.cpp +++ b/hotspot/src/share/vm/oops/cpCache.cpp @@ -306,6 +306,7 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle& adapter->size_of_parameters()); if (TraceInvokeDynamic) { + ttyLocker ttyl; tty->print_cr("set_method_handle bc=%d appendix=" PTR_FORMAT "%s method_type=" PTR_FORMAT "%s method=" PTR_FORMAT " ", invoke_code, p2i(appendix()), (has_appendix ? "" : " (unused)"), @@ -357,6 +358,7 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle& set_bytecode_1(invoke_code); NOT_PRODUCT(verify(tty)); if (TraceInvokeDynamic) { + ttyLocker ttyl; this->print(tty, 0); } } diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp index ac237fa0f43..b391f222d6a 100644 --- a/hotspot/src/share/vm/oops/klass.cpp +++ b/hotspot/src/share/vm/oops/klass.cpp @@ -45,6 +45,20 @@ #include "gc/g1/g1SATBCardTableModRefBS.hpp" #endif // INCLUDE_ALL_GCS +bool Klass::is_cloneable() const { + return _access_flags.is_cloneable_fast() || + is_subtype_of(SystemDictionary::Cloneable_klass()); +} + +void Klass::set_is_cloneable() { + if (name() != vmSymbols::java_lang_invoke_MemberName()) { + _access_flags.set_is_cloneable_fast(); + } else { + assert(is_final(), "no subclasses allowed"); + // MemberName cloning should not be intrinsified and always happen in JVM_Clone. + } +} + void Klass::set_name(Symbol* n) { _name = n; if (_name != NULL) _name->increment_refcount(); diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp index 462ae43b52b..00fe28d614b 100644 --- a/hotspot/src/share/vm/oops/klass.hpp +++ b/hotspot/src/share/vm/oops/klass.hpp @@ -538,13 +538,14 @@ protected: bool has_final_method() const { return _access_flags.has_final_method(); } void set_has_finalizer() { _access_flags.set_has_finalizer(); } void set_has_final_method() { _access_flags.set_has_final_method(); } - bool is_cloneable() const { return _access_flags.is_cloneable(); } - void set_is_cloneable() { _access_flags.set_is_cloneable(); } bool has_vanilla_constructor() const { return _access_flags.has_vanilla_constructor(); } void set_has_vanilla_constructor() { _access_flags.set_has_vanilla_constructor(); } bool has_miranda_methods () const { return access_flags().has_miranda_methods(); } void set_has_miranda_methods() { _access_flags.set_has_miranda_methods(); } + bool is_cloneable() const; + void set_is_cloneable(); + // Biased locking support // Note: the prototype header is always set up to be at least the // prototype markOop. If biased locking is enabled it may further be diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp index 46a0c1e584e..eebf233a555 100644 --- a/hotspot/src/share/vm/oops/method.cpp +++ b/hotspot/src/share/vm/oops/method.cpp @@ -1201,8 +1201,10 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid, m->set_vtable_index(Method::nonvirtual_vtable_index); m->link_method(m, CHECK_(empty)); - if (TraceMethodHandles && (Verbose || WizardMode)) + if (TraceMethodHandles && (Verbose || WizardMode)) { + ttyLocker ttyl; m->print_on(tty); + } return m; } diff --git a/hotspot/src/share/vm/opto/addnode.cpp b/hotspot/src/share/vm/opto/addnode.cpp index de7356db4b9..416b9c35ff9 100644 --- a/hotspot/src/share/vm/opto/addnode.cpp +++ b/hotspot/src/share/vm/opto/addnode.cpp @@ -52,7 +52,7 @@ uint AddNode::hash() const { //------------------------------Identity--------------------------------------- // If either input is a constant 0, return the other input. -Node *AddNode::Identity( PhaseTransform *phase ) { +Node* AddNode::Identity(PhaseGVN* phase) { const Type *zero = add_id(); // The additive identity if( phase->type( in(1) )->higher_equal( zero ) ) return in(2); if( phase->type( in(2) )->higher_equal( zero ) ) return in(1); @@ -204,7 +204,7 @@ Node *AddNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value----------------------------------------- // An add node sums it's two _in. If one input is an RSD, we must mixin // the other input's symbols. -const Type *AddNode::Value( PhaseTransform *phase ) const { +const Type* AddNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -326,7 +326,7 @@ Node *AddINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Fold (x-y)+y OR y+(x-y) into x -Node *AddINode::Identity( PhaseTransform *phase ) { +Node* AddINode::Identity(PhaseGVN* phase) { if( in(1)->Opcode() == Op_SubI && phase->eqv(in(1)->in(2),in(2)) ) { return in(1)->in(1); } @@ -443,7 +443,7 @@ Node *AddLNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Fold (x-y)+y OR y+(x-y) into x -Node *AddLNode::Identity( PhaseTransform *phase ) { +Node* AddLNode::Identity(PhaseGVN* phase) { if( in(1)->Opcode() == Op_SubL && phase->eqv(in(1)->in(2),in(2)) ) { return in(1)->in(1); } @@ -561,7 +561,7 @@ Node *AddDNode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Identity--------------------------------------- // If one input is a constant 0, return the other input. -Node *AddPNode::Identity( PhaseTransform *phase ) { +Node* AddPNode::Identity(PhaseGVN* phase) { return ( phase->type( in(Offset) )->higher_equal( TypeX_ZERO ) ) ? in(Address) : this; } @@ -659,7 +659,7 @@ const Type *AddPNode::bottom_type() const { } //------------------------------Value------------------------------------------ -const Type *AddPNode::Value( PhaseTransform *phase ) const { +const Type* AddPNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(Address) ); const Type *t2 = phase->type( in(Offset) ); @@ -733,7 +733,7 @@ uint AddPNode::match_edge(uint idx) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *OrINode::Identity( PhaseTransform *phase ) { +Node* OrINode::Identity(PhaseGVN* phase) { // x | x => x if (phase->eqv(in(1), in(2))) { return in(1); @@ -774,7 +774,7 @@ const Type *OrINode::add_ring( const Type *t0, const Type *t1 ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *OrLNode::Identity( PhaseTransform *phase ) { +Node* OrLNode::Identity(PhaseGVN* phase) { // x | x => x if (phase->eqv(in(1), in(2))) { return in(1); diff --git a/hotspot/src/share/vm/opto/addnode.hpp b/hotspot/src/share/vm/opto/addnode.hpp index 31aed490e78..8166050d228 100644 --- a/hotspot/src/share/vm/opto/addnode.hpp +++ b/hotspot/src/share/vm/opto/addnode.hpp @@ -47,7 +47,7 @@ public: // Handle algebraic identities here. If we have an identity, return the Node // we are equivalent to. We look for "add of zero" as an identity. - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // We also canonicalize the Node, moving constants to the right input, // and flatten expressions (so that 1+x+2 becomes x+3). @@ -55,7 +55,7 @@ public: // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; // Check if this addition involves the additive identity virtual const Type *add_of_identity( const Type *t1, const Type *t2 ) const; @@ -80,7 +80,7 @@ public: virtual const Type *add_id() const { return TypeInt::ZERO; } virtual const Type *bottom_type() const { return TypeInt::INT; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegI; } }; @@ -94,7 +94,7 @@ public: virtual const Type *add_id() const { return TypeLong::ZERO; } virtual const Type *bottom_type() const { return TypeLong::LONG; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegL; } }; @@ -109,7 +109,7 @@ public: virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeF::ZERO; } virtual const Type *bottom_type() const { return Type::FLOAT; } - virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint ideal_reg() const { return Op_RegF; } }; @@ -124,7 +124,7 @@ public: virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeD::ZERO; } virtual const Type *bottom_type() const { return Type::DOUBLE; } - virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint ideal_reg() const { return Op_RegD; } }; @@ -142,9 +142,9 @@ public: init_class_id(Class_AddP); } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const; virtual uint ideal_reg() const { return Op_RegP; } Node *base_node() { assert( req() > Base, "Missing base"); return in(Base); } @@ -170,7 +170,7 @@ public: virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeInt::ZERO; } virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegI; } }; @@ -184,7 +184,7 @@ public: virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeLong::ZERO; } virtual const Type *bottom_type() const { return TypeLong::LONG; } - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegL; } }; diff --git a/hotspot/src/share/vm/opto/arraycopynode.hpp b/hotspot/src/share/vm/opto/arraycopynode.hpp index 468b5463da2..e17ea1f57a6 100644 --- a/hotspot/src/share/vm/opto/arraycopynode.hpp +++ b/hotspot/src/share/vm/opto/arraycopynode.hpp @@ -89,7 +89,6 @@ private: static const TypePtr* get_address_type(PhaseGVN *phase, Node* n); Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count); - Node* conv_I2X_offset(PhaseGVN *phase, Node* offset, const TypeAryPtr* ary_t); bool prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest, BasicType& copy_type, const Type*& value_type, bool& disjoint_bases); diff --git a/hotspot/src/share/vm/opto/block.hpp b/hotspot/src/share/vm/opto/block.hpp index d84529deb88..d7aa5bb783c 100644 --- a/hotspot/src/share/vm/opto/block.hpp +++ b/hotspot/src/share/vm/opto/block.hpp @@ -499,6 +499,7 @@ class PhaseCFG : public Phase { void convert_NeverBranch_to_Goto(Block *b); CFGLoop* create_loop_tree(); + bool is_dominator(Node* dom_node, Node* node); #ifndef PRODUCT bool _trace_opto_pipelining; // tracing flag diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index ffdf6a2d29b..ffe97dc68ab 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -49,7 +49,7 @@ uint StartNode::size_of() const { return sizeof(*this); } uint StartNode::cmp( const Node &n ) const { return _domain == ((StartNode&)n)._domain; } const Type *StartNode::bottom_type() const { return _domain; } -const Type *StartNode::Value(PhaseTransform *phase) const { return _domain; } +const Type* StartNode::Value(PhaseGVN* phase) const { return _domain; } #ifndef PRODUCT void StartNode::dump_spec(outputStream *st) const { st->print(" #"); _domain->dump_on(st);} void StartNode::dump_compact_spec(outputStream *st) const { /* empty */ } @@ -173,7 +173,7 @@ Node *ReturnNode::Ideal(PhaseGVN *phase, bool can_reshape){ return remove_dead_region(phase, can_reshape) ? this : NULL; } -const Type *ReturnNode::Value( PhaseTransform *phase ) const { +const Type* ReturnNode::Value(PhaseGVN* phase) const { return ( phase->type(in(TypeFunc::Control)) == Type::TOP) ? Type::TOP : Type::BOTTOM; @@ -218,7 +218,7 @@ Node *RethrowNode::Ideal(PhaseGVN *phase, bool can_reshape){ return remove_dead_region(phase, can_reshape) ? this : NULL; } -const Type *RethrowNode::Value( PhaseTransform *phase ) const { +const Type* RethrowNode::Value(PhaseGVN* phase) const { return (phase->type(in(TypeFunc::Control)) == Type::TOP) ? Type::TOP : Type::BOTTOM; @@ -685,7 +685,7 @@ void CallNode::dump_spec(outputStream *st) const { #endif const Type *CallNode::bottom_type() const { return tf()->range(); } -const Type *CallNode::Value(PhaseTransform *phase) const { +const Type* CallNode::Value(PhaseGVN* phase) const { if (phase->type(in(0)) == Type::TOP) return Type::TOP; return tf()->range(); } @@ -1133,7 +1133,7 @@ Node *SafePointNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Remove obviously duplicate safepoints -Node *SafePointNode::Identity( PhaseTransform *phase ) { +Node* SafePointNode::Identity(PhaseGVN* phase) { // If you have back to back safepoints, remove one if( in(TypeFunc::Control)->is_SafePoint() ) @@ -1156,7 +1156,7 @@ Node *SafePointNode::Identity( PhaseTransform *phase ) { } //------------------------------Value------------------------------------------ -const Type *SafePointNode::Value( PhaseTransform *phase ) const { +const Type* SafePointNode::Value(PhaseGVN* phase) const { if( phase->type(in(0)) == Type::TOP ) return Type::TOP; if( phase->eqv( in(0), this ) ) return Type::TOP; // Dead infinite loop return Type::CONTROL; diff --git a/hotspot/src/share/vm/opto/callnode.hpp b/hotspot/src/share/vm/opto/callnode.hpp index cbe9d90e773..00979ae0d53 100644 --- a/hotspot/src/share/vm/opto/callnode.hpp +++ b/hotspot/src/share/vm/opto/callnode.hpp @@ -76,7 +76,7 @@ public: virtual bool pinned() const { return true; }; virtual const Type *bottom_type() const; virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual void calling_convention( BasicType* sig_bt, VMRegPair *parm_reg, uint length ) const; virtual const RegMask &in_RegMask(uint) const; @@ -127,7 +127,7 @@ public: virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash virtual bool depends_only_on_test() const { return false; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return NotAMachineReg; } virtual uint match_edge(uint idx) const; #ifndef PRODUCT @@ -148,7 +148,7 @@ class RethrowNode : public Node { virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash virtual bool depends_only_on_test() const { return false; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return NotAMachineReg; } #ifndef PRODUCT @@ -465,11 +465,11 @@ public: // Standard Node stuff virtual int Opcode() const; virtual bool pinned() const { return true; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return Type::CONTROL; } virtual const TypePtr *adr_type() const { return _adr_type; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return 0; } virtual const RegMask &in_RegMask(uint) const; virtual const RegMask &out_RegMask() const; @@ -593,9 +593,9 @@ public: void set_generator(CallGenerator* cg) { _generator = cg; } virtual const Type *bottom_type() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node* Identity(PhaseGVN* phase) { return this; } virtual uint cmp( const Node &n ) const; virtual uint size_of() const = 0; virtual void calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const; diff --git a/hotspot/src/share/vm/opto/castnode.cpp b/hotspot/src/share/vm/opto/castnode.cpp index 44ea7c39814..634ec0bcc1b 100644 --- a/hotspot/src/share/vm/opto/castnode.cpp +++ b/hotspot/src/share/vm/opto/castnode.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "opto/addnode.hpp" +#include "opto/callnode.hpp" #include "opto/castnode.hpp" #include "opto/connode.hpp" #include "opto/matcher.hpp" @@ -33,14 +34,22 @@ //============================================================================= // If input is already higher or equal to cast type, then this is an identity. -Node *ConstraintCastNode::Identity( PhaseTransform *phase ) { +Node* ConstraintCastNode::Identity(PhaseGVN* phase) { + Node* dom = dominating_cast(phase); + if (dom != NULL) { + assert(_carry_dependency, "only for casts that carry a dependency"); + return dom; + } + if (_carry_dependency) { + return this; + } return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this; } //------------------------------Value------------------------------------------ // Take 'join' of input and cast-up type -const Type *ConstraintCastNode::Value( PhaseTransform *phase ) const { - if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP; +const Type* ConstraintCastNode::Value(PhaseGVN* phase) const { + if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; const Type* ft = phase->type(in(1))->filter_speculative(_type); #ifdef ASSERT @@ -69,26 +78,78 @@ const Type *ConstraintCastNode::Value( PhaseTransform *phase ) const { //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies -Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape){ +Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape) { return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; } -uint CastIINode::size_of() const { +uint ConstraintCastNode::cmp(const Node &n) const { + return TypeNode::cmp(n) && ((ConstraintCastNode&)n)._carry_dependency == _carry_dependency; +} + +uint ConstraintCastNode::size_of() const { return sizeof(*this); } -uint CastIINode::cmp(const Node &n) const { - return TypeNode::cmp(n) && ((CastIINode&)n)._carry_dependency == _carry_dependency; -} - -Node *CastIINode::Identity(PhaseTransform *phase) { - if (_carry_dependency) { - return this; +Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency) { + switch(opcode) { + case Op_CastII: { + Node* cast = new CastIINode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; } - return ConstraintCastNode::Identity(phase); + case Op_CastPP: { + Node* cast = new CastPPNode(n, t, carry_dependency); + cast->set_req(0, c); + return cast; + } + case Op_CheckCastPP: return new CheckCastPPNode(c, n, t, carry_dependency); + default: + fatal("Bad opcode %d", opcode); + } + return NULL; } -const Type *CastIINode::Value(PhaseTransform *phase) const { +TypeNode* ConstraintCastNode::dominating_cast(PhaseTransform *phase) const { + if (!carry_dependency()) { + return NULL; + } + Node* val = in(1); + Node* ctl = in(0); + int opc = Opcode(); + if (ctl == NULL) { + return NULL; + } + for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) { + Node* u = val->fast_out(i); + if (u != this && + u->Opcode() == opc && + u->in(0) != NULL && + u->bottom_type()->higher_equal(type())) { + if (phase->is_dominator(u->in(0), ctl)) { + return u->as_Type(); + } + if (is_CheckCastPP() && u->in(1)->is_Proj() && u->in(1)->in(0)->is_Allocate() && + u->in(0)->is_Proj() && u->in(0)->in(0)->is_Initialize() && + u->in(1)->in(0)->as_Allocate()->initialization() == u->in(0)->in(0)) { + // CheckCastPP following an allocation always dominates all + // use of the allocation result + return u->as_Type(); + } + } + } + return NULL; +} + +#ifndef PRODUCT +void ConstraintCastNode::dump_spec(outputStream *st) const { + TypeNode::dump_spec(st); + if (_carry_dependency) { + st->print(" carry dependency"); + } +} +#endif + +const Type* CastIINode::Value(PhaseGVN* phase) const { const Type *res = ConstraintCastNode::Value(phase); // Try to improve the type of the CastII if we recognize a CmpI/If @@ -154,11 +215,81 @@ const Type *CastIINode::Value(PhaseTransform *phase) const { return res; } +Node *CastIINode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* progress = ConstraintCastNode::Ideal(phase, can_reshape); + if (progress != NULL) { + return progress; + } + + // transform: + // (CastII (AddI x const)) -> (AddI (CastII x) const) + // So the AddI has a chance to be optimized out + if (in(1)->Opcode() == Op_AddI) { + Node* in2 = in(1)->in(2); + const TypeInt* in2_t = phase->type(in2)->isa_int(); + if (in2_t != NULL && in2_t->singleton()) { + int in2_const = in2_t->_lo; + const TypeInt* current_type = _type->is_int(); + jlong new_lo_long = ((jlong)current_type->_lo) - in2_const; + jlong new_hi_long = ((jlong)current_type->_hi) - in2_const; + int new_lo = (int)new_lo_long; + int new_hi = (int)new_hi_long; + if (((jlong)new_lo) == new_lo_long && ((jlong)new_hi) == new_hi_long) { + Node* in1 = in(1)->in(1); + CastIINode* new_cast = (CastIINode*)clone(); + AddINode* new_add = (AddINode*)in(1)->clone(); + new_cast->set_type(TypeInt::make(new_lo, new_hi, current_type->_widen)); + new_cast->set_req(1, in1); + new_add->set_req(1, phase->transform(new_cast)); + return new_add; + } + } + } + // Similar to ConvI2LNode::Ideal() for the same reasons + if (can_reshape && !phase->C->major_progress()) { + const TypeInt* this_type = this->type()->is_int(); + const TypeInt* in_type = phase->type(in(1))->isa_int(); + if (in_type != NULL && this_type != NULL && + (in_type->_lo != this_type->_lo || + in_type->_hi != this_type->_hi)) { + int lo1 = this_type->_lo; + int hi1 = this_type->_hi; + int w1 = this_type->_widen; + + if (lo1 >= 0) { + // Keep a range assertion of >=0. + lo1 = 0; hi1 = max_jint; + } else if (hi1 < 0) { + // Keep a range assertion of <0. + lo1 = min_jint; hi1 = -1; + } else { + lo1 = min_jint; hi1 = max_jint; + } + const TypeInt* wtype = TypeInt::make(MAX2(in_type->_lo, lo1), + MIN2(in_type->_hi, hi1), + MAX2((int)in_type->_widen, w1)); + if (wtype != type()) { + set_type(wtype); + return this; + } + } + } + return NULL; +} + +uint CastIINode::cmp(const Node &n) const { + return ConstraintCastNode::cmp(n) && ((CastIINode&)n)._range_check_dependency == _range_check_dependency; +} + +uint CastIINode::size_of() const { + return sizeof(*this); +} + #ifndef PRODUCT -void CastIINode::dump_spec(outputStream *st) const { - TypeNode::dump_spec(st); - if (_carry_dependency) { - st->print(" carry dependency"); +void CastIINode::dump_spec(outputStream* st) const { + ConstraintCastNode::dump_spec(st); + if (_range_check_dependency) { + st->print(" range check dependency"); } } #endif @@ -166,7 +297,15 @@ void CastIINode::dump_spec(outputStream *st) const { //============================================================================= //------------------------------Identity--------------------------------------- // If input is already higher or equal to cast type, then this is an identity. -Node *CheckCastPPNode::Identity( PhaseTransform *phase ) { +Node* CheckCastPPNode::Identity(PhaseGVN* phase) { + Node* dom = dominating_cast(phase); + if (dom != NULL) { + assert(_carry_dependency, "only for casts that carry a dependency"); + return dom; + } + if (_carry_dependency) { + return this; + } // Toned down to rescue meeting at a Phi 3 different oops all implementing // the same interface. CompileTheWorld starting at 502, kd12rc1.zip. return (phase->type(in(1)) == phase->type(this)) ? in(1) : this; @@ -174,7 +313,7 @@ Node *CheckCastPPNode::Identity( PhaseTransform *phase ) { //------------------------------Value------------------------------------------ // Take 'join' of input and cast-up type, unless working with an Interface -const Type *CheckCastPPNode::Value( PhaseTransform *phase ) const { +const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP; const Type *inn = phase->type(in(1)); @@ -255,16 +394,9 @@ const Type *CheckCastPPNode::Value( PhaseTransform *phase ) const { // return join; } -//------------------------------Ideal------------------------------------------ -// Return a node which is more "ideal" than the current node. Strip out -// control copies -Node *CheckCastPPNode::Ideal(PhaseGVN *phase, bool can_reshape){ - return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; -} - //============================================================================= //------------------------------Value------------------------------------------ -const Type *CastX2PNode::Value( PhaseTransform *phase ) const { +const Type* CastX2PNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; if (t->base() == Type_X && t->singleton()) { @@ -328,14 +460,14 @@ Node *CastX2PNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Identity--------------------------------------- -Node *CastX2PNode::Identity( PhaseTransform *phase ) { +Node* CastX2PNode::Identity(PhaseGVN* phase) { if (in(1)->Opcode() == Op_CastP2X) return in(1)->in(1); return this; } //============================================================================= //------------------------------Value------------------------------------------ -const Type *CastP2XNode::Value( PhaseTransform *phase ) const { +const Type* CastP2XNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; if (t->base() == Type::RawPtr && t->singleton()) { @@ -350,7 +482,7 @@ Node *CastP2XNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Identity--------------------------------------- -Node *CastP2XNode::Identity( PhaseTransform *phase ) { +Node* CastP2XNode::Identity(PhaseGVN* phase) { if (in(1)->Opcode() == Op_CastX2P) return in(1)->in(1); return this; } diff --git a/hotspot/src/share/vm/opto/castnode.hpp b/hotspot/src/share/vm/opto/castnode.hpp index 535b0e6610b..9c2dfcb2e9d 100644 --- a/hotspot/src/share/vm/opto/castnode.hpp +++ b/hotspot/src/share/vm/opto/castnode.hpp @@ -32,38 +32,62 @@ //------------------------------ConstraintCastNode----------------------------- // cast to a different range class ConstraintCastNode: public TypeNode { + protected: + // Can this node be removed post CCP or does it carry a required dependency? + const bool _carry_dependency; + virtual uint cmp( const Node &n ) const; + virtual uint size_of() const; + public: - ConstraintCastNode (Node *n, const Type *t ): TypeNode(t,2) { + ConstraintCastNode(Node *n, const Type *t, bool carry_dependency) + : TypeNode(t,2), _carry_dependency(carry_dependency) { init_class_id(Class_ConstraintCast); init_req(1, n); } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual int Opcode() const; virtual uint ideal_reg() const = 0; + virtual bool depends_only_on_test() const { return !_carry_dependency; } + bool carry_dependency() const { return _carry_dependency; } + TypeNode* dominating_cast(PhaseTransform *phase) const; + static Node* make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency); + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif }; //------------------------------CastIINode------------------------------------- // cast integer to integer (different range) class CastIINode: public ConstraintCastNode { - private: - // Can this node be removed post CCP or does it carry a required dependency? - const bool _carry_dependency; - protected: - virtual uint cmp( const Node &n ) const; + // Is this node dependent on a range check? + const bool _range_check_dependency; + virtual uint cmp(const Node &n) const; virtual uint size_of() const; public: - CastIINode(Node *n, const Type *t, bool carry_dependency = false) - : ConstraintCastNode(n,t), _carry_dependency(carry_dependency) {} + CastIINode(Node* n, const Type* t, bool carry_dependency = false, bool range_check_dependency = false) + : ConstraintCastNode(n, t, carry_dependency), _range_check_dependency(range_check_dependency) { + init_class_id(Class_CastII); + } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + const bool has_range_check() { +#ifdef _LP64 + return _range_check_dependency; +#else + assert(!_range_check_dependency, "Should not have range check dependency"); + return false; +#endif + } + #ifndef PRODUCT - virtual void dump_spec(outputStream *st) const; + virtual void dump_spec(outputStream* st) const; #endif }; @@ -71,24 +95,25 @@ class CastIINode: public ConstraintCastNode { // cast pointer to pointer (different type) class CastPPNode: public ConstraintCastNode { public: - CastPPNode (Node *n, const Type *t ): ConstraintCastNode(n, t) {} + CastPPNode (Node *n, const Type *t, bool carry_dependency = false) + : ConstraintCastNode(n, t, carry_dependency) { + } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } }; //------------------------------CheckCastPPNode-------------------------------- // for _checkcast, cast pointer to pointer (different type), without JOIN, -class CheckCastPPNode: public TypeNode { +class CheckCastPPNode: public ConstraintCastNode { public: - CheckCastPPNode( Node *c, Node *n, const Type *t ) : TypeNode(t,2) { + CheckCastPPNode(Node *c, Node *n, const Type *t, bool carry_dependency = false) + : ConstraintCastNode(n, t, carry_dependency) { init_class_id(Class_CheckCastPP); init_req(0, c); - init_req(1, n); } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } }; @@ -100,9 +125,9 @@ class CastX2PNode : public Node { public: CastX2PNode( Node *n ) : Node(NULL, n) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegP; } virtual const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } }; @@ -114,9 +139,9 @@ class CastP2XNode : public Node { public: CastP2XNode( Node *ctrl, Node *n ) : Node(ctrl, n) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegX; } virtual const Type *bottom_type() const { return TypeX_X; } // Return false to keep node from moving away from an associated card mark. diff --git a/hotspot/src/share/vm/opto/cfgnode.cpp b/hotspot/src/share/vm/opto/cfgnode.cpp index 454a29636a6..12aa7c7d6b4 100644 --- a/hotspot/src/share/vm/opto/cfgnode.cpp +++ b/hotspot/src/share/vm/opto/cfgnode.cpp @@ -27,6 +27,7 @@ #include "memory/allocation.inline.hpp" #include "oops/objArrayKlass.hpp" #include "opto/addnode.hpp" +#include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" @@ -47,7 +48,7 @@ //============================================================================= //------------------------------Value------------------------------------------ // Compute the type of the RegionNode. -const Type *RegionNode::Value( PhaseTransform *phase ) const { +const Type* RegionNode::Value(PhaseGVN* phase) const { for( uint i=1; itype(in(1)) : Type::TOP; @@ -902,24 +903,35 @@ const Type *PhiNode::Value( PhaseTransform *phase ) const { return Type::TOP; // Check for trip-counted loop. If so, be smarter. - CountedLoopNode *l = r->is_CountedLoop() ? r->as_CountedLoop() : NULL; - if( l && l->can_be_counted_loop(phase) && - ((const Node*)l->phi() == this) ) { // Trip counted loop! + CountedLoopNode* l = r->is_CountedLoop() ? r->as_CountedLoop() : NULL; + if (l && ((const Node*)l->phi() == this)) { // Trip counted loop! // protect against init_trip() or limit() returning NULL - const Node *init = l->init_trip(); - const Node *limit = l->limit(); - if( init != NULL && limit != NULL && l->stride_is_con() ) { - const TypeInt *lo = init ->bottom_type()->isa_int(); - const TypeInt *hi = limit->bottom_type()->isa_int(); - if( lo && hi ) { // Dying loops might have TOP here - int stride = l->stride_con(); - if( stride < 0 ) { // Down-counter loop - const TypeInt *tmp = lo; lo = hi; hi = tmp; - stride = -stride; + if (l->can_be_counted_loop(phase)) { + const Node *init = l->init_trip(); + const Node *limit = l->limit(); + const Node* stride = l->stride(); + if (init != NULL && limit != NULL && stride != NULL) { + const TypeInt* lo = phase->type(init)->isa_int(); + const TypeInt* hi = phase->type(limit)->isa_int(); + const TypeInt* stride_t = phase->type(stride)->isa_int(); + if (lo != NULL && hi != NULL && stride_t != NULL) { // Dying loops might have TOP here + assert(stride_t->_hi >= stride_t->_lo, "bad stride type"); + if (stride_t->_hi < 0) { // Down-counter loop + swap(lo, hi); + return TypeInt::make(MIN2(lo->_lo, hi->_lo) , hi->_hi, 3); + } else if (stride_t->_lo >= 0) { + return TypeInt::make(lo->_lo, MAX2(lo->_hi, hi->_hi), 3); + } } - if( lo->_hi < hi->_lo ) // Reversed endpoints are well defined :-( - return TypeInt::make(lo->_lo,hi->_hi,3); } + } else if (l->in(LoopNode::LoopBackControl) != NULL && + in(LoopNode::EntryControl) != NULL && + phase->type(l->in(LoopNode::LoopBackControl)) == Type::TOP) { + // During CCP, if we saturate the type of a counted loop's Phi + // before the special code for counted loop above has a chance + // to run (that is as long as the type of the backedge's control + // is top), we might end up with non monotonic types + return phase->type(in(LoopNode::EntryControl)); } } @@ -1142,13 +1154,13 @@ Node* PhiNode::is_cmove_id(PhaseTransform* phase, int true_path) { //------------------------------Identity--------------------------------------- // Check for Region being Identity. -Node *PhiNode::Identity( PhaseTransform *phase ) { +Node* PhiNode::Identity(PhaseGVN* phase) { // Check for no merging going on // (There used to be special-case code here when this->region->is_Loop. // It would check for a tributary phi on the backedge that the main phi // trivially, perhaps with a single cast. The unique_input method // does all this and more, by reducing such tributaries to 'this'.) - Node* uin = unique_input(phase); + Node* uin = unique_input(phase, false); if (uin != NULL) { return uin; } @@ -1165,11 +1177,10 @@ Node *PhiNode::Identity( PhaseTransform *phase ) { //-----------------------------unique_input------------------------------------ // Find the unique value, discounting top, self-loops, and casts. // Return top if there are no inputs, and self if there are multiple. -Node* PhiNode::unique_input(PhaseTransform* phase) { - // 1) One unique direct input, or - // 2) some of the inputs have an intervening ConstraintCast and - // the type of input is the same or sharper (more specific) - // than the phi's type. +Node* PhiNode::unique_input(PhaseTransform* phase, bool uncast) { + // 1) One unique direct input, + // or if uncast is true: + // 2) some of the inputs have an intervening ConstraintCast // 3) an input is a self loop // // 1) input or 2) input or 3) input __ @@ -1180,8 +1191,7 @@ Node* PhiNode::unique_input(PhaseTransform* phase) { Node* r = in(0); // RegionNode if (r == NULL) return in(1); // Already degraded to a Copy - Node* uncasted_input = NULL; // The unique uncasted input (ConstraintCasts removed) - Node* direct_input = NULL; // The unique direct input + Node* input = NULL; // The unique direct input (maybe uncasted = ConstraintCasts removed) for (uint i = 1, cnt = req(); i < cnt; ++i) { Node* rc = r->in(i); @@ -1190,34 +1200,37 @@ Node* PhiNode::unique_input(PhaseTransform* phase) { Node* n = in(i); if (n == NULL) continue; - Node* un = n->uncast(); + Node* un = n; + if (uncast) { +#ifdef ASSERT + Node* m = un->uncast(); +#endif + while (un != NULL && un->req() == 2 && un->is_ConstraintCast()) { + Node* next = un->in(1); + if (phase->type(next)->isa_rawptr() && phase->type(un)->isa_oopptr()) { + // risk exposing raw ptr at safepoint + break; + } + un = next; + } + assert(m == un || un->in(1) == m, "Only expected at CheckCastPP from allocation"); + } if (un == NULL || un == this || phase->type(un) == Type::TOP) { continue; // ignore if top, or in(i) and "this" are in a data cycle } - // Check for a unique uncasted input - if (uncasted_input == NULL) { - uncasted_input = un; - } else if (uncasted_input != un) { - uncasted_input = NodeSentinel; // no unique uncasted input - } - // Check for a unique direct input - if (direct_input == NULL) { - direct_input = n; - } else if (direct_input != n) { - direct_input = NodeSentinel; // no unique direct input + // Check for a unique input (maybe uncasted) + if (input == NULL) { + input = un; + } else if (input != un) { + input = NodeSentinel; // no unique input } } - if (direct_input == NULL) { + if (input == NULL) { return phase->C->top(); // no inputs } - assert(uncasted_input != NULL,""); - if (direct_input != NodeSentinel) { - return direct_input; // one unique direct input - } - if (uncasted_input != NodeSentinel && - phase->type(uncasted_input)->higher_equal(type())) { - return uncasted_input; // one unique uncasted input + if (input != NodeSentinel) { + return input; // one unique direct input } // Nothing. @@ -1650,7 +1663,12 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { return top; } - Node* uin = unique_input(phase); + bool uncasted = false; + Node* uin = unique_input(phase, false); + if (uin == NULL) { + uncasted = true; + uin = unique_input(phase, true); + } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) // IGVN transformation return top; @@ -1683,6 +1701,31 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } + if (uncasted) { + const Type* phi_type = bottom_type(); + assert(phi_type->isa_int() || phi_type->isa_ptr(), "bad phi type"); + int opcode; + if (phi_type->isa_int()) { + opcode = Op_CastII; + } else { + const Type* uin_type = phase->type(uin); + if (phi_type->join(TypePtr::NOTNULL) == uin_type->join(TypePtr::NOTNULL)) { + opcode = Op_CastPP; + } else { + opcode = Op_CheckCastPP; + } + } + // Add a cast to carry the control dependency of the Phi that is + // going away + Node* cast = ConstraintCastNode::make_cast(opcode, r, uin, phi_type, true); + cast = phase->transform(cast); + // set all inputs to the new cast so the Phi is removed by Identity + for (uint i = 1; i < req(); i++) { + set_req(i, cast); + } + uin = cast; + } + // One unique input. debug_only(Node* ident = Identity(phase)); // The unique input must eventually be detected by the Identity call. @@ -1699,7 +1742,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { return NULL; } - Node* opt = NULL; int true_path = is_diamond_phi(); if( true_path != 0 ) { @@ -2048,13 +2090,13 @@ void PhiNode::dump_spec(outputStream *st) const { //============================================================================= -const Type *GotoNode::Value( PhaseTransform *phase ) const { +const Type* GotoNode::Value(PhaseGVN* phase) const { // If the input is reachable, then we are executed. // If the input is not reachable, then we are not executed. return phase->type(in(0)); } -Node *GotoNode::Identity( PhaseTransform *phase ) { +Node* GotoNode::Identity(PhaseGVN* phase) { return in(0); // Simple copy of incoming control } @@ -2116,7 +2158,7 @@ const Type *PCTableNode::bottom_type() const { //------------------------------Value------------------------------------------ // Compute the type of the PCTableNode. If reachable it is a tuple of // Control, otherwise the table targets are not reachable -const Type *PCTableNode::Value( PhaseTransform *phase ) const { +const Type* PCTableNode::Value(PhaseGVN* phase) const { if( phase->type(in(0)) == Type::CONTROL ) return bottom_type(); return Type::TOP; // All paths dead? Then so are we @@ -2161,7 +2203,7 @@ void JumpProjNode::related(GrowableArray *in_rel, GrowableArray *o //------------------------------Value------------------------------------------ // Check for being unreachable, or for coming from a Rethrow. Rethrow's cannot // have the default "fall_through_index" path. -const Type *CatchNode::Value( PhaseTransform *phase ) const { +const Type* CatchNode::Value(PhaseGVN* phase) const { // Unreachable? Then so are all paths from here. if( phase->type(in(0)) == Type::TOP ) return Type::TOP; // First assume all paths are reachable @@ -2205,7 +2247,7 @@ uint CatchProjNode::cmp( const Node &n ) const { //------------------------------Identity--------------------------------------- // If only 1 target is possible, choose it if it is the main control -Node *CatchProjNode::Identity( PhaseTransform *phase ) { +Node* CatchProjNode::Identity(PhaseGVN* phase) { // If my value is control and no other value is, then treat as ID const TypeTuple *t = phase->type(in(0))->is_tuple(); if (t->field_at(_con) != Type::CONTROL) return this; @@ -2248,7 +2290,7 @@ void CatchProjNode::dump_spec(outputStream *st) const { //============================================================================= //------------------------------Identity--------------------------------------- // Check for CreateEx being Identity. -Node *CreateExNode::Identity( PhaseTransform *phase ) { +Node* CreateExNode::Identity(PhaseGVN* phase) { if( phase->type(in(1)) == Type::TOP ) return in(1); if( phase->type(in(0)) == Type::TOP ) return in(0); // We only come from CatchProj, unless the CatchProj goes away. @@ -2264,7 +2306,7 @@ Node *CreateExNode::Identity( PhaseTransform *phase ) { //============================================================================= //------------------------------Value------------------------------------------ // Check for being unreachable. -const Type *NeverBranchNode::Value( PhaseTransform *phase ) const { +const Type* NeverBranchNode::Value(PhaseGVN* phase) const { if (!in(0) || in(0)->is_top()) return Type::TOP; return bottom_type(); } diff --git a/hotspot/src/share/vm/opto/cfgnode.hpp b/hotspot/src/share/vm/opto/cfgnode.hpp index a87a70a41e6..8651b672121 100644 --- a/hotspot/src/share/vm/opto/cfgnode.hpp +++ b/hotspot/src/share/vm/opto/cfgnode.hpp @@ -91,8 +91,8 @@ public: virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash virtual bool depends_only_on_test() const { return false; } virtual const Type *bottom_type() const { return Type::CONTROL; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const RegMask &out_RegMask() const; bool try_clean_mem_phi(PhaseGVN *phase); @@ -175,7 +175,14 @@ public: // Determine a unique non-trivial input, if any. // Ignore casts if it helps. Return NULL on failure. - Node* unique_input(PhaseTransform *phase); + Node* unique_input(PhaseTransform *phase, bool uncast); + Node* unique_input(PhaseTransform *phase) { + Node* uin = unique_input(phase, false); + if (uin == NULL) { + uin = unique_input(phase, true); + } + return uin; + } // Check for a simple dead loop. enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop }; @@ -198,8 +205,8 @@ public: type()->higher_equal(tp); } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const RegMask &out_RegMask() const; virtual const RegMask &in_RegMask(uint) const; @@ -227,8 +234,8 @@ public: virtual const Node *is_block_proj() const { return this; } virtual bool depends_only_on_test() const { return false; } virtual const Type *bottom_type() const { return Type::CONTROL; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual const RegMask &out_RegMask() const; #ifndef PRODUCT @@ -270,13 +277,6 @@ class IfNode : public MultiBranchNode { virtual uint size_of() const { return sizeof(*this); } private: - ProjNode* range_check_trap_proj() { - int flip_test = 0; - Node* l = NULL; - Node* r = NULL; - return range_check_trap_proj(flip_test, l, r); - } - // Helper methods for fold_compares bool cmpi_folds(PhaseIterGVN* igvn); bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn); @@ -377,7 +377,7 @@ public: virtual bool pinned() const { return true; } virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual int required_outcnt() const { return 2; } virtual const RegMask &out_RegMask() const; Node* fold_compares(PhaseIterGVN* phase); @@ -411,7 +411,7 @@ public: class IfProjNode : public CProjNode { public: IfProjNode(IfNode *ifnode, uint idx) : CProjNode(ifnode,idx) {} - virtual Node *Identity(PhaseTransform *phase); + virtual Node* Identity(PhaseGVN* phase); protected: // Type of If input when this branch is always taken @@ -465,7 +465,7 @@ public: init_req(1, idx); } virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *bottom_type() const; virtual bool pinned() const { return true; } @@ -525,7 +525,7 @@ public: init_class_id(Class_Catch); } virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; // CatchProjNode controls which exception handler is targetted after a call. @@ -553,7 +553,7 @@ public: } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual const Type *bottom_type() const { return Type::CONTROL; } int handler_bci() const { return _handler_bci; } bool is_handler_proj() const { return _handler_bci >= 0; } @@ -572,7 +572,7 @@ public: init_req(1, i_o); } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual bool pinned() const { return true; } uint match_edge(uint idx) const { return 0; } virtual uint ideal_reg() const { return Op_RegP; } @@ -588,7 +588,7 @@ public: virtual int Opcode() const; virtual bool pinned() const { return true; }; virtual const Type *bottom_type() const { return TypeTuple::IFBOTH; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual int required_outcnt() const { return 2; } virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } diff --git a/hotspot/src/share/vm/opto/classes.hpp b/hotspot/src/share/vm/opto/classes.hpp index 16ccc5817b6..e101a9768a2 100644 --- a/hotspot/src/share/vm/opto/classes.hpp +++ b/hotspot/src/share/vm/opto/classes.hpp @@ -112,7 +112,6 @@ macro(ConvI2L) macro(ConvL2D) macro(ConvL2F) macro(ConvL2I) -macro(CosD) macro(CountedLoop) macro(CountedLoopEnd) macro(CountLeadingZerosI) @@ -229,7 +228,6 @@ macro(RoundFloat) macro(SafePoint) macro(SafePointScalarObject) macro(SCMemProj) -macro(SinD) macro(SqrtD) macro(Start) macro(StartOSR) diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 41483faf907..bf5597753b6 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -38,6 +38,7 @@ #include "opto/c2compiler.hpp" #include "opto/callGenerator.hpp" #include "opto/callnode.hpp" +#include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/chaitin.hpp" #include "opto/compile.hpp" @@ -402,6 +403,13 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) { remove_macro_node(n); } } + // Remove useless CastII nodes with range check dependency + for (int i = range_check_cast_count() - 1; i >= 0; i--) { + Node* cast = range_check_cast_node(i); + if (!useful.member(cast)) { + remove_range_check_cast(cast); + } + } // Remove useless expensive node for (int i = C->expensive_count()-1; i >= 0; i--) { Node* n = C->expensive_node(i); @@ -926,7 +934,6 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr compiler, has_unsafe_access(), SharedRuntime::is_wide_vector(max_vector_size()), - _directive, rtm_state() ); @@ -1178,6 +1185,7 @@ void Compile::Init(int aliaslevel) { _macro_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); _predicate_opaqs = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); _expensive_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); + _range_check_casts = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); register_library_intrinsics(); } @@ -1924,6 +1932,22 @@ void Compile::cleanup_loop_predicates(PhaseIterGVN &igvn) { assert(predicate_count()==0, "should be clean!"); } +void Compile::add_range_check_cast(Node* n) { + assert(n->isa_CastII()->has_range_check(), "CastII should have range check dependency"); + assert(!_range_check_casts->contains(n), "duplicate entry in range check casts"); + _range_check_casts->append(n); +} + +// Remove all range check dependent CastIINodes. +void Compile::remove_range_check_casts(PhaseIterGVN &igvn) { + for (int i = range_check_cast_count(); i > 0; i--) { + Node* cast = range_check_cast_node(i-1); + assert(cast->isa_CastII()->has_range_check(), "CastII should have range check dependency"); + igvn.replace_node(cast, cast->in(1)); + } + assert(range_check_cast_count() == 0, "should be empty"); +} + // StringOpts and late inlining of string methods void Compile::inline_string_calls(bool parse_time) { { @@ -2284,6 +2308,12 @@ void Compile::Optimize() { PhaseIdealLoop::verify(igvn); } + if (range_check_cast_count() > 0) { + // No more loop optimizations. Remove all range check dependent CastIINodes. + C->remove_range_check_casts(igvn); + igvn.optimize(); + } + { TracePhase tp("macroExpand", &timers[_t_macroExpand]); PhaseMacroExpand mex(igvn); @@ -2296,17 +2326,17 @@ void Compile::Optimize() { DEBUG_ONLY( _modified_nodes = NULL; ) } // (End scope of igvn; run destructor if necessary for asserts.) - process_print_inlining(); - // A method with only infinite loops has no edges entering loops from root - { - TracePhase tp("graphReshape", &timers[_t_graphReshaping]); - if (final_graph_reshaping()) { - assert(failing(), "must bail out w/ explicit message"); - return; - } - } + process_print_inlining(); + // A method with only infinite loops has no edges entering loops from root + { + TracePhase tp("graphReshape", &timers[_t_graphReshaping]); + if (final_graph_reshaping()) { + assert(failing(), "must bail out w/ explicit message"); + return; + } + } - print_method(PHASE_OPTIMIZE_FINISHED, 2); + print_method(PHASE_OPTIMIZE_FINISHED, 2); } @@ -2874,7 +2904,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { Node* use = m->fast_out(i); if (use->is_Mem() || use->is_EncodeNarrowPtr()) { use->ensure_control_or_add_prec(n->in(0)); - } else if (use->in(0) == NULL) { + } else { switch(use->Opcode()) { case Op_AddP: case Op_DecodeN: @@ -3087,6 +3117,16 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { #endif +#ifdef ASSERT + case Op_CastII: + // Verify that all range check dependent CastII nodes were removed. + if (n->isa_CastII()->has_range_check()) { + n->dump(3); + assert(false, "Range check dependent CastII node was not removed"); + } + break; +#endif + case Op_ModI: if (UseDivMod) { // Check if a%b and a/b both exist @@ -3962,7 +4002,7 @@ int Compile::static_subtype_check(ciKlass* superk, ciKlass* subk) { return SSC_full_test; } -Node* Compile::conv_I2X_index(PhaseGVN *phase, Node* idx, const TypeInt* sizetype) { +Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetype, Node* ctrl) { #ifdef _LP64 // The scaled index operand to AddP must be a clean 64-bit value. // Java allows a 32-bit int to be incremented to a negative @@ -3975,13 +4015,31 @@ Node* Compile::conv_I2X_index(PhaseGVN *phase, Node* idx, const TypeInt* sizetyp // number. (The prior range check has ensured this.) // This assertion is used by ConvI2LNode::Ideal. int index_max = max_jint - 1; // array size is max_jint, index is one less - if (sizetype != NULL) index_max = sizetype->_hi - 1; - const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); - idx = phase->transform(new ConvI2LNode(idx, lidxtype)); + if (sizetype != NULL) index_max = sizetype->_hi - 1; + const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax); + idx = constrained_convI2L(phase, idx, iidxtype, ctrl); #endif return idx; } +// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check) +Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl) { + if (ctrl != NULL) { + // Express control dependency by a CastII node with a narrow type. + value = new CastIINode(value, itype, false, true /* range check dependency */); + // Make the CastII node dependent on the control input to prevent the narrowed ConvI2L + // node from floating above the range check during loop optimizations. Otherwise, the + // ConvI2L node may be eliminated independently of the range check, causing the data path + // to become TOP while the control path is still there (although it's unreachable). + value->set_req(0, ctrl); + // Save CastII node to remove it after loop optimizations. + phase->C->add_range_check_cast(value); + value = phase->transform(value); + } + const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen); + return phase->transform(new ConvI2LNode(value, ltype)); +} + // The message about the current inlining is accumulated in // _print_inlining_stream and transfered into the _print_inlining_list // once we know whether inlining succeeds or not. For regular diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp index e012ea660f1..871abe245a7 100644 --- a/hotspot/src/share/vm/opto/compile.hpp +++ b/hotspot/src/share/vm/opto/compile.hpp @@ -400,6 +400,7 @@ class Compile : public Phase { GrowableArray* _macro_nodes; // List of nodes which need to be expanded before matching. GrowableArray* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates. GrowableArray* _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common + GrowableArray* _range_check_casts; // List of CastII nodes with a range check dependency ConnectionGraph* _congraph; #ifndef PRODUCT IdealGraphPrinter* _printer; @@ -753,7 +754,7 @@ class Compile : public Phase { void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); - assert(!_macro_nodes->contains(n), " duplicate entry in expand list"); + assert(!_macro_nodes->contains(n), "duplicate entry in expand list"); _macro_nodes->append(n); } void remove_macro_node(Node * n) { @@ -773,10 +774,23 @@ class Compile : public Phase { } } void add_predicate_opaq(Node * n) { - assert(!_predicate_opaqs->contains(n), " duplicate entry in predicate opaque1"); + assert(!_predicate_opaqs->contains(n), "duplicate entry in predicate opaque1"); assert(_macro_nodes->contains(n), "should have already been in macro list"); _predicate_opaqs->append(n); } + + // Range check dependent CastII nodes that can be removed after loop optimizations + void add_range_check_cast(Node* n); + void remove_range_check_cast(Node* n) { + if (_range_check_casts->contains(n)) { + _range_check_casts->remove(n); + } + } + Node* range_check_cast_node(int idx) const { return _range_check_casts->at(idx); } + int range_check_cast_count() const { return _range_check_casts->length(); } + // Remove all range check dependent CastIINodes. + void remove_range_check_casts(PhaseIterGVN &igvn); + // remove the opaque nodes that protect the predicates so that the unused checks and // uncommon traps will be eliminated from the graph. void cleanup_loop_predicates(PhaseIterGVN &igvn); @@ -1292,7 +1306,12 @@ class Compile : public Phase { enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; int static_subtype_check(ciKlass* superk, ciKlass* subk); - static Node* conv_I2X_index(PhaseGVN *phase, Node* offset, const TypeInt* sizetype); + static Node* conv_I2X_index(PhaseGVN* phase, Node* offset, const TypeInt* sizetype, + // Optional control dependency (for example, on range check) + Node* ctrl = NULL); + + // Convert integer value to a narrowed long type dependent on ctrl (for example, a range check) + static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl); // Auxiliary method for randomized fuzzing/stressing static bool randomized_select(int count); diff --git a/hotspot/src/share/vm/opto/convertnode.cpp b/hotspot/src/share/vm/opto/convertnode.cpp index 63a391abc97..bc111ccae7a 100644 --- a/hotspot/src/share/vm/opto/convertnode.cpp +++ b/hotspot/src/share/vm/opto/convertnode.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "opto/addnode.hpp" +#include "opto/castnode.hpp" #include "opto/convertnode.hpp" #include "opto/matcher.hpp" #include "opto/phaseX.hpp" @@ -32,7 +33,7 @@ //============================================================================= //------------------------------Identity--------------------------------------- -Node *Conv2BNode::Identity( PhaseTransform *phase ) { +Node* Conv2BNode::Identity(PhaseGVN* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); if( t == TypeInt::ZERO ) return in(1); @@ -42,7 +43,7 @@ Node *Conv2BNode::Identity( PhaseTransform *phase ) { } //------------------------------Value------------------------------------------ -const Type *Conv2BNode::Value( PhaseTransform *phase ) const { +const Type* Conv2BNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == TypeInt::ZERO ) return TypeInt::ZERO; @@ -64,7 +65,7 @@ const Type *Conv2BNode::Value( PhaseTransform *phase ) const { // The conversions operations are all Alpha sorted. Please keep it that way! //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvD2FNode::Value( PhaseTransform *phase ) const { +const Type* ConvD2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::DOUBLE ) return Type::FLOAT; @@ -75,13 +76,13 @@ const Type *ConvD2FNode::Value( PhaseTransform *phase ) const { //------------------------------Identity--------------------------------------- // Float's can be converted to doubles with no loss of bits. Hence // converting a float to a double and back to a float is a NOP. -Node *ConvD2FNode::Identity(PhaseTransform *phase) { +Node* ConvD2FNode::Identity(PhaseGVN* phase) { return (in(1)->Opcode() == Op_ConvF2D) ? in(1)->in(1) : this; } //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvD2INode::Value( PhaseTransform *phase ) const { +const Type* ConvD2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::DOUBLE ) return TypeInt::INT; @@ -100,13 +101,13 @@ Node *ConvD2INode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Int's can be converted to doubles with no loss of bits. Hence // converting an integer to a double and back to an integer is a NOP. -Node *ConvD2INode::Identity(PhaseTransform *phase) { +Node* ConvD2INode::Identity(PhaseGVN* phase) { return (in(1)->Opcode() == Op_ConvI2D) ? in(1)->in(1) : this; } //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvD2LNode::Value( PhaseTransform *phase ) const { +const Type* ConvD2LNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::DOUBLE ) return TypeLong::LONG; @@ -115,7 +116,7 @@ const Type *ConvD2LNode::Value( PhaseTransform *phase ) const { } //------------------------------Identity--------------------------------------- -Node *ConvD2LNode::Identity(PhaseTransform *phase) { +Node* ConvD2LNode::Identity(PhaseGVN* phase) { // Remove ConvD2L->ConvL2D->ConvD2L sequences. if( in(1) ->Opcode() == Op_ConvL2D && in(1)->in(1)->Opcode() == Op_ConvD2L ) @@ -133,7 +134,7 @@ Node *ConvD2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvF2DNode::Value( PhaseTransform *phase ) const { +const Type* ConvF2DNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::FLOAT ) return Type::DOUBLE; @@ -143,7 +144,7 @@ const Type *ConvF2DNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvF2INode::Value( PhaseTransform *phase ) const { +const Type* ConvF2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::FLOAT ) return TypeInt::INT; @@ -152,7 +153,7 @@ const Type *ConvF2INode::Value( PhaseTransform *phase ) const { } //------------------------------Identity--------------------------------------- -Node *ConvF2INode::Identity(PhaseTransform *phase) { +Node* ConvF2INode::Identity(PhaseGVN* phase) { // Remove ConvF2I->ConvI2F->ConvF2I sequences. if( in(1) ->Opcode() == Op_ConvI2F && in(1)->in(1)->Opcode() == Op_ConvF2I ) @@ -170,7 +171,7 @@ Node *ConvF2INode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvF2LNode::Value( PhaseTransform *phase ) const { +const Type* ConvF2LNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::FLOAT ) return TypeLong::LONG; @@ -179,7 +180,7 @@ const Type *ConvF2LNode::Value( PhaseTransform *phase ) const { } //------------------------------Identity--------------------------------------- -Node *ConvF2LNode::Identity(PhaseTransform *phase) { +Node* ConvF2LNode::Identity(PhaseGVN* phase) { // Remove ConvF2L->ConvL2F->ConvF2L sequences. if( in(1) ->Opcode() == Op_ConvL2F && in(1)->in(1)->Opcode() == Op_ConvF2L ) @@ -197,7 +198,7 @@ Node *ConvF2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvI2DNode::Value( PhaseTransform *phase ) const { +const Type* ConvI2DNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); @@ -207,7 +208,7 @@ const Type *ConvI2DNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvI2FNode::Value( PhaseTransform *phase ) const { +const Type* ConvI2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); @@ -216,7 +217,7 @@ const Type *ConvI2FNode::Value( PhaseTransform *phase ) const { } //------------------------------Identity--------------------------------------- -Node *ConvI2FNode::Identity(PhaseTransform *phase) { +Node* ConvI2FNode::Identity(PhaseGVN* phase) { // Remove ConvI2F->ConvF2I->ConvI2F sequences. if( in(1) ->Opcode() == Op_ConvF2I && in(1)->in(1)->Opcode() == Op_ConvI2F ) @@ -226,7 +227,7 @@ Node *ConvI2FNode::Identity(PhaseTransform *phase) { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvI2LNode::Value( PhaseTransform *phase ) const { +const Type* ConvI2LNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); @@ -293,7 +294,8 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { } #ifdef _LP64 - // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) , + // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) or + // ConvI2L(CastII(AddI(x, y))) to AddL(ConvI2L(CastII(x)), ConvI2L(CastII(y))), // but only if x and y have subranges that cannot cause 32-bit overflow, // under the assumption that x+y is in my own subrange this->type(). @@ -317,6 +319,13 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* z = in(1); int op = z->Opcode(); + Node* ctrl = NULL; + if (op == Op_CastII && z->as_CastII()->has_range_check()) { + // Skip CastII node but save control dependency + ctrl = z->in(0); + z = z->in(1); + op = z->Opcode(); + } if (op == Op_AddI || op == Op_SubI) { Node* x = z->in(1); Node* y = z->in(2); @@ -374,9 +383,10 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { rylo = -ryhi; ryhi = -rylo0; } - - Node* cx = phase->transform( new ConvI2LNode(x, TypeLong::make(rxlo, rxhi, widen)) ); - Node* cy = phase->transform( new ConvI2LNode(y, TypeLong::make(rylo, ryhi, widen)) ); + assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow"); + assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow"); + Node* cx = phase->C->constrained_convI2L(phase, x, TypeInt::make(rxlo, rxhi, widen), ctrl); + Node* cy = phase->C->constrained_convI2L(phase, y, TypeInt::make(rylo, ryhi, widen), ctrl); switch (op) { case Op_AddI: return new AddLNode(cx, cy); case Op_SubI: return new SubLNode(cx, cy); @@ -390,7 +400,7 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvL2DNode::Value( PhaseTransform *phase ) const { +const Type* ConvL2DNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); @@ -400,7 +410,7 @@ const Type *ConvL2DNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ConvL2FNode::Value( PhaseTransform *phase ) const { +const Type* ConvL2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); @@ -410,14 +420,14 @@ const Type *ConvL2FNode::Value( PhaseTransform *phase ) const { //============================================================================= //----------------------------Identity----------------------------------------- -Node *ConvL2INode::Identity( PhaseTransform *phase ) { +Node* ConvL2INode::Identity(PhaseGVN* phase) { // Convert L2I(I2L(x)) => x if (in(1)->Opcode() == Op_ConvI2L) return in(1)->in(1); return this; } //------------------------------Value------------------------------------------ -const Type *ConvL2INode::Value( PhaseTransform *phase ) const { +const Type* ConvL2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); @@ -469,7 +479,7 @@ Node *ConvL2INode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Identity--------------------------------------- // Remove redundant roundings -Node *RoundFloatNode::Identity( PhaseTransform *phase ) { +Node* RoundFloatNode::Identity(PhaseGVN* phase) { assert(Matcher::strict_fp_requires_explicit_rounding, "should only generate for Intel"); // Do not round constants if (phase->type(in(1))->base() == Type::FloatCon) return in(1); @@ -483,14 +493,14 @@ Node *RoundFloatNode::Identity( PhaseTransform *phase ) { } //------------------------------Value------------------------------------------ -const Type *RoundFloatNode::Value( PhaseTransform *phase ) const { +const Type* RoundFloatNode::Value(PhaseGVN* phase) const { return phase->type( in(1) ); } //============================================================================= //------------------------------Identity--------------------------------------- // Remove redundant roundings. Incoming arguments are already rounded. -Node *RoundDoubleNode::Identity( PhaseTransform *phase ) { +Node* RoundDoubleNode::Identity(PhaseGVN* phase) { assert(Matcher::strict_fp_requires_explicit_rounding, "should only generate for Intel"); // Do not round constants if (phase->type(in(1))->base() == Type::DoubleCon) return in(1); @@ -506,7 +516,7 @@ Node *RoundDoubleNode::Identity( PhaseTransform *phase ) { } //------------------------------Value------------------------------------------ -const Type *RoundDoubleNode::Value( PhaseTransform *phase ) const { +const Type* RoundDoubleNode::Value(PhaseGVN* phase) const { return phase->type( in(1) ); } diff --git a/hotspot/src/share/vm/opto/convertnode.hpp b/hotspot/src/share/vm/opto/convertnode.hpp index ff19db51e55..0a3e78b18dc 100644 --- a/hotspot/src/share/vm/opto/convertnode.hpp +++ b/hotspot/src/share/vm/opto/convertnode.hpp @@ -36,8 +36,8 @@ class Conv2BNode : public Node { Conv2BNode( Node *i ) : Node(0,i) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return Op_RegI; } }; @@ -49,8 +49,8 @@ class ConvD2FNode : public Node { ConvD2FNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::FLOAT; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegF; } }; @@ -61,8 +61,8 @@ class ConvD2INode : public Node { ConvD2INode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegI; } }; @@ -74,8 +74,8 @@ class ConvD2LNode : public Node { ConvD2LNode( Node *dbl ) : Node(0,dbl) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeLong::LONG; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegL; } }; @@ -87,7 +87,7 @@ class ConvF2DNode : public Node { ConvF2DNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::DOUBLE; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return Op_RegD; } }; @@ -98,8 +98,8 @@ class ConvF2INode : public Node { ConvF2INode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegI; } }; @@ -111,8 +111,8 @@ class ConvF2LNode : public Node { ConvF2LNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeLong::LONG; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegL; } }; @@ -124,7 +124,7 @@ class ConvI2DNode : public Node { ConvI2DNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::DOUBLE; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return Op_RegD; } }; @@ -135,8 +135,8 @@ class ConvI2FNode : public Node { ConvI2FNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::FLOAT; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegF; } }; @@ -148,7 +148,7 @@ class ConvI2LNode : public TypeNode { : TypeNode(t, 2) { init_req(1, in1); } virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegL; } }; @@ -160,7 +160,7 @@ class ConvL2DNode : public Node { ConvL2DNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::DOUBLE; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return Op_RegD; } }; @@ -171,7 +171,7 @@ class ConvL2FNode : public Node { ConvL2FNode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::FLOAT; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const { return Op_RegF; } }; @@ -182,8 +182,8 @@ class ConvL2INode : public Node { ConvL2INode( Node *in1 ) : Node(0,in1) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegI; } }; @@ -195,8 +195,8 @@ class RoundFloatNode: public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; }; @@ -207,8 +207,8 @@ class RoundDoubleNode: public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/hotspot/src/share/vm/opto/countbitsnode.cpp b/hotspot/src/share/vm/opto/countbitsnode.cpp index 152c7699ca7..f9de303ffe4 100644 --- a/hotspot/src/share/vm/opto/countbitsnode.cpp +++ b/hotspot/src/share/vm/opto/countbitsnode.cpp @@ -29,7 +29,7 @@ #include "opto/type.hpp" //------------------------------Value------------------------------------------ -const Type* CountLeadingZerosINode::Value(PhaseTransform* phase) const { +const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); @@ -51,7 +51,7 @@ const Type* CountLeadingZerosINode::Value(PhaseTransform* phase) const { } //------------------------------Value------------------------------------------ -const Type* CountLeadingZerosLNode::Value(PhaseTransform* phase) const { +const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); @@ -74,7 +74,7 @@ const Type* CountLeadingZerosLNode::Value(PhaseTransform* phase) const { } //------------------------------Value------------------------------------------ -const Type* CountTrailingZerosINode::Value(PhaseTransform* phase) const { +const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); @@ -96,7 +96,7 @@ const Type* CountTrailingZerosINode::Value(PhaseTransform* phase) const { } //------------------------------Value------------------------------------------ -const Type* CountTrailingZerosLNode::Value(PhaseTransform* phase) const { +const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); diff --git a/hotspot/src/share/vm/opto/countbitsnode.hpp b/hotspot/src/share/vm/opto/countbitsnode.hpp index 0ead252ee89..1a8a65269ab 100644 --- a/hotspot/src/share/vm/opto/countbitsnode.hpp +++ b/hotspot/src/share/vm/opto/countbitsnode.hpp @@ -44,7 +44,7 @@ class CountLeadingZerosINode : public CountBitsNode { public: CountLeadingZerosINode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- CountLeadingZerosLNode -------------------------------------------- @@ -53,7 +53,7 @@ class CountLeadingZerosLNode : public CountBitsNode { public: CountLeadingZerosLNode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- CountTrailingZerosINode ------------------------------------------- @@ -62,7 +62,7 @@ class CountTrailingZerosINode : public CountBitsNode { public: CountTrailingZerosINode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- CountTrailingZerosLNode ------------------------------------------- @@ -71,7 +71,7 @@ class CountTrailingZerosLNode : public CountBitsNode { public: CountTrailingZerosLNode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- PopCountINode ----------------------------------------------------- diff --git a/hotspot/src/share/vm/opto/divnode.cpp b/hotspot/src/share/vm/opto/divnode.cpp index 002417d0f55..de839cc2c6b 100644 --- a/hotspot/src/share/vm/opto/divnode.cpp +++ b/hotspot/src/share/vm/opto/divnode.cpp @@ -457,7 +457,7 @@ static Node *transform_long_divide( PhaseGVN *phase, Node *dividend, jlong divis //============================================================================= //------------------------------Identity--------------------------------------- // If the divisor is 1, we are an identity on the dividend. -Node *DivINode::Identity( PhaseTransform *phase ) { +Node* DivINode::Identity(PhaseGVN* phase) { return (phase->type( in(2) )->higher_equal(TypeInt::ONE)) ? in(1) : this; } @@ -493,7 +493,7 @@ Node *DivINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A DivINode divides its inputs. The third input is a Control input, used to // prevent hoisting the divide above an unsafe test. -const Type *DivINode::Value( PhaseTransform *phase ) const { +const Type* DivINode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -559,7 +559,7 @@ const Type *DivINode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- // If the divisor is 1, we are an identity on the dividend. -Node *DivLNode::Identity( PhaseTransform *phase ) { +Node* DivLNode::Identity(PhaseGVN* phase) { return (phase->type( in(2) )->higher_equal(TypeLong::ONE)) ? in(1) : this; } @@ -595,7 +595,7 @@ Node *DivLNode::Ideal( PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A DivLNode divides its inputs. The third input is a Control input, used to // prevent hoisting the divide above an unsafe test. -const Type *DivLNode::Value( PhaseTransform *phase ) const { +const Type* DivLNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -662,7 +662,7 @@ const Type *DivLNode::Value( PhaseTransform *phase ) const { //------------------------------Value------------------------------------------ // An DivFNode divides its inputs. The third input is a Control input, used to // prevent hoisting the divide above an unsafe test. -const Type *DivFNode::Value( PhaseTransform *phase ) const { +const Type* DivFNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -705,7 +705,7 @@ const Type *DivFNode::Value( PhaseTransform *phase ) const { //------------------------------isA_Copy--------------------------------------- // Dividing by self is 1. // If the divisor is 1, we are an identity on the dividend. -Node *DivFNode::Identity( PhaseTransform *phase ) { +Node* DivFNode::Identity(PhaseGVN* phase) { return (phase->type( in(2) ) == TypeF::ONE) ? in(1) : this; } @@ -750,7 +750,7 @@ Node *DivFNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // An DivDNode divides its inputs. The third input is a Control input, used to // prevent hoisting the divide above an unsafe test. -const Type *DivDNode::Value( PhaseTransform *phase ) const { +const Type* DivDNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -800,7 +800,7 @@ const Type *DivDNode::Value( PhaseTransform *phase ) const { //------------------------------isA_Copy--------------------------------------- // Dividing by self is 1. // If the divisor is 1, we are an identity on the dividend. -Node *DivDNode::Identity( PhaseTransform *phase ) { +Node* DivDNode::Identity(PhaseGVN* phase) { return (phase->type( in(2) ) == TypeD::ONE) ? in(1) : this; } @@ -972,7 +972,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type *ModINode::Value( PhaseTransform *phase ) const { +const Type* ModINode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -1145,7 +1145,7 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type *ModLNode::Value( PhaseTransform *phase ) const { +const Type* ModLNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -1186,7 +1186,7 @@ const Type *ModLNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ModFNode::Value( PhaseTransform *phase ) const { +const Type* ModFNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -1230,7 +1230,7 @@ const Type *ModFNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *ModDNode::Value( PhaseTransform *phase ) const { +const Type* ModDNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); diff --git a/hotspot/src/share/vm/opto/divnode.hpp b/hotspot/src/share/vm/opto/divnode.hpp index 195803e2000..b1bf06e6750 100644 --- a/hotspot/src/share/vm/opto/divnode.hpp +++ b/hotspot/src/share/vm/opto/divnode.hpp @@ -44,9 +44,9 @@ class DivINode : public Node { public: DivINode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -57,9 +57,9 @@ class DivLNode : public Node { public: DivLNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } }; @@ -70,9 +70,9 @@ class DivFNode : public Node { public: DivFNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return Type::FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } }; @@ -83,9 +83,9 @@ class DivDNode : public Node { public: DivDNode( Node *c, Node *dividend, Node *divisor ) : Node(c,dividend, divisor) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } }; @@ -96,7 +96,7 @@ class ModINode : public Node { public: ModINode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } @@ -108,7 +108,7 @@ class ModLNode : public Node { public: ModLNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } @@ -120,7 +120,7 @@ class ModFNode : public Node { public: ModFNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return Type::FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } }; @@ -131,7 +131,7 @@ class ModDNode : public Node { public: ModDNode( Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } }; @@ -147,9 +147,9 @@ public: mod_proj_num = 1 // remainder }; virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node* Identity(PhaseGVN* phase) { return this; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { return NULL; } - virtual const Type *Value( PhaseTransform *phase ) const { return bottom_type(); } + virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } virtual uint hash() const { return Node::hash(); } virtual bool is_CFG() const { return false; } virtual uint ideal_reg() const { return NotAMachineReg; } diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index 98bade2457e..310ac7fa70e 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -810,6 +810,7 @@ void ConnectionGraph::add_call_node(CallNode* call) { if (cik->is_subclass_of(_compile->env()->Thread_klass()) || cik->is_subclass_of(_compile->env()->Reference_klass()) || !cik->is_instance_klass() || // StressReflectiveCode + !cik->as_instance_klass()->can_be_instantiated() || cik->as_instance_klass()->has_finalizer()) { es = PointsToNode::GlobalEscape; } diff --git a/hotspot/src/share/vm/opto/gcm.cpp b/hotspot/src/share/vm/opto/gcm.cpp index 833be199daa..48376b2f57a 100644 --- a/hotspot/src/share/vm/opto/gcm.cpp +++ b/hotspot/src/share/vm/opto/gcm.cpp @@ -101,7 +101,32 @@ void PhaseCFG::replace_block_proj_ctrl( Node *n ) { } } -static bool is_dominator(Block* d, Block* n) { +bool PhaseCFG::is_dominator(Node* dom_node, Node* node) { + if (dom_node == node) { + return true; + } + Block* d = get_block_for_node(dom_node); + Block* n = get_block_for_node(node); + if (d == n) { + if (dom_node->is_block_start()) { + return true; + } + if (node->is_block_start()) { + return false; + } + if (dom_node->is_block_proj()) { + return false; + } + if (node->is_block_proj()) { + return true; + } +#ifdef ASSERT + node->dump(); + dom_node->dump(); +#endif + fatal("unhandled"); + return false; + } return d->dom_lca(n) == d; } @@ -145,19 +170,15 @@ void PhaseCFG::schedule_pinned_nodes(VectorSet &visited) { if (n == NULL) { n = m; } else { - Block* bn = get_block_for_node(n); - Block* bm = get_block_for_node(m); - assert(is_dominator(bn, bm) || is_dominator(bm, bn), "one must dominate the other"); - n = is_dominator(bn, bm) ? m : n; + assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other"); + n = is_dominator(n, m) ? m : n; } } } if (n != NULL) { assert(node->in(0), "control should have been set"); - Block* bn = get_block_for_node(n); - Block* bnode = get_block_for_node(node->in(0)); - assert(is_dominator(bn, bnode) || is_dominator(bnode, bn), "one must dominate the other"); - if (!is_dominator(bn, bnode)) { + assert(is_dominator(n, node->in(0)) || is_dominator(node->in(0), n), "one must dominate the other"); + if (!is_dominator(n, node->in(0))) { node->set_req(0, n); } } diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp index 97ae61503a0..f3389bd969b 100644 --- a/hotspot/src/share/vm/opto/graphKit.cpp +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -1658,7 +1658,7 @@ Node* GraphKit::store_oop_to_unknown(Node* ctl, //-------------------------array_element_address------------------------- Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt, - const TypeInt* sizetype) { + const TypeInt* sizetype, Node* ctrl) { uint shift = exact_log2(type2aelembytes(elembt)); uint header = arrayOopDesc::base_offset_in_bytes(elembt); @@ -1671,7 +1671,7 @@ Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt, // must be correct type for alignment purposes Node* base = basic_plus_adr(ary, header); - idx = Compile::conv_I2X_index(&_gvn, idx, sizetype); + idx = Compile::conv_I2X_index(&_gvn, idx, sizetype, ctrl); Node* scale = _gvn.transform( new LShiftXNode(idx, intcon(shift)) ); return basic_plus_adr(ary, base, scale); } @@ -3407,8 +3407,7 @@ Node* GraphKit::new_instance(Node* klass_node, if (layout_is_con) { assert(!StressReflectiveCode, "stress mode does not use these paths"); bool must_go_slow = Klass::layout_helper_needs_slow_path(layout_con); - initial_slow_test = must_go_slow? intcon(1): extra_slow_test; - + initial_slow_test = must_go_slow ? intcon(1) : extra_slow_test; } else { // reflective case // This reflective path is used by Unsafe.allocateInstance. // (It may be stress-tested by specifying StressReflectiveCode.) @@ -3507,10 +3506,6 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) Node* initial_slow_cmp = _gvn.transform( new CmpUNode( length, intcon( fast_size_limit ) ) ); Node* initial_slow_test = _gvn.transform( new BoolNode( initial_slow_cmp, BoolTest::gt ) ); - if (initial_slow_test->is_Bool()) { - // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick. - initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); - } // --- Size Computation --- // array_size = round_to_heap(array_header + (length << elem_shift)); @@ -3556,13 +3551,35 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) Node* lengthx = ConvI2X(length); Node* headerx = ConvI2X(header_size); #ifdef _LP64 - { const TypeLong* tllen = _gvn.find_long_type(lengthx); - if (tllen != NULL && tllen->_lo < 0) { + { const TypeInt* tilen = _gvn.find_int_type(length); + if (tilen != NULL && tilen->_lo < 0) { // Add a manual constraint to a positive range. Cf. array_element_address. - jlong size_max = arrayOopDesc::max_array_length(T_BYTE); - if (size_max > tllen->_hi) size_max = tllen->_hi; - const TypeLong* tlcon = TypeLong::make(CONST64(0), size_max, Type::WidenMin); - lengthx = _gvn.transform( new ConvI2LNode(length, tlcon)); + jint size_max = fast_size_limit; + if (size_max > tilen->_hi) size_max = tilen->_hi; + const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin); + + // Only do a narrow I2L conversion if the range check passed. + IfNode* iff = new IfNode(control(), initial_slow_test, PROB_MIN, COUNT_UNKNOWN); + _gvn.transform(iff); + RegionNode* region = new RegionNode(3); + _gvn.set_type(region, Type::CONTROL); + lengthx = new PhiNode(region, TypeLong::LONG); + _gvn.set_type(lengthx, TypeLong::LONG); + + // Range check passed. Use ConvI2L node with narrow type. + Node* passed = IfFalse(iff); + region->init_req(1, passed); + // Make I2L conversion control dependent to prevent it from + // floating above the range check during loop optimizations. + lengthx->init_req(1, C->constrained_convI2L(&_gvn, length, tlcon, passed)); + + // Range check failed. Use ConvI2L with wide type because length may be invalid. + region->init_req(2, IfTrue(iff)); + lengthx->init_req(2, ConvI2X(length)); + + set_control(region); + record_for_igvn(region); + record_for_igvn(lengthx); } } #endif @@ -3593,6 +3610,11 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) Node *mem = reset_memory(); set_all_memory(mem); // Create new memory state + if (initial_slow_test->is_Bool()) { + // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick. + initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); + } + // Create the AllocateArrayNode and its result projections AllocateArrayNode* alloc = new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT), @@ -4341,20 +4363,51 @@ void GraphKit::store_String_coder(Node* ctrl, Node* str, Node* value) { value, T_BYTE, coder_field_idx, MemNode::unordered); } -Node* GraphKit::compress_string(Node* src, Node* dst, Node* count) { +// Capture src and dst memory state with a MergeMemNode +Node* GraphKit::capture_memory(const TypePtr* src_type, const TypePtr* dst_type) { + if (src_type == dst_type) { + // Types are equal, we don't need a MergeMemNode + return memory(src_type); + } + MergeMemNode* merge = MergeMemNode::make(map()->memory()); + record_for_igvn(merge); // fold it up later, if possible + int src_idx = C->get_alias_index(src_type); + int dst_idx = C->get_alias_index(dst_type); + merge->set_memory_at(src_idx, memory(src_idx)); + merge->set_memory_at(dst_idx, memory(dst_idx)); + return merge; +} + +Node* GraphKit::compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count) { assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported"); - uint idx = C->get_alias_index(TypeAryPtr::BYTES); - StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), memory(idx), src, dst, count); + assert(src_type == TypeAryPtr::BYTES || src_type == TypeAryPtr::CHARS, "invalid source type"); + // If input and output memory types differ, capture both states to preserve + // the dependency between preceding and subsequent loads/stores. + // For example, the following program: + // StoreB + // compress_string + // LoadB + // has this memory graph (use->def): + // LoadB -> compress_string -> CharMem + // ... -> StoreB -> ByteMem + // The intrinsic hides the dependency between LoadB and StoreB, causing + // the load to read from memory not containing the result of the StoreB. + // The correct memory graph should look like this: + // LoadB -> compress_string -> MergeMem(CharMem, StoreB(ByteMem)) + Node* mem = capture_memory(src_type, TypeAryPtr::BYTES); + StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, src, dst, count); Node* res_mem = _gvn.transform(new SCMemProjNode(str)); - set_memory(res_mem, idx); + set_memory(res_mem, TypeAryPtr::BYTES); return str; } -void GraphKit::inflate_string(Node* src, Node* dst, Node* count) { +void GraphKit::inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count) { assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported"); - uint idx = C->get_alias_index(TypeAryPtr::BYTES); - StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), memory(idx), src, dst, count); - set_memory(_gvn.transform(str), idx); + assert(dst_type == TypeAryPtr::BYTES || dst_type == TypeAryPtr::CHARS, "invalid dest type"); + // Capture src and dst memory (see comment in 'compress_string'). + Node* mem = capture_memory(TypeAryPtr::BYTES, dst_type); + StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, src, dst, count); + set_memory(_gvn.transform(str), dst_type); } void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) { diff --git a/hotspot/src/share/vm/opto/graphKit.hpp b/hotspot/src/share/vm/opto/graphKit.hpp index 4b4be335cd3..7bb1f6946db 100644 --- a/hotspot/src/share/vm/opto/graphKit.hpp +++ b/hotspot/src/share/vm/opto/graphKit.hpp @@ -634,7 +634,9 @@ class GraphKit : public Phase { // Return addressing for an array element. Node* array_element_address(Node* ary, Node* idx, BasicType elembt, // Optional constraint on the array size: - const TypeInt* sizetype = NULL); + const TypeInt* sizetype = NULL, + // Optional control dependency (for example, on range check) + Node* ctrl = NULL); // Return a load of array element at idx. Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype); @@ -881,8 +883,9 @@ class GraphKit : public Phase { Node* load_String_coder(Node* ctrl, Node* str); void store_String_value(Node* ctrl, Node* str, Node* value); void store_String_coder(Node* ctrl, Node* str, Node* value); - Node* compress_string(Node* src, Node* dst, Node* count); - void inflate_string(Node* src, Node* dst, Node* count); + Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type); + Node* compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count); + void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count); void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count); // Handy for making control flow diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp index c8205690bb0..a2cea88273f 100644 --- a/hotspot/src/share/vm/opto/ifnode.cpp +++ b/hotspot/src/share/vm/opto/ifnode.cpp @@ -45,7 +45,7 @@ extern int explicit_null_checks_elided; //============================================================================= //------------------------------Value------------------------------------------ // Return a tuple for whichever arm of the IF is reachable -const Type *IfNode::Value( PhaseTransform *phase ) const { +const Type* IfNode::Value(PhaseGVN* phase) const { if( !in(0) ) return Type::TOP; if( phase->type(in(0)) == Type::TOP ) return Type::TOP; @@ -1104,7 +1104,8 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV if (ctrl == fail) { Node* init_n = stack.node_at(1); assert(init_n->Opcode() == Op_ConvI2L, "unexpected first node"); - Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size); + // Create a new narrow ConvI2L node that is dependent on the range check + Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size, fail); // The type of the ConvI2L may be widen and so the new // ConvI2L may not be better than an existing ConvI2L @@ -1527,16 +1528,19 @@ Node* IfNode::search_identical(int dist) { //------------------------------Identity--------------------------------------- // If the test is constant & we match, then we are the input Control -Node *IfProjNode::Identity(PhaseTransform *phase) { +Node* IfProjNode::Identity(PhaseGVN* phase) { // Can only optimize if cannot go the other way const TypeTuple *t = phase->type(in(0))->is_tuple(); - if (t == TypeTuple::IFNEITHER || - // kill dead branch first otherwise the IfNode's control will - // have 2 control uses (the IfNode that doesn't go away because - // it still has uses and this branch of the - // If). Node::has_special_unique_user() will cause this node to - // be reprocessed once the dead branch is killed. - (always_taken(t) && in(0)->outcnt() == 1)) { + if (t == TypeTuple::IFNEITHER || (always_taken(t) && + // During parsing (GVN) we don't remove dead code aggressively. + // Cut off dead branch and let PhaseRemoveUseless take care of it. + (!phase->is_IterGVN() || + // During IGVN, first wait for the dead branch to be killed. + // Otherwise, the IfNode's control will have two control uses (the IfNode + // that doesn't go away because it still has uses and this branch of the + // If) which breaks other optimizations. Node::has_special_unique_user() + // will cause this node to be reprocessed once the dead branch is killed. + in(0)->outcnt() == 1))) { // IfNode control return in(0)->in(0); } diff --git a/hotspot/src/share/vm/opto/intrinsicnode.cpp b/hotspot/src/share/vm/opto/intrinsicnode.cpp index e72c3e221e1..064e73aab2c 100644 --- a/hotspot/src/share/vm/opto/intrinsicnode.cpp +++ b/hotspot/src/share/vm/opto/intrinsicnode.cpp @@ -55,7 +55,7 @@ Node* StrIntrinsicNode::Ideal(PhaseGVN* phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type* StrIntrinsicNode::Value(PhaseTransform* phase) const { +const Type* StrIntrinsicNode::Value(PhaseGVN* phase) const { if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; return bottom_type(); } @@ -93,7 +93,7 @@ Node* EncodeISOArrayNode::Ideal(PhaseGVN* phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type* EncodeISOArrayNode::Value(PhaseTransform* phase) const { +const Type* EncodeISOArrayNode::Value(PhaseGVN* phase) const { if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; return bottom_type(); } diff --git a/hotspot/src/share/vm/opto/intrinsicnode.hpp b/hotspot/src/share/vm/opto/intrinsicnode.hpp index d5bc0c70742..450638076b8 100644 --- a/hotspot/src/share/vm/opto/intrinsicnode.hpp +++ b/hotspot/src/share/vm/opto/intrinsicnode.hpp @@ -77,7 +77,7 @@ class StrIntrinsicNode: public Node { virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; ArgEncoding encoding() const { return _encoding; } }; @@ -177,7 +177,7 @@ class EncodeISOArrayNode: public Node { virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; }; #endif // SHARE_VM_OPTO_INTRINSICNODE_HPP diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index 57614f7cddf..3edfc5e0ef0 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -949,6 +949,13 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto // and the edge will be lost. This is why this code should be // executed only when Precedent (== TypeFunc::Parms) edge is present. Node *x = n->in(TypeFunc::Parms); + if (x != NULL && get_block_for_node(x) == block && n->find_prec_edge(x) != -1) { + // Old edge to node within same block will get removed, but no precedence + // edge will get added because it already exists. Update ready count. + int cnt = ready_cnt.at(n->_idx); + assert(cnt > 1, "MemBar node %d must not get ready here", n->_idx); + ready_cnt.at_put(n->_idx, cnt-1); + } n->del_req(TypeFunc::Parms); n->add_prec(x); } diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index 9d6483248cc..753612c206f 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -971,8 +971,10 @@ Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1_start, Node str1_start, cnt1, str2_start, cnt2, ae); break; case Op_StrEquals: + // We already know that cnt1 == cnt2 here (checked in 'inline_string_equals'). + // Use the constant length if there is one because optimized match rule may exist. result = new StrEqualsNode(control(), memory(TypeAryPtr::BYTES), - str1_start, str2_start, cnt1, ae); + str1_start, str2_start, cnt2->is_Con() ? cnt2 : cnt1, ae); break; default: ShouldNotReachHere(); @@ -1131,7 +1133,7 @@ bool LibraryCallKit::inline_objects_checkIndex() { //------------------------------inline_string_indexOf------------------------ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { - if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) { + if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } Node* src = argument(0); @@ -1175,7 +1177,7 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; } - if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) { + if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments"); @@ -1260,7 +1262,7 @@ bool LibraryCallKit::inline_string_indexOfChar() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; } - if (!Matcher::has_match_rule(Op_StrIndexOfChar) || !(UseSSE > 4)) { + if (!Matcher::match_rule_supported(Op_StrIndexOfChar)) { return false; } assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments"); @@ -1359,9 +1361,9 @@ bool LibraryCallKit::inline_string_copy(bool compress) { // 'dst_start' points to dst array + scaled offset Node* count = NULL; if (compress) { - count = compress_string(src_start, dst_start, length); + count = compress_string(src_start, TypeAryPtr::get_array_body_type(src_elem), dst_start, length); } else { - inflate_string(src_start, dst_start, length); + inflate_string(src_start, dst_start, TypeAryPtr::get_array_body_type(dst_elem), length); } if (alloc != NULL) { @@ -1587,7 +1589,7 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) { (void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered, false, false, true /* mismatched */); } else { - ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, MemNode::unordered, + ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered, LoadNode::DependsOnlyOnTest, false, false, true /* mismatched */); set_result(ch); } @@ -1628,8 +1630,6 @@ bool LibraryCallKit::inline_trig(vmIntrinsics::ID id) { Node* n = NULL; switch (id) { - case vmIntrinsics::_dsin: n = new SinDNode(C, control(), arg); break; - case vmIntrinsics::_dcos: n = new CosDNode(C, control(), arg); break; case vmIntrinsics::_dtan: n = new TanDNode(C, control(), arg); break; default: fatal_unexpected_iid(id); break; } @@ -1692,16 +1692,6 @@ bool LibraryCallKit::inline_trig(vmIntrinsics::ID id) { // Slow path - non-blocking leaf call Node* call = NULL; switch (id) { - case vmIntrinsics::_dsin: - call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), - CAST_FROM_FN_PTR(address, SharedRuntime::dsin), - "Sin", NULL, arg, top()); - break; - case vmIntrinsics::_dcos: - call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), - CAST_FROM_FN_PTR(address, SharedRuntime::dcos), - "Cos", NULL, arg, top()); - break; case vmIntrinsics::_dtan: call = make_runtime_call(RC_LEAF, OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dtan), @@ -1752,17 +1742,21 @@ bool LibraryCallKit::inline_math_native(vmIntrinsics::ID id) { #define FN_PTR(f) CAST_FROM_FN_PTR(address, f) switch (id) { // These intrinsics are not properly supported on all hardware - case vmIntrinsics::_dcos: return Matcher::has_match_rule(Op_CosD) ? inline_trig(id) : - runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dcos), "COS"); - case vmIntrinsics::_dsin: return Matcher::has_match_rule(Op_SinD) ? inline_trig(id) : - runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dsin), "SIN"); + case vmIntrinsics::_dsin: + return StubRoutines::dsin() != NULL ? + runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dsin(), "dsin") : + runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dsin), "SIN"); + case vmIntrinsics::_dcos: + return StubRoutines::dcos() != NULL ? + runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dcos(), "dcos") : + runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dcos), "COS"); case vmIntrinsics::_dtan: return Matcher::has_match_rule(Op_TanD) ? inline_trig(id) : runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dtan), "TAN"); case vmIntrinsics::_dlog: return StubRoutines::dlog() != NULL ? - runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dlog(), "dlog") : - runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dlog), "LOG"); + runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dlog(), "dlog") : + runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dlog), "LOG"); case vmIntrinsics::_dlog10: return Matcher::has_match_rule(Op_Log10D) ? inline_math(id) : runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dlog10), "LOG10"); @@ -3085,7 +3079,7 @@ bool LibraryCallKit::inline_native_isInterrupted() { set_control( _gvn.transform(new IfTrueNode(iff_arg))); #else // To return true on Windows you must read the _interrupted field - // and check the the event state i.e. take the slow path. + // and check the event state i.e. take the slow path. #endif // TARGET_OS_FAMILY_windows // (d) Otherwise, go to the slow path. @@ -3159,7 +3153,7 @@ Node* LibraryCallKit::load_klass_from_mirror_common(Node* mirror, } //--------------------(inline_native_Class_query helpers)--------------------- -// Use this for JVM_ACC_INTERFACE, JVM_ACC_IS_CLONEABLE, JVM_ACC_HAS_FINALIZER. +// Use this for JVM_ACC_INTERFACE, JVM_ACC_IS_CLONEABLE_FAST, JVM_ACC_HAS_FINALIZER. // Fall through if (mods & mask) == bits, take the guard otherwise. Node* LibraryCallKit::generate_access_flags_guard(Node* kls, int modifier_mask, int modifier_bits, RegionNode* region) { // Branch around if the given klass has the given modifier bit set. @@ -4497,14 +4491,14 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) { generate_virtual_guard(obj_klass, slow_region); } - // The object must be cloneable and must not have a finalizer. + // The object must be easily cloneable and must not have a finalizer. // Both of these conditions may be checked in a single test. - // We could optimize the cloneable test further, but we don't care. + // We could optimize the test further, but we don't care. generate_access_flags_guard(obj_klass, // Test both conditions: - JVM_ACC_IS_CLONEABLE | JVM_ACC_HAS_FINALIZER, + JVM_ACC_IS_CLONEABLE_FAST | JVM_ACC_HAS_FINALIZER, // Must be cloneable but not finalizer: - JVM_ACC_IS_CLONEABLE, + JVM_ACC_IS_CLONEABLE_FAST, slow_region); } diff --git a/hotspot/src/share/vm/opto/locknode.hpp b/hotspot/src/share/vm/opto/locknode.hpp index 515a40c4663..b0e62ecef00 100644 --- a/hotspot/src/share/vm/opto/locknode.hpp +++ b/hotspot/src/share/vm/opto/locknode.hpp @@ -96,7 +96,7 @@ public: virtual uint size_of() const; virtual uint cmp( const Node &n ) const ; // Always fail, except on self virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const { return TypeInt::CC; } + virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; } const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} void create_lock_counter(JVMState* s); @@ -123,7 +123,7 @@ public: virtual uint hash() const ; // { return NO_HASH; } virtual uint cmp( const Node &n ) const ; // Always fail, except on self virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const { return TypeInt::CC; } + virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; } const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} }; diff --git a/hotspot/src/share/vm/opto/loopPredicate.cpp b/hotspot/src/share/vm/opto/loopPredicate.cpp index 9cc6961931e..56d21451ef8 100644 --- a/hotspot/src/share/vm/opto/loopPredicate.cpp +++ b/hotspot/src/share/vm/opto/loopPredicate.cpp @@ -519,7 +519,31 @@ class Invariance : public StackObj { _lpt(lpt), _phase(lpt->_phase), _visited(area), _invariant(area), _stack(area, 10 /* guess */), _clone_visited(area), _old_new(area) - {} + { + Node* head = _lpt->_head; + Node* entry = head->in(LoopNode::EntryControl); + if (entry->outcnt() != 1) { + // If a node is pinned between the predicates and the loop + // entry, we won't be able to move any node in the loop that + // depends on it above it in a predicate. Mark all those nodes + // as non loop invariatnt. + Unique_Node_List wq; + wq.push(entry); + for (uint next = 0; next < wq.size(); ++next) { + Node *n = wq.at(next); + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + if (!u->is_CFG()) { + Node* c = _phase->get_ctrl(u); + if (_lpt->is_member(_phase->get_loop(c)) || _phase->is_dominator(c, head)) { + _visited.set(u->_idx); + wq.push(u); + } + } + } + } + } + } // Map old to n for invariance computation and clone void map_ctrl(Node* old, Node* n) { @@ -641,6 +665,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree *loop, Node* ctrl, if (scale != 1) { ConNode* con_scale = _igvn.intcon(scale); + set_ctrl(con_scale, C->root()); max_idx_expr = new MulINode(max_idx_expr, con_scale); register_new_node(max_idx_expr, ctrl); if (TraceLoopPredicate) predString->print("* %d ", scale); diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp index cbff6c2b879..dd05872370a 100644 --- a/hotspot/src/share/vm/opto/loopTransform.cpp +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -2660,7 +2660,7 @@ bool IdealLoopTree::iteration_split( PhaseIdealLoop *phase, Node_List &old_new ) //============================================================================= // Process all the loops in the loop tree and replace any fill -// patterns with an intrisc version. +// patterns with an intrinsic version. bool PhaseIdealLoop::do_intrinsify_fill() { bool changed = false; for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { @@ -2758,8 +2758,9 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st } // Make sure the address expression can be handled. It should be - // head->phi * elsize + con. head->phi might have a ConvI2L. + // head->phi * elsize + con. head->phi might have a ConvI2L(CastII()). Node* elements[4]; + Node* cast = NULL; Node* conv = NULL; bool found_index = false; int count = store->in(MemNode::Address)->as_AddP()->unpack_offsets(elements, ARRAY_SIZE(elements)); @@ -2774,6 +2775,12 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st conv = value; value = value->in(1); } + if (value->Opcode() == Op_CastII && + value->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + cast = value; + value = value->in(1); + } #endif if (value != head->phi()) { msg = "unhandled shift in address"; @@ -2786,9 +2793,16 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st } } } else if (n->Opcode() == Op_ConvI2L && conv == NULL) { - if (n->in(1) == head->phi()) { + conv = n; + n = n->in(1); + if (n->Opcode() == Op_CastII && + n->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + cast = n; + n = n->in(1); + } + if (n == head->phi()) { found_index = true; - conv = n; } else { msg = "unhandled input to ConvI2L"; } @@ -2847,6 +2861,7 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st // Address elements are ok if (con) ok.set(con->_idx); if (shift) ok.set(shift->_idx); + if (cast) ok.set(cast->_idx); if (conv) ok.set(conv->_idx); for (uint i = 0; msg == NULL && i < lpt->_body.size(); i++) { diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp index 3faa0c2d319..8996def6d6f 100644 --- a/hotspot/src/share/vm/opto/loopnode.cpp +++ b/hotspot/src/share/vm/opto/loopnode.cpp @@ -815,6 +815,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { C->print_method(PHASE_AFTER_CLOOPS, 3); + // Capture bounds of the loop in the induction variable Phi before + // subsequent transformation (iteration splitting) obscures the + // bounds + l->phi()->as_Phi()->set_type(l->phi()->Value(&_igvn)); + return true; } @@ -897,7 +902,7 @@ int CountedLoopEndNode::stride_con() const { //============================================================================= //------------------------------Value----------------------------------------- -const Type *LoopLimitNode::Value( PhaseTransform *phase ) const { +const Type* LoopLimitNode::Value(PhaseGVN* phase) const { const Type* init_t = phase->type(in(Init)); const Type* limit_t = phase->type(in(Limit)); const Type* stride_t = phase->type(in(Stride)); @@ -1011,7 +1016,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // If stride == 1 return limit node. -Node *LoopLimitNode::Identity( PhaseTransform *phase ) { +Node* LoopLimitNode::Identity(PhaseGVN* phase) { int stride_con = phase->type(in(Stride))->is_int()->get_con(); if (stride_con == 1 || stride_con == -1) return in(Limit); @@ -3483,7 +3488,7 @@ void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, N // Second pass finds latest legal placement, and ideal loop placement. void PhaseIdealLoop::build_loop_late_post( Node *n ) { - if (n->req() == 2 && n->Opcode() == Op_ConvI2L && !C->major_progress() && !_verify_only) { + if (n->req() == 2 && (n->Opcode() == Op_ConvI2L || n->Opcode() == Op_CastII) && !C->major_progress() && !_verify_only) { _igvn._worklist.push(n); // Maybe we'll normalize it, if no more loops. } diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp index 59cfc690c7e..2e20eaf40b0 100644 --- a/hotspot/src/share/vm/opto/loopnode.hpp +++ b/hotspot/src/share/vm/opto/loopnode.hpp @@ -339,9 +339,9 @@ class LoopLimitNode : public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); }; // -----------------------------IdealLoopTree---------------------------------- diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp index 9be560add6a..8246600f82f 100644 --- a/hotspot/src/share/vm/opto/loopopts.cpp +++ b/hotspot/src/share/vm/opto/loopopts.cpp @@ -25,7 +25,9 @@ #include "precompiled.hpp" #include "memory/allocation.inline.hpp" #include "opto/addnode.hpp" +#include "opto/castnode.hpp" #include "opto/connode.hpp" +#include "opto/castnode.hpp" #include "opto/divnode.hpp" #include "opto/loopnode.hpp" #include "opto/matcher.hpp" @@ -900,6 +902,14 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) { Node *m = remix_address_expressions( n ); if( m ) return m; + if (n->is_ConstraintCast()) { + Node* dom_cast = n->as_ConstraintCast()->dominating_cast(this); + if (dom_cast != NULL) { + _igvn.replace_node(n, dom_cast); + return dom_cast; + } + } + // Determine if the Node has inputs from some local Phi. // Returns the block to clone thru. Node *n_blk = has_local_phi_input( n ); @@ -988,6 +998,9 @@ static bool merge_point_safe(Node* region) { #ifdef _LP64 if (m->Opcode() == Op_ConvI2L) return false; + if (m->is_CastII() && m->isa_CastII()->has_range_check()) { + return false; + } #endif } } diff --git a/hotspot/src/share/vm/opto/machnode.cpp b/hotspot/src/share/vm/opto/machnode.cpp index c4e6953ab9f..fc5b08625e5 100644 --- a/hotspot/src/share/vm/opto/machnode.cpp +++ b/hotspot/src/share/vm/opto/machnode.cpp @@ -652,7 +652,7 @@ const RegMask &MachSafePointNode::in_RegMask( uint idx ) const { uint MachCallNode::cmp( const Node &n ) const { return _tf == ((MachCallNode&)n)._tf; } const Type *MachCallNode::bottom_type() const { return tf()->range(); } -const Type *MachCallNode::Value(PhaseTransform *phase) const { return tf()->range(); } +const Type* MachCallNode::Value(PhaseGVN* phase) const { return tf()->range(); } #ifndef PRODUCT void MachCallNode::dump_spec(outputStream *st) const { diff --git a/hotspot/src/share/vm/opto/machnode.hpp b/hotspot/src/share/vm/opto/machnode.hpp index 25cbdc648e7..60bdc9b8768 100644 --- a/hotspot/src/share/vm/opto/machnode.hpp +++ b/hotspot/src/share/vm/opto/machnode.hpp @@ -863,7 +863,7 @@ public: virtual const Type *bottom_type() const; virtual bool pinned() const { return false; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const RegMask &in_RegMask(uint) const; virtual int ret_addr_offset() { return 0; } diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index 93c93b58206..80584956ffb 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -244,7 +244,7 @@ void PhaseMacroExpand::eliminate_card_mark(Node* p2x) { } else { // G1 pre/post barriers assert(p2x->outcnt() <= 2, "expects 1 or 2 users: Xor and URShift nodes"); - // It could be only one user, URShift node, in Object.clone() instrinsic + // It could be only one user, URShift node, in Object.clone() intrinsic // but the new allocation is passed to arraycopy stub and it could not // be scalar replaced. So we don't check the case. @@ -1813,10 +1813,11 @@ PhaseMacroExpand::initialize_object(AllocateNode* alloc, // there can be two Allocates to one Initialize. The answer in all these // edge cases is safety first. It is always safe to clear immediately // within an Allocate, and then (maybe or maybe not) clear some more later. - if (!ZeroTLAB) + if (!(UseTLAB && ZeroTLAB)) { rawmem = ClearArrayNode::clear_memory(control, rawmem, object, header_size, size_in_bytes, &_igvn); + } } else { if (!init->is_complete()) { // Try to win by zeroing only what the init does not store. diff --git a/hotspot/src/share/vm/opto/macroArrayCopy.cpp b/hotspot/src/share/vm/opto/macroArrayCopy.cpp index 7e3279c68aa..82816b2462b 100644 --- a/hotspot/src/share/vm/opto/macroArrayCopy.cpp +++ b/hotspot/src/share/vm/opto/macroArrayCopy.cpp @@ -295,7 +295,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* // out-edges of the dest, we need to avoid making derived pointers // from it until we have checked its uses.) if (ReduceBulkZeroing - && !ZeroTLAB // pointless if already zeroed + && !(UseTLAB && ZeroTLAB) // pointless if already zeroed && basic_elem_type != T_CONFLICT // avoid corner case && !src->eqv_uncast(dest) && alloc != NULL diff --git a/hotspot/src/share/vm/opto/mathexactnode.cpp b/hotspot/src/share/vm/opto/mathexactnode.cpp index 4c167d255a8..196dd343e06 100644 --- a/hotspot/src/share/vm/opto/mathexactnode.cpp +++ b/hotspot/src/share/vm/opto/mathexactnode.cpp @@ -247,11 +247,11 @@ Node* OverflowLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return IdealHelper::Ideal(this, phase, can_reshape); } -const Type* OverflowINode::Value(PhaseTransform* phase) const { +const Type* OverflowINode::Value(PhaseGVN* phase) const { return IdealHelper::Value(this, phase); } -const Type* OverflowLNode::Value(PhaseTransform* phase) const { +const Type* OverflowLNode::Value(PhaseGVN* phase) const { return IdealHelper::Value(this, phase); } diff --git a/hotspot/src/share/vm/opto/mathexactnode.hpp b/hotspot/src/share/vm/opto/mathexactnode.hpp index 3e037cf568b..0c948e3dc34 100644 --- a/hotspot/src/share/vm/opto/mathexactnode.hpp +++ b/hotspot/src/share/vm/opto/mathexactnode.hpp @@ -48,7 +48,7 @@ public: OverflowINode(Node* in1, Node* in2) : OverflowNode(in1, in2) {} virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual bool will_overflow(jint v1, jint v2) const = 0; virtual bool can_overflow(const Type* t1, const Type* t2) const = 0; @@ -61,7 +61,7 @@ public: OverflowLNode(Node* in1, Node* in2) : OverflowNode(in1, in2) {} virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseTransform* phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual bool will_overflow(jlong v1, jlong v2) const = 0; virtual bool can_overflow(const Type* t1, const Type* t2) const = 0; diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index 2995b7dfade..44483491a9e 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -1069,7 +1069,7 @@ bool LoadNode::is_instance_field_load_with_local_phi(Node* ctrl) { //------------------------------Identity--------------------------------------- // Loads are identity if previous store is to same address -Node *LoadNode::Identity( PhaseTransform *phase ) { +Node* LoadNode::Identity(PhaseGVN* phase) { // If the previous store-maker is the right kind of Store, and the store is // to the same address, then we are equal to the value stored. Node* mem = in(Memory); @@ -1615,7 +1615,7 @@ static const Type* fold_stable_ary_elem(const TypeAryPtr* ary, int off, BasicTyp } //------------------------------Value----------------------------------------- -const Type *LoadNode::Value( PhaseTransform *phase ) const { +const Type* LoadNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP Node* mem = in(MemNode::Memory); const Type *t1 = phase->type(mem); @@ -1901,7 +1901,7 @@ Node *LoadBNode::Ideal(PhaseGVN *phase, bool can_reshape) { return LoadNode::Ideal(phase, can_reshape); } -const Type* LoadBNode::Value(PhaseTransform *phase) const { +const Type* LoadBNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); if (value != NULL && value->is_Con() && @@ -1931,7 +1931,7 @@ Node* LoadUBNode::Ideal(PhaseGVN* phase, bool can_reshape) { return LoadNode::Ideal(phase, can_reshape); } -const Type* LoadUBNode::Value(PhaseTransform *phase) const { +const Type* LoadUBNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); if (value != NULL && value->is_Con() && @@ -1961,7 +1961,7 @@ Node *LoadUSNode::Ideal(PhaseGVN *phase, bool can_reshape) { return LoadNode::Ideal(phase, can_reshape); } -const Type* LoadUSNode::Value(PhaseTransform *phase) const { +const Type* LoadUSNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); if (value != NULL && value->is_Con() && @@ -1993,7 +1993,7 @@ Node *LoadSNode::Ideal(PhaseGVN *phase, bool can_reshape) { return LoadNode::Ideal(phase, can_reshape); } -const Type* LoadSNode::Value(PhaseTransform *phase) const { +const Type* LoadSNode::Value(PhaseGVN* phase) const { Node* mem = in(MemNode::Memory); Node* value = can_see_stored_value(mem,phase); if (value != NULL && value->is_Con() && @@ -2026,7 +2026,7 @@ Node* LoadKlassNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const } //------------------------------Value------------------------------------------ -const Type *LoadKlassNode::Value( PhaseTransform *phase ) const { +const Type* LoadKlassNode::Value(PhaseGVN* phase) const { return klass_value_common(phase); } @@ -2036,7 +2036,7 @@ bool LoadKlassNode::can_remove_control() const { return false; } -const Type *LoadNode::klass_value_common( PhaseTransform *phase ) const { +const Type* LoadNode::klass_value_common(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(MemNode::Memory) ); if (t1 == Type::TOP) return Type::TOP; @@ -2172,11 +2172,11 @@ const Type *LoadNode::klass_value_common( PhaseTransform *phase ) const { //------------------------------Identity--------------------------------------- // To clean up reflective code, simplify k.java_mirror.as_klass to plain k. // Also feed through the klass in Allocate(...klass...)._klass. -Node* LoadKlassNode::Identity( PhaseTransform *phase ) { +Node* LoadKlassNode::Identity(PhaseGVN* phase) { return klass_identity_common(phase); } -Node* LoadNode::klass_identity_common(PhaseTransform *phase ) { +Node* LoadNode::klass_identity_common(PhaseGVN* phase) { Node* x = LoadNode::Identity(phase); if (x != this) return x; @@ -2231,7 +2231,7 @@ Node* LoadNode::klass_identity_common(PhaseTransform *phase ) { //------------------------------Value------------------------------------------ -const Type *LoadNKlassNode::Value( PhaseTransform *phase ) const { +const Type* LoadNKlassNode::Value(PhaseGVN* phase) const { const Type *t = klass_value_common(phase); if (t == Type::TOP) return t; @@ -2242,7 +2242,7 @@ const Type *LoadNKlassNode::Value( PhaseTransform *phase ) const { //------------------------------Identity--------------------------------------- // To clean up reflective code, simplify k.java_mirror.as_klass to narrow k. // Also feed through the klass in Allocate(...klass...)._klass. -Node* LoadNKlassNode::Identity( PhaseTransform *phase ) { +Node* LoadNKlassNode::Identity(PhaseGVN* phase) { Node *x = klass_identity_common(phase); const Type *t = phase->type( x ); @@ -2254,7 +2254,7 @@ Node* LoadNKlassNode::Identity( PhaseTransform *phase ) { } //------------------------------Value----------------------------------------- -const Type *LoadRangeNode::Value( PhaseTransform *phase ) const { +const Type* LoadRangeNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(MemNode::Memory) ); if( t1 == Type::TOP ) return Type::TOP; @@ -2302,7 +2302,7 @@ Node *LoadRangeNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Identity--------------------------------------- // Feed through the length in AllocateArray(...length...)._length. -Node* LoadRangeNode::Identity( PhaseTransform *phase ) { +Node* LoadRangeNode::Identity(PhaseGVN* phase) { Node* x = LoadINode::Identity(phase); if (x != this) return x; @@ -2473,7 +2473,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value----------------------------------------- -const Type *StoreNode::Value( PhaseTransform *phase ) const { +const Type* StoreNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(MemNode::Memory) ); if( t1 == Type::TOP ) return Type::TOP; @@ -2488,7 +2488,7 @@ const Type *StoreNode::Value( PhaseTransform *phase ) const { // Remove redundant stores: // Store(m, p, Load(m, p)) changes to m. // Store(, p, x) -> Store(m, p, x) changes to Store(m, p, x). -Node *StoreNode::Identity( PhaseTransform *phase ) { +Node* StoreNode::Identity(PhaseGVN* phase) { Node* mem = in(MemNode::Memory); Node* adr = in(MemNode::Address); Node* val = in(MemNode::ValueIn); @@ -2642,7 +2642,7 @@ Node *StoreCNode::Ideal(PhaseGVN *phase, bool can_reshape){ //============================================================================= //------------------------------Identity--------------------------------------- -Node *StoreCMNode::Identity( PhaseTransform *phase ) { +Node* StoreCMNode::Identity(PhaseGVN* phase) { // No need to card mark when storing a null ptr Node* my_store = in(MemNode::OopStore); if (my_store->is_Store()) { @@ -2671,7 +2671,7 @@ Node *StoreCMNode::Ideal(PhaseGVN *phase, bool can_reshape){ } //------------------------------Value----------------------------------------- -const Type *StoreCMNode::Value( PhaseTransform *phase ) const { +const Type* StoreCMNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t = phase->type( in(MemNode::Memory) ); if( t == Type::TOP ) return Type::TOP; @@ -2689,7 +2689,7 @@ const Type *StoreCMNode::Value( PhaseTransform *phase ) const { //============================================================================= //----------------------------------SCMemProjNode------------------------------ -const Type * SCMemProjNode::Value( PhaseTransform *phase ) const +const Type* SCMemProjNode::Value(PhaseGVN* phase) const { return bottom_type(); } @@ -2745,7 +2745,7 @@ uint ClearArrayNode::match_edge(uint idx) const { //------------------------------Identity--------------------------------------- // Clearing a zero length array does nothing -Node *ClearArrayNode::Identity( PhaseTransform *phase ) { +Node* ClearArrayNode::Identity(PhaseGVN* phase) { return phase->type(in(2))->higher_equal(TypeX::ZERO) ? in(1) : this; } @@ -3001,7 +3001,7 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type *MemBarNode::Value( PhaseTransform *phase ) const { +const Type* MemBarNode::Value(PhaseGVN* phase) const { if( !in(0) ) return Type::TOP; if( phase->type(in(0)) == Type::TOP ) return Type::TOP; @@ -3850,7 +3850,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr, bool do_zeroing = true; // we might give up if inits are very sparse int big_init_gaps = 0; // how many large gaps have we seen? - if (ZeroTLAB) do_zeroing = false; + if (UseTLAB && ZeroTLAB) do_zeroing = false; if (!ReduceFieldZeroing && !ReduceBulkZeroing) do_zeroing = false; for (uint i = InitializeNode::RawStores, limit = req(); i < limit; i++) { @@ -3951,7 +3951,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr, remove_extra_zeroes(); // clear out all the zmems left over add_req(inits); - if (!ZeroTLAB) { + if (!(UseTLAB && ZeroTLAB)) { // If anything remains to be zeroed, zero it all now. zeroes_done = align_size_down(zeroes_done, BytesPerInt); // if it is the last unused 4 bytes of an instance, forget about it @@ -4143,7 +4143,7 @@ uint MergeMemNode::cmp( const Node &n ) const { } //------------------------------Identity--------------------------------------- -Node* MergeMemNode::Identity(PhaseTransform *phase) { +Node* MergeMemNode::Identity(PhaseGVN* phase) { // Identity if this merge point does not record any interesting memory // disambiguations. Node* base_mem = base_memory(); diff --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp index cab41bcdf0d..994b782dfbb 100644 --- a/hotspot/src/share/vm/opto/memnode.hpp +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -207,7 +207,7 @@ public: // Handle algebraic identities here. If we have an identity, return the Node // we are equivalent to. We look for Load of a Store. - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // If the load is from Field memory and the pointer is non-null, it might be possible to // zero out the control input. @@ -223,11 +223,11 @@ public: // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; // Common methods for LoadKlass and LoadNKlass nodes. - const Type *klass_value_common( PhaseTransform *phase ) const; - Node *klass_identity_common( PhaseTransform *phase ); + const Type* klass_value_common(PhaseGVN* phase) const; + Node* klass_identity_common(PhaseGVN* phase); virtual uint ideal_reg() const; virtual const Type *bottom_type() const; @@ -284,7 +284,7 @@ public: virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreB; } virtual BasicType memory_type() const { return T_BYTE; } }; @@ -298,7 +298,7 @@ public: virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreB; } virtual BasicType memory_type() const { return T_BYTE; } }; @@ -312,7 +312,7 @@ public: virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreC; } virtual BasicType memory_type() const { return T_CHAR; } }; @@ -326,7 +326,7 @@ public: virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value(PhaseTransform *phase) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreC; } virtual BasicType memory_type() const { return T_SHORT; } }; @@ -350,8 +350,8 @@ public: LoadRangeNode(Node *c, Node *mem, Node *adr, const TypeInt *ti = TypeInt::POS) : LoadINode(c, mem, adr, TypeAryPtr::RANGE, ti, MemNode::unordered) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; @@ -483,8 +483,8 @@ public: LoadKlassNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeKlassPtr *tk, MemOrd mo) : LoadPNode(c, mem, adr, at, tk, mo) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual bool depends_only_on_test() const { return true; } // Polymorphic factory method: @@ -503,8 +503,8 @@ public: virtual int store_Opcode() const { return Op_StoreNKlass; } virtual BasicType memory_type() const { return T_NARROWKLASS; } - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); virtual bool depends_only_on_test() const { return true; } }; @@ -581,10 +581,10 @@ public: // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; // Check for identity function on memory (Load then Store at same address) - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // Do not match memory edge virtual uint match_edge(uint idx) const; @@ -746,9 +746,9 @@ public: "bad oop alias idx"); } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual BasicType memory_type() const { return T_VOID; } // unspecific int oop_alias_idx() const { return _oop_alias_idx; } }; @@ -782,7 +782,7 @@ public: return ctrl->in(MemNode::Memory)->adr_type(); } virtual uint ideal_reg() const { return 0;} // memory projections don't have a register - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; #ifndef PRODUCT virtual void dump_spec(outputStream *st) const {}; #endif @@ -934,7 +934,7 @@ public: // ClearArray modifies array elements, and so affects only the // array memory addressed by the bottom_type of its base address. virtual const class TypePtr *adr_type() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint match_edge(uint idx) const; @@ -983,7 +983,7 @@ public: MemBarNode(Compile* C, int alias_idx, Node* precedent); virtual int Opcode() const = 0; virtual const class TypePtr *adr_type() const { return _adr_type; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint match_edge(uint idx) const { return 0; } virtual const Type *bottom_type() const { return TypeTuple::MEMBAR; } @@ -1199,7 +1199,7 @@ public: static MergeMemNode* make(Node* base_memory); virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return NotAMachineReg; } virtual uint match_edge(uint idx) const { return 0; } diff --git a/hotspot/src/share/vm/opto/movenode.cpp b/hotspot/src/share/vm/opto/movenode.cpp index ee797cf5d28..32c9877f64e 100644 --- a/hotspot/src/share/vm/opto/movenode.cpp +++ b/hotspot/src/share/vm/opto/movenode.cpp @@ -121,7 +121,7 @@ Node *CMoveNode::is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f //------------------------------Identity--------------------------------------- // Conditional-move is an identity if both inputs are the same, or the test // true or false. -Node *CMoveNode::Identity( PhaseTransform *phase ) { +Node* CMoveNode::Identity(PhaseGVN* phase) { if( phase->eqv(in(IfFalse),in(IfTrue)) ) // C-moving identical inputs? return in(IfFalse); // Then it doesn't matter if( phase->type(in(Condition)) == TypeInt::ZERO ) @@ -149,7 +149,7 @@ Node *CMoveNode::Identity( PhaseTransform *phase ) { //------------------------------Value------------------------------------------ // Result is the meet of inputs -const Type *CMoveNode::Value( PhaseTransform *phase ) const { +const Type* CMoveNode::Value(PhaseGVN* phase) const { if( phase->type(in(Condition)) == Type::TOP ) return Type::TOP; return phase->type(in(IfFalse))->meet_speculative(phase->type(in(IfTrue))); @@ -351,7 +351,7 @@ Node *CMoveDNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type *MoveL2DNode::Value( PhaseTransform *phase ) const { +const Type* MoveL2DNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeLong *tl = t->is_long(); @@ -362,7 +362,7 @@ const Type *MoveL2DNode::Value( PhaseTransform *phase ) const { } //------------------------------Value------------------------------------------ -const Type *MoveI2FNode::Value( PhaseTransform *phase ) const { +const Type* MoveI2FNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; const TypeInt *ti = t->is_int(); @@ -373,7 +373,7 @@ const Type *MoveI2FNode::Value( PhaseTransform *phase ) const { } //------------------------------Value------------------------------------------ -const Type *MoveF2INode::Value( PhaseTransform *phase ) const { +const Type* MoveF2INode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::FLOAT ) return TypeInt::INT; @@ -384,7 +384,7 @@ const Type *MoveF2INode::Value( PhaseTransform *phase ) const { } //------------------------------Value------------------------------------------ -const Type *MoveD2LNode::Value( PhaseTransform *phase ) const { +const Type* MoveD2LNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return Type::TOP; if( t == Type::DOUBLE ) return TypeLong::LONG; diff --git a/hotspot/src/share/vm/opto/movenode.hpp b/hotspot/src/share/vm/opto/movenode.hpp index 4cd94185063..b84e784a285 100644 --- a/hotspot/src/share/vm/opto/movenode.hpp +++ b/hotspot/src/share/vm/opto/movenode.hpp @@ -45,8 +45,8 @@ class CMoveNode : public TypeNode { init_req(IfTrue,right); } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); static CMoveNode *make(Node *c, Node *bol, Node *left, Node *right, const Type *t); // Helper function to spot cmove graph shapes static Node *is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ); @@ -104,7 +104,7 @@ class MoveI2FNode : public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::FLOAT; } virtual uint ideal_reg() const { return Op_RegF; } - virtual const Type* Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; class MoveL2DNode : public Node { @@ -113,7 +113,7 @@ class MoveL2DNode : public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type* Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; class MoveF2INode : public Node { @@ -122,7 +122,7 @@ class MoveF2INode : public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } - virtual const Type* Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; class MoveD2LNode : public Node { @@ -131,7 +131,7 @@ class MoveD2LNode : public Node { virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } - virtual const Type* Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //------------------------------BinaryNode------------------------------------- diff --git a/hotspot/src/share/vm/opto/mulnode.cpp b/hotspot/src/share/vm/opto/mulnode.cpp index 4e796ce72d3..d5a7de73e92 100644 --- a/hotspot/src/share/vm/opto/mulnode.cpp +++ b/hotspot/src/share/vm/opto/mulnode.cpp @@ -46,7 +46,7 @@ uint MulNode::hash() const { //------------------------------Identity--------------------------------------- // Multiplying a one preserves the other argument -Node *MulNode::Identity( PhaseTransform *phase ) { +Node* MulNode::Identity(PhaseGVN* phase) { register const Type *one = mul_id(); // The multiplicative identity if( phase->type( in(1) )->higher_equal( one ) ) return in(2); if( phase->type( in(2) )->higher_equal( one ) ) return in(1); @@ -139,7 +139,7 @@ Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value----------------------------------------- -const Type *MulNode::Value( PhaseTransform *phase ) const { +const Type* MulNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); // Either input is TOP ==> the result is TOP @@ -381,7 +381,7 @@ const Type *MulDNode::mul_ring(const Type *t0, const Type *t1) const { //============================================================================= //------------------------------Value------------------------------------------ -const Type *MulHiLNode::Value( PhaseTransform *phase ) const { +const Type* MulHiLNode::Value(PhaseGVN* phase) const { // Either input is TOP ==> the result is TOP const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -432,7 +432,7 @@ const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const { //------------------------------Identity--------------------------------------- // Masking off the high bits of an unsigned load is not required -Node *AndINode::Identity( PhaseTransform *phase ) { +Node* AndINode::Identity(PhaseGVN* phase) { // x & x => x if (phase->eqv(in(1), in(2))) return in(1); @@ -562,7 +562,7 @@ const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const { //------------------------------Identity--------------------------------------- // Masking off the high bits of an unsigned load is not required -Node *AndLNode::Identity( PhaseTransform *phase ) { +Node* AndLNode::Identity(PhaseGVN* phase) { // x & x => x if (phase->eqv(in(1), in(2))) return in(1); @@ -639,7 +639,7 @@ Node *AndLNode::Ideal(PhaseGVN *phase, bool can_reshape) { //============================================================================= //------------------------------Identity--------------------------------------- -Node *LShiftINode::Identity( PhaseTransform *phase ) { +Node* LShiftINode::Identity(PhaseGVN* phase) { const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerInt - 1 ) ) == 0 ) ? in(1) : this; } @@ -704,7 +704,7 @@ Node *LShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A LShiftINode shifts its input2 left by input1 amount. -const Type *LShiftINode::Value( PhaseTransform *phase ) const { +const Type* LShiftINode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); // Either input is TOP ==> the result is TOP @@ -751,7 +751,7 @@ const Type *LShiftINode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *LShiftLNode::Identity( PhaseTransform *phase ) { +Node* LShiftLNode::Identity(PhaseGVN* phase) { const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; } @@ -813,7 +813,7 @@ Node *LShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A LShiftLNode shifts its input2 left by input1 amount. -const Type *LShiftLNode::Value( PhaseTransform *phase ) const { +const Type* LShiftLNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); // Either input is TOP ==> the result is TOP @@ -860,7 +860,7 @@ const Type *LShiftLNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *RShiftINode::Identity( PhaseTransform *phase ) { +Node* RShiftINode::Identity(PhaseGVN* phase) { const TypeInt *t2 = phase->type(in(2))->isa_int(); if( !t2 ) return this; if ( t2->is_con() && ( t2->get_con() & ( BitsPerInt - 1 ) ) == 0 ) @@ -959,7 +959,7 @@ Node *RShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A RShiftINode shifts its input2 right by input1 amount. -const Type *RShiftINode::Value( PhaseTransform *phase ) const { +const Type* RShiftINode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); // Either input is TOP ==> the result is TOP @@ -1014,14 +1014,14 @@ const Type *RShiftINode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *RShiftLNode::Identity( PhaseTransform *phase ) { +Node* RShiftLNode::Identity(PhaseGVN* phase) { const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; } //------------------------------Value------------------------------------------ // A RShiftLNode shifts its input2 right by input1 amount. -const Type *RShiftLNode::Value( PhaseTransform *phase ) const { +const Type* RShiftLNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); // Either input is TOP ==> the result is TOP @@ -1072,7 +1072,7 @@ const Type *RShiftLNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *URShiftINode::Identity( PhaseTransform *phase ) { +Node* URShiftINode::Identity(PhaseGVN* phase) { const TypeInt *ti = phase->type( in(2) )->isa_int(); if ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerInt - 1 ) ) == 0 ) return in(1); @@ -1168,7 +1168,7 @@ Node *URShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A URShiftINode shifts its input2 right by input1 amount. -const Type *URShiftINode::Value( PhaseTransform *phase ) const { +const Type* URShiftINode::Value(PhaseGVN* phase) const { // (This is a near clone of RShiftINode::Value.) const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); @@ -1242,7 +1242,7 @@ const Type *URShiftINode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Identity--------------------------------------- -Node *URShiftLNode::Identity( PhaseTransform *phase ) { +Node* URShiftLNode::Identity(PhaseGVN* phase) { const TypeInt *ti = phase->type( in(2) )->isa_int(); // shift count is an int return ( ti && ti->is_con() && ( ti->get_con() & ( BitsPerLong - 1 ) ) == 0 ) ? in(1) : this; } @@ -1297,7 +1297,7 @@ Node *URShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // A URShiftINode shifts its input2 right by input1 amount. -const Type *URShiftLNode::Value( PhaseTransform *phase ) const { +const Type* URShiftLNode::Value(PhaseGVN* phase) const { // (This is a near clone of RShiftLNode::Value.) const Type *t1 = phase->type( in(1) ); const Type *t2 = phase->type( in(2) ); diff --git a/hotspot/src/share/vm/opto/mulnode.hpp b/hotspot/src/share/vm/opto/mulnode.hpp index c3adc433fda..987f2e9e695 100644 --- a/hotspot/src/share/vm/opto/mulnode.hpp +++ b/hotspot/src/share/vm/opto/mulnode.hpp @@ -47,7 +47,7 @@ public: // Handle algebraic identities here. If we have an identity, return the Node // we are equivalent to. We look for "add of zero" as an identity. - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // We also canonicalize the Node, moving constants to the right input, // and flatten expressions (so that 1+x+2 becomes x+3). @@ -55,7 +55,7 @@ public: // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; // Supplied function returns the product of the inputs. // This also type-checks the inputs for sanity. Guaranteed never to @@ -146,7 +146,7 @@ class MulHiLNode : public Node { public: MulHiLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } }; @@ -159,7 +159,7 @@ public: AndINode( Node *in1, Node *in2 ) : MulINode(in1,in2) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual const Type *mul_ring( const Type *, const Type * ) const; const Type *mul_id() const { return TypeInt::MINUS_1; } const Type *add_id() const { return TypeInt::ZERO; } @@ -176,7 +176,7 @@ public: AndLNode( Node *in1, Node *in2 ) : MulLNode(in1,in2) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual const Type *mul_ring( const Type *, const Type * ) const; const Type *mul_id() const { return TypeLong::MINUS_1; } const Type *add_id() const { return TypeLong::ZERO; } @@ -191,9 +191,9 @@ class LShiftINode : public Node { public: LShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -204,9 +204,9 @@ class LShiftLNode : public Node { public: LShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } }; @@ -217,9 +217,9 @@ class RShiftINode : public Node { public: RShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -230,8 +230,8 @@ class RShiftLNode : public Node { public: RShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } }; @@ -243,9 +243,9 @@ class URShiftINode : public Node { public: URShiftINode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } }; @@ -256,9 +256,9 @@ class URShiftLNode : public Node { public: URShiftLNode( Node *in1, Node *in2 ) : Node(0,in1,in2) {} virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type *bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } }; diff --git a/hotspot/src/share/vm/opto/multnode.cpp b/hotspot/src/share/vm/opto/multnode.cpp index 83ef36b7621..184e6a94bf4 100644 --- a/hotspot/src/share/vm/opto/multnode.cpp +++ b/hotspot/src/share/vm/opto/multnode.cpp @@ -147,7 +147,7 @@ void ProjNode::check_con() const { } //------------------------------Value------------------------------------------ -const Type *ProjNode::Value( PhaseTransform *phase ) const { +const Type* ProjNode::Value(PhaseGVN* phase) const { if (in(0) == NULL) return Type::TOP; return proj_type(phase->type(in(0))); } diff --git a/hotspot/src/share/vm/opto/multnode.hpp b/hotspot/src/share/vm/opto/multnode.hpp index 25f8c503436..ec32fea977b 100644 --- a/hotspot/src/share/vm/opto/multnode.hpp +++ b/hotspot/src/share/vm/opto/multnode.hpp @@ -81,7 +81,7 @@ public: virtual const Type *bottom_type() const; virtual const TypePtr *adr_type() const; virtual bool pinned() const; - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual uint ideal_reg() const; virtual const RegMask &out_RegMask() const; diff --git a/hotspot/src/share/vm/opto/narrowptrnode.cpp b/hotspot/src/share/vm/opto/narrowptrnode.cpp index de88eb6393b..412ecae69c5 100644 --- a/hotspot/src/share/vm/opto/narrowptrnode.cpp +++ b/hotspot/src/share/vm/opto/narrowptrnode.cpp @@ -26,7 +26,7 @@ #include "opto/narrowptrnode.hpp" #include "opto/phaseX.hpp" -Node* DecodeNNode::Identity(PhaseTransform* phase) { +Node* DecodeNNode::Identity(PhaseGVN* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); @@ -37,7 +37,7 @@ Node* DecodeNNode::Identity(PhaseTransform* phase) { return this; } -const Type *DecodeNNode::Value( PhaseTransform *phase ) const { +const Type* DecodeNNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; if (t == TypeNarrowOop::NULL_PTR) return TypePtr::NULL_PTR; @@ -46,7 +46,7 @@ const Type *DecodeNNode::Value( PhaseTransform *phase ) const { return t->make_ptr(); } -Node* EncodePNode::Identity(PhaseTransform* phase) { +Node* EncodePNode::Identity(PhaseGVN* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); @@ -57,7 +57,7 @@ Node* EncodePNode::Identity(PhaseTransform* phase) { return this; } -const Type *EncodePNode::Value( PhaseTransform *phase ) const { +const Type* EncodePNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; if (t == TypePtr::NULL_PTR) return TypeNarrowOop::NULL_PTR; @@ -67,7 +67,7 @@ const Type *EncodePNode::Value( PhaseTransform *phase ) const { } -Node* DecodeNKlassNode::Identity(PhaseTransform* phase) { +Node* DecodeNKlassNode::Identity(PhaseGVN* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); @@ -78,7 +78,7 @@ Node* DecodeNKlassNode::Identity(PhaseTransform* phase) { return this; } -const Type *DecodeNKlassNode::Value( PhaseTransform *phase ) const { +const Type* DecodeNKlassNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; assert(t != TypeNarrowKlass::NULL_PTR, "null klass?"); @@ -87,7 +87,7 @@ const Type *DecodeNKlassNode::Value( PhaseTransform *phase ) const { return t->make_ptr(); } -Node* EncodePKlassNode::Identity(PhaseTransform* phase) { +Node* EncodePKlassNode::Identity(PhaseGVN* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); @@ -98,7 +98,7 @@ Node* EncodePKlassNode::Identity(PhaseTransform* phase) { return this; } -const Type *EncodePKlassNode::Value( PhaseTransform *phase ) const { +const Type* EncodePKlassNode::Value(PhaseGVN* phase) const { const Type *t = phase->type( in(1) ); if (t == Type::TOP) return Type::TOP; assert (t != TypePtr::NULL_PTR, "null klass?"); diff --git a/hotspot/src/share/vm/opto/narrowptrnode.hpp b/hotspot/src/share/vm/opto/narrowptrnode.hpp index feecbd0b906..e900bf7d93e 100644 --- a/hotspot/src/share/vm/opto/narrowptrnode.hpp +++ b/hotspot/src/share/vm/opto/narrowptrnode.hpp @@ -52,8 +52,8 @@ class EncodePNode : public EncodeNarrowPtrNode { init_class_id(Class_EncodeP); } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; }; //------------------------------EncodePKlass-------------------------------- @@ -67,8 +67,8 @@ class EncodePKlassNode : public EncodeNarrowPtrNode { init_class_id(Class_EncodePKlass); } virtual int Opcode() const; - virtual Node *Identity( PhaseTransform *phase ); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual Node* Identity(PhaseGVN* phase); + virtual const Type* Value(PhaseGVN* phase) const; }; //------------------------------DecodeNarrowPtr-------------------------------- @@ -95,8 +95,8 @@ class DecodeNNode : public DecodeNarrowPtrNode { init_class_id(Class_DecodeN); } virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; //------------------------------DecodeNKlass-------------------------------- @@ -110,8 +110,8 @@ class DecodeNKlassNode : public DecodeNarrowPtrNode { init_class_id(Class_DecodeNKlass); } virtual int Opcode() const; - virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Identity( PhaseTransform *phase ); + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node* Identity(PhaseGVN* phase); }; #endif // SHARE_VM_OPTO_NARROWPTRNODE_HPP diff --git a/hotspot/src/share/vm/opto/node.cpp b/hotspot/src/share/vm/opto/node.cpp index ac093690bc5..42ca23d212a 100644 --- a/hotspot/src/share/vm/opto/node.cpp +++ b/hotspot/src/share/vm/opto/node.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "libadt/vectset.hpp" #include "memory/allocation.inline.hpp" +#include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" #include "opto/loopnode.hpp" @@ -516,6 +517,11 @@ Node *Node::clone() const { C->add_macro_node(n); if (is_expensive()) C->add_expensive_node(n); + // If the cloned node is a range check dependent CastII, add it to the list. + CastIINode* cast = n->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + C->add_range_check_cast(cast); + } n->set_idx(C->next_unique()); // Get new unique index as well debug_only( n->verify_construction() ); @@ -644,6 +650,11 @@ void Node::destruct() { if (is_expensive()) { compile->remove_expensive_node(this); } + CastIINode* cast = isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + compile->remove_range_check_cast(cast); + } + if (is_SafePoint()) { as_SafePoint()->delete_replaced_nodes(); } @@ -796,8 +807,9 @@ void Node::del_req( uint idx ) { // First remove corresponding def-use edge Node *n = in(idx); if (n != NULL) n->del_out((Node *)this); - _in[idx] = in(--_cnt); // Compact the array - _in[_cnt] = NULL; // NULL out emptied slot + _in[idx] = in(--_cnt); // Compact the array + // Avoid spec violation: Gap in prec edges. + close_prec_gap_at(_cnt); Compile::current()->record_modified_node(this); } @@ -810,10 +822,11 @@ void Node::del_req_ordered( uint idx ) { // First remove corresponding def-use edge Node *n = in(idx); if (n != NULL) n->del_out((Node *)this); - if (idx < _cnt - 1) { // Not last edge ? - Copy::conjoint_words_to_lower((HeapWord*)&_in[idx+1], (HeapWord*)&_in[idx], ((_cnt-idx-1)*sizeof(Node*))); + if (idx < --_cnt) { // Not last edge ? + Copy::conjoint_words_to_lower((HeapWord*)&_in[idx+1], (HeapWord*)&_in[idx], ((_cnt-idx)*sizeof(Node*))); } - _in[--_cnt] = NULL; // NULL out emptied slot + // Avoid spec violation: Gap in prec edges. + close_prec_gap_at(_cnt); Compile::current()->record_modified_node(this); } @@ -845,10 +858,12 @@ int Node::replace_edge(Node* old, Node* neww) { uint nrep = 0; for (uint i = 0; i < len(); i++) { if (in(i) == old) { - if (i < req()) + if (i < req()) { set_req(i, neww); - else + } else { + assert(find_prec_edge(neww) == -1, "spec violation: duplicated prec edge (node %d -> %d)", _idx, neww->_idx); set_prec(i, neww); + } nrep++; } } @@ -907,7 +922,7 @@ int Node::disconnect_inputs(Node *n, Compile* C) { Node* Node::uncast() const { // Should be inline: //return is_ConstraintCast() ? uncast_helper(this) : (Node*) this; - if (is_ConstraintCast() || is_CheckCastPP()) + if (is_ConstraintCast()) return uncast_helper(this); else return (Node*) this; @@ -961,8 +976,6 @@ Node* Node::uncast_helper(const Node* p) { break; } else if (p->is_ConstraintCast()) { p = p->in(1); - } else if (p->is_CheckCastPP()) { - p = p->in(1); } else { break; } @@ -982,24 +995,27 @@ void Node::add_prec( Node *n ) { // Find a precedence edge to move uint i = _cnt; - while( in(i) != NULL ) i++; + while( in(i) != NULL ) { + if (in(i) == n) return; // Avoid spec violation: duplicated prec edge. + i++; + } _in[i] = n; // Stuff prec edge over NULL if ( n != NULL) n->add_out((Node *)this); // Add mirror edge + +#ifdef ASSERT + while ((++i)<_max) { assert(_in[i] == NULL, "spec violation: Gap in prec edges (node %d)", _idx); } +#endif } //------------------------------rm_prec---------------------------------------- // Remove a precedence input. Precedence inputs are unordered, with // duplicates removed and NULLs packed down at the end. void Node::rm_prec( uint j ) { - - // Find end of precedence list to pack NULLs - uint i; - for( i=j; i<_max; i++ ) - if( !_in[i] ) // Find the NULL at end of prec edge list - break; - if (_in[j] != NULL) _in[j]->del_out((Node *)this); - _in[j] = _in[--i]; // Move last element over removed guy - _in[i] = NULL; // NULL out last element + assert(j < _max, "oob: i=%d, _max=%d", j, _max); + assert(j >= _cnt, "not a precedence edge"); + if (_in[j] == NULL) return; // Avoid spec violation: Gap in prec edges. + _in[j]->del_out((Node *)this); + close_prec_gap_at(j); } //------------------------------size_of---------------------------------------- @@ -1066,13 +1082,13 @@ void Node::raise_bottom_type(const Type* new_type) { //------------------------------Identity--------------------------------------- // Return a node that the given node is equivalent to. -Node *Node::Identity( PhaseTransform * ) { +Node* Node::Identity(PhaseGVN* phase) { return this; // Default to no identities } //------------------------------Value------------------------------------------ // Compute a new Type for a node using the Type of the inputs. -const Type *Node::Value( PhaseTransform * ) const { +const Type* Node::Value(PhaseGVN* phase) const { return bottom_type(); // Default to worst-case Type } @@ -1374,6 +1390,10 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { if (dead->is_expensive()) { igvn->C->remove_expensive_node(dead); } + CastIINode* cast = dead->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + igvn->C->remove_range_check_cast(cast); + } igvn->C->record_dead_node(dead->_idx); // Kill all inputs to the dead guy for (uint i=0; i < dead->req(); i++) { @@ -2451,7 +2471,7 @@ uint TypeNode::hash() const { uint TypeNode::cmp( const Node &n ) const { return !Type::cmp( _type, ((TypeNode&)n)._type ); } const Type *TypeNode::bottom_type() const { return _type; } -const Type *TypeNode::Value( PhaseTransform * ) const { return _type; } +const Type* TypeNode::Value(PhaseGVN* phase) const { return _type; } //------------------------------ideal_reg-------------------------------------- uint TypeNode::ideal_reg() const { diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index d737f537868..b7900ad276d 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,7 @@ class CallLeafNode; class CallNode; class CallRuntimeNode; class CallStaticJavaNode; +class CastIINode; class CatchNode; class CatchProjNode; class CheckCastPPNode; @@ -423,6 +424,16 @@ protected: } // Find first occurrence of n among my edges: int find_edge(Node* n); + int find_prec_edge(Node* n) { + for (uint i = req(); i < len(); i++) { + if (_in[i] == n) return i; + if (_in[i] == NULL) { + DEBUG_ONLY( while ((++i) < len()) assert(_in[i] == NULL, "Gap in prec edges!"); ) + break; + } + } + return -1; + } int replace_edge(Node* old, Node* neww); int replace_edges_in_range(Node* old, Node* neww, int start, int end); // NULL out all inputs to eliminate incoming Def-Use edges. @@ -476,6 +487,19 @@ private: debug_only(_last_del = n; ++_del_tick); #endif } + // Close gap after removing edge. + void close_prec_gap_at(uint gap) { + assert(_cnt <= gap && gap < _max, "no valid prec edge"); + uint i = gap; + Node *last = NULL; + for (; i < _max-1; ++i) { + Node *next = _in[i+1]; + if (next == NULL) break; + last = next; + } + _in[gap] = last; // Move last slot to empty one. + _in[i] = NULL; // NULL out last slot. + } public: // Globally replace this node by a given new node, updating all uses. @@ -492,13 +516,23 @@ public: // Add or remove precedence edges void add_prec( Node *n ); void rm_prec( uint i ); + + // Note: prec(i) will not necessarily point to n if edge already exists. void set_prec( uint i, Node *n ) { - assert( is_not_dead(n), "can not use dead node"); - assert( i >= _cnt, "not a precedence edge"); + assert(i < _max, "oob: i=%d, _max=%d", i, _max); + assert(is_not_dead(n), "can not use dead node"); + assert(i >= _cnt, "not a precedence edge"); + // Avoid spec violation: duplicated prec edge. + if (_in[i] == n) return; + if (n == NULL || find_prec_edge(n) != -1) { + rm_prec(i); + return; + } if (_in[i] != NULL) _in[i]->del_out((Node *)this); _in[i] = n; if (n != NULL) n->add_out((Node *)this); } + // Set this node's index, used by cisc_version to replace current node void set_idx(uint new_idx) { const node_idx_t* ref = &_idx; @@ -620,7 +654,8 @@ public: DEFINE_CLASS_ID(Type, Node, 2) DEFINE_CLASS_ID(Phi, Type, 0) DEFINE_CLASS_ID(ConstraintCast, Type, 1) - DEFINE_CLASS_ID(CheckCastPP, Type, 2) + DEFINE_CLASS_ID(CastII, ConstraintCast, 0) + DEFINE_CLASS_ID(CheckCastPP, ConstraintCast, 1) DEFINE_CLASS_ID(CMove, Type, 3) DEFINE_CLASS_ID(SafePointScalarObject, Type, 4) DEFINE_CLASS_ID(DecodeNarrowPtr, Type, 5) @@ -751,6 +786,7 @@ public: DEFINE_CLASS_QUERY(Catch) DEFINE_CLASS_QUERY(CatchProj) DEFINE_CLASS_QUERY(CheckCastPP) + DEFINE_CLASS_QUERY(CastII) DEFINE_CLASS_QUERY(ConstraintCast) DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) @@ -893,10 +929,10 @@ public: // Return an existing node which computes the same function as this node. // The optimistic combined algorithm requires this to return a Node which // is a small number of steps away (e.g., one of my inputs). - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // Return the set of values this Node can take on at runtime. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; // Return a node which is more "ideal" than the current node. // The invariants on this call are subtle. If in doubt, read the @@ -1663,7 +1699,7 @@ public: TypeNode( const Type *t, uint required ) : Node(required), _type(t) { init_class_id(Class_Type); } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const; virtual uint ideal_reg() const; #ifndef PRODUCT diff --git a/hotspot/src/share/vm/opto/opaquenode.cpp b/hotspot/src/share/vm/opto/opaquenode.cpp index cc0bdebc79e..f6afc7ebccf 100644 --- a/hotspot/src/share/vm/opto/opaquenode.cpp +++ b/hotspot/src/share/vm/opto/opaquenode.cpp @@ -40,7 +40,7 @@ uint Opaque1Node::cmp( const Node &n ) const { // call to IterGVN and any chance of hitting this code. Hence there's no // phase-ordering problem with stripping Opaque1 in IGVN followed by some // more loop optimizations that require it. -Node *Opaque1Node::Identity( PhaseTransform *phase ) { +Node* Opaque1Node::Identity(PhaseGVN* phase) { return phase->C->major_progress() ? this : in(1); } @@ -76,7 +76,7 @@ Node *ProfileBooleanNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } -Node *ProfileBooleanNode::Identity( PhaseTransform *phase ) { +Node* ProfileBooleanNode::Identity(PhaseGVN* phase) { if (_delay_removal) { return this; } else { diff --git a/hotspot/src/share/vm/opto/opaquenode.hpp b/hotspot/src/share/vm/opto/opaquenode.hpp index ba298864486..5c4c7e42100 100644 --- a/hotspot/src/share/vm/opto/opaquenode.hpp +++ b/hotspot/src/share/vm/opto/opaquenode.hpp @@ -50,7 +50,7 @@ class Opaque1Node : public Node { Node* original_loop_limit() { return req()==3 ? in(2) : NULL; } virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::INT; } - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); }; //------------------------------Opaque2Node------------------------------------ @@ -109,7 +109,7 @@ class ProfileBooleanNode : public Node { virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual Node *Identity(PhaseTransform *phase); + virtual Node* Identity(PhaseGVN* phase); virtual const Type *bottom_type() const { return TypeInt::BOOL; } }; diff --git a/hotspot/src/share/vm/opto/parse2.cpp b/hotspot/src/share/vm/opto/parse2.cpp index f6adb5d6241..e03abd4f931 100644 --- a/hotspot/src/share/vm/opto/parse2.cpp +++ b/hotspot/src/share/vm/opto/parse2.cpp @@ -166,7 +166,9 @@ Node* Parse::array_addressing(BasicType type, int vals, const Type* *result2) { // Check for always knowing you are throwing a range-check exception if (stopped()) return top(); - Node* ptr = array_element_address(ary, idx, type, sizetype); + // Make array address computation control dependent to prevent it + // from floating above the range check during loop optimizations. + Node* ptr = array_element_address(ary, idx, type, sizetype, control()); if (result2 != NULL) *result2 = elemtype; @@ -466,12 +468,14 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi) // of all possible ranges for a switch statement // The key_val input must be converted to a pointer offset and scaled. // Compare Parse::array_addressing above. -#ifdef _LP64 + // Clean the 32-bit int into a real 64-bit offset. // Otherwise, the jint value 0 might turn into an offset of 0x0800000000. - const TypeLong* lkeytype = TypeLong::make(CONST64(0), num_cases-1, Type::WidenMin); - key_val = _gvn.transform( new ConvI2LNode(key_val, lkeytype) ); -#endif + const TypeInt* ikeytype = TypeInt::make(0, num_cases, Type::WidenMin); + // Make I2L conversion control dependent to prevent it from + // floating above the range check during loop optimizations. + key_val = C->conv_I2X_index(&_gvn, key_val, ikeytype, control()); + // Shift the value by wordsize so we have an index into the table, rather // than a switch value Node *shiftWord = _gvn.MakeConX(wordSize); diff --git a/hotspot/src/share/vm/opto/phaseX.cpp b/hotspot/src/share/vm/opto/phaseX.cpp index 0274f0e7c98..1f6de0d1700 100644 --- a/hotspot/src/share/vm/opto/phaseX.cpp +++ b/hotspot/src/share/vm/opto/phaseX.cpp @@ -26,6 +26,7 @@ #include "memory/allocation.inline.hpp" #include "opto/block.hpp" #include "opto/callnode.hpp" +#include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/idealGraphPrinter.hpp" #include "opto/loopnode.hpp" @@ -835,6 +836,22 @@ Node *PhaseGVN::transform_no_reclaim( Node *n ) { return k; } +bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) { + if (d->is_top() || n->is_top()) { + return false; + } + assert(d->is_CFG() && n->is_CFG(), "must have CFG nodes"); + int i = 0; + while (d != n) { + n = IfNode::up_one_dom(n, linear_only); + i++; + if (n == NULL || i >= 10) { + return false; + } + } + return true; +} + #ifdef ASSERT //------------------------------dead_loop_check-------------------------------- // Check for a simple dead loop when a data node references itself directly @@ -1396,6 +1413,10 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { if (dead->is_expensive()) { C->remove_expensive_node(dead); } + CastIINode* cast = dead->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + C->remove_range_check_cast(cast); + } } } // while (_stack.is_nonempty()) } @@ -1525,7 +1546,7 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) { } // If changed Cast input, check Phi users for simple cycles - if( use->is_ConstraintCast() || use->is_CheckCastPP() ) { + if (use->is_ConstraintCast()) { for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { Node* u = use->fast_out(i2); if (u->is_Phi()) diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp index 990383e76c6..837118d177a 100644 --- a/hotspot/src/share/vm/opto/phaseX.hpp +++ b/hotspot/src/share/vm/opto/phaseX.hpp @@ -330,6 +330,9 @@ public: // Delayed node rehash if this is an IGVN phase virtual void igvn_rehash_node_delayed(Node* n) {} + // true if CFG node d dominates CFG node n + virtual bool is_dominator(Node *d, Node *n) { fatal("unimplemented for this pass"); return false; }; + #ifndef PRODUCT void dump_old2new_map() const; void dump_new( uint new_lidx ) const; @@ -397,6 +400,9 @@ public: //------------------------------PhaseGVN--------------------------------------- // Phase for performing local, pessimistic GVN-style optimizations. class PhaseGVN : public PhaseValues { +protected: + bool is_dominator_helper(Node *d, Node *n, bool linear_only); + public: PhaseGVN( Arena *arena, uint est_max_size ) : PhaseValues( arena, est_max_size ) {} PhaseGVN( PhaseGVN *gvn ) : PhaseValues( gvn ) {} @@ -415,6 +421,8 @@ public: _types = gvn->_types; } + bool is_dominator(Node *d, Node *n) { return is_dominator_helper(d, n, true); } + // Check for a simple dead loop when a data node references itself. DEBUG_ONLY(void dead_loop_check(Node *n);) }; @@ -545,6 +553,8 @@ public: _table.check_no_speculative_types(); } + bool is_dominator(Node *d, Node *n) { return is_dominator_helper(d, n, false); } + #ifndef PRODUCT protected: // Sub-quadratic implementation of VerifyIterativeGVN. diff --git a/hotspot/src/share/vm/opto/rootnode.cpp b/hotspot/src/share/vm/opto/rootnode.cpp index e5542f6ff74..dab56c1cf2b 100644 --- a/hotspot/src/share/vm/opto/rootnode.cpp +++ b/hotspot/src/share/vm/opto/rootnode.cpp @@ -79,7 +79,7 @@ Node *HaltNode::Ideal(PhaseGVN *phase, bool can_reshape) { } //------------------------------Value------------------------------------------ -const Type *HaltNode::Value( PhaseTransform *phase ) const { +const Type* HaltNode::Value(PhaseGVN* phase) const { return ( phase->type(in(TypeFunc::Control)) == Type::TOP) ? Type::TOP : Type::BOTTOM; diff --git a/hotspot/src/share/vm/opto/rootnode.hpp b/hotspot/src/share/vm/opto/rootnode.hpp index 3be5dfa7673..a15d9edf6d7 100644 --- a/hotspot/src/share/vm/opto/rootnode.hpp +++ b/hotspot/src/share/vm/opto/rootnode.hpp @@ -42,9 +42,9 @@ public: virtual int Opcode() const; virtual const Node *is_block_proj() const { return this; } virtual const Type *bottom_type() const { return Type::BOTTOM; } - virtual Node *Identity( PhaseTransform *phase ) { return this; } + virtual Node* Identity(PhaseGVN* phase) { return this; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const { return Type::BOTTOM; } + virtual const Type* Value(PhaseGVN* phase) const { return Type::BOTTOM; } }; //------------------------------HaltNode--------------------------------------- @@ -55,7 +55,7 @@ public: virtual int Opcode() const; virtual bool pinned() const { return true; }; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const; virtual bool is_CFG() const { return true; } virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash diff --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp index 241384eccc6..79036c9d539 100644 --- a/hotspot/src/share/vm/opto/stringopts.cpp +++ b/hotspot/src/share/vm/opto/stringopts.cpp @@ -822,11 +822,10 @@ bool StringConcat::validate_mem_flow() { } } else if (ctrl->is_IfTrue()) { // null checks, class checks iff = ctrl->in(0)->as_If(); - assert(iff->is_If(), "must be if"); // Verify that the other arm is an uncommon trap Node* otherproj = iff->proj_out(1 - ctrl->as_Proj()->_con); CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); - assert(strcmp(call->_name, "uncommon_trap") == 0, "must be uncommond trap"); + assert(strcmp(call->_name, "uncommon_trap") == 0, "must be uncommon trap"); ctrl = iff->in(0); } else { break; @@ -914,6 +913,13 @@ bool StringConcat::validate_control_flow() { BoolNode* b = iff->in(1)->isa_Bool(); if (b == NULL) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("unexpected input to IfNode"); + iff->in(1)->dump(); + tty->cr(); + } +#endif fail = true; break; } @@ -1460,7 +1466,7 @@ void PhaseStringOpts::copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* s // Use fast intrinsic Node* src = kit.array_element_address(src_array, kit.intcon(0), T_BYTE); Node* dst = kit.array_element_address(dst_array, start, T_BYTE); - kit.inflate_string(src, dst, __ value(count)); + kit.inflate_string(src, dst, TypeAryPtr::BYTES, __ value(count)); } else { // No intrinsic available, use slow method kit.inflate_string_slow(src_array, dst_array, start, __ value(count)); diff --git a/hotspot/src/share/vm/opto/subnode.cpp b/hotspot/src/share/vm/opto/subnode.cpp index 6e27f53c0a4..4250c6b8a20 100644 --- a/hotspot/src/share/vm/opto/subnode.cpp +++ b/hotspot/src/share/vm/opto/subnode.cpp @@ -46,7 +46,7 @@ //============================================================================= //------------------------------Identity--------------------------------------- // If right input is a constant 0, return the left input. -Node *SubNode::Identity( PhaseTransform *phase ) { +Node* SubNode::Identity(PhaseGVN* phase) { assert(in(1) != this, "Must already have called Value"); assert(in(2) != this, "Must already have called Value"); @@ -100,7 +100,7 @@ const Type* SubNode::Value_common(PhaseTransform *phase) const { return NULL; } -const Type* SubNode::Value(PhaseTransform *phase) const { +const Type* SubNode::Value(PhaseGVN* phase) const { const Type* t = Value_common(phase); if (t != NULL) { return t; @@ -378,7 +378,7 @@ const Type *SubLNode::sub( const Type *t1, const Type *t2 ) const { //============================================================================= //------------------------------Value------------------------------------------ // A subtract node differences its two inputs. -const Type *SubFPNode::Value( PhaseTransform *phase ) const { +const Type* SubFPNode::Value(PhaseGVN* phase) const { const Node* in1 = in(1); const Node* in2 = in(2); // Either input is TOP ==> the result is TOP @@ -494,7 +494,7 @@ const Type *SubDNode::sub( const Type *t1, const Type *t2 ) const { // Unlike SubNodes, compare must still flatten return value to the // range -1, 0, 1. // And optimizations like those for (X + Y) - X fail if overflow happens. -Node *CmpNode::Identity( PhaseTransform *phase ) { +Node* CmpNode::Identity(PhaseGVN* phase) { return this; } @@ -611,7 +611,7 @@ const Type *CmpUNode::sub( const Type *t1, const Type *t2 ) const { return TypeInt::CC; // else use worst case results } -const Type* CmpUNode::Value(PhaseTransform *phase) const { +const Type* CmpUNode::Value(PhaseGVN* phase) const { const Type* t = SubNode::Value_common(phase); if (t != NULL) { return t; @@ -1053,7 +1053,7 @@ Node *CmpNNode::Ideal( PhaseGVN *phase, bool can_reshape ) { //------------------------------Value------------------------------------------ // Simplify an CmpF (compare 2 floats ) node, based on local information. // If both inputs are constants, compare them. -const Type *CmpFNode::Value( PhaseTransform *phase ) const { +const Type* CmpFNode::Value(PhaseGVN* phase) const { const Node* in1 = in(1); const Node* in2 = in(2); // Either input is TOP ==> the result is TOP @@ -1083,7 +1083,7 @@ const Type *CmpFNode::Value( PhaseTransform *phase ) const { //------------------------------Value------------------------------------------ // Simplify an CmpD (compare 2 doubles ) node, based on local information. // If both inputs are constants, compare them. -const Type *CmpDNode::Value( PhaseTransform *phase ) const { +const Type* CmpDNode::Value(PhaseGVN* phase) const { const Node* in1 = in(1); const Node* in2 = in(2); // Either input is TOP ==> the result is TOP @@ -1423,7 +1423,7 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { //------------------------------Value------------------------------------------ // Simplify a Bool (convert condition codes to boolean (1 or 0)) node, // based on local information. If the input is constant, do it. -const Type *BoolNode::Value( PhaseTransform *phase ) const { +const Type* BoolNode::Value(PhaseGVN* phase) const { return _test.cc2logical( phase->type( in(1) ) ); } @@ -1466,7 +1466,7 @@ bool BoolNode::is_counted_loop_exit_test() { //============================================================================= //------------------------------Value------------------------------------------ // Compute sqrt -const Type *SqrtDNode::Value( PhaseTransform *phase ) const { +const Type* SqrtDNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; @@ -1475,32 +1475,10 @@ const Type *SqrtDNode::Value( PhaseTransform *phase ) const { return TypeD::make( sqrt( d ) ); } -//============================================================================= -//------------------------------Value------------------------------------------ -// Compute cos -const Type *CosDNode::Value( PhaseTransform *phase ) const { - const Type *t1 = phase->type( in(1) ); - if( t1 == Type::TOP ) return Type::TOP; - if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; - double d = t1->getd(); - return TypeD::make( StubRoutines::intrinsic_cos( d ) ); -} - -//============================================================================= -//------------------------------Value------------------------------------------ -// Compute sin -const Type *SinDNode::Value( PhaseTransform *phase ) const { - const Type *t1 = phase->type( in(1) ); - if( t1 == Type::TOP ) return Type::TOP; - if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; - double d = t1->getd(); - return TypeD::make( StubRoutines::intrinsic_sin( d ) ); -} - //============================================================================= //------------------------------Value------------------------------------------ // Compute tan -const Type *TanDNode::Value( PhaseTransform *phase ) const { +const Type* TanDNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; @@ -1511,7 +1489,7 @@ const Type *TanDNode::Value( PhaseTransform *phase ) const { //============================================================================= //------------------------------Value------------------------------------------ // Compute log10 -const Type *Log10DNode::Value( PhaseTransform *phase ) const { +const Type* Log10DNode::Value(PhaseGVN* phase) const { const Type *t1 = phase->type( in(1) ); if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; diff --git a/hotspot/src/share/vm/opto/subnode.hpp b/hotspot/src/share/vm/opto/subnode.hpp index 3ee7b4763b1..1812853a8a9 100644 --- a/hotspot/src/share/vm/opto/subnode.hpp +++ b/hotspot/src/share/vm/opto/subnode.hpp @@ -45,11 +45,11 @@ public: // Handle algebraic identities here. If we have an identity, return the Node // we are equivalent to. We look for "add of zero" as an identity. - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; const Type* Value_common( PhaseTransform *phase ) const; // Supplied function returns the subtractend of the inputs. @@ -97,7 +97,7 @@ class SubFPNode : public SubNode { protected: SubFPNode( Node *in1, Node *in2 ) : SubNode(in1,in2) {} public: - const Type *Value( PhaseTransform *phase ) const; + const Type* Value(PhaseGVN* phase) const; }; // NOTE: SubFNode should be taken away and replaced by add and negate @@ -135,7 +135,7 @@ public: CmpNode( Node *in1, Node *in2 ) : SubNode(in1,in2) { init_class_id(Class_Cmp); } - virtual Node *Identity( PhaseTransform *phase ); + virtual Node* Identity(PhaseGVN* phase); const Type *add_id() const { return TypeInt::ZERO; } const Type *bottom_type() const { return TypeInt::CC; } virtual uint ideal_reg() const { return Op_RegFlags; } @@ -165,7 +165,7 @@ public: CmpUNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} virtual int Opcode() const; virtual const Type *sub( const Type *, const Type * ) const; - const Type *Value( PhaseTransform *phase ) const; + const Type* Value(PhaseGVN* phase) const; bool is_index_range_check() const; }; @@ -219,7 +219,7 @@ public: CmpFNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} virtual int Opcode() const; virtual const Type *sub( const Type *, const Type * ) const { ShouldNotReachHere(); return NULL; } - const Type *Value( PhaseTransform *phase ) const; + const Type* Value(PhaseGVN* phase) const; }; //------------------------------CmpF3Node-------------------------------------- @@ -247,7 +247,7 @@ public: CmpDNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} virtual int Opcode() const; virtual const Type *sub( const Type *, const Type * ) const { ShouldNotReachHere(); return NULL; } - const Type *Value( PhaseTransform *phase ) const; + const Type* Value(PhaseGVN* phase) const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; @@ -309,7 +309,7 @@ public: BoolNode* negate(PhaseGVN* phase); virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } uint match_edge(uint idx) const { return 0; } virtual uint ideal_reg() const { return Op_RegI; } @@ -408,35 +408,6 @@ public: virtual uint ideal_reg() const { return Op_RegD; } }; -//------------------------------CosDNode--------------------------------------- -// Cosinus of a double -class CosDNode : public Node { -public: - CosDNode(Compile* C, Node *c, Node *in1) : Node(c, in1) { - init_flags(Flag_is_expensive); - C->add_expensive_node(this); - } - virtual int Opcode() const; - const Type *bottom_type() const { return Type::DOUBLE; } - virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type *Value( PhaseTransform *phase ) const; -}; - -//------------------------------CosDNode--------------------------------------- -// Sinus of a double -class SinDNode : public Node { -public: - SinDNode(Compile* C, Node *c, Node *in1) : Node(c, in1) { - init_flags(Flag_is_expensive); - C->add_expensive_node(this); - } - virtual int Opcode() const; - const Type *bottom_type() const { return Type::DOUBLE; } - virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type *Value( PhaseTransform *phase ) const; -}; - - //------------------------------TanDNode--------------------------------------- // tangens of a double class TanDNode : public Node { @@ -448,7 +419,7 @@ public: virtual int Opcode() const; const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; @@ -474,7 +445,7 @@ public: virtual int Opcode() const; const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //------------------------------Log10DNode--------------------------------------- @@ -488,7 +459,7 @@ public: virtual int Opcode() const; const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } - virtual const Type *Value( PhaseTransform *phase ) const; + virtual const Type* Value(PhaseGVN* phase) const; }; //-------------------------------ReverseBytesINode-------------------------------- diff --git a/hotspot/src/share/vm/opto/superword.cpp b/hotspot/src/share/vm/opto/superword.cpp index 67d27e35bf8..b7495936443 100644 --- a/hotspot/src/share/vm/opto/superword.cpp +++ b/hotspot/src/share/vm/opto/superword.cpp @@ -3343,6 +3343,11 @@ bool SWPointer::scaled_iv(Node* n) { return true; } } else if (opc == Op_ConvI2L) { + if (n->in(1)->Opcode() == Op_CastII && + n->in(1)->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + n = n->in(1); + } if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) return true; @@ -3437,11 +3442,19 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (invariant(n)) { if (opc == Op_ConvI2L) { n = n->in(1); + if (n->Opcode() == Op_CastII && + n->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + assert(invariant(n), "sanity"); + n = n->in(1); + } + } + if (n->bottom_type()->isa_int()) { + _negate_invar = negate; + _invar = n; + NOT_PRODUCT(_tracer.offset_plus_k_10(n, _invar, _negate_invar, _offset);) + return true; } - _negate_invar = negate; - _invar = n; - NOT_PRODUCT(_tracer.offset_plus_k_10(n, _invar, _negate_invar, _offset);) - return true; } NOT_PRODUCT(_tracer.offset_plus_k_11(n);) diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index 04dad288395..2067afd58d5 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -2104,6 +2104,56 @@ JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject } JVM_END +JVM_ENTRY(jint, JVM_ConstantPoolGetClassRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetClassRefIndexAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp(THREAD, sun_reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK_0); + constantTag tag = cp->tag_at(index); + if (!tag.is_field_or_method()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return (jint) cp->uncached_klass_ref_index_at(index); +} +JVM_END + +JVM_ENTRY(jint, JVM_ConstantPoolGetNameAndTypeRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetNameAndTypeRefIndexAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp(THREAD, sun_reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK_0); + constantTag tag = cp->tag_at(index); + if (!tag.is_invoke_dynamic() && !tag.is_field_or_method()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + return (jint) cp->uncached_name_and_type_ref_index_at(index); +} +JVM_END + +JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetNameAndTypeRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetNameAndTypeRefInfoAt"); + JvmtiVMObjectAllocEventCollector oam; + constantPoolHandle cp(THREAD, sun_reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK_NULL); + constantTag tag = cp->tag_at(index); + if (!tag.is_name_and_type()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + } + Symbol* member_name = cp->symbol_at(cp->name_ref_index_at(index)); + Symbol* member_sig = cp->symbol_at(cp->signature_ref_index_at(index)); + objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::String_klass(), 2, CHECK_NULL); + objArrayHandle dest(THREAD, dest_o); + Handle str = java_lang_String::create_from_symbol(member_name, CHECK_NULL); + dest->obj_at_put(0, str()); + str = java_lang_String::create_from_symbol(member_sig, CHECK_NULL); + dest->obj_at_put(1, str()); + return (jobjectArray) JNIHandles::make_local(dest()); +} +JVM_END + JVM_ENTRY(jint, JVM_ConstantPoolGetIntAt(JNIEnv *env, jobject obj, jobject unused, jint index)) { JVMWrapper("JVM_ConstantPoolGetIntAt"); @@ -2186,6 +2236,28 @@ JVM_ENTRY(jstring, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject obj, jobject u } JVM_END +JVM_ENTRY(jbyte, JVM_ConstantPoolGetTagAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +{ + JVMWrapper("JVM_ConstantPoolGetTagAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, sun_reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK_0); + constantTag tag = cp->tag_at(index); + jbyte result = tag.value(); + // If returned tag values are not from the JVM spec, e.g. tags from 100 to 105, + // they are changed to the corresponding tags from the JVM spec, so that java code in + // sun.reflect.ConstantPool will return only tags from the JVM spec, not internal ones. + if (tag.is_klass_or_reference()) { + result = JVM_CONSTANT_Class; + } else if (tag.is_string_index()) { + result = JVM_CONSTANT_String; + } else if (tag.is_method_type_in_error()) { + result = JVM_CONSTANT_MethodType; + } else if (tag.is_method_handle_in_error()) { + result = JVM_CONSTANT_MethodHandle; + } + return result; +} +JVM_END // Assertion support. ////////////////////////////////////////////////////////// diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h index 2e995cabf4d..af12c25bf1d 100644 --- a/hotspot/src/share/vm/prims/jvm.h +++ b/hotspot/src/share/vm/prims/jvm.h @@ -87,7 +87,7 @@ extern "C" { * class. */ -#define JVM_INTERFACE_VERSION 4 +#define JVM_INTERFACE_VERSION 5 JNIEXPORT jobjectArray JNICALL JVM_GetMethodParameters(JNIEnv *env, jobject method); @@ -532,6 +532,15 @@ JNIEXPORT jobject JNICALL JVM_ConstantPoolGetFieldAtIfLoaded JNIEXPORT jobjectArray JNICALL JVM_ConstantPoolGetMemberRefInfoAt (JNIEnv *env, jobject obj, jobject unused, jint index); +JNIEXPORT jobjectArray JNICALL JVM_ConstantPoolGetNameAndTypeRefInfoAt +(JNIEnv *env, jobject obj, jobject unused, jint index); + +JNIEXPORT jint JNICALL JVM_ConstantPoolGetNameAndTypeRefIndexAt +(JNIEnv *env, jobject obj, jobject unused, jint index); + +JNIEXPORT jint JNICALL JVM_ConstantPoolGetClassRefIndexAt +(JNIEnv *env, jobject obj, jobject unused, jint index); + JNIEXPORT jint JNICALL JVM_ConstantPoolGetIntAt (JNIEnv *env, jobject obj, jobject unused, jint index); @@ -550,6 +559,9 @@ JNIEXPORT jstring JNICALL JVM_ConstantPoolGetStringAt JNIEXPORT jstring JNICALL JVM_ConstantPoolGetUTF8At (JNIEnv *env, jobject obj, jobject unused, jint index); +JNIEXPORT jbyte JNICALL JVM_ConstantPoolGetTagAt +(JNIEnv *env, jobject unused, jobject jcpool, jint index); + /* * java.security.* */ diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index cbd94be37ad..00c33b644d3 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -202,6 +202,7 @@ oop MethodHandles::init_method_MemberName(Handle mname, CallInfo& info) { assert(m_klass->verify_itable_index(vmindex), ""); flags |= IS_METHOD | (JVM_REF_invokeInterface << REFERENCE_KIND_SHIFT); if (TraceInvokeDynamic) { + ttyLocker ttyl; ResourceMark rm; tty->print_cr("memberName: invokeinterface method_holder::method: %s, itableindex: %d, access_flags:", Method::name_and_sig_as_C_string(m->method_holder(), m->name(), m->signature()), @@ -242,6 +243,7 @@ oop MethodHandles::init_method_MemberName(Handle mname, CallInfo& info) { m_klass = m_klass_non_interface; } if (TraceInvokeDynamic) { + ttyLocker ttyl; ResourceMark rm; tty->print_cr("memberName: invokevirtual method_holder::method: %s, receiver: %s, vtableindex: %d, access_flags:", Method::name_and_sig_as_C_string(m->method_holder(), m->name(), m->signature()), diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index 202d419ab46..52594f9f5a0 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -565,14 +565,15 @@ WB_ENTRY(jboolean, WB_IsIntrinsicAvailable(JNIEnv* env, jobject o, jobject metho methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(method_id)); DirectiveSet* directive; + AbstractCompiler* comp = CompileBroker::compiler((int)compLevel); if (compilation_context != NULL) { compilation_context_id = reflected_method_to_jmid(thread, env, compilation_context); CHECK_JNI_EXCEPTION_(env, JNI_FALSE); methodHandle cch(THREAD, Method::checked_resolve_jmethod_id(compilation_context_id)); - directive = DirectivesStack::getMatchingDirective(cch, CompileBroker::compiler((int)compLevel)); + directive = DirectivesStack::getMatchingDirective(cch, comp); } else { // Calling with NULL matches default directive - directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler((int)compLevel)); + directive = DirectivesStack::getDefaultDirective(comp); } bool result = CompileBroker::compiler(compLevel)->is_intrinsic_available(mh, directive); DirectivesStack::release(directive); @@ -1095,6 +1096,7 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo CodeBlobStub stub(code); jobjectArray codeBlob = codeBlob2objectArray(thread, env, &stub); + CHECK_JNI_EXCEPTION_(env, NULL); env->SetObjectArrayElement(result, 0, codeBlob); jobject level = integerBox(thread, env, code->comp_level()); @@ -1180,6 +1182,7 @@ WB_ENTRY(jobjectArray, WB_GetCodeHeapEntries(JNIEnv* env, jobject o, jint blob_t for (GrowableArrayIterator it = blobs.begin(); it != blobs.end(); ++it) { jobjectArray obj = codeBlob2objectArray(thread, env, *it); + CHECK_JNI_EXCEPTION_(env, NULL); env->SetObjectArrayElement(result, i, obj); ++i; } diff --git a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp index 5d9a3096787..1d046e6abe0 100644 --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp @@ -165,7 +165,7 @@ bool AdvancedThresholdPolicy::is_method_profiled(Method* method) { // Called with the queue locked and with at least one element CompileTask* AdvancedThresholdPolicy::select_task(CompileQueue* compile_queue) { #if INCLUDE_JVMCI - CompileTask *max_non_jvmci_task = NULL; + CompileTask *max_blocking_task = NULL; #endif CompileTask *max_task = NULL; Method* max_method = NULL; @@ -197,13 +197,25 @@ CompileTask* AdvancedThresholdPolicy::select_task(CompileQueue* compile_queue) { max_method = method; } } +#if INCLUDE_JVMCI + if (UseJVMCICompiler && task->is_blocking()) { + if (max_blocking_task == NULL || compare_methods(method, max_blocking_task->method())) { + max_blocking_task = task; + } + } +#endif task = next_task; } #if INCLUDE_JVMCI if (UseJVMCICompiler) { - if (max_non_jvmci_task != NULL) { - max_task = max_non_jvmci_task; + if (max_blocking_task != NULL) { + // In blocking compilation mode, the CompileBroker will make + // compilations submitted by a JVMCI compiler thread non-blocking. These + // compilations should be scheduled after all blocking compilations + // to service non-compiler related compilations sooner and reduce the + // chance of such compilations timing out. + max_task = max_blocking_task; max_method = max_task->method(); } } diff --git a/hotspot/src/share/vm/runtime/compilationPolicy.cpp b/hotspot/src/share/vm/runtime/compilationPolicy.cpp index d6c45d1247f..aa0333968e7 100644 --- a/hotspot/src/share/vm/runtime/compilationPolicy.cpp +++ b/hotspot/src/share/vm/runtime/compilationPolicy.cpp @@ -159,6 +159,26 @@ bool CompilationPolicy::is_compilation_enabled() { return !delay_compilation_during_startup() && CompileBroker::should_compile_new_jobs(); } +CompileTask* CompilationPolicy::select_task_helper(CompileQueue* compile_queue) { +#if INCLUDE_JVMCI + if (UseJVMCICompiler && !BackgroundCompilation) { + /* + * In blocking compilation mode, the CompileBroker will make + * compilations submitted by a JVMCI compiler thread non-blocking. These + * compilations should be scheduled after all blocking compilations + * to service non-compiler related compilations sooner and reduce the + * chance of such compilations timing out. + */ + for (CompileTask* task = compile_queue->first(); task != NULL; task = task->next()) { + if (task->is_blocking()) { + return task; + } + } + } +#endif + return compile_queue->first(); +} + #ifndef PRODUCT void CompilationPolicy::print_time() { tty->print_cr ("Accumulated compilationPolicy times:"); @@ -339,7 +359,7 @@ void NonTieredCompPolicy::disable_compilation(Method* method) { } CompileTask* NonTieredCompPolicy::select_task(CompileQueue* compile_queue) { - return compile_queue->first(); + return select_task_helper(compile_queue); } bool NonTieredCompPolicy::is_mature(Method* method) { diff --git a/hotspot/src/share/vm/runtime/compilationPolicy.hpp b/hotspot/src/share/vm/runtime/compilationPolicy.hpp index 054671a846d..ae2128adb30 100644 --- a/hotspot/src/share/vm/runtime/compilationPolicy.hpp +++ b/hotspot/src/share/vm/runtime/compilationPolicy.hpp @@ -58,6 +58,8 @@ public: static void set_policy(CompilationPolicy* policy) { _policy = policy; } static CompilationPolicy* policy() { return _policy; } + static CompileTask* select_task_helper(CompileQueue* compile_queue); + // Profiling elapsedTimer* accumulated_time() { return &_accumulated_time; } void print_time() PRODUCT_RETURN; diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 62f177a7400..f257a01a143 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -826,9 +826,6 @@ public: notproduct(bool, StressCriticalJNINatives, false, \ "Exercise register saving code in critical natives") \ \ - product(bool, UseSSE42Intrinsics, false, \ - "SSE4.2 versions of intrinsics") \ - \ product(bool, UseAESIntrinsics, false, \ "Use intrinsics for AES versions of crypto") \ \ diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index aa57bbc4601..84235d8803f 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -421,7 +421,10 @@ void before_exit(JavaThread* thread) { assert(_before_exit_status == BEFORE_EXIT_DONE, "invalid state"); return; case BEFORE_EXIT_DONE: - return; + // need block to avoid SS compiler bug + { + return; + } } } @@ -432,8 +435,7 @@ void before_exit(JavaThread* thread) { if (HAS_PENDING_EXCEPTION) { Handle exception(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; - ttyLocker ttyl; - java_lang_Throwable::print_stack_trace(exception, tty); + java_lang_Throwable::java_printStackTrace(exception, THREAD); } #endif diff --git a/hotspot/src/share/vm/runtime/orderAccess.inline.hpp b/hotspot/src/share/vm/runtime/orderAccess.inline.hpp index eae1b965c00..bc3237351ea 100644 --- a/hotspot/src/share/vm/runtime/orderAccess.inline.hpp +++ b/hotspot/src/share/vm/runtime/orderAccess.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 SAP AG. All rights reserved. + * Copyright (c) 2014 SAP SE. 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 diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index 51a977a08f9..5b862a01fab 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -726,6 +726,7 @@ address SharedRuntime::compute_compiled_exc_handler(nmethod* nm, address ret_pc, #endif if (t == NULL) { + ttyLocker ttyl; tty->print_cr("MISSING EXCEPTION HANDLER for pc " INTPTR_FORMAT " and handler bci %d", p2i(ret_pc), handler_bci); tty->print_cr(" Exception:"); exception->print(); @@ -2759,7 +2760,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler(CompLevel_simple)); if (directive->PrintAssemblyOption) { - Disassembler::decode(nm, tty); + nm->print_code(); } DirectivesStack::release(directive); } diff --git a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp index db3c1cc1089..187d3b1e208 100644 --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp @@ -168,7 +168,7 @@ void SimpleThresholdPolicy::handle_counter_overflow(Method* method) { // Called with the queue locked and with at least one element CompileTask* SimpleThresholdPolicy::select_task(CompileQueue* compile_queue) { - return compile_queue->first(); + return select_task_helper(compile_queue); } void SimpleThresholdPolicy::reprofile(ScopeDesc* trap_scope, bool is_osr) { diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp index 2682be50945..0f2c5b65e17 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.cpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp @@ -155,6 +155,10 @@ address StubRoutines::_vectorizedMismatch = NULL; address StubRoutines::_dexp = NULL; address StubRoutines::_dlog = NULL; address StubRoutines::_dpow = NULL; +address StubRoutines::_dsin = NULL; +address StubRoutines::_dcos = NULL; +address StubRoutines::_dlibm_sin_cos_huge = NULL; +address StubRoutines::_dlibm_reduce_pi04l = NULL; double (* StubRoutines::_intrinsic_log10 )(double) = NULL; double (* StubRoutines::_intrinsic_sin )(double) = NULL; diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp index a3f905aed11..38fe86e631b 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.hpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp @@ -214,6 +214,10 @@ class StubRoutines: AllStatic { static address _dexp; static address _dlog; static address _dpow; + static address _dsin; + static address _dcos; + static address _dlibm_sin_cos_huge; + static address _dlibm_reduce_pi04l; // These are versions of the java.lang.Math methods which perform // the same operations as the intrinsic version. They are used for @@ -388,6 +392,10 @@ class StubRoutines: AllStatic { static address dexp() { return _dexp; } static address dlog() { return _dlog; } static address dpow() { return _dpow; } + static address dsin() { return _dsin; } + static address dcos() { return _dcos; } + static address dlibm_reduce_pi04l() { return _dlibm_reduce_pi04l; } + static address dlibm_sin_cos_huge() { return _dlibm_sin_cos_huge; } static address select_fill_function(BasicType t, bool aligned, const char* &name); diff --git a/hotspot/src/share/vm/runtime/vframe.hpp b/hotspot/src/share/vm/runtime/vframe.hpp index 654d6b823b1..dde2f246812 100644 --- a/hotspot/src/share/vm/runtime/vframe.hpp +++ b/hotspot/src/share/vm/runtime/vframe.hpp @@ -406,6 +406,7 @@ inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) { // as it were a native compiled frame (no Java-level assumptions). #ifdef ASSERT if (WizardMode) { + ttyLocker ttyl; tty->print_cr("Error in fill_from_frame: pc_desc for " INTPTR_FORMAT " not found or invalid at %d", p2i(_frame.pc()), decode_offset); diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 9893d96fd75..c6519c03600 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -860,6 +860,8 @@ typedef CompactHashtable SymbolCompactHashTable; static_field(StubRoutines, _dexp, address) \ static_field(StubRoutines, _dlog, address) \ static_field(StubRoutines, _dpow, address) \ + static_field(StubRoutines, _dsin, address) \ + static_field(StubRoutines, _dcos, address) \ static_field(StubRoutines, _vectorizedMismatch, address) \ static_field(StubRoutines, _jbyte_arraycopy, address) \ static_field(StubRoutines, _jshort_arraycopy, address) \ @@ -2054,8 +2056,6 @@ typedef CompactHashtable SymbolCompactHashTable; declare_c2_type(NegNode, Node) \ declare_c2_type(NegFNode, NegNode) \ declare_c2_type(NegDNode, NegNode) \ - declare_c2_type(CosDNode, Node) \ - declare_c2_type(SinDNode, Node) \ declare_c2_type(TanDNode, Node) \ declare_c2_type(AtanDNode, Node) \ declare_c2_type(SqrtDNode, Node) \ @@ -2396,7 +2396,7 @@ typedef CompactHashtable SymbolCompactHashTable; declare_constant(JVM_ACC_HAS_MIRANDA_METHODS) \ declare_constant(JVM_ACC_HAS_VANILLA_CONSTRUCTOR) \ declare_constant(JVM_ACC_HAS_FINALIZER) \ - declare_constant(JVM_ACC_IS_CLONEABLE) \ + declare_constant(JVM_ACC_IS_CLONEABLE_FAST) \ declare_constant(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) \ declare_constant(JVM_ACC_PROMOTED_FLAGS) \ declare_constant(JVM_ACC_FIELD_ACCESS_WATCHED) \ diff --git a/hotspot/src/share/vm/utilities/accessFlags.hpp b/hotspot/src/share/vm/utilities/accessFlags.hpp index 2cab120a880..8e9219fe8bc 100644 --- a/hotspot/src/share/vm/utilities/accessFlags.hpp +++ b/hotspot/src/share/vm/utilities/accessFlags.hpp @@ -61,7 +61,7 @@ enum { JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable JVM_ACC_HAS_VANILLA_CONSTRUCTOR = 0x20000000, // True if klass has a vanilla default constructor JVM_ACC_HAS_FINALIZER = 0x40000000, // True if klass has a non-empty finalize() method - JVM_ACC_IS_CLONEABLE = (int)0x80000000,// True if klass supports the Clonable interface + JVM_ACC_IS_CLONEABLE_FAST = (int)0x80000000,// True if klass implements the Cloneable interface and can be optimized in generated code JVM_ACC_HAS_FINAL_METHOD = 0x01000000, // True if klass has final method // Klass* and Method* flags @@ -143,7 +143,7 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC { bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; } bool has_finalizer () const { return (_flags & JVM_ACC_HAS_FINALIZER ) != 0; } bool has_final_method () const { return (_flags & JVM_ACC_HAS_FINAL_METHOD ) != 0; } - bool is_cloneable () const { return (_flags & JVM_ACC_IS_CLONEABLE ) != 0; } + bool is_cloneable_fast () const { return (_flags & JVM_ACC_IS_CLONEABLE_FAST ) != 0; } // Klass* and Method* flags bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; } void set_has_localvariable_table() { atomic_set_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } @@ -210,7 +210,7 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC { void set_has_vanilla_constructor() { atomic_set_bits(JVM_ACC_HAS_VANILLA_CONSTRUCTOR); } void set_has_finalizer() { atomic_set_bits(JVM_ACC_HAS_FINALIZER); } void set_has_final_method() { atomic_set_bits(JVM_ACC_HAS_FINAL_METHOD); } - void set_is_cloneable() { atomic_set_bits(JVM_ACC_IS_CLONEABLE); } + void set_is_cloneable_fast() { atomic_set_bits(JVM_ACC_IS_CLONEABLE_FAST); } void set_has_miranda_methods() { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS); } public: diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp index 56a84a32ef2..8d6a9398764 100644 --- a/hotspot/src/share/vm/utilities/debug.cpp +++ b/hotspot/src/share/vm/utilities/debug.cpp @@ -480,12 +480,13 @@ extern "C" void nm(intptr_t p) { extern "C" void disnm(intptr_t p) { Command c("disnm"); CodeBlob* cb = CodeCache::find_blob((address) p); - nmethod* nm = cb->as_nmethod_or_null(); - if (nm) { - nm->print(); - Disassembler::decode(nm); - } else { - cb->print(); + if (cb != NULL) { + nmethod* nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + nm->print(); + } else { + cb->print(); + } Disassembler::decode(cb); } } diff --git a/hotspot/src/share/vm/utilities/elfFuncDescTable.cpp b/hotspot/src/share/vm/utilities/elfFuncDescTable.cpp index c5a00606d55..abd80bed137 100644 --- a/hotspot/src/share/vm/utilities/elfFuncDescTable.cpp +++ b/hotspot/src/share/vm/utilities/elfFuncDescTable.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/share/vm/utilities/elfFuncDescTable.hpp b/hotspot/src/share/vm/utilities/elfFuncDescTable.hpp index 0b767a2c7b4..4ba4b0b0cb5 100644 --- a/hotspot/src/share/vm/utilities/elfFuncDescTable.hpp +++ b/hotspot/src/share/vm/utilities/elfFuncDescTable.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp index 93f2eaaac98..1892e1a5cb3 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2013 SAP AG. All rights reserved. + * Copyright (c) 2012, 2013 SAP SE. 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 diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index 9e37c98d693..ee6ad0e5ddb 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -1242,38 +1242,6 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt log_done = true; } - - static bool skip_OnError = false; - if (!skip_OnError && OnError && OnError[0]) { - skip_OnError = true; - - out.print_raw_cr("#"); - out.print_raw ("# -XX:OnError=\""); - out.print_raw (OnError); - out.print_raw_cr("\""); - - char* cmd; - const char* ptr = OnError; - while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ - out.print_raw ("# Executing "); -#if defined(LINUX) || defined(_ALLBSD_SOURCE) - out.print_raw ("/bin/sh -c "); -#elif defined(SOLARIS) - out.print_raw ("/usr/bin/sh -c "); -#endif - out.print_raw ("\""); - out.print_raw (cmd); - out.print_raw_cr("\" ..."); - - if (os::fork_and_exec(cmd) < 0) { - out.print_cr("os::fork_and_exec failed: %s (%d)", strerror(errno), errno); - } - } - - // done with OnError - OnError = NULL; - } - static bool skip_replay = ReplayCompiles; // Do not overwrite file during replay if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) { skip_replay = true; @@ -1303,6 +1271,40 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt print_bug_submit_message(&out, _thread); } + static bool skip_OnError = false; + if (!skip_OnError && OnError && OnError[0]) { + skip_OnError = true; + + // Flush output and finish logs before running OnError commands. + ostream_abort(); + + out.print_raw_cr("#"); + out.print_raw ("# -XX:OnError=\""); + out.print_raw (OnError); + out.print_raw_cr("\""); + + char* cmd; + const char* ptr = OnError; + while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ + out.print_raw ("# Executing "); +#if defined(LINUX) || defined(_ALLBSD_SOURCE) + out.print_raw ("/bin/sh -c "); +#elif defined(SOLARIS) + out.print_raw ("/usr/bin/sh -c "); +#endif + out.print_raw ("\""); + out.print_raw (cmd); + out.print_raw_cr("\" ..."); + + if (os::fork_and_exec(cmd) < 0) { + out.print_cr("os::fork_and_exec failed: %s (%d)", strerror(errno), errno); + } + } + + // done with OnError + OnError = NULL; + } + if (!UseOSErrorReporting) { // os::abort() will call abort hooks, try it first. static bool skip_os_abort = false; diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups index d93eefb068a..f7d983f5a8f 100644 --- a/hotspot/test/TEST.groups +++ b/hotspot/test/TEST.groups @@ -289,6 +289,7 @@ hotspot_compiler_3 = \ compiler/jsr292/ \ compiler/loopopts/ \ compiler/macronodes/ \ + compiler/memoryinitialization/ \ compiler/osr/ \ compiler/regalloc/ \ compiler/runtime/ \ diff --git a/hotspot/test/compiler/c2/6772683/InterruptedTest.java b/hotspot/test/compiler/c2/6772683/InterruptedTest.java index ca7fd5a98b5..aa513fa02aa 100644 --- a/hotspot/test/compiler/c2/6772683/InterruptedTest.java +++ b/hotspot/test/compiler/c2/6772683/InterruptedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,39 @@ * @test * @bug 6772683 * @summary Thread.isInterrupted() fails to return true on multiprocessor PC - * @run main/othervm InterruptedTest + * @run main/othervm InterruptedTest 100 */ public class InterruptedTest { public static void main(String[] args) throws Exception { + /* The value of the threshold determines for how many seconds + * the main thread must wait for the worker thread. On highly + * loaded systems it can take a while until the worker thread + * obtains CPU time and is able to check if it was interrupted + * by the main thread. The higher the threshold the likelier + * the worker thread can check if it was interrupted (that is + * required for successul test execution). + */ + int threshold = 100; + + if (args.length != 1) { + System.out.println("Incorrect number of arguments"); + System.exit(1); + } + + try { + threshold = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + System.out.println("Invalid argument format"); + System.exit(1); + } + + if (threshold < 1) { + System.out.println("Threshold must be at least 1"); + System.exit(1); + } + Thread workerThread = new Thread("worker") { public void run() { System.out.println("Worker thread: running..."); @@ -42,24 +69,27 @@ public class InterruptedTest { }; System.out.println("Main thread: starts a worker thread..."); workerThread.start(); - System.out.println("Main thread: waits at most 5s for the worker thread to die..."); + System.out.println("Main thread: waits 5 seconds after starting the worker thread"); workerThread.join(5000); // Wait 5 sec to let run() method to be compiled + int ntries = 0; - while (workerThread.isAlive() && ntries < 5) { + while (workerThread.isAlive() && ntries < threshold) { System.out.println("Main thread: interrupts the worker thread..."); workerThread.interrupt(); if (workerThread.isInterrupted()) { System.out.println("Main thread: worker thread is interrupted"); } ntries++; - System.out.println("Main thread: waits for the worker thread to die..."); + System.out.println("Main thread: waits 1 second for the worker thread to die..."); workerThread.join(1000); // Wait 1 sec and try again } - if (ntries == 5) { - System.out.println("Main thread: the worker thread dod not die"); + + if (ntries == threshold) { + System.out.println("Main thread: the worker thread did not die after " + + ntries + " seconds have elapsed"); System.exit(97); } + System.out.println("Main thread: bye"); } - } diff --git a/hotspot/test/compiler/c2/6880034/Test6880034.java b/hotspot/test/compiler/c2/6880034/Test6880034.java index e834f23e549..72af0958b7e 100644 --- a/hotspot/test/compiler/c2/6880034/Test6880034.java +++ b/hotspot/test/compiler/c2/6880034/Test6880034.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 SAP AG. All Rights Reserved. + * Copyright (c) 2009 SAP SE. 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 diff --git a/hotspot/test/compiler/c2/6910484/Test.java b/hotspot/test/compiler/c2/6910484/Test.java index 9835b34c6bb..3907c98fd2a 100644 --- a/hotspot/test/compiler/c2/6910484/Test.java +++ b/hotspot/test/compiler/c2/6910484/Test.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 SAP. All Rights Reserved. + * Copyright (c) 2009 SAP SE. 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 diff --git a/hotspot/test/compiler/c2/8007294/Test8007294.java b/hotspot/test/compiler/c2/8007294/Test8007294.java index 33f0fb8bbab..a335ba7fe96 100644 --- a/hotspot/test/compiler/c2/8007294/Test8007294.java +++ b/hotspot/test/compiler/c2/8007294/Test8007294.java @@ -24,6 +24,7 @@ /* * @test * @bug 8007294 + * @bug 8146999 * @summary ReduceFieldZeroing doesn't check for dependent load and can lead to incorrect execution * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline -XX:-UseOnStackReplacement -XX:-BackgroundCompilation Test8007294 * @@ -82,6 +83,7 @@ public class Test8007294 { } } for (int i = 0; i < 20000; i++) { + test2(0); // pollute profile int res = test2(1); if (res != 2) { System.out.println("FAILED test2 = " + res); diff --git a/hotspot/test/compiler/codegen/8005033/Test8005033.java b/hotspot/test/compiler/codegen/8005033/Test8005033.java index e6706390841..1918136d67e 100644 --- a/hotspot/test/compiler/codegen/8005033/Test8005033.java +++ b/hotspot/test/compiler/codegen/8005033/Test8005033.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 SAP AG. All Rights Reserved. + * Copyright (c) 2012 SAP SE. 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 diff --git a/hotspot/test/compiler/codegen/IntRotateWithImmediate.java b/hotspot/test/compiler/codegen/IntRotateWithImmediate.java index 1d617a41526..e174de699e8 100644 --- a/hotspot/test/compiler/codegen/IntRotateWithImmediate.java +++ b/hotspot/test/compiler/codegen/IntRotateWithImmediate.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 SAP AG. All Rights Reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/test/compiler/compilercontrol/commandfile/CompileOnlyTest.java b/hotspot/test/compiler/compilercontrol/commandfile/CompileOnlyTest.java index 87a5efd732e..7fccb4577ac 100644 --- a/hotspot/test/compiler/compilercontrol/commandfile/CompileOnlyTest.java +++ b/hotspot/test/compiler/compilercontrol/commandfile/CompileOnlyTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=compileonly * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build CompileOnlyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commandfile.CompileOnlyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commandfile/ExcludeTest.java b/hotspot/test/compiler/compilercontrol/commandfile/ExcludeTest.java index 0e5dc7948e0..afe38547431 100644 --- a/hotspot/test/compiler/compilercontrol/commandfile/ExcludeTest.java +++ b/hotspot/test/compiler/compilercontrol/commandfile/ExcludeTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=exclude * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build ExcludeTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commandfile.ExcludeTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commandfile/LogTest.java b/hotspot/test/compiler/compilercontrol/commandfile/LogTest.java index 60463073253..a3b7c8f26b4 100644 --- a/hotspot/test/compiler/compilercontrol/commandfile/LogTest.java +++ b/hotspot/test/compiler/compilercontrol/commandfile/LogTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=log * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build LogTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commandfile.LogTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commandfile/PrintTest.java b/hotspot/test/compiler/compilercontrol/commandfile/PrintTest.java index 2040b4aaac8..a5f3ed978f8 100644 --- a/hotspot/test/compiler/compilercontrol/commandfile/PrintTest.java +++ b/hotspot/test/compiler/compilercontrol/commandfile/PrintTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=print * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build PrintTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commandfile.PrintTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commands/CompileOnlyTest.java b/hotspot/test/compiler/compilercontrol/commands/CompileOnlyTest.java index ad4328fc018..1509473fc40 100644 --- a/hotspot/test/compiler/compilercontrol/commands/CompileOnlyTest.java +++ b/hotspot/test/compiler/compilercontrol/commands/CompileOnlyTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=compileonly * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build CompileOnlyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commands.CompileOnlyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commands/ExcludeTest.java b/hotspot/test/compiler/compilercontrol/commands/ExcludeTest.java index 4be5f37998f..86f18c60180 100644 --- a/hotspot/test/compiler/compilercontrol/commands/ExcludeTest.java +++ b/hotspot/test/compiler/compilercontrol/commands/ExcludeTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=exclude * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build ExcludeTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commands.ExcludeTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commands/LogTest.java b/hotspot/test/compiler/compilercontrol/commands/LogTest.java index 2b28436ed1a..4a000fe0a29 100644 --- a/hotspot/test/compiler/compilercontrol/commands/LogTest.java +++ b/hotspot/test/compiler/compilercontrol/commands/LogTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=log * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build LogTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commands.LogTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/commands/PrintTest.java b/hotspot/test/compiler/compilercontrol/commands/PrintTest.java index 62af5e0552b..27313abe197 100644 --- a/hotspot/test/compiler/compilercontrol/commands/PrintTest.java +++ b/hotspot/test/compiler/compilercontrol/commands/PrintTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests CompileCommand=print * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build PrintTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.commands.PrintTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/directives/CompileOnlyTest.java b/hotspot/test/compiler/compilercontrol/directives/CompileOnlyTest.java index 12a522d777b..45034afa1ff 100644 --- a/hotspot/test/compiler/compilercontrol/directives/CompileOnlyTest.java +++ b/hotspot/test/compiler/compilercontrol/directives/CompileOnlyTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests directives to be able to compile only specified methods * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build CompileOnlyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.directives.CompileOnlyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/directives/ExcludeTest.java b/hotspot/test/compiler/compilercontrol/directives/ExcludeTest.java index cd6d01c65a1..72b30bf8720 100644 --- a/hotspot/test/compiler/compilercontrol/directives/ExcludeTest.java +++ b/hotspot/test/compiler/compilercontrol/directives/ExcludeTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests directives to be able to exclude methods from compilation * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build ExcludeTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.directives.ExcludeTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/directives/LogTest.java b/hotspot/test/compiler/compilercontrol/directives/LogTest.java index cca0b7e6754..f3147caa73e 100644 --- a/hotspot/test/compiler/compilercontrol/directives/LogTest.java +++ b/hotspot/test/compiler/compilercontrol/directives/LogTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests directives to be able to turn on LogCompilation * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build LogTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.directives.LogTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/directives/PrintTest.java b/hotspot/test/compiler/compilercontrol/directives/PrintTest.java index 466f452f48c..204e42bd130 100644 --- a/hotspot/test/compiler/compilercontrol/directives/PrintTest.java +++ b/hotspot/test/compiler/compilercontrol/directives/PrintTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests directives to be able to turn on print_assembly * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build PrintTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.directives.PrintTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/AddAndRemoveTest.java b/hotspot/test/compiler/compilercontrol/jcmd/AddAndRemoveTest.java index 937500b996f..474e2bfe36b 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/AddAndRemoveTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/AddAndRemoveTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests directives to be able to add and remove directives * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build AddAndRemoveTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.AddAndRemoveTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/AddCompileOnlyTest.java b/hotspot/test/compiler/compilercontrol/jcmd/AddCompileOnlyTest.java index 010cc16661f..ef0df375170 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/AddCompileOnlyTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/AddCompileOnlyTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests jcmd to be able to add a directive to compile only specified methods * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build AddCompileOnlyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.AddCompileOnlyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/AddExcludeTest.java b/hotspot/test/compiler/compilercontrol/jcmd/AddExcludeTest.java index e17d104879c..20282fbd41c 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/AddExcludeTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/AddExcludeTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests jcmd to be able to add a directive to exclude only specified methods * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build AddExcludeTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.AddExcludeTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/AddLogTest.java b/hotspot/test/compiler/compilercontrol/jcmd/AddLogTest.java index 12610af9777..aaf6abb78f2 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/AddLogTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/AddLogTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests jcmd to be able to add a directive to log only specified methods * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build AddLogTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.AddLogTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/AddPrintAssemblyTest.java b/hotspot/test/compiler/compilercontrol/jcmd/AddPrintAssemblyTest.java index b3a0379e9ee..054f44d308b 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/AddPrintAssemblyTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/AddPrintAssemblyTest.java @@ -27,7 +27,8 @@ * @summary Tests jcmd to be able to add a directive to print assembly * only for specified methods * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build AddPrintAssemblyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.AddPrintAssemblyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java index aaccbc3b58e..25966f9f726 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java @@ -27,7 +27,8 @@ * @ignore 8140405 * @summary Tests jcmd to be able to clear directives added via options * @library /testlibrary /test/lib /compiler/testlibrary ../share / - * @build ClearDirectivesFileStackTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.ClearDirectivesFileStackTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java index 0731e0ba8c2..f9f382da110 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests clear JCMD command * @library /testlibrary /test/lib /compiler/testlibrary ../share / - * @build ClearDirectivesStackTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.ClearDirectivesStackTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java b/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java index 7e3225f6587..632e79e6c22 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests jcmd to be able to add a directive to compile only specified methods * @library /testlibrary /test/lib /compiler/testlibrary ../share / - * @build pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.PrintDirectivesTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission @@ -80,4 +81,4 @@ public class PrintDirectivesTest extends AbstractTestBase { Scenario scenario = builder.build(); scenario.execute(); } -} \ No newline at end of file +} diff --git a/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java b/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java index a955b47f4fa..326a8a0b4b6 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java @@ -27,7 +27,8 @@ * @summary Tests jcmd to be able to add a lot of huge directive files with * parallel executed jcmds until timeout has reached * @library /testlibrary /test/lib /compiler/testlibrary ../share / - * @build StressAddMultiThreadedTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.StressAddMultiThreadedTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils * compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox diff --git a/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java b/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java index 071ecd3ddf4..f8509128321 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Tests jcmd to be able to add a lot of huge directives * @library /testlibrary /test/lib /compiler/testlibrary ../share / - * @build StressAddSequentiallyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.jcmd.StressAddSequentiallyTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils * compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox diff --git a/hotspot/test/compiler/compilercontrol/matcher/MethodMatcherTest.java b/hotspot/test/compiler/compilercontrol/matcher/MethodMatcherTest.java index e38ec6e8b83..00ca74d108d 100644 --- a/hotspot/test/compiler/compilercontrol/matcher/MethodMatcherTest.java +++ b/hotspot/test/compiler/compilercontrol/matcher/MethodMatcherTest.java @@ -41,7 +41,7 @@ import java.util.regex.Pattern; * @bug 8135068 * @summary Tests CompilerCommand's method matcher * @library /testlibrary /test/lib /compiler/whitebox ../share / - * @build MethodMatcherTest + * @build compiler.compilercontrol.matcher.MethodMatcherTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions diff --git a/hotspot/test/compiler/compilercontrol/mixed/RandomCommandsTest.java b/hotspot/test/compiler/compilercontrol/mixed/RandomCommandsTest.java index 7837b16d480..c175bb04ce3 100644 --- a/hotspot/test/compiler/compilercontrol/mixed/RandomCommandsTest.java +++ b/hotspot/test/compiler/compilercontrol/mixed/RandomCommandsTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Randomly generates commands with random types * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build RandomCommandsTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.mixed.RandomCommandsTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/mixed/RandomValidCommandsTest.java b/hotspot/test/compiler/compilercontrol/mixed/RandomValidCommandsTest.java index 951789991f7..3c67043621f 100644 --- a/hotspot/test/compiler/compilercontrol/mixed/RandomValidCommandsTest.java +++ b/hotspot/test/compiler/compilercontrol/mixed/RandomValidCommandsTest.java @@ -26,7 +26,8 @@ * @bug 8137167 * @summary Randomly generates valid commands with random types * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / - * @build RandomValidCommandsTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * @build compiler.compilercontrol.mixed.RandomValidCommandsTest + * pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/compilercontrol/share/pool/SubMethodHolder.java b/hotspot/test/compiler/compilercontrol/share/pool/SubMethodHolder.java new file mode 100644 index 00000000000..7ccc7393fbb --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/share/pool/SubMethodHolder.java @@ -0,0 +1,49 @@ +package pool; + +import jdk.test.lib.Pair; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * A helper class that creates executables and callables for internal classes + * It's necessary to have this class to make all helper lambdas not contain + * any of class names that could be used as a pattern (Internal*, *Klass*) + */ +public abstract class SubMethodHolder extends MethodHolder { + @Override + public List>> getAllMethods() { + List>> pairs = new ArrayList<>(); + { + Method method = getMethod(this, "method", Float.class); + Pair> pair = new Pair<>(method, + () -> method.invoke(this, 3.141592f)); + pairs.add(pair); + } + { + Method method = getMethod(this, "methodDup"); + Pair> pair = new Pair<>(method, + () -> method.invoke(this)); + pairs.add(pair); + } + { + Method method = getMethod(this, "smethod", Integer.class); + Pair> pair = new Pair<>(method, + () -> method.invoke(this, 1024)); + pairs.add(pair); + } + try { + Constructor constructor = this.getClass().getConstructor(); + Pair> pair = new Pair<>(constructor, + constructor::newInstance); + pairs.add(pair); + } catch (NoSuchMethodException e) { + throw new Error("TESTBUG: unable to get constructor"); + } + return pairs; + } +} diff --git a/hotspot/test/compiler/compilercontrol/share/pool/sub/Klass.java b/hotspot/test/compiler/compilercontrol/share/pool/sub/Klass.java index 9d677544e74..f1a3b3ecb53 100644 --- a/hotspot/test/compiler/compilercontrol/share/pool/sub/Klass.java +++ b/hotspot/test/compiler/compilercontrol/share/pool/sub/Klass.java @@ -23,13 +23,8 @@ package pool.sub; -import jdk.test.lib.Pair; import pool.MethodHolder; - -import java.lang.reflect.Executable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; +import pool.SubMethodHolder; /** * Simple class with methods to test signatures @@ -53,7 +48,7 @@ public class Klass extends MethodHolder { } // Internal class and constructor - public static class Internal extends MethodHolder { + public static class Internal extends SubMethodHolder { public Internal() { } public Double method(Float fl) { return Double.valueOf(fl); } @@ -66,27 +61,5 @@ public class Klass extends MethodHolder { Integer var = 1024; return arg + var; } - - @Override - public List>> getAllMethods() { - List>> pairs = new ArrayList<>(); - Pair> pair = new Pair<> - (getMethod(this, "method", Float.class), - () -> this.method(3.141592f)); - pairs.add(pair); - pair = new Pair<>(getMethod(this, "methodDup"), this::methodDup); - pairs.add(pair); - pair = new Pair<>(getMethod(this, "smethod", Integer.class), - () -> smethod(1024)); - pairs.add(pair); - try { - pair = new Pair<>(this.getClass().getConstructor(), - Internal::new); - pairs.add(pair); - } catch (NoSuchMethodException e) { - throw new Error("TESTBUG: unable to get constructor"); - } - return pairs; - } } } diff --git a/hotspot/test/compiler/compilercontrol/share/pool/subpack/KlassDup.java b/hotspot/test/compiler/compilercontrol/share/pool/subpack/KlassDup.java index 6349abdc167..f5e930ba96a 100644 --- a/hotspot/test/compiler/compilercontrol/share/pool/subpack/KlassDup.java +++ b/hotspot/test/compiler/compilercontrol/share/pool/subpack/KlassDup.java @@ -23,13 +23,8 @@ package pool.subpack; -import jdk.test.lib.Pair; import pool.MethodHolder; - -import java.lang.reflect.Executable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; +import pool.SubMethodHolder; /** * This is a clone of the pool.sub.Klass used to test pattern matching @@ -54,7 +49,7 @@ public class KlassDup extends MethodHolder { } // Internal class and constructor - public static class Internal extends MethodHolder { + public static class Internal extends SubMethodHolder { public Internal() { } public Double method(Float fl) { return Double.valueOf(fl); } @@ -67,27 +62,5 @@ public class KlassDup extends MethodHolder { Integer var = 1024; return arg + var; } - - @Override - public List>> getAllMethods() { - List>> pairs = new ArrayList<>(); - Pair> pair = new Pair<> - (getMethod(this, "method", Float.class), - () -> this.method(3.141592f)); - pairs.add(pair); - pair = new Pair<>(getMethod(this, "methodDup"), this::methodDup); - pairs.add(pair); - pair = new Pair<>(getMethod(this, "smethod", Integer.class), - () -> smethod(1024)); - pairs.add(pair); - try { - pair = new Pair<>(this.getClass().getConstructor(), - Internal::new); - pairs.add(pair); - } catch (NoSuchMethodException e) { - throw new Error("TESTBUG: unable to get constructor"); - } - return pairs; - } } } diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/AbstractCommandBuilder.java b/hotspot/test/compiler/compilercontrol/share/scenario/AbstractCommandBuilder.java index 0d78cadd8e0..eafbefb31f1 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/AbstractCommandBuilder.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/AbstractCommandBuilder.java @@ -105,15 +105,15 @@ public abstract class AbstractCommandBuilder Map states = new HashMap<>(); for (Pair> pair : METHODS) { Executable exec = pair.first; - State state = getState(commandList, states, exec); + State state = getState(commandList, exec); states.put(exec, state); } return states; } private State getState(List commandList, - Map states, Executable exec) { - State state = states.getOrDefault(exec, new State()); + Executable exec) { + State state = new State(); MethodDescriptor execDesc = new MethodDescriptor(exec); for (CompileCommand compileCommand : commandList) { if (compileCommand.isValid()) { @@ -149,7 +149,8 @@ public abstract class AbstractCommandBuilder && (compileCommand.command == Command.COMPILEONLY)) { MethodDescriptor md = compileCommand.methodDescriptor; if (!execDesc.getCanonicalString().matches(md.getRegexp()) - && (state.getCompilableOptional( + // if compilation state wasn't set before + && (!state.getCompilableOptional( // no matter C1, C2 or both Scenario.Compiler.C2).isPresent())) { /* compileonly excludes only methods that haven't been diff --git a/hotspot/test/compiler/controldependency/TestEliminatedCastPPAtPhi.java b/hotspot/test/compiler/controldependency/TestEliminatedCastPPAtPhi.java new file mode 100644 index 00000000000..9c3daafbb9b --- /dev/null +++ b/hotspot/test/compiler/controldependency/TestEliminatedCastPPAtPhi.java @@ -0,0 +1,105 @@ +/* + * 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 8139771 + * @summary Eliminating CastPP nodes at Phis when they all come from a unique input may cause crash + * @requires vm.gc=="Serial" | vm.gc=="Parallel" + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+IgnoreUnrecognizedVMOptions -XX:+StressGCM TestEliminatedCastPPAtPhi + * + */ + +public class TestEliminatedCastPPAtPhi { + + static TestEliminatedCastPPAtPhi saved; + static TestEliminatedCastPPAtPhi saved_not_null; + + int f; + + static int test(TestEliminatedCastPPAtPhi obj, int[] array, boolean flag) { + int ret = array[0] + array[20]; + saved = obj; + if (obj == null) { + return ret; + } + saved_not_null = obj; + + // empty loop to be optimized out. Delays range check smearing + // for the array access below until the if diamond is + // optimized out + int i = 0; + for (; i < 10; i++); + + ret += array[i]; + + TestEliminatedCastPPAtPhi res; + if (flag) { + // load is optimized out and res is obj here + res = saved; + } else { + // load is optimized out and res is non null CastPP of res here + res = saved_not_null; + } + // null check + CastPP here for res field load below + + // 1) null check is pushed in the branches of the if above by + // split through phi because res is non null in the second + // branch and the null check can be optimized out in that + // branch. The Castpp stays here. + + // 2) null check in the first branch is also optimized out + // because a dominating null check is found (the explicit null + // check at the beggining of the test) + + // 3) the Phi for the if above merges a CastPP'ed value and + // the same value so it's optimized out and replaced by the + // uncasted value: obj + + // 4) the if above has 2 empty branches so it's optimized + // out. The control of the CastPP that is still here is now + // the success branch of the range check for the array access + // above + + // 5) the loop above is optimized out, i = 10, the range check + // for the array access above is optimized out and all its + // uses are replaced by the range check for the array accesses + // at the beginning of the method. The castPP here is one of + // the uses and so its control is now the range check at the + // beginning of the method: the control of the CastPP bypasses + // the explicit null check + + return ret + res.f; + } + + static public void main(String[] args) { + int[] array = new int[100]; + TestEliminatedCastPPAtPhi obj = new TestEliminatedCastPPAtPhi(); + for (int i = 0; i < 20000; i++) { + test(obj, array, (i%2) == 0); + } + test(null, array, true); + } + +} diff --git a/hotspot/test/compiler/escapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java b/hotspot/test/compiler/escapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java index d70ec5cb268..ce7be99f7a1 100644 --- a/hotspot/test/compiler/escapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java +++ b/hotspot/test/compiler/escapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 SAP AG. All Rights Reserved. + * Copyright (c) 2014 SAP SE. 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 diff --git a/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java b/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java index d48702e6da0..ff2c7e366df 100644 --- a/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java +++ b/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java @@ -23,6 +23,8 @@ import java.lang.reflect.Executable; import java.util.concurrent.Callable; import java.util.Objects; + +import jdk.test.lib.*; import compiler.whitebox.CompilerWhiteBoxTest; /* * @test @@ -105,17 +107,16 @@ public class IntrinsicAvailableTest extends CompilerWhiteBoxTest { } } - protected boolean isServerVM() { - return VMName.toLowerCase().contains("server"); - } - public void test() throws Exception { Executable intrinsicMethod = testCase.getExecutable(); - if (isServerVM()) { + if (Platform.isServer()) { if (TIERED_COMPILATION) { checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_SIMPLE); } - checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_FULL_OPTIMIZATION); + // Dont bother check JVMCI compiler - returns false on all intrinsics. + if (!Boolean.valueOf(getVMOption("UseJVMCICompiler"))) { + checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_FULL_OPTIMIZATION); + } } else { checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_SIMPLE); } diff --git a/hotspot/test/compiler/intrinsics/string/TestStringIntrinsicMemoryFlow.java b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsicMemoryFlow.java new file mode 100644 index 00000000000..86d7b309179 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsicMemoryFlow.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8144212 + * @summary Check for correct memory flow with the String compress/inflate intrinsics. + * @library /testlibrary + * @run main TestStringIntrinsicMemoryFlow + */ +public class TestStringIntrinsicMemoryFlow { + + public static void main(String[] args) { + for (int i = 0; i < 100_000; ++i) { + String s = "MyString"; + char[] c = {'M'}; + char res = testInflate1(s); + Asserts.assertEquals(res, 'M', "testInflate1 failed"); + res = testInflate2(s); + Asserts.assertEquals(res, (char)42, "testInflate2 failed"); + res = testCompress1(c); + Asserts.assertEquals(res, 'M', "testCompress1 failed"); + byte resB = testCompress2(c); + Asserts.assertEquals(resB, (byte)42, "testCompress2 failed"); + } + } + + private static char testInflate1(String s) { + char c[] = new char[1]; + // Inflate String from byte[] to char[] + s.getChars(0, 1, c, 0); + // Read char[] memory written by inflate intrinsic + return c[0]; + } + + private static char testInflate2(String s) { + char c1[] = new char[1]; + char c2[] = new char[1]; + c2[0] = 42; + // Inflate String from byte[] to char[] + s.getChars(0, 1, c1, 0); + // Read char[] memory written before inflation + return c2[0]; + } + + private static char testCompress1(char[] c) { + // Compress String from char[] to byte[] + String s = new String(c); + // Read the memory written by compress intrinsic + return s.charAt(0); + } + + private static byte testCompress2(char[] c) { + byte b1[] = new byte[1]; + b1[0] = 42; + // Compress String from char[] to byte[] + new String(c); + // Read byte[] memory written before compression + return b1[0]; + } +} diff --git a/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics2.java b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics2.java new file mode 100644 index 00000000000..21c64179531 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/string/TestStringIntrinsics2.java @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016 SAP SE. 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 8145336 + * @summary PPC64: fix string intrinsics after CompactStrings change + * @library /testlibrary /../../test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -XX:MaxInlineSize=100 + * -XX:MinInliningThreshold=0 + * TestStringIntrinsics2 + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.function.Function; + +import static jdk.test.lib.Asserts.*; +import sun.hotspot.WhiteBox; + +public class TestStringIntrinsics2 { + // ------------------------------------------------------------------------ + // + // We test the following cases: + // - no match in string. Do we miss the end condition? Will crash if we read + // past the string. + // - no match in string, but after the string there is a match. + // Do we incorrectly report this match? We had a case where we stepped + // a few chars past the string, this test would report that error. The + // one above would not. + // - The needle is exactly at the end of the string. + // - The needle spans the end of the string + // + // A special case are needles of length 1. For these we test: + // - needle is first char + // - needle is last char + // - no match + // - match behind string. + // + // We test all these for an unknown needle, and needles known to the compiler + // of lengths 5, 2 and 1. + + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public enum Role { + TEST_ENTRY, + TEST_HELPER + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Test { + Role role(); + int compileAt() default 0; + int warmup() default 0; + String[] warmupArgs() default {}; + } + + // All this mess is needed to avoid try/catch inside the lambdas below. + // See: http://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8-streams + @SuppressWarnings ("unchecked") + private static void throwAsUnchecked(Exception exception) throws E { + throw (E)exception; + } + @FunctionalInterface + public interface Consumer_WithExceptions { + void accept(T t) throws E; + } + public static Consumer rethrowConsumer(Consumer_WithExceptions consumer) { + return t -> { + try { consumer.accept(t); } + catch (Exception exception) { throwAsUnchecked(exception); } + }; + } + + public static void main(String[] args) throws Exception { + + // Warmup helper methods + Arrays.stream(TestStringIntrinsics2.class.getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(Test.class)) + .filter(m -> m.getAnnotation(Test.class).warmup() > 0) + .forEach(rethrowConsumer(m -> { + Test a = m.getAnnotation(Test.class); + System.out.println("Warming up " + m + " " + a.warmup() + " time(s) "); + for (int i=0; i < a.warmup(); i++) { + m.invoke(null, (Object[])a.warmupArgs()); + } + })); + + // Compile helper methods + Arrays.stream(TestStringIntrinsics2.class.getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(Test.class)) + .filter(m -> m.getAnnotation(Test.class).compileAt() > 0) + .forEach(rethrowConsumer(m -> { + Test a = m.getAnnotation(Test.class); + if (WB.isMethodCompilable(m, a.compileAt())) { + WB.enqueueMethodForCompilation(m, a.compileAt()); + while (WB.isMethodQueuedForCompilation(m)) Thread.sleep(10); + System.out.println(m + " compiled at " + WB.getMethodCompilationLevel(m)); + } else { + System.out.println("Can't compile " + m + " at level " + a.compileAt()); + } + })); + + // Run test methods + Arrays.stream(TestStringIntrinsics2.class.getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(Test.class)) + .filter(m -> m.getAnnotation(Test.class).role() == Role.TEST_ENTRY) + .forEach(rethrowConsumer(m -> { + System.out.print("Executing " + m); + m.invoke(null, (Object[])null); + System.out.println(" - OK"); + })); + } + + static String text = "\n" + ""; + static String text2 = "\n" + ""; + static String[] ss = text.split("\n"); + static String[] ss2 = null; + static String needle = ""; + + @Test(role = Role.TEST_ENTRY) + public static void test_indexOf_no_match() { + int res = indexOf_no_match_unknown_needle(ss[0], ""); + assertEquals(res, -1, "test_indexOf_no_match_unknown_needle matched at: " + res); + res = indexOf_no_match_imm_needle(ss[0]); + assertEquals(res, -1, "test_indexOf_no_match_imm_needle matched at: " + res); + res = indexOf_no_match_imm2_needle(ss[0]); + assertEquals(res, -1, "test_indexOf_no_match_imm2_needle matched at: " + res); + + if (ss2 == null) ss2 = text.split("\n"); + res = indexOf_no_match_unknown_needle(ss2[0], ""); + assertEquals(res, -1, "test_indexOf_no_match_unknown_needle matched at: " + res); + res = indexOf_no_match_imm_needle(ss2[0]); + assertEquals(res, -1, "test_indexOf_no_match_imm_needle matched at: " + res); + res = indexOf_no_match_imm2_needle(ss2[0]); + assertEquals(res, -1, "test_indexOf_no_match_imm2_needle matched at: " + res); + res = indexOf_no_match_imm1_needle(ss2[0]); + assertEquals(res, -1, "test_indexOf_no_match_imm1_needle matched at: " + res); + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "", "" }) + static int indexOf_no_match_unknown_needle(String s, String needle) { + int index = s.indexOf(needle); + return index; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static int indexOf_no_match_imm_needle(String s) { + int index = s.indexOf(""); + return index; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static int indexOf_no_match_imm2_needle(String s) { + int index = s.indexOf("" }) + static int indexOf_no_match_imm1_needle(String s) { + int index = s.indexOf("m"); + return index; + } + + @Test(role = Role.TEST_ENTRY) + public static void test_indexOf_reads_past_string() { + if (ss == null) ss = text.split("\n"); + String res = indexOf_reads_past_string_unknown_needle(ss[0], ""); + assertEquals(res, null, "test_indexOf_reads_past_string_unknown_needle " + res); + res = indexOf_reads_past_string_imm_needle(ss[0]); + assertEquals(res, null, "test_indexOf_reads_past_string_imm_needle " + res); + res = indexOf_reads_past_string_imm2_needle(ss[0]); + assertEquals(res, null, "test_indexOf_reads_past_string_imm2_needle " + res); + res = indexOf_reads_past_string_imm1_needle(ss[0]); + assertEquals(res, null, "test_indexOf_reads_past_string_imm1_needle " + res); + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "", "" }) + static String indexOf_reads_past_string_unknown_needle(String s, String needle) { + int index = s.indexOf(needle); + if (index > s.length()) { + return "Found needle \"" + needle + "\" behind string of length " + s.length() + + " at position " + index + "."; + } + return null; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static String indexOf_reads_past_string_imm_needle(String s) { + int index = s.indexOf(""); + if (index > s.length()) { + return "Found needle \"\" behind string of length " + s.length() + " at position " + index + "."; + } + return null; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static String indexOf_reads_past_string_imm2_needle(String s) { + int index = s.indexOf(" s.length()) { + return "Found needle \"" }) + static String indexOf_reads_past_string_imm1_needle(String s) { + int index = s.indexOf("h"); + if (index > s.length()) { + return "Found needle \""); + assertEquals(len3, res + 5, testname); + res = indexOf_match_at_end_of_string_unknown_needle(text4, ""); + assertEquals(len4, res + 5, testname); + res = indexOf_match_at_end_of_string_unknown_needle(text5, ""); + assertEquals(len5, res + 5, testname); + res = indexOf_match_at_end_of_string_unknown_needle(text6, ""); + assertEquals(len6, res + 5, testname); + + res = indexOf_match_at_end_of_string_imm_needle(text3); + assertEquals(len3, res + 5, testname); + res = indexOf_match_at_end_of_string_imm_needle(text4); + assertEquals(len4, res + 5, testname); + res = indexOf_match_at_end_of_string_imm_needle(text5); + assertEquals(len5, res + 5, testname); + res = indexOf_match_at_end_of_string_imm_needle(text6); + assertEquals(len6, res + 5, testname); + + res = indexOf_match_at_end_of_string_imm2_needle(text7); + assertEquals(text7.length(), res + 2, testname); + res = indexOf_match_at_end_of_string_imm2_needle(text8); + assertEquals(text8.length(), res + 2, testname); + res = indexOf_match_at_end_of_string_imm2_needle(text9); + assertEquals(text9.length(), res + 2, testname); + res = indexOf_match_at_end_of_string_imm2_needle(text10); + assertEquals(text10.length(), res + 2, testname); + + res = indexOf_match_at_end_of_string_imm1_needle(text7); + assertEquals(text7.length(), res + 1, testname); + res = indexOf_match_at_end_of_string_imm1_needle(text8); + assertEquals(text8.length(), res + 1, testname); + res = indexOf_match_at_end_of_string_imm1_needle(text9); + assertEquals(text9.length(), res + 1, testname); + res = indexOf_match_at_end_of_string_imm1_needle(text10); + assertEquals(text10.length(), res + 1, testname); + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "", "" }) + static int indexOf_match_at_end_of_string_unknown_needle(String s, String needle) { + int index = s.indexOf(needle); + return index; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static int indexOf_match_at_end_of_string_imm_needle(String s) { + int index = s.indexOf(""); + return index; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static int indexOf_match_at_end_of_string_imm2_needle(String s) { + int index = s.indexOf("" }) + static int indexOf_match_at_end_of_string_imm1_needle(String s) { + int index = s.indexOf("h"); + return index; + } + + static String s0_1 = text3.substring(0, len3-1); + static String s0_2 = text3.substring(0, len3-2); + static String s0_3 = text3.substring(0, len3-3); + static String s0_4 = text3.substring(0, len3-4); + static String s1_1 = text4.substring(0, len4-1); + static String s1_2 = text4.substring(0, len4-2); + static String s1_3 = text4.substring(0, len4-3); + static String s1_4 = text4.substring(0, len4-4); + static String s2_1 = text5.substring(0, len5-1); + static String s2_2 = text5.substring(0, len5-2); + static String s2_3 = text5.substring(0, len5-3); + static String s2_4 = text5.substring(0, len5-4); + static String s3_1 = text6.substring(0, len6-1); + static String s3_2 = text6.substring(0, len6-2); + static String s3_3 = text6.substring(0, len6-3); + static String s3_4 = text6.substring(0, len6-4); + + static String s0_1x = text7 .substring(0, text7 .length()-1); + static String s1_1x = text8 .substring(0, text8 .length()-1); + static String s2_1x = text9 .substring(0, text9 .length()-1); + static String s3_1x = text10.substring(0, text10.length()-1); + + @Test(role = Role.TEST_ENTRY) + public static void test_indexOf_match_spans_end_of_string() { + String res = null; + res = indexOf_match_spans_end_of_string_unknown_needle(s0_1, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s0_1 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s0_2, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s0_2 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s0_3, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s0_3 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s0_4, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s0_4 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s1_1, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s1_1 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s1_2, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s1_2 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s1_3, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s1_3 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s1_4, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s1_4 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s2_1, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s2_1 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s2_2, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s2_2 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s2_3, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s2_3 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s2_4, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s2_4 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s3_1, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s3_1 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s3_2, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s3_2 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s3_3, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s3_3 " + res); + res = indexOf_match_spans_end_of_string_unknown_needle(s3_4, ""); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_unknown_needle s3_4 " + res); + + res = indexOf_match_spans_end_of_string_imm_needle(s0_1); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s0_1 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s0_2); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s0_2 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s0_3); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s0_3 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s0_4); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s0_4 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s1_1); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s1_1 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s1_2); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s1_2 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s1_3); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s1_3 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s1_4); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s1_4 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s2_1); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s2_1 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s2_2); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s2_2 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s2_3); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s2_3 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s2_4); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s2_4 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s3_1); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s3_1 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s3_2); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s3_2 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s3_3); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s3_3 " + res); + res = indexOf_match_spans_end_of_string_imm_needle(s3_4); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm_needle s3_4 " + res); + + res = indexOf_match_spans_end_of_string_imm2_needle(s0_1x); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm2_needle s0_1x " + res); + res = indexOf_match_spans_end_of_string_imm2_needle(s1_1x); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm2_needle s1_1x " + res); + res = indexOf_match_spans_end_of_string_imm2_needle(s2_1x); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm2_needle s2_1x " + res); + res = indexOf_match_spans_end_of_string_imm2_needle(s3_1x); + assertEquals(res, null, "test_indexOf_match_spans_end_of_string_imm2_needle s3_1x " + res); + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "" }) + static String indexOf_match_spans_end_of_string_unknown_needle(String s, String needle) { + int index = s.indexOf(needle); + if (index > -1) { + return "Found needle \"" + needle + "\" that is spanning the end of the string: " + s + "."; + } + return null; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { ""); + if (index > -1) { + return "Found needle \"\" that is spanning the end of the string: " + s + "."; + } + return null; + } + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "<" }) + static String indexOf_match_spans_end_of_string_imm2_needle(String s) { + int index = s.indexOf(" -1) { + return "Found needle \"0 + { + // check first character optimization + assertEquals(1, asmStringCompareTo("5", "4"), + "TestOther.asmStringCompareTo(\"5\", \"4\")"); + + // check real comparisons + assertEquals(1, asmStringCompareTo("diff5", "diff4"), + "TestOther.asmStringCompareTo(\"diff5\", \"diff4\")"); + assertEquals(10, asmStringCompareTo("123456789A", ""), + "TestOther.asmStringCompareTo(\"123456789A\", \"\")"); + assertEquals(10, asmStringCompareTo("ZYX123456789A", "ZYX"), + "TestOther.asmStringCompareTo(\"ZYX123456789A\", \"ZYX\")"); + } + + // very long strings (100k) + { + char[] ac = new char[(100 * 1024)]; + for (int i = 0; i < (100 * 1024); i += 315) + ac[i] = (char) ((i % 12) + 'a'); + char[] bc = new char[(100 * 1024)]; + for (int i = 0; i < (100 * 1024); i += 315) + bc[i] = (char) ((i % 12) + 'a'); + + ac[(100 * 1024) - 1] = '2'; + bc[(100 * 1024) - 1] = '2'; + String a1 = new String(ac); + String b1 = new String(bc); + assertEquals(0, asmStringCompareTo(a1, b1), + "TestOther.asmStringCompareTo(very_long_strings_1)"); + + ac[(100 * 1024) - 1] = 'X'; + bc[(100 * 1024) - 1] = 'Z'; + String a2 = new String(ac); + String b2 = new String(bc); + assertEquals(-2, asmStringCompareTo(a2, b2), + "TestOther.asmStringCompareTo(very_long_strings_2)"); + } + + // very very long strings (2M) + { + char[] ac = new char[(2 * 1024 * 1024)]; + for (int i = 0; i < (2 * 1024 * 1024); i += 315) + ac[i] = (char) ((i % 12) + 'a'); + char[] bc = new char[(2 * 1024 * 1024)]; + for (int i = 0; i < (2 * 1024 * 1024); i += 315) + bc[i] = (char) ((i % 12) + 'a'); + + ac[(2 * 1024 * 1024) - 1] = '3'; + bc[(2 * 1024 * 1024) - 1] = '3'; + String a1 = new String(ac); + String b1 = new String(bc); + assertEquals(0, asmStringCompareTo(a1, b1), + "TestOther.asmStringCompareTo(very_very_long_strings_1)"); + + ac[(2 * 1024 * 1024) - 1] = 'W'; + bc[(2 * 1024 * 1024) - 1] = 'Z'; + String a2 = new String(ac); + String b2 = new String(bc); + assertEquals(-3, asmStringCompareTo(a2, b2), + "TestOther.asmStringCompareTo(very_very_long_strings_2)"); + } + } + + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1, warmupArgs = { "abc", "abcd" }) + public static boolean asmStringEquals(String a, String b) { + return a.equals(b); + } + + static String a1 = "abcd"; + static String b1 = "abcd"; + static final String a2 = "1234"; + static final String b2 = "1234"; + + @Test(role = Role.TEST_HELPER, compileAt = 4, warmup = 1) + public static boolean asmStringEqualsConst() { + boolean ret = a1.equals(b1); + ret &= a2.equals(b2); + ret &= !a2.equals(b1); + ret &= "ABCD".equals("ABCD"); + return ret; + } + + + @Test(role = Role.TEST_ENTRY) + public static void test_asmStringEquals() { + // null + { + assertFalse(asmStringEquals("not null", null), + "TestOther.asmStringEquals(\"not null\", null)"); + } + + // true + { + // check constant optimization + assertTrue(asmStringEqualsConst(), + "TestOther.asmStringEqualsConst(\"\", \"\")"); + + // check length 0 optimization + assertTrue(asmStringEquals("", ""), + "TestOther.asmStringEquals(\"\", \"\")"); + + // check first character optimization + assertTrue(asmStringEquals("A", "A"), + "TestOther.asmStringEquals(\"A\", \"A\")"); + + // check real comparisons + assertTrue(asmStringEquals(new String("eq") + new String("ual"), "equal"), + "TestOther.asmStringEquals(\"equal\", \"equal\")"); + assertTrue(asmStringEquals("textABC", "textABC"), + "TestOther.asmStringEquals(\"textABC\", \"textABC\")"); + assertTrue(asmStringEquals(new String("abcdefgh01234") + + new String("56abcdefgh0123456abcdefgh0123456"), + "abcdefgh0123456abcdefgh0123456abcdefgh0123456"), + "TestOther.asmStringEquals(\"abcdefgh0123456abcdefgh0123456abcdefgh0123456\", " + + "\"abcdefgh0123456abcdefgh0123456abcdefgh0123456\")"); + } + } + +} diff --git a/hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java b/hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java index 884295cf77e..51481e87bb4 100644 --- a/hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java +++ b/hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * sun.hotspot.WhiteBox$WhiteBoxPermission * java.lang.invoke.RedefineTest * Agent - * jdk.test.lib.Asserts * @run main Agent agent.jar java.lang.invoke.RedefineTest * @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar * -XX:+IgnoreUnrecognizedVMOptions diff --git a/hotspot/test/compiler/jvmci/code/CodeInstallationTest.java b/hotspot/test/compiler/jvmci/code/CodeInstallationTest.java index 7ef8ab6c01a..aa0899ed256 100644 --- a/hotspot/test/compiler/jvmci/code/CodeInstallationTest.java +++ b/hotspot/test/compiler/jvmci/code/CodeInstallationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,12 @@ import java.lang.reflect.Method; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationResult; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.hotspot.HotSpotCompiledCode; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.runtime.JVMCI; import jdk.vm.ci.runtime.JVMCIBackend; import jdk.vm.ci.sparc.SPARC; @@ -65,12 +65,12 @@ public class CodeInstallationTest { void compile(TestAssembler asm); } - private TestAssembler createAssembler(CompilationResult result) { + private TestAssembler createAssembler() { Architecture arch = codeCache.getTarget().arch; if (arch instanceof AMD64) { - return new AMD64TestAssembler(result, codeCache); + return new AMD64TestAssembler(codeCache); } else if (arch instanceof SPARC) { - return new SPARCTestAssembler(result, codeCache); + return new SPARCTestAssembler(codeCache); } else { Assert.fail("unsupported architecture"); return null; @@ -87,17 +87,14 @@ public class CodeInstallationTest { } protected void test(TestCompiler compiler, Method method, Object... args) { - CompilationResult result = new CompilationResult(method.getName()); - TestAssembler asm = createAssembler(result); + HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method); + TestAssembler asm = createAssembler(); asm.emitPrologue(); compiler.compile(asm); - asm.finish(); - result.close(); - - ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(method); - InstalledCode installed = codeCache.addCode(resolvedMethod, result, null, null); + HotSpotCompiledCode code = asm.finish(resolvedMethod); + InstalledCode installed = codeCache.addCode(resolvedMethod, code, null, null); try { Object expected = method.invoke(null, args); diff --git a/hotspot/test/compiler/jvmci/code/DataPatchTest.java b/hotspot/test/compiler/jvmci/code/DataPatchTest.java index 42bf40c1af4..af438a306bf 100644 --- a/hotspot/test/compiler/jvmci/code/DataPatchTest.java +++ b/hotspot/test/compiler/jvmci/code/DataPatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,9 +30,8 @@ package compiler.jvmci.code; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; -import jdk.vm.ci.code.DataSection.Data; import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.site.DataSectionReference; import jdk.vm.ci.hotspot.HotSpotConstant; import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.meta.ResolvedJavaType; @@ -53,12 +52,11 @@ public class DataPatchTest extends CodeInstallationTest { test(compiler, getMethod("getConstClass")); } - @Test public void testInlineObject() { test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant c = (HotSpotConstant) type.getJavaClass(); + HotSpotConstant c = (HotSpotConstant) constantReflection.asJavaClass(type); Register ret = asm.emitLoadPointer(c); asm.emitPointerRet(ret); }); @@ -69,7 +67,7 @@ public class DataPatchTest extends CodeInstallationTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedOops); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant c = (HotSpotConstant) type.getJavaClass(); + HotSpotConstant c = (HotSpotConstant) constantReflection.asJavaClass(type); Register compressed = asm.emitLoadPointer((HotSpotConstant) c.compress()); Register ret = asm.emitUncompressPointer(compressed, HotSpotVMConfig.config().narrowOopBase, HotSpotVMConfig.config().narrowOopShift); asm.emitPointerRet(ret); @@ -80,9 +78,8 @@ public class DataPatchTest extends CodeInstallationTest { public void testDataSectionReference() { test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant c = (HotSpotConstant) type.getJavaClass(); - Data data = codeCache.createDataItem(c); - DataSectionReference ref = asm.result.getDataSection().insertData(data); + HotSpotConstant c = (HotSpotConstant) constantReflection.asJavaClass(type); + DataSectionReference ref = asm.emitDataItem(c); Register ret = asm.emitLoadPointer(ref); asm.emitPointerRet(ret); }); @@ -93,10 +90,9 @@ public class DataPatchTest extends CodeInstallationTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedOops); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant c = (HotSpotConstant) type.getJavaClass(); + HotSpotConstant c = (HotSpotConstant) constantReflection.asJavaClass(type); HotSpotConstant cCompressed = (HotSpotConstant) c.compress(); - Data data = codeCache.createDataItem(cCompressed); - DataSectionReference ref = asm.result.getDataSection().insertData(data); + DataSectionReference ref = asm.emitDataItem(cCompressed); Register compressed = asm.emitLoadNarrowPointer(ref); Register ret = asm.emitUncompressPointer(compressed, HotSpotVMConfig.config().narrowOopBase, HotSpotVMConfig.config().narrowOopShift); asm.emitPointerRet(ret); @@ -107,7 +103,7 @@ public class DataPatchTest extends CodeInstallationTest { public void testInlineMetadata() { test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - Register klass = asm.emitLoadPointer((HotSpotConstant) type.getObjectHub()); + Register klass = asm.emitLoadPointer((HotSpotConstant) constantReflection.asObjectHub(type)); Register ret = asm.emitLoadPointer(klass, HotSpotVMConfig.config().classMirrorOffset); asm.emitPointerRet(ret); }); @@ -118,7 +114,7 @@ public class DataPatchTest extends CodeInstallationTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedClassPointers); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant hub = (HotSpotConstant) type.getObjectHub(); + HotSpotConstant hub = (HotSpotConstant) constantReflection.asObjectHub(type); Register narrowKlass = asm.emitLoadPointer((HotSpotConstant) hub.compress()); Register klass = asm.emitUncompressPointer(narrowKlass, HotSpotVMConfig.config().narrowKlassBase, HotSpotVMConfig.config().narrowKlassShift); Register ret = asm.emitLoadPointer(klass, HotSpotVMConfig.config().classMirrorOffset); @@ -130,9 +126,8 @@ public class DataPatchTest extends CodeInstallationTest { public void testMetadataInDataSection() { test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant hub = (HotSpotConstant) type.getObjectHub(); - Data data = codeCache.createDataItem(hub); - DataSectionReference ref = asm.result.getDataSection().insertData(data); + HotSpotConstant hub = (HotSpotConstant) constantReflection.asObjectHub(type); + DataSectionReference ref = asm.emitDataItem(hub); Register klass = asm.emitLoadPointer(ref); Register ret = asm.emitLoadPointer(klass, HotSpotVMConfig.config().classMirrorOffset); asm.emitPointerRet(ret); @@ -144,10 +139,9 @@ public class DataPatchTest extends CodeInstallationTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedClassPointers); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); - HotSpotConstant hub = (HotSpotConstant) type.getObjectHub(); + HotSpotConstant hub = (HotSpotConstant) constantReflection.asObjectHub(type); HotSpotConstant narrowHub = (HotSpotConstant) hub.compress(); - Data data = codeCache.createDataItem(narrowHub); - DataSectionReference ref = asm.result.getDataSection().insertData(data); + DataSectionReference ref = asm.emitDataItem(narrowHub); Register narrowKlass = asm.emitLoadNarrowPointer(ref); Register klass = asm.emitUncompressPointer(narrowKlass, HotSpotVMConfig.config().narrowKlassBase, HotSpotVMConfig.config().narrowKlassShift); Register ret = asm.emitLoadPointer(klass, HotSpotVMConfig.config().classMirrorOffset); diff --git a/hotspot/test/compiler/jvmci/code/InterpreterFrameSizeTest.java b/hotspot/test/compiler/jvmci/code/InterpreterFrameSizeTest.java new file mode 100644 index 00000000000..9f5e9cfe106 --- /dev/null +++ b/hotspot/test/compiler/jvmci/code/InterpreterFrameSizeTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9") & os.arch != "aarch64" + * @compile CodeInstallationTest.java DebugInfoTest.java TestAssembler.java amd64/AMD64TestAssembler.java sparc/SPARCTestAssembler.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI compiler.jvmci.code.InterpreterFrameSizeTest + */ + +package compiler.jvmci.code; + +import java.lang.reflect.Method; + +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaValue; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; + +import org.junit.Assert; +import org.junit.Test; + +public class InterpreterFrameSizeTest extends CodeInstallationTest { + + HotSpotCodeCacheProvider hotspotCodeCache() { + return (HotSpotCodeCacheProvider) codeCache; + } + + @Test + public void testNull() { + try { + hotspotCodeCache().interpreterFrameSize(null); + } catch (NullPointerException npe) { + System.out.println("threw NPE as expected"); + return; + } + Assert.fail("expected NullPointerException"); + } + + @Test + public void test() { + ResolvedJavaMethod resolvedMethod = metaAccess.lookupJavaMethod(getMethod("testNull")); + + int bci = 0; + int numLocals = resolvedMethod.getMaxLocals(); + int numStack = 0; + JavaValue[] values = new JavaValue[numLocals]; + JavaKind[] slotKinds = new JavaKind[numLocals]; + BytecodeFrame frame = new BytecodeFrame(null, resolvedMethod, bci, false, false, values, slotKinds, numLocals, numStack, 0); + int size = hotspotCodeCache().interpreterFrameSize(frame); + System.out.println("Frame size is " + size + " bytes"); + if (size <= 0) { + Assert.fail("expected non-zero result"); + } + } +} diff --git a/hotspot/test/compiler/jvmci/code/SimpleDebugInfoTest.java b/hotspot/test/compiler/jvmci/code/SimpleDebugInfoTest.java index dffd336d2a8..041a184bc16 100644 --- a/hotspot/test/compiler/jvmci/code/SimpleDebugInfoTest.java +++ b/hotspot/test/compiler/jvmci/code/SimpleDebugInfoTest.java @@ -217,7 +217,7 @@ public class SimpleDebugInfoTest extends DebugInfoTest { public void testConstObject() { ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); DebugInfoCompiler compiler = (asm, values) -> { - values[0] = type.getJavaClass(); + values[0] = constantReflection.asJavaClass(type); return null; }; testObjectOnStack(compiler); @@ -228,7 +228,7 @@ public class SimpleDebugInfoTest extends DebugInfoTest { public void testRegObject() { ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) type.getJavaClass()); + Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); values[0] = reg.asValue(target.getLIRKind(JavaKind.Object)); return null; }; @@ -240,7 +240,7 @@ public class SimpleDebugInfoTest extends DebugInfoTest { public void testStackObject() { ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) type.getJavaClass()); + Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); values[0] = asm.emitPointerToStack(reg); return null; }; @@ -253,7 +253,7 @@ public class SimpleDebugInfoTest extends DebugInfoTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedOops); ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) type.getJavaClass(); + HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); values[0] = reg.asValue(asm.narrowOopKind); return null; @@ -267,7 +267,7 @@ public class SimpleDebugInfoTest extends DebugInfoTest { Assume.assumeTrue(HotSpotVMConfig.config().useCompressedOops); ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) type.getJavaClass(); + HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); values[0] = asm.emitNarrowPointerToStack(reg); return null; diff --git a/hotspot/test/compiler/jvmci/code/TestAssembler.java b/hotspot/test/compiler/jvmci/code/TestAssembler.java index 520e19b0e32..4ed5b938dcd 100644 --- a/hotspot/test/compiler/jvmci/code/TestAssembler.java +++ b/hotspot/test/compiler/jvmci/code/TestAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,30 @@ package compiler.jvmci.code; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.ArrayList; import java.util.Arrays; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; import jdk.vm.ci.code.DebugInfo; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.site.ConstantReference; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.InfopointReason; +import jdk.vm.ci.code.site.Reference; +import jdk.vm.ci.code.site.Site; +import jdk.vm.ci.hotspot.HotSpotCompiledCode; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; +import jdk.vm.ci.hotspot.HotSpotCompiledNmethod; import jdk.vm.ci.hotspot.HotSpotConstant; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; +import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.VMConstant; /** * Simple assembler used by the code installation tests. @@ -49,6 +62,7 @@ public abstract class TestAssembler { /** * Emit code to grow the stack frame. + * * @param size the size in bytes that the stack should grow */ public abstract void emitGrowStack(int size); @@ -84,18 +98,20 @@ public abstract class TestAssembler { public abstract Register emitLoadFloat(float value); /** - * Emit code to load a constant oop or metaspace pointer to a register. - * The pointer may be wide or narrow, depending on {@link HotSpotConstant#isCompressed() c.isCompressed()}. + * Emit code to load a constant oop or metaspace pointer to a register. The pointer may be wide + * or narrow, depending on {@link HotSpotConstant#isCompressed() c.isCompressed()}. */ public abstract Register emitLoadPointer(HotSpotConstant c); /** - * Emit code to load a wide pointer from the {@link DataSection} to a register. + * Emit code to load a wide pointer from the {@link HotSpotCompiledCode#dataSection} to a + * register. */ public abstract Register emitLoadPointer(DataSectionReference ref); /** - * Emit code to load a narrow pointer from the {@link DataSection} to a register. + * Emit code to load a narrow pointer from the {@link HotSpotCompiledCode#dataSection} to a + * register. */ public abstract Register emitLoadNarrowPointer(DataSectionReference ref); @@ -149,14 +165,13 @@ public abstract class TestAssembler { */ public abstract void emitTrap(DebugInfo info); - protected int position() { - return data.position(); - } - - public final CompilationResult result; public final LIRKind narrowOopKind; - private ByteBuffer data; + protected final Buffer code; + protected final Buffer data; + private final ArrayList sites; + private final ArrayList dataPatches; + protected final CodeCacheProvider codeCache; private final Register[] registers; @@ -166,11 +181,14 @@ public abstract class TestAssembler { private int stackAlignment; private int curStackSlot; - protected TestAssembler(CompilationResult result, CodeCacheProvider codeCache, int initialFrameSize, int stackAlignment, PlatformKind narrowOopKind, Register... registers) { - this.result = result; + protected TestAssembler(CodeCacheProvider codeCache, int initialFrameSize, int stackAlignment, PlatformKind narrowOopKind, Register... registers) { this.narrowOopKind = LIRKind.reference(narrowOopKind); - this.data = ByteBuffer.allocate(32).order(ByteOrder.nativeOrder()); + this.code = new Buffer(); + this.data = new Buffer(); + this.sites = new ArrayList<>(); + this.dataPatches = new ArrayList<>(); + this.codeCache = codeCache; this.registers = registers; @@ -198,38 +216,96 @@ public abstract class TestAssembler { return StackSlot.get(kind, -curStackSlot, true); } - public void finish() { - result.setTotalFrameSize(frameSize); - result.setTargetCode(data.array(), data.position()); + protected void recordImplicitException(DebugInfo info) { + sites.add(new Infopoint(code.position(), info, InfopointReason.IMPLICIT_EXCEPTION)); } - private void ensureSize(int length) { - if (length >= data.limit()) { - byte[] newBuf = Arrays.copyOf(data.array(), length * 4); - ByteBuffer newData = ByteBuffer.wrap(newBuf); - newData.order(data.order()); - newData.position(data.position()); - data = newData; + protected void recordDataPatchInCode(Reference ref) { + sites.add(new DataPatch(code.position(), ref)); + } + + protected void recordDataPatchInData(Reference ref) { + dataPatches.add(new DataPatch(data.position(), ref)); + } + + public DataSectionReference emitDataItem(HotSpotConstant c) { + DataSectionReference ref = new DataSectionReference(); + ref.setOffset(data.position()); + + recordDataPatchInData(new ConstantReference((VMConstant) c)); + if (c.isCompressed()) { + data.emitInt(0xDEADDEAD); + } else { + data.emitLong(0xDEADDEADDEADDEADL); + } + + return ref; + } + + public HotSpotCompiledCode finish(HotSpotResolvedJavaMethod method) { + int id = method.allocateCompileId(0); + byte[] finishedCode = code.finish(); + Site[] finishedSites = sites.toArray(new Site[0]); + byte[] finishedData = data.finish(); + DataPatch[] finishedDataPatches = dataPatches.toArray(new DataPatch[0]); + return new HotSpotCompiledNmethod(method.getName(), finishedCode, finishedCode.length, finishedSites, new Assumption[0], new ResolvedJavaMethod[]{method}, new Comment[0], finishedData, 16, + finishedDataPatches, false, frameSize, 0, method, 0, id, 0L, false); + } + + protected static class Buffer { + + private ByteBuffer data = ByteBuffer.allocate(32).order(ByteOrder.nativeOrder()); + + private void ensureSize(int length) { + if (length >= data.limit()) { + byte[] newBuf = Arrays.copyOf(data.array(), length * 4); + ByteBuffer newData = ByteBuffer.wrap(newBuf); + newData.order(data.order()); + newData.position(data.position()); + data = newData; + } + } + + public int position() { + return data.position(); + } + + public void emitByte(int b) { + ensureSize(data.position() + 1); + data.put((byte) (b & 0xFF)); + } + + public void emitShort(int b) { + ensureSize(data.position() + 2); + data.putShort((short) b); + } + + public void emitInt(int b) { + ensureSize(data.position() + 4); + data.putInt(b); + } + + public void emitLong(long b) { + ensureSize(data.position() + 8); + data.putLong(b); + } + + public void emitFloat(float f) { + ensureSize(data.position() + 4); + data.putFloat(f); + } + + public void align(int alignment) { + int pos = data.position(); + int misaligned = pos % alignment; + if (misaligned != 0) { + pos += alignment - misaligned; + data.position(pos); + } + } + + private byte[] finish() { + return Arrays.copyOf(data.array(), data.position()); } } - - protected void emitByte(int b) { - ensureSize(data.position() + 1); - data.put((byte) (b & 0xFF)); - } - - protected void emitShort(int b) { - ensureSize(data.position() + 2); - data.putShort((short) b); - } - - protected void emitInt(int b) { - ensureSize(data.position() + 4); - data.putInt(b); - } - - protected void emitLong(long b) { - ensureSize(data.position() + 8); - data.putLong(b); - } } diff --git a/hotspot/test/compiler/jvmci/code/amd64/AMD64TestAssembler.java b/hotspot/test/compiler/jvmci/code/amd64/AMD64TestAssembler.java index efd001c18d4..c67192674a2 100644 --- a/hotspot/test/compiler/jvmci/code/amd64/AMD64TestAssembler.java +++ b/hotspot/test/compiler/jvmci/code/amd64/AMD64TestAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,13 @@ package compiler.jvmci.code.amd64; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.amd64.AMD64Kind; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.ConstantReference; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; -import jdk.vm.ci.code.DataSection.Data; import jdk.vm.ci.code.DebugInfo; -import jdk.vm.ci.code.InfopointReason; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.CallingConvention.Type; +import jdk.vm.ci.code.site.ConstantReference; +import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.hotspot.HotSpotCallingConventionType; import jdk.vm.ci.hotspot.HotSpotConstant; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.VMConstant; @@ -45,88 +41,112 @@ import compiler.jvmci.code.TestAssembler; public class AMD64TestAssembler extends TestAssembler { - public AMD64TestAssembler(CompilationResult result, CodeCacheProvider codeCache) { - super(result, codeCache, 16, 16, AMD64Kind.DWORD, AMD64.rax, AMD64.rcx, AMD64.rdi, AMD64.r8, AMD64.r9, AMD64.r10); + public AMD64TestAssembler(CodeCacheProvider codeCache) { + super(codeCache, 16, 16, AMD64Kind.DWORD, AMD64.rax, AMD64.rcx, AMD64.rdi, AMD64.r8, AMD64.r9, AMD64.r10); } + private void emitFatNop() { + // 5 byte NOP: + // NOP DWORD ptr [EAX + EAX*1 + 00H] + code.emitByte(0x0F); + code.emitByte(0x1F); + code.emitByte(0x44); + code.emitByte(0x00); + code.emitByte(0x00); + } + + @Override public void emitPrologue() { - emitByte(0x50 | AMD64.rbp.encoding); // PUSH rbp - emitMove(true, AMD64.rbp, AMD64.rsp); // MOV rbp, rsp + // WARNING: Initial instruction MUST be 5 bytes or longer so that + // NativeJump::patch_verified_entry will be able to patch out the entry + // code safely. + emitFatNop(); + code.emitByte(0x50 | AMD64.rbp.encoding); // PUSH rbp + emitMove(true, AMD64.rbp, AMD64.rsp); // MOV rbp, rsp } + @Override public void emitGrowStack(int size) { // SUB rsp, size - emitByte(0x48); - emitByte(0x81); - emitByte(0xEC); - emitInt(size); + code.emitByte(0x48); + code.emitByte(0x81); + code.emitByte(0xEC); + code.emitInt(size); } + @Override public Register emitIntArg0() { - return codeCache.getRegisterConfig().getCallingConventionRegisters(Type.JavaCall, JavaKind.Int)[0]; + return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)[0]; } + @Override public Register emitIntArg1() { - return codeCache.getRegisterConfig().getCallingConventionRegisters(Type.JavaCall, JavaKind.Int)[1]; + return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)[1]; } private void emitREX(boolean w, int r, int x, int b) { int wrxb = (w ? 0x08 : 0) | ((r >> 3) << 2) | ((x >> 3) << 1) | (b >> 3); if (wrxb != 0) { - emitByte(0x40 | wrxb); + code.emitByte(0x40 | wrxb); } } private void emitModRMReg(boolean w, int opcode, int r, int m) { emitREX(w, r, 0, m); - emitByte((byte) opcode); - emitByte((byte) 0xC0 | ((r & 0x7) << 3) | (m & 0x7)); + code.emitByte((byte) opcode); + code.emitByte((byte) 0xC0 | ((r & 0x7) << 3) | (m & 0x7)); } private void emitModRMMemory(boolean w, int opcode, int r, int b, int offset) { emitREX(w, r, 0, b); - emitByte((byte) opcode); - emitByte((byte) 0x80 | ((r & 0x7) << 3) | (b & 0x7)); - emitInt(offset); + code.emitByte((byte) opcode); + code.emitByte((byte) 0x80 | ((r & 0x7) << 3) | (b & 0x7)); + code.emitInt(offset); } + @Override public Register emitLoadInt(int c) { Register ret = newRegister(); emitREX(false, 0, 0, ret.encoding); - emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 - emitInt(c); + code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 + code.emitInt(c); return ret; } + @Override public Register emitLoadLong(long c) { Register ret = newRegister(); emitREX(true, 0, 0, ret.encoding); - emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r64, imm64 - emitLong(c); + code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r64, imm64 + code.emitLong(c); return ret; } + @Override public Register emitLoadFloat(float c) { - Data data = codeCache.createDataItem(JavaConstant.forFloat(c)); - DataSectionReference ref = result.getDataSection().insertData(data); - result.recordDataPatch(position(), ref); + DataSectionReference ref = new DataSectionReference(); + ref.setOffset(data.position()); + data.emitFloat(c); + + recordDataPatchInCode(ref); Register ret = AMD64.xmm0; emitREX(false, ret.encoding, 0, 0); - emitByte(0xF3); - emitByte(0x0F); - emitByte(0x10); // MOVSS xmm1, xmm2/m32 - emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // xmm, [rip+offset] - emitInt(0xDEADDEAD); + code.emitByte(0xF3); + code.emitByte(0x0F); + code.emitByte(0x10); // MOVSS xmm1, xmm2/m32 + code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // xmm, [rip+offset] + code.emitInt(0xDEADDEAD); return ret; } + @Override public Register emitLoadPointer(HotSpotConstant c) { - result.recordDataPatch(position(), new ConstantReference((VMConstant) c)); + recordDataPatchInCode(new ConstantReference((VMConstant) c)); if (c.isCompressed()) { Register ret = newRegister(); emitREX(false, 0, 0, ret.encoding); - emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 - emitInt(0xDEADDEAD); + code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 + code.emitInt(0xDEADDEAD); return ret; } else { return emitLoadLong(0xDEADDEADDEADDEADl); @@ -134,68 +154,77 @@ public class AMD64TestAssembler extends TestAssembler { } private Register emitLoadPointer(DataSectionReference ref, boolean narrow) { - result.recordDataPatch(position(), ref); + recordDataPatchInCode(ref); Register ret = newRegister(); emitREX(!narrow, ret.encoding, 0, 0); - emitByte(0x8B); // MOV r64,r/m64 - emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // r64, [rip+offset] - emitInt(0xDEADDEAD); + code.emitByte(0x8B); // MOV r64,r/m64 + code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // r64, [rip+offset] + code.emitInt(0xDEADDEAD); return ret; } + @Override public Register emitLoadPointer(DataSectionReference ref) { return emitLoadPointer(ref, false); } + @Override public Register emitLoadNarrowPointer(DataSectionReference ref) { return emitLoadPointer(ref, true); } + @Override public Register emitLoadPointer(Register b, int offset) { Register ret = newRegister(); emitModRMMemory(true, 0x8B, ret.encoding, b.encoding, offset); // MOV r64,r/m64 return ret; } + @Override public StackSlot emitIntToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(AMD64Kind.DWORD)); emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); // MOV r/m32,r32 return ret; } + @Override public StackSlot emitLongToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(AMD64Kind.QWORD)); emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); // MOV r/m64,r64 return ret; } + @Override public StackSlot emitFloatToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(AMD64Kind.SINGLE)); emitREX(false, a.encoding, 0, 0); - emitByte(0xF3); - emitByte(0x0F); - emitByte(0x11); // MOVSS xmm2/m32, xmm1 - emitByte(0x85 | ((a.encoding & 0x7) << 3)); // [rbp+offset] - emitInt(ret.getRawOffset() + 16); + code.emitByte(0xF3); + code.emitByte(0x0F); + code.emitByte(0x11); // MOVSS xmm2/m32, xmm1 + code.emitByte(0x85 | ((a.encoding & 0x7) << 3)); // [rbp+offset] + code.emitInt(ret.getRawOffset() + 16); return ret; } + @Override public StackSlot emitPointerToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.reference(AMD64Kind.QWORD)); emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); // MOV r/m64,r64 return ret; } + @Override public StackSlot emitNarrowPointerToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.reference(AMD64Kind.DWORD)); emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); // MOV r/m32,r32 return ret; } + @Override public Register emitUncompressPointer(Register compressed, long base, int shift) { if (shift > 0) { emitModRMReg(true, 0xC1, 4, compressed.encoding); - emitByte(shift); + code.emitByte(shift); } if (base == 0) { return compressed; @@ -206,6 +235,7 @@ public class AMD64TestAssembler extends TestAssembler { } } + @Override public Register emitIntAdd(Register a, Register b) { emitModRMReg(false, 0x03, a.encoding, b.encoding); return a; @@ -217,26 +247,29 @@ public class AMD64TestAssembler extends TestAssembler { } } + @Override public void emitIntRet(Register a) { - emitMove(false, AMD64.rax, a); // MOV eax, ... - emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp - emitByte(0x58 | AMD64.rbp.encoding); // POP rbp - emitByte(0xC3); // RET + emitMove(false, AMD64.rax, a); // MOV eax, ... + emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp + code.emitByte(0x58 | AMD64.rbp.encoding); // POP rbp + code.emitByte(0xC3); // RET } + @Override public void emitPointerRet(Register a) { - emitMove(true, AMD64.rax, a); // MOV rax, ... - emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp - emitByte(0x58 | AMD64.rbp.encoding); // POP rbp - emitByte(0xC3); // RET + emitMove(true, AMD64.rax, a); // MOV rax, ... + emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp + code.emitByte(0x58 | AMD64.rbp.encoding); // POP rbp + code.emitByte(0xC3); // RET } + @Override public void emitTrap(DebugInfo info) { - result.recordInfopoint(position(), info, InfopointReason.IMPLICIT_EXCEPTION); + recordImplicitException(info); // MOV rax, [0] - emitByte(0x8B); - emitByte(0x04); - emitByte(0x25); - emitInt(0); + code.emitByte(0x8B); + code.emitByte(0x04); + code.emitByte(0x25); + code.emitInt(0); } } diff --git a/hotspot/test/compiler/jvmci/code/sparc/SPARCTestAssembler.java b/hotspot/test/compiler/jvmci/code/sparc/SPARCTestAssembler.java index b37ed57c77a..7b423641e18 100644 --- a/hotspot/test/compiler/jvmci/code/sparc/SPARCTestAssembler.java +++ b/hotspot/test/compiler/jvmci/code/sparc/SPARCTestAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,17 +24,15 @@ package compiler.jvmci.code.sparc; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.ConstantReference; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; -import jdk.vm.ci.code.DataSection.Data; import jdk.vm.ci.code.DebugInfo; -import jdk.vm.ci.code.InfopointReason; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.CallingConvention.Type; +import jdk.vm.ci.code.site.ConstantReference; +import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.hotspot.HotSpotCallingConventionType; +import jdk.vm.ci.hotspot.HotSpotCompiledCode; import jdk.vm.ci.hotspot.HotSpotConstant; -import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.VMConstant; @@ -47,48 +45,53 @@ public class SPARCTestAssembler extends TestAssembler { private static final int MASK13 = (1 << 13) - 1; - public SPARCTestAssembler(CompilationResult result, CodeCacheProvider codeCache) { - super(result, codeCache, 0, 16, SPARCKind.WORD, SPARC.l0, SPARC.l1, SPARC.l2, SPARC.l3, SPARC.l4, SPARC.l5, SPARC.l6, SPARC.l7); + public SPARCTestAssembler(CodeCacheProvider codeCache) { + super(codeCache, 0, 16, SPARCKind.WORD, SPARC.l0, SPARC.l1, SPARC.l2, SPARC.l3, SPARC.l4, SPARC.l5, SPARC.l6, SPARC.l7); } private void emitOp2(Register rd, int op2, int imm22) { - emitInt((0b00 << 30) | (rd.encoding << 25) | (op2 << 22) | imm22); + code.emitInt((0b00 << 30) | (rd.encoding << 25) | (op2 << 22) | imm22); } private void emitOp3(int op, Register rd, int op3, Register rs1, Register rs2) { - emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | rs2.encoding); + code.emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | rs2.encoding); } private void emitOp3(int op, Register rd, int op3, Register rs1, int simm13) { - emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | (1 << 13) | (simm13 & MASK13)); + code.emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | (1 << 13) | (simm13 & MASK13)); } private void emitNop() { - emitInt(1 << 24); + code.emitInt(1 << 24); } + @Override public void emitPrologue() { emitOp3(0b10, SPARC.sp, 0b111100, SPARC.sp, -SPARC.REGISTER_SAFE_AREA_SIZE); // SAVE sp, -128, sp } @Override - public void finish() { + public HotSpotCompiledCode finish(HotSpotResolvedJavaMethod method) { frameSize += SPARC.REGISTER_SAFE_AREA_SIZE; - super.finish(); + return super.finish(method); } + @Override public void emitGrowStack(int size) { emitOp3(0b10, SPARC.sp, 0b000100, SPARC.sp, size); // SUB sp, size, sp } + @Override public Register emitIntArg0() { - return codeCache.getRegisterConfig().getCallingConventionRegisters(Type.JavaCallee, JavaKind.Int)[0]; + return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCallee, JavaKind.Int)[0]; } + @Override public Register emitIntArg1() { - return codeCache.getRegisterConfig().getCallingConventionRegisters(Type.JavaCallee, JavaKind.Int)[1]; + return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCallee, JavaKind.Int)[1]; } + @Override public Register emitLoadInt(int c) { Register ret = newRegister(); int hi = c >>> 10; @@ -104,41 +107,48 @@ public class SPARCTestAssembler extends TestAssembler { return ret; } + @Override public Register emitLoadLong(long c) { - if ((c & 0xFFFFFFFF) == c) { + if ((c & 0xFFFF_FFFFL) == c) { return emitLoadInt((int) c); } else { - Data data = codeCache.createDataItem(JavaConstant.forLong(c)); - DataSectionReference ref = result.getDataSection().insertData(data); + DataSectionReference ref = new DataSectionReference(); + data.align(8); + ref.setOffset(data.position()); + data.emitLong(c); return emitLoadPointer(ref); } } private void emitPatchableSethi(Register ret, boolean wide) { - int startPos = position(); + int startPos = code.position(); emitOp2(ret, 0b100, 0); // SETHI 0, ret if (wide) { // pad for later patching - while (position() < (startPos + 28)) { + while (code.position() < (startPos + 28)) { emitNop(); } } } + @Override public Register emitLoadFloat(float c) { - Data data = codeCache.createDataItem(JavaConstant.forFloat(c)); - DataSectionReference ref = result.getDataSection().insertData(data); + DataSectionReference ref = new DataSectionReference(); + data.align(4); + ref.setOffset(data.position()); + data.emitFloat(c); Register ptr = newRegister(); - result.recordDataPatch(position(), ref); + recordDataPatchInCode(ref); emitPatchableSethi(ptr, true); emitOp3(0b11, SPARC.f0, 0b100000, ptr, 0); // LDF [ptr+0], f0 return SPARC.f0; } + @Override public Register emitLoadPointer(HotSpotConstant c) { Register ret = newRegister(); - result.recordDataPatch(position(), new ConstantReference((VMConstant) c)); + recordDataPatchInCode(new ConstantReference((VMConstant) c)); emitPatchableSethi(ret, !c.isCompressed()); emitOp3(0b10, ret, 0b000010, ret, 0); // OR ret, 0, ret @@ -146,58 +156,67 @@ public class SPARCTestAssembler extends TestAssembler { return ret; } + @Override public Register emitLoadPointer(DataSectionReference ref) { Register ret = newRegister(); - result.recordDataPatch(position(), ref); + recordDataPatchInCode(ref); emitPatchableSethi(ret, true); emitOp3(0b11, ret, 0b001011, ret, 0); // LDX [ret+0], ret return ret; } + @Override public Register emitLoadNarrowPointer(DataSectionReference ref) { Register ret = newRegister(); - result.recordDataPatch(position(), ref); + recordDataPatchInCode(ref); emitPatchableSethi(ret, true); emitOp3(0b11, ret, 0b000000, ret, 0); // LDUW [ret+0], ret return ret; } + @Override public Register emitLoadPointer(Register b, int offset) { Register ret = newRegister(); emitOp3(0b11, ret, 0b001011, b, offset); // LDX [b+offset], ret return ret; } + @Override public StackSlot emitIntToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(SPARCKind.WORD)); emitOp3(0b11, a, 0b000100, SPARC.fp, ret.getRawOffset() + SPARC.STACK_BIAS); // STW a, [fp+offset] return ret; } + @Override public StackSlot emitLongToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(SPARCKind.XWORD)); emitOp3(0b11, a, 0b001110, SPARC.fp, ret.getRawOffset() + SPARC.STACK_BIAS); // STX a, [fp+offset] return ret; } + @Override public StackSlot emitFloatToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.value(SPARCKind.SINGLE)); emitOp3(0b11, a, 0b100100, SPARC.fp, ret.getRawOffset() + SPARC.STACK_BIAS); // STF a, [fp+offset] return ret; } + @Override public StackSlot emitPointerToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.reference(SPARCKind.XWORD)); emitOp3(0b11, a, 0b001110, SPARC.fp, ret.getRawOffset() + SPARC.STACK_BIAS); // STX a, [fp+offset] return ret; } + @Override public StackSlot emitNarrowPointerToStack(Register a) { StackSlot ret = newStackSlot(LIRKind.reference(SPARCKind.WORD)); emitOp3(0b11, a, 0b000100, SPARC.fp, ret.getRawOffset() + SPARC.STACK_BIAS); // STW a, [fp+offset] return ret; } + @Override public Register emitUncompressPointer(Register compressed, long base, int shift) { Register ret; if (shift > 0) { @@ -215,6 +234,7 @@ public class SPARCTestAssembler extends TestAssembler { } } + @Override public Register emitIntAdd(Register a, Register b) { Register ret = newRegister(); emitOp3(0b10, ret, 0b00000, a, b); // ADD a, b, ret @@ -227,18 +247,31 @@ public class SPARCTestAssembler extends TestAssembler { } } + @Override public void emitIntRet(Register a) { emitPointerRet(a); } + @Override public void emitPointerRet(Register a) { emitMove(SPARC.i0, a); emitOp3(0b10, SPARC.g0, 0b111000, SPARC.i7, 8); // JMPL [i7+8], g0 emitOp3(0b10, SPARC.g0, 0b111101, SPARC.g0, SPARC.g0); // RESTORE g0, g0, g0 } + @Override public void emitTrap(DebugInfo info) { - result.recordInfopoint(position(), info, InfopointReason.IMPLICIT_EXCEPTION); + recordImplicitException(info); emitOp3(0b11, SPARC.g0, 0b001011, SPARC.g0, 0); // LDX [g0+0], g0 } + + @Override + public DataSectionReference emitDataItem(HotSpotConstant c) { + if (c.isCompressed()) { + data.align(4); + } else { + data.align(8); + } + return super.emitDataItem(c); + } } diff --git a/hotspot/test/compiler/jvmci/common/CompilerToVMHelper.java b/hotspot/test/compiler/jvmci/common/CompilerToVMHelper.java index b5f4574b3a0..f9602f33064 100644 --- a/hotspot/test/compiler/jvmci/common/CompilerToVMHelper.java +++ b/hotspot/test/compiler/jvmci/common/CompilerToVMHelper.java @@ -148,14 +148,6 @@ public class CompilerToVMHelper { return CTVM.getMetadata(target, compiledCode, metaData); } - public static void notifyCompilationStatistics(int id, - HotSpotResolvedJavaMethod method, boolean osr, - int processedBytecodes, long time, long timeUnitsPerSecond, - InstalledCode installedCode) { - CTVM.notifyCompilationStatistics(id, (HotSpotResolvedJavaMethodImpl) method, osr, processedBytecodes, - time, timeUnitsPerSecond, installedCode); - } - public static void resetCompilationStatistics() { CTVM.resetCompilationStatistics(); } diff --git a/hotspot/test/compiler/jvmci/common/JVMCIHelpers.java b/hotspot/test/compiler/jvmci/common/JVMCIHelpers.java index 35cece6db07..a99c0908fc5 100644 --- a/hotspot/test/compiler/jvmci/common/JVMCIHelpers.java +++ b/hotspot/test/compiler/jvmci/common/JVMCIHelpers.java @@ -25,6 +25,7 @@ package compiler.jvmci.common; import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.CompilationRequest; +import jdk.vm.ci.code.CompilationRequestResult; import jdk.vm.ci.hotspot.HotSpotVMEventListener; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.runtime.JVMCICompiler; @@ -43,8 +44,9 @@ public class JVMCIHelpers { public static class EmptyHotspotCompiler implements JVMCICompiler { @Override - public void compileMethod(CompilationRequest request) { + public CompilationRequestResult compileMethod(CompilationRequest request) { // do nothing + return CompilationRequestResult.failure("no compiler configured", true); } } diff --git a/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java b/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java index 195b7d52cd7..b06697634b2 100644 --- a/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java +++ b/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,16 @@ import java.lang.reflect.Method; import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.CodeCacheProvider; -import jdk.vm.ci.code.CompilationResult; import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Site; +import jdk.vm.ci.hotspot.HotSpotCompiledCode; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; +import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider; +import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.PlatformKind; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider; import jdk.vm.ci.runtime.JVMCI; import jdk.vm.ci.runtime.JVMCIBackend; @@ -67,22 +71,18 @@ public class CodeInstallerTest { dummyMethod = metaAccess.lookupJavaMethod(method); } - protected void installCode(CompilationResult result) { - result.close(); - codeCache.addCode(dummyMethod, result, null, null); - } - - protected CompilationResult createEmptyCompilationResult() { - CompilationResult ret = new CompilationResult(); - ret.setTotalFrameSize(0); - return ret; + protected void installEmptyCode(Site[] sites, Assumption[] assumptions, Comment[] comments, int dataSectionAlignment, DataPatch[] dataSectionPatches) { + HotSpotCompiledCode code = new HotSpotCompiledCode("dummyMethod", new byte[0], 0, sites, assumptions, new ResolvedJavaMethod[]{dummyMethod}, comments, new byte[8], dataSectionAlignment, + dataSectionPatches, false, 0, 0); + codeCache.addCode(dummyMethod, code, null, null); } protected Register getRegister(PlatformKind kind, int index) { + int idx = index; Register[] allRegs = arch.getAvailableValueRegisters(); for (int i = 0; i < allRegs.length; i++) { if (arch.canStoreValue(allRegs[i].getRegisterCategory(), kind)) { - if (index-- == 0) { + if (idx-- == 0) { return allRegs[i]; } } diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java b/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java index f40dea820eb..928bd74522e 100644 --- a/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,20 +30,18 @@ package compiler.jvmci.errors; -import static jdk.vm.ci.code.CompilationResult.ConstantReference; -import static jdk.vm.ci.code.CompilationResult.DataPatch; -import static jdk.vm.ci.code.CompilationResult.DataSectionReference; -import static jdk.vm.ci.code.CompilationResult.Infopoint; -import static jdk.vm.ci.code.CompilationResult.Reference; -import static jdk.vm.ci.code.DataSection.Data; -import static jdk.vm.ci.code.DataSection.DataBuilder; -import static jdk.vm.ci.meta.Assumptions.Assumption; - -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.InfopointReason; +import jdk.vm.ci.code.site.ConstantReference; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.InfopointReason; +import jdk.vm.ci.code.site.Mark; +import jdk.vm.ci.code.site.Reference; +import jdk.vm.ci.code.site.Site; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; import jdk.vm.ci.hotspot.HotSpotConstant; -import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.VMConstant; import org.junit.Test; @@ -82,153 +80,104 @@ public class TestInvalidCompilationResult extends CodeInstallerTest { @Test(expected = JVMCIError.class) public void testInvalidAssumption() { - CompilationResult result = createEmptyCompilationResult(); - result.setAssumptions(new Assumption[]{new InvalidAssumption()}); - installCode(result); + installEmptyCode(new Site[0], new Assumption[]{new InvalidAssumption()}, new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInvalidAlignment() { - CompilationResult result = createEmptyCompilationResult(); - result.getDataSection().insertData(new Data(7, 1, DataBuilder.zero(1))); - installCode(result); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 7, new DataPatch[0]); } @Test(expected = NullPointerException.class) public void testNullDataPatchInDataSection() { - CompilationResult result = createEmptyCompilationResult(); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(null); - buffer.put((byte) 0); - }); - result.getDataSection().insertData(data); - installCode(result); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{null}); } @Test(expected = NullPointerException.class) public void testNullReferenceInDataSection() { - CompilationResult result = createEmptyCompilationResult(); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), null)); - buffer.put((byte) 0); - }); - result.getDataSection().insertData(data); - installCode(result); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{new DataPatch(0, null)}); } @Test(expected = JVMCIError.class) public void testInvalidDataSectionReference() { - CompilationResult result = createEmptyCompilationResult(); - DataSectionReference ref = result.getDataSection().insertData(new Data(1, 1, DataBuilder.zero(1))); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), ref)); - buffer.put((byte) 0); - }); - result.getDataSection().insertData(data); - installCode(result); + DataSectionReference ref = new DataSectionReference(); + ref.setOffset(0); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{new DataPatch(0, ref)}); } @Test(expected = JVMCIError.class) public void testInvalidNarrowMethodInDataSection() { - CompilationResult result = createEmptyCompilationResult(); HotSpotConstant c = (HotSpotConstant) dummyMethod.getEncoding(); - Data data = new Data(4, 4, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference((VMConstant) c.compress()))); - buffer.putInt(0); - }); - result.getDataSection().insertData(data); - installCode(result); + ConstantReference ref = new ConstantReference((VMConstant) c.compress()); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{new DataPatch(0, ref)}); } @Test(expected = NullPointerException.class) public void testNullConstantInDataSection() { - CompilationResult result = createEmptyCompilationResult(); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference(null))); - }); - result.getDataSection().insertData(data); - installCode(result); + ConstantReference ref = new ConstantReference(null); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{new DataPatch(0, ref)}); } @Test(expected = JVMCIError.class) public void testInvalidConstantInDataSection() { - CompilationResult result = createEmptyCompilationResult(); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference(new InvalidVMConstant()))); - }); - result.getDataSection().insertData(data); - installCode(result); + ConstantReference ref = new ConstantReference(new InvalidVMConstant()); + installEmptyCode(new Site[0], new Assumption[0], new Comment[0], 16, new DataPatch[]{new DataPatch(0, ref)}); } @Test(expected = NullPointerException.class) public void testNullReferenceInCode() { - CompilationResult result = createEmptyCompilationResult(); - result.recordDataPatch(0, null); - installCode(result); + installEmptyCode(new Site[]{new DataPatch(0, null)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = NullPointerException.class) public void testNullConstantInCode() { - CompilationResult result = createEmptyCompilationResult(); - result.recordDataPatch(0, new ConstantReference(null)); - installCode(result); + ConstantReference ref = new ConstantReference(null); + installEmptyCode(new Site[]{new DataPatch(0, ref)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInvalidConstantInCode() { - CompilationResult result = createEmptyCompilationResult(); - result.recordDataPatch(0, new ConstantReference(new InvalidVMConstant())); - installCode(result); + ConstantReference ref = new ConstantReference(new InvalidVMConstant()); + installEmptyCode(new Site[]{new DataPatch(0, ref)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInvalidReference() { - CompilationResult result = createEmptyCompilationResult(); - result.recordDataPatch(0, new InvalidReference()); - installCode(result); + InvalidReference ref = new InvalidReference(); + installEmptyCode(new Site[]{new DataPatch(0, ref)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testOutOfBoundsDataSectionReference() { - CompilationResult result = createEmptyCompilationResult(); DataSectionReference ref = new DataSectionReference(); ref.setOffset(0x1000); - result.recordDataPatch(0, ref); - installCode(result); + installEmptyCode(new Site[]{new DataPatch(0, ref)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInvalidMark() { - CompilationResult result = createEmptyCompilationResult(); - result.recordMark(0, new Object()); - installCode(result); + installEmptyCode(new Site[]{new Mark(0, new Object())}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInvalidMarkInt() { - CompilationResult result = createEmptyCompilationResult(); - result.recordMark(0, -1); - installCode(result); + installEmptyCode(new Site[]{new Mark(0, -1)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = NullPointerException.class) - public void testNullInfopoint() { - CompilationResult result = createEmptyCompilationResult(); - result.addInfopoint(null); - installCode(result); + public void testNullSite() { + installEmptyCode(new Site[]{null}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testInfopointMissingDebugInfo() { - CompilationResult result = createEmptyCompilationResult(); - result.addInfopoint(new Infopoint(0, null, InfopointReason.METHOD_START)); - installCode(result); + Infopoint info = new Infopoint(0, null, InfopointReason.METHOD_START); + installEmptyCode(new Site[]{info}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = JVMCIError.class) public void testSafepointMissingDebugInfo() { - CompilationResult result = createEmptyCompilationResult(); - result.addInfopoint(new Infopoint(0, null, InfopointReason.SAFEPOINT)); - installCode(result); + Infopoint info = new Infopoint(0, null, InfopointReason.SAFEPOINT); + installEmptyCode(new Site[]{info}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } } diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java b/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java index ede956c1b82..e95d5cea6dc 100644 --- a/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,24 +30,26 @@ package compiler.jvmci.errors; -import static jdk.vm.ci.code.CompilationResult.Infopoint; - import jdk.vm.ci.code.BytecodeFrame; -import jdk.vm.ci.code.CompilationResult; import jdk.vm.ci.code.DebugInfo; -import jdk.vm.ci.code.InfopointReason; import jdk.vm.ci.code.Location; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.StackSlot; import jdk.vm.ci.code.VirtualObject; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.InfopointReason; +import jdk.vm.ci.code.site.Site; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; import jdk.vm.ci.hotspot.HotSpotReferenceMap; +import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaValue; import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.Value; -import jdk.vm.ci.common.JVMCIError; import org.junit.Test; @@ -67,10 +69,7 @@ public class TestInvalidDebugInfo extends CodeInstallerTest { BytecodeFrame frame = new BytecodeFrame(null, dummyMethod, 0, false, false, values, slotKinds, locals, stack, locks); DebugInfo info = new DebugInfo(frame, vobj); info.setReferenceMap(new HotSpotReferenceMap(new Location[0], new Location[0], new int[0], 8)); - - CompilationResult result = createEmptyCompilationResult(); - result.addInfopoint(new Infopoint(0, info, InfopointReason.SAFEPOINT)); - installCode(result); + installEmptyCode(new Site[]{new Infopoint(0, info, InfopointReason.SAFEPOINT)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = NullPointerException.class) diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java b/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java index df0b60adb0f..f5efaf91375 100644 --- a/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,21 +30,22 @@ package compiler.jvmci.errors; -import static jdk.vm.ci.code.CompilationResult.Infopoint; - import jdk.vm.ci.code.BytecodePosition; -import jdk.vm.ci.code.CompilationResult; import jdk.vm.ci.code.DebugInfo; -import jdk.vm.ci.code.InfopointReason; import jdk.vm.ci.code.Location; import jdk.vm.ci.code.ReferenceMap; import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Infopoint; +import jdk.vm.ci.code.site.InfopointReason; +import jdk.vm.ci.code.site.Site; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; import jdk.vm.ci.hotspot.HotSpotReferenceMap; import jdk.vm.ci.hotspot.HotSpotVMConfig; +import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.PlatformKind; -import jdk.vm.ci.common.JVMCIError; import org.junit.Test; @@ -60,10 +61,7 @@ public class TestInvalidOopMap extends CodeInstallerTest { BytecodePosition pos = new BytecodePosition(null, dummyMethod, 0); DebugInfo info = new DebugInfo(pos); info.setReferenceMap(refMap); - - CompilationResult result = createEmptyCompilationResult(); - result.addInfopoint(new Infopoint(0, info, InfopointReason.SAFEPOINT)); - installCode(result); + installEmptyCode(new Site[]{new Infopoint(0, info, InfopointReason.SAFEPOINT)}, new Assumption[0], new Comment[0], 16, new DataPatch[0]); } @Test(expected = NullPointerException.class) diff --git a/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java b/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java index a1dfa84924c..2b9d8c9332e 100644 --- a/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java +++ b/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java @@ -58,10 +58,15 @@ import compiler.jvmci.common.testcases.SimpleClass; import jdk.test.lib.Asserts; import java.lang.reflect.Method; import jdk.vm.ci.hotspot.HotSpotVMEventListener; -import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.CompiledCode; import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.code.site.DataPatch; +import jdk.vm.ci.code.site.Site; +import jdk.vm.ci.meta.Assumptions.Assumption; +import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; -import jdk.vm.ci.hotspot.HotSpotCompilationRequest; +import jdk.vm.ci.hotspot.HotSpotCompiledCode; +import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment; import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; @@ -102,17 +107,15 @@ public class JvmciNotifyInstallEventTest implements HotSpotVMEventListener { } HotSpotResolvedJavaMethod method = CTVMUtilities .getResolvedMethod(SimpleClass.class, testMethod); - CompilationResult compResult = new CompilationResult(METHOD_NAME); - HotSpotCompilationRequest compRequest = new HotSpotCompilationRequest(method, -1, 0L); - // to pass sanity check of default -1 - compResult.setTotalFrameSize(0); - compResult.close(); - codeCache.installCode(compRequest, compResult, /* installedCode = */ null, /* speculationLog = */ null, + HotSpotCompiledCode compiledCode = new HotSpotCompiledCode(METHOD_NAME, new byte[0], 0, new Site[0], + new Assumption[0], new ResolvedJavaMethod[]{method}, new Comment[0], new byte[0], 16, + new DataPatch[0], false, 0, 0); + codeCache.installCode(method, compiledCode, /* installedCode = */ null, /* speculationLog = */ null, /* isDefault = */ false); Asserts.assertEQ(gotInstallNotification, 1, "Got unexpected event count after 1st install attempt"); // since "empty" compilation result is ok, a second attempt should be ok - codeCache.installCode(compRequest, compResult, /* installedCode = */ null, /* speculationLog = */ null, + codeCache.installCode(method, compiledCode, /* installedCode = */ null, /* speculationLog = */ null, /* isDefault = */ false); Asserts.assertEQ(gotInstallNotification, 2, "Got unexpected event count after 2nd install attempt"); @@ -120,7 +123,7 @@ public class JvmciNotifyInstallEventTest implements HotSpotVMEventListener { @Override public void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, - InstalledCode installedCode, CompilationResult compResult) { + InstalledCode installedCode, CompiledCode compiledCode) { gotInstallNotification++; } } diff --git a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java index bb3da636e2c..13dd0999fee 100644 --- a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java +++ b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaMethod.java @@ -264,21 +264,37 @@ public class TestResolvedJavaMethod extends MethodUniverse { } } - @Test(timeout = 1000L) - public void getAnnotationTest() throws NoSuchMethodException { - ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest")); - Test annotation = method.getAnnotation(Test.class); - assertNotNull(annotation); - assertEquals(1000L, annotation.timeout()); + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface TestAnnotation { + long value(); } - @Test(timeout = 1000L) + @Test + @TestAnnotation(value = 1000L) + public void getAnnotationTest() throws NoSuchMethodException { + ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest")); + TestAnnotation annotation = method.getAnnotation(TestAnnotation.class); + assertNotNull(annotation); + assertEquals(1000L, annotation.value()); + } + + @Test + @TestAnnotation(value = 1000L) public void getAnnotationsTest() throws NoSuchMethodException { ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest")); Annotation[] annotations = method.getAnnotations(); assertNotNull(annotations); - assertEquals(1, annotations.length); - assertEquals(1000L, ((Test) annotations[0]).timeout()); + assertEquals(2, annotations.length); + TestAnnotation annotation = null; + for (Annotation a : annotations) { + if (a instanceof TestAnnotation) { + annotation = (TestAnnotation) a; + break; + } + } + assertNotNull(annotation); + assertEquals(1000L, annotation.value()); } @Retention(RetentionPolicy.RUNTIME) diff --git a/hotspot/test/compiler/loopopts/BadPredicateAfterPartialPeel.java b/hotspot/test/compiler/loopopts/BadPredicateAfterPartialPeel.java new file mode 100644 index 00000000000..186f1cc1a7d --- /dev/null +++ b/hotspot/test/compiler/loopopts/BadPredicateAfterPartialPeel.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/** + * @test + * @bug 8146792 + * @summary Predicate moved after partial peel may lead to broken graph + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileOnly=BadPredicateAfterPartialPeel::m -XX:CompileCommand=dontinline,BadPredicateAfterPartialPeel::not_inlined* -XX:CompileCommand=quiet BadPredicateAfterPartialPeel + * + */ + +public class BadPredicateAfterPartialPeel { + + static void not_inlined1() {} + static void not_inlined4() {} + + static int m_helper(int i, int i3, int i4) { + return i3 == 4 ? i4 : i; + } + + static float[] array = new float[1000]; + static int[] array2 = new int[1000]; + + boolean flag; + int j; + + static void m(BadPredicateAfterPartialPeel o1, BadPredicateAfterPartialPeel o2, BadPredicateAfterPartialPeel o, int i4) { + int i1 = 1; + + // To delay partial peeling to the loop opts pass right before CCP + int i2 = 0; + for (; i2 < 10; i2 += i1); + i2 = i2 / 10; + + // Simplified during CCP: + int i3 = 2; + for (; i3 < 4; i3 *= 2); + + // Loop is partial peeled right before CCP + int i = 0; + boolean b = true; + + not_inlined1(); + + array[0] = -1; + do { + // peeled section starts here + o.flag = false; + o.j = 0; + + if (b) { + // The following store will be pinned between + // predicates and the loop after partial peeling. All + // control flow will be optimized out and so nothing + // will prevent predicates from being moved out the + // loop. + array[i] = 0; + } + if (array[0] != 0) { + } + if (i >= 10) { + // peeled section ends here + return; + } + i += i2; + b = false; + int i5 = m_helper(i, i3, i4); // This will be simpliflied during CCP + if (array[i5] != 0) { // and this will become a predicate + } + if (o2.flag) { + } + // A bunch of stuff to grow loop body size and prevent peeling: + array2[0] = 0; + array2[1] = 0; + array2[2] = 0; + array2[3] = 0; + array2[4] = 0; + array2[5] = 0; + array2[6] = 0; + array2[7] = 0; + array2[8] = 0; + array2[9] = 0; + array2[10] = 0; + array2[11] = 0; + array2[12] = 0; + array2[13] = 0; + array2[14] = 0; + array2[15] = 0; + array2[16] = 0; + array2[17] = 0; + array2[18] = 0; + array2[19] = 0; + array2[20] = 0; + array2[21] = 0; + array2[22] = 0; + array2[23] = 0; + array2[24] = 0; + array2[25] = 0; + array2[26] = 0; + array2[27] = 0; + array2[28] = 0; + array2[29] = 0; + array2[30] = 0; + array2[31] = 0; + array2[32] = 0; + array2[33] = 0; + array2[34] = 0; + array2[35] = 0; + array2[36] = 0; + array2[37] = 0; + array2[38] = 0; + array2[39] = 0; + array2[40] = 0; + array2[41] = 0; + array2[42] = 0; + array2[43] = 0; + array2[44] = 0; + array2[45] = 0; + array2[46] = 0; + array2[47] = 0; + array2[48] = 0; + array2[49] = 0; + array2[50] = 0; + array2[51] = 0; + array2[52] = 0; + array2[53] = 0; + array2[54] = 0; + array2[55] = 0; + array2[56] = 0; + array2[57] = 0; + array2[58] = 0; + array2[59] = 0; + array2[60] = 0; + array2[61] = 0; + array2[62] = 0; + array2[63] = 0; + array2[64] = 0; + array2[65] = 0; + array2[66] = 0; + array2[67] = 0; + array2[68] = 0; + array2[69] = 0; + array2[70] = 0; + array2[71] = 0; + array2[72] = 0; + array2[73] = 0; + array2[74] = 0; + array2[75] = 0; + array2[76] = 0; + array2[77] = 0; + array2[78] = 0; + array2[79] = 0; + array2[80] = 0; + array2[81] = 0; + array2[82] = 0; + array2[83] = 0; + array2[84] = 0; + array2[85] = 0; + array2[86] = 0; + array2[87] = 0; + array2[88] = 0; + array2[89] = 0; + array2[90] = 0; + array2[91] = 0; + array2[92] = 0; + array2[93] = 0; + array2[94] = 0; + array2[95] = 0; + array2[96] = 0; + array2[97] = 0; + array2[98] = 0; + array2[99] = 0; + + array2[100] = 0; + array2[101] = 0; + array2[102] = 0; + array2[103] = 0; + array2[104] = 0; + array2[105] = 0; + array2[106] = 0; + array2[107] = 0; + array2[108] = 0; + array2[109] = 0; + array2[110] = 0; + array2[111] = 0; + array2[112] = 0; + array2[113] = 0; + array2[114] = 0; + array2[115] = 0; + array2[116] = 0; + array2[117] = 0; + array2[118] = 0; + array2[119] = 0; + array2[120] = 0; + array2[121] = 0; + array2[122] = 0; + array2[123] = 0; + array2[124] = 0; + array2[125] = 0; + array2[126] = 0; + array2[127] = 0; + array2[128] = 0; + array2[129] = 0; + array2[130] = 0; + array2[131] = 0; + array2[132] = 0; + array2[133] = 0; + array2[134] = 0; + array2[135] = 0; + array2[136] = 0; + array2[137] = 0; + array2[138] = 0; + array2[139] = 0; + array2[140] = 0; + array2[141] = 0; + array2[142] = 0; + array2[143] = 0; + array2[144] = 0; + array2[145] = 0; + array2[146] = 0; + array2[147] = 0; + array2[148] = 0; + array2[149] = 0; + array2[150] = 0; + array2[151] = 0; + array2[152] = 0; + array2[153] = 0; + array2[154] = 0; + array2[155] = 0; + array2[156] = 0; + array2[157] = 0; + array2[158] = 0; + array2[159] = 0; + array2[160] = 0; + array2[161] = 0; + array2[162] = 0; + array2[163] = 0; + array2[164] = 0; + array2[165] = 0; + array2[166] = 0; + array2[167] = 0; + array2[168] = 0; + array2[169] = 0; + array2[170] = 0; + array2[171] = 0; + array2[172] = 0; + array2[173] = 0; + array2[174] = 0; + array2[175] = 0; + array2[176] = 0; + array2[177] = 0; + array2[178] = 0; + array2[179] = 0; + array2[180] = 0; + array2[181] = 0; + array2[182] = 0; + array2[183] = 0; + array2[184] = 0; + array2[185] = 0; + array2[186] = 0; + array2[187] = 0; + array2[188] = 0; + array2[189] = 0; + array2[190] = 0; + array2[191] = 0; + array2[192] = 0; + array2[193] = 0; + array2[194] = 0; + array2[195] = 0; + array2[196] = 0; + array2[197] = 0; + array2[198] = 0; + array2[199] = 0; + if (o1.j >= 20) { + break; + } + o1.j++; + } while(true); + not_inlined4(); + } + + static public void main(String[] args) { + BadPredicateAfterPartialPeel o1 = new BadPredicateAfterPartialPeel(); + BadPredicateAfterPartialPeel o2 = new BadPredicateAfterPartialPeel(); + for (int i = 0; i < 20000; i++) { + o1.j = 0; + m(o1, o2, o2, 0); + m_helper(i, 2, i); // pollute profile + } + } +} diff --git a/hotspot/test/compiler/loopopts/TestLoopPeeling.java b/hotspot/test/compiler/loopopts/TestLoopPeeling.java new file mode 100644 index 00000000000..d2d2e3d7848 --- /dev/null +++ b/hotspot/test/compiler/loopopts/TestLoopPeeling.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8078262 + * @summary Tests correct dominator information after loop peeling. + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestLoopPeeling::test* TestLoopPeeling + */ +public class TestLoopPeeling { + + public int[] array = new int[100]; + + public static void main(String args[]) { + TestLoopPeeling test = new TestLoopPeeling(); + try { + test.testArrayAccess(0, 1); + test.testArrayAllocation(0, 1); + } catch (Exception e) { + // Ignore exceptions + } + } + + public void testArrayAccess(int index, int inc) { + int storeIndex = -1; + + for (; index < 10; index += inc) { + // This loop invariant check triggers loop peeling because it can + // be moved out of the loop (see 'IdealLoopTree::policy_peeling'). + if (inc == 42) return; + + // This loop variant usage of LShiftL( ConvI2L( Phi(storeIndex) ) ) + // prevents the split if optimization that would otherwise clone the + // LShiftL and ConvI2L nodes and assign them to their corresponding array + // address computation (see 'PhaseIdealLoop::split_if_with_blocks_post'). + if (storeIndex > 0 && array[storeIndex] == 42) return; + + if (index == 42) { + // This store and the corresponding range check are moved out of the + // loop and both used after old loop and the peeled iteration exit. + // For the peeled iteration, storeIndex is always -1 and the ConvI2L + // is replaced by TOP. However, the range check is not folded because + // we don't do the split if optimization in PhaseIdealLoop2. + // As a result, we have a (dead) control path from the peeled iteration + // to the StoreI but the data path is removed. + array[storeIndex] = 1; + return; + } + + storeIndex++; + } + } + + public byte[] testArrayAllocation(int index, int inc) { + int allocationCount = -1; + byte[] result; + + for (; index < 10; index += inc) { + // This loop invariant check triggers loop peeling because it can + // be moved out of the loop (see 'IdealLoopTree::policy_peeling'). + if (inc == 42) return null; + + if (index == 42) { + // This allocation and the corresponding size check are moved out of the + // loop and both used after old loop and the peeled iteration exit. + // For the peeled iteration, allocationCount is always -1 and the ConvI2L + // is replaced by TOP. However, the size check is not folded because + // we don't do the split if optimization in PhaseIdealLoop2. + // As a result, we have a (dead) control path from the peeled iteration + // to the allocation but the data path is removed. + result = new byte[allocationCount]; + return result; + } + + allocationCount++; + } + return null; + } +} + diff --git a/hotspot/test/compiler/loopopts/UseCountedLoopSafepoints.java b/hotspot/test/compiler/loopopts/UseCountedLoopSafepoints.java index c769b5b5aba..689b7f40be7 100644 --- a/hotspot/test/compiler/loopopts/UseCountedLoopSafepoints.java +++ b/hotspot/test/compiler/loopopts/UseCountedLoopSafepoints.java @@ -28,6 +28,7 @@ * @summary Test that C2 flag UseCountedLoopSafepoints ensures a safepoint is kept in a CountedLoop * @library /testlibrary * @modules java.base + * @ignore 8146096 * @run main UseCountedLoopSafepoints */ diff --git a/hotspot/test/compiler/loopopts/superword/TestBestAlign.java b/hotspot/test/compiler/loopopts/superword/TestBestAlign.java index 9063798e63a..9609859701d 100644 --- a/hotspot/test/compiler/loopopts/superword/TestBestAlign.java +++ b/hotspot/test/compiler/loopopts/superword/TestBestAlign.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 SAP AG. All Rights Reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/test/compiler/membars/DekkerTest.java b/hotspot/test/compiler/membars/DekkerTest.java index bef045701ec..83eb923c6f2 100644 --- a/hotspot/test/compiler/membars/DekkerTest.java +++ b/hotspot/test/compiler/membars/DekkerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 SAP AG. All Rights Reserved. + * Copyright (c) 2013 SAP SE. 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 diff --git a/hotspot/test/compiler/memoryinitialization/ZeroTLABTest.java b/hotspot/test/compiler/memoryinitialization/ZeroTLABTest.java new file mode 100644 index 00000000000..9be412a6db2 --- /dev/null +++ b/hotspot/test/compiler/memoryinitialization/ZeroTLABTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @bug 8086053 + * @run main/othervm -Xcomp -XX:+UseTLAB -XX:+ZeroTLAB ZeroTLABTest + * @run main/othervm -Xcomp -XX:+UseTLAB -XX:-ZeroTLAB ZeroTLABTest + * @run main/othervm -Xcomp -XX:-UseTLAB -XX:+ZeroTLAB ZeroTLABTest + * @run main/othervm -Xcomp -XX:-UseTLAB -XX:-ZeroTLAB ZeroTLABTest + */ +public class ZeroTLABTest { + public static void main(String args[]) { + System.out.println("Test PASSED"); + } +} diff --git a/hotspot/test/compiler/runtime/7141637/SpreadNullArg.java b/hotspot/test/compiler/runtime/7141637/SpreadNullArg.java index 195e106d542..fac5d4723a0 100644 --- a/hotspot/test/compiler/runtime/7141637/SpreadNullArg.java +++ b/hotspot/test/compiler/runtime/7141637/SpreadNullArg.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java b/hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java index 771ffb0bd68..2f0ec3c0353 100644 --- a/hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java +++ b/hotspot/test/compiler/stringopts/TestOptimizeStringConcat.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 SAP AG. All Rights Reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java b/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java index f12972c5cd2..f4e0f776f7e 100644 --- a/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java +++ b/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 SAP AG. All Rights Reserved. + * Copyright (c) 2015 SAP SE. 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 diff --git a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java index c04f8ad9377..5d7d3852f43 100644 --- a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java +++ b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java @@ -64,14 +64,18 @@ public class TestPerfCountersAndMemoryPools { throws Exception { MemoryPoolMXBean pool = getMemoryPool(memoryPoolName); + // First, call all the methods to let them allocate their own slab of metadata + getMinCapacity(perfNS); + getCapacity(perfNS); + getUsed(perfNS); + pool.getUsage().getInit(); + pool.getUsage().getUsed(); + pool.getUsage().getCommitted(); + assertEQ(1L, 1L); + // Must do a GC to update performance counters System.gc(); assertEQ(getMinCapacity(perfNS), pool.getUsage().getInit()); - - // Must do a second GC to update the perfomance counters again, since - // the call pool.getUsage().getInit() could have allocated some - // metadata. - System.gc(); assertEQ(getUsed(perfNS), pool.getUsage().getUsed()); assertEQ(getCapacity(perfNS), pool.getUsage().getCommitted()); } diff --git a/hotspot/test/runtime/7100935/TestConjointAtomicArraycopy.java b/hotspot/test/runtime/7100935/TestConjointAtomicArraycopy.java index 3e1711888ab..6f82740cd72 100644 --- a/hotspot/test/runtime/7100935/TestConjointAtomicArraycopy.java +++ b/hotspot/test/runtime/7100935/TestConjointAtomicArraycopy.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7100935/TestShortArraycopy.java b/hotspot/test/runtime/7100935/TestShortArraycopy.java index 81b048912e3..edcbcbbf51e 100644 --- a/hotspot/test/runtime/7100935/TestShortArraycopy.java +++ b/hotspot/test/runtime/7100935/TestShortArraycopy.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7107135/Test.java b/hotspot/test/runtime/7107135/Test.java index 84f3ab3393f..9b489347bd4 100644 --- a/hotspot/test/runtime/7107135/Test.java +++ b/hotspot/test/runtime/7107135/Test.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002-2013, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7107135/Test7107135.sh b/hotspot/test/runtime/7107135/Test7107135.sh index 30604ff6313..5371b3a2d95 100644 --- a/hotspot/test/runtime/7107135/Test7107135.sh +++ b/hotspot/test/runtime/7107135/Test7107135.sh @@ -2,7 +2,7 @@ # # Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2011 SAP AG. All Rights Reserved. +# Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7107135/TestMT.java b/hotspot/test/runtime/7107135/TestMT.java index edea698ac8c..4fc297dcd03 100644 --- a/hotspot/test/runtime/7107135/TestMT.java +++ b/hotspot/test/runtime/7107135/TestMT.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002-2013, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7107135/test.c b/hotspot/test/runtime/7107135/test.c index 602063f640e..3e39eaeb7f2 100644 --- a/hotspot/test/runtime/7107135/test.c +++ b/hotspot/test/runtime/7107135/test.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002-2013, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011 SAP SE. 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 diff --git a/hotspot/test/runtime/7158988/FieldMonitor.java b/hotspot/test/runtime/7158988/FieldMonitor.java index 42b82768530..cfe210ce69d 100644 --- a/hotspot/test/runtime/7158988/FieldMonitor.java +++ b/hotspot/test/runtime/7158988/FieldMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 SAP AG. All Rights Reserved. + * Copyright (c) 2012 SAP SE. 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 diff --git a/hotspot/test/runtime/7158988/TestPostFieldModification.java b/hotspot/test/runtime/7158988/TestPostFieldModification.java index d730003b267..8f51d4d32c4 100644 --- a/hotspot/test/runtime/7158988/TestPostFieldModification.java +++ b/hotspot/test/runtime/7158988/TestPostFieldModification.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 SAP AG. All Rights Reserved. + * Copyright (c) 2012 SAP SE. 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 diff --git a/hotspot/test/runtime/Unsafe/AllocateInstance.java b/hotspot/test/runtime/Unsafe/AllocateInstance.java index 9beb7219e0b..4393a081de4 100644 --- a/hotspot/test/runtime/Unsafe/AllocateInstance.java +++ b/hotspot/test/runtime/Unsafe/AllocateInstance.java @@ -35,21 +35,7 @@ import sun.misc.Unsafe; import static jdk.test.lib.Asserts.*; public class AllocateInstance { - public static void main(String args[]) throws Exception { - Unsafe unsafe = Utils.getUnsafe(); - - // allocateInstance() should not result in a call to the constructor - TestClass tc = (TestClass)unsafe.allocateInstance(TestClass.class); - assertFalse(tc.calledConstructor); - - // allocateInstance() on an abstract class should result in an InstantiationException - try { - AbstractClass ac = (AbstractClass)unsafe.allocateInstance(AbstractClass.class); - throw new RuntimeException("Did not get expected InstantiationException"); - } catch (InstantiationException e) { - // Expected - } - } + static final Unsafe UNSAFE = Utils.getUnsafe(); class TestClass { public boolean calledConstructor = false; @@ -59,7 +45,41 @@ public class AllocateInstance { } } + static void testConstructorCall() throws InstantiationException { + // allocateInstance() should not result in a call to the constructor + TestClass tc = (TestClass)UNSAFE.allocateInstance(TestClass.class); + assertFalse(tc.calledConstructor); + } + abstract class AbstractClass { public AbstractClass() {} } + + static void testAbstractClass() { + try { + AbstractClass ac = (AbstractClass) UNSAFE.allocateInstance(AbstractClass.class); + throw new AssertionError("Should throw InstantiationException for an abstract class"); + } catch (InstantiationException e) { + // Expected + } + } + + interface AnInterface {} + + static void testInterface() { + try { + AnInterface ai = (AnInterface) UNSAFE.allocateInstance(AnInterface.class); + throw new AssertionError("Should throw InstantiationException for an interface"); + } catch (InstantiationException e) { + // Expected + } + } + + public static void main(String args[]) throws Exception { + for (int i = 0; i < 20_000; i++) { + testConstructorCall(); + testAbstractClass(); + testInterface(); + } + } } diff --git a/hotspot/test/serviceability/jvmti/8036666/GetObjectLockCount.java b/hotspot/test/serviceability/jvmti/8036666/GetObjectLockCount.java index 47e4104bd3d..404f4dd0468 100644 --- a/hotspot/test/serviceability/jvmti/8036666/GetObjectLockCount.java +++ b/hotspot/test/serviceability/jvmti/8036666/GetObjectLockCount.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 SAP AG. All Rights Reserved. + * Copyright (c) 2014 SAP SE. 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 diff --git a/hotspot/test/serviceability/jvmti/8036666/RecursiveObjectLock.java b/hotspot/test/serviceability/jvmti/8036666/RecursiveObjectLock.java index d1a35edd6c3..55107e9b0ee 100644 --- a/hotspot/test/serviceability/jvmti/8036666/RecursiveObjectLock.java +++ b/hotspot/test/serviceability/jvmti/8036666/RecursiveObjectLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 SAP AG. All Rights Reserved. + * Copyright (c) 2014 SAP SE. 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 diff --git a/hotspot/test/testlibrary/ClassFileInstaller.java b/hotspot/test/testlibrary/ClassFileInstaller.java index 303e96e5a87..4e042dacde4 100644 --- a/hotspot/test/testlibrary/ClassFileInstaller.java +++ b/hotspot/test/testlibrary/ClassFileInstaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ +import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -42,6 +43,9 @@ public class ClassFileInstaller { // Convert dotted class name to a path to a class file String pathName = arg.replace('.', '/').concat(".class"); InputStream is = cl.getResourceAsStream(pathName); + if (is == null) { + throw new FileNotFoundException(pathName); + } // Create the class file's package directory Path p = Paths.get(pathName); diff --git a/jaxp/.hgtags b/jaxp/.hgtags index 06990f8df41..75132348c9c 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -345,3 +345,4 @@ c8d0845877a811ab4350935892f826929359a3ff jdk-9+95 d45bcd374f6057851e3c2dcd45607cd362afadfa jdk-9+100 d3e834ff74e724a2b92a558e18e8cbf81c6dbc59 jdk-9+101 9dcf193c0b6cf22c0e89e2dc705a2c0f520ae064 jdk-9+102 +bdbf2342b21bd8ecad1b4e6499a0dfb314952bd7 jdk-9+103 diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 25d05bf7a7f..9909ccb59aa 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -348,3 +348,4 @@ b55cebc47555293cf9c2aefb3bf63c56e847ab19 jdk-9+96 d0a97e57d2336238edf6a4cd60aafe67deb7258d jdk-9+100 3e99318616da903e0dc8f07f9f9203dc1bd49921 jdk-9+101 0868b93587cc99df3a4f4d3817a1aa756bea60ab jdk-9+102 +eb5e005a17e50d7d8340daaf21a5c3c5ae358d68 jdk-9+103 diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java index a0329cf7744..b6d9f4d2dc9 100644 --- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java +++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,11 +29,12 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; @@ -105,9 +106,9 @@ class ContextFinder { /** * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped, - * throw the wrapped exception. + * throw the wrapped exception. Otherwise returns exception to be wrapped for further processing. */ - private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException { + private static Throwable handleInvocationTargetException(InvocationTargetException x) throws JAXBException { Throwable t = x.getTargetException(); if (t != null) { if (t instanceof JAXBException) @@ -118,7 +119,9 @@ class ContextFinder { throw (RuntimeException) t; if (t instanceof Error) throw (Error) t; + return t; } + return x; } @@ -157,9 +160,10 @@ class ContextFinder { } catch (ClassNotFoundException x) { throw new JAXBException(Messages.format(Messages.PROVIDER_NOT_FOUND, className), x); - } catch (RuntimeException x) { + } catch (RuntimeException | JAXBException x) { // avoid wrapping RuntimeException to JAXBException, // because it indicates a bug in this code. + // JAXBException re-thrown as is throw x; } catch (Exception x) { // can't catch JAXBException because the method is hidden behind @@ -189,8 +193,9 @@ class ContextFinder { try { Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class); // any failure in invoking this method would be considered fatal - context = m.invoke(null, contextPath, classLoader, properties); - } catch (NoSuchMethodException e) { + Object obj = instantiateProviderIfNecessary(m); + context = m.invoke(obj, contextPath, classLoader, properties); + } catch (NoSuchMethodException ignored) { // it's not an error for the provider not to have this method. } @@ -198,8 +203,9 @@ class ContextFinder { // try the old method that doesn't take properties. compatible with 1.0. // it is an error for an implementation not to have both forms of the createContext method. Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class); + Object obj = instantiateProviderIfNecessary(m); // any failure in invoking this method would be considered fatal - context = m.invoke(null, contextPath, classLoader); + context = m.invoke(obj, contextPath, classLoader); } if (!(context instanceof JAXBContext)) { @@ -208,18 +214,11 @@ class ContextFinder { } return (JAXBContext) context; } catch (InvocationTargetException x) { - handleInvocationTargetException(x); - // for other exceptions, wrap the internal target exception - // with a JAXBException - Throwable e = x; - if (x.getTargetException() != null) - e = x.getTargetException(); - + // throw if it is exception not to be wrapped + // otherwise, wrap with a JAXBException + Throwable e = handleInvocationTargetException(x); throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e); - } catch (RuntimeException x) { - // avoid wrapping RuntimeException to JAXBException, - // because it indicates a bug in this code. - throw x; + } catch (Exception x) { // can't catch JAXBException because the method is hidden behind // reflection. Root element collisions detected in the call to @@ -229,6 +228,23 @@ class ContextFinder { } } + private static Object instantiateProviderIfNecessary(Method m) throws JAXBException { + Class declaringClass = m.getDeclaringClass(); + try { + if (JAXBContextFactory.class.isAssignableFrom(declaringClass)) { + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + return declaringClass.newInstance(); + } + }); + } + return null; + } catch (PrivilegedActionException e) { + throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, declaringClass, e), e); + } + } + /** * Create an instance of a class using the thread context ClassLoader */ @@ -255,7 +271,8 @@ class ContextFinder { try { Method m = spFactory.getMethod("createContext", Class[].class, Map.class); - Object context = m.invoke(null, classes, properties); + Object obj = instantiateProviderIfNecessary(m); + Object context = m.invoke(obj, classes, properties); if (!(context instanceof JAXBContext)) { // the cast would fail, so generate an exception with a nice message throw handleClassCastException(context.getClass(), JAXBContext.class); @@ -264,13 +281,10 @@ class ContextFinder { } catch (NoSuchMethodException | IllegalAccessException e) { throw new JAXBException(e); - } catch (InvocationTargetException e) { - handleInvocationTargetException(e); - - Throwable x = e; - if (e.getTargetException() != null) - x = e.getTargetException(); + // throw if it is exception not to be wrapped + // otherwise, wrap with a JAXBException + Throwable x = handleInvocationTargetException(e); throw new JAXBException(x); } diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java index c5185236400..adfb479a849 100644 --- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java +++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,7 +94,7 @@ import java.io.InputStream; * allows the merging of global elements and type definitions across a set of schemas (listed * in the {@code contextPath}). Since each schema in the schema set can belong * to distinct namespaces, the unification of schemas to an unmarshalling - * context should be namespace independent. This means that a client + * context must be namespace independent. This means that a client * application is able to unmarshal XML documents that are instances of * any of the schemas listed in the {@code contextPath}. For example: * @@ -200,21 +200,28 @@ import java.io.InputStream; * *

Discovery of JAXB implementation

*

- * When one of the {@code newInstance} methods is called, a JAXB implementation is discovered - * by the following steps. + * To create an instance of {@link JAXBContext}, one of {@code JAXBContext.newInstance(...)} methods is invoked. After + * JAX-B implementation is discovered, call is delegated to appropriate provider's method {@code createContext(...)} + * passing parameters from the original call. + *

+ * JAX-B implementation discovery happens each time {@code JAXBContext.newInstance} is invoked. If there is no user + * specific configuration provided, default JAX-B provider must be returned. + *

+ * Implementation discovery consists of following steps: * *

    * *
  1. - * For each package/class explicitly passed in to the {@link #newInstance} method, in the order they are specified, - * {@code jaxb.properties} file is looked up in its package, by using the associated classloader — + * Packages/classes explicitly passed in to the {@link #newInstance} method are processed in the order they are + * specified, until {@code jaxb.properties} file is looked up in its package, by using the associated classloader — * this is {@link Class#getClassLoader() the owner class loader} for a {@link Class} argument, and for a package * the specified {@link ClassLoader}. * *

    - * If such a file is discovered, it is {@link Properties#load(InputStream) loaded} as a property file, and - * the value of the {@link #JAXB_CONTEXT_FACTORY} key will be assumed to be the provider factory class. - * This class is then loaded by the associated class loader discussed above. + * If such a resource is discovered, it is {@link Properties#load(InputStream) loaded} as a property file, and + * the value of the {@link #JAXB_CONTEXT_FACTORY} key will be assumed to be the provider factory class. If no value + * found, {@code "javax.xml.bind.context.factory"} is used as a key for backwards compatibility reasons. This class is + * then loaded by the associated class loader discussed above. * *

    * This phase of the look up allows some packages to force the use of a certain JAXB implementation. @@ -222,7 +229,9 @@ import java.io.InputStream; * *

  2. * If the system property {@link #JAXB_CONTEXT_FACTORY} exists, then its value is assumed to be the provider - * factory class. This phase of the look up enables per-JVM override of the JAXB implementation. + * factory class. If no such property exists, properties {@code "javax.xml.bind.context.factory"} and + * {@code "javax.xml.bind.JAXBContext"} are checked too (in this order), for backwards compatibility reasons. This phase + * of the look up enables per-JVM override of the JAXB implementation. * *
  3. * Provider of {@link javax.xml.bind.JAXBContextFactory} is loaded using the service-provider loading @@ -235,43 +244,58 @@ import java.io.InputStream; *
    * In case of {@link java.util.ServiceConfigurationError service * configuration error} a {@link javax.xml.bind.JAXBException} will be thrown. - *
  4. * *
  5. * Look for resource {@code /META-INF/services/javax.xml.bind.JAXBContext} using provided class loader. * Methods without class loader parameter use {@code Thread.currentThread().getContextClassLoader()}. - * If such a resource exists, its content is assumed to be the provider factory class and must supply - * an implementation class containing the following method signatures: + * If such a resource exists, its content is assumed to be the provider factory class. * - *
    - *
    - * public static JAXBContext createContext(
    - *                                      String contextPath,
    - *                                      ClassLoader classLoader,
    - *                                      Map<String,Object> properties throws JAXBException
    - *
    - * public static JAXBContext createContext(
    - *                                      Class[] classes,
    - *                                      Map<String,Object> properties ) throws JAXBException
    - * 
    * This configuration method is deprecated. * *
  6. * Finally, if all the steps above fail, then the rest of the look up is unspecified. That said, * the recommended behavior is to simply look for some hard-coded platform default JAXB implementation. - * This phase of the look up is so that JavaSE can have its own JAXB implementation as the last resort. + * This phase of the look up is so that Java SE can have its own JAXB implementation as the last resort. *
* *

- * Once the provider factory class {@link javax.xml.bind.JAXBContextFactory} is discovered, one of its methods - * {@link javax.xml.bind.JAXBContextFactory#createContext(String, ClassLoader, java.util.Map)} or - * {@link javax.xml.bind.JAXBContextFactory#createContext(Class[], java.util.Map)} is invoked - * to create a {@link JAXBContext}. + * Once the provider factory class is discovered, context creation is delegated to one of its + * {@code createContext(...)} methods. + * + * For backward compatibility reasons, there are two ways how to implement provider factory class: + *

    + *
  1. the class is implementation of {@link javax.xml.bind.JAXBContextFactory}. It must also implement no-arg + * constructor. If discovered in other step then 3, new instance using no-arg constructor is created first. + * After that, appropriate instance method is invoked on this instance. + *
  2. the class is not implementation of interface above and then it is mandated to implement the following + * static method signatures: + *
    + *
    + * public static JAXBContext createContext(
    + *                                      String contextPath,
    + *                                      ClassLoader classLoader,
    + *                                      Map<String,Object> properties ) throws JAXBException
    + *
    + * public static JAXBContext createContext(
    + *                                      Class[] classes,
    + *                                      Map<String,Object> properties ) throws JAXBException
    + * 
    + * In this scenario, appropriate static method is used instead of instance method. This approach is incompatible + * with {@link java.util.ServiceLoader} so it can't be used with step 3. + *
+ *

+ * There is no difference in behavior of given method {@code createContext(...)} regardless of whether it uses approach + * 1 (JAXBContextFactory) or 2 (no interface, static methods). * * @apiNote - *

Service discovery method using file /META-INF/services/javax.xml.bind.JAXBContext (described in step 4) - * and leveraging provider's static methods is supported only to allow backwards compatibility, but it is strongly - * recommended to migrate to standard ServiceLoader mechanism (described in step 3). + * Service discovery method using resource {@code /META-INF/services/javax.xml.bind.JAXBContext} (described in step 4) + * is supported only to allow backwards compatibility, it is strongly recommended to migrate to standard + * {@link java.util.ServiceLoader} mechanism (described in step 3). The difference here is the resource name, which + * doesn't match service's type name. + *

+ * Also using providers implementing interface {@link JAXBContextFactory} is preferred over using ones defining + * static methods, same as {@link JAXBContext#JAXB_CONTEXT_FACTORY} property is preferred over property + * {@code "javax.xml.bind.context.factory"} * * @implNote * Within the last step, if Glassfish AS environment detected, its specific service loader is used to find factory class. @@ -308,16 +332,10 @@ public abstract class JAXBContext { * the context class loader of the current thread. * * @throws JAXBException if an error was encountered while creating the - * {@code JAXBContext} such as - *

    - *
  1. failure to locate either ObjectFactory.class or jaxb.index in the packages
  2. - *
  3. an ambiguity among global elements contained in the contextPath
  4. - *
  5. failure to locate a value for the context factory provider property
  6. - *
  7. mixing schema derived packages from different providers on the same contextPath
  8. - *
+ * {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details. */ public static JAXBContext newInstance( String contextPath ) - throws JAXBException { + throws JAXBException { //return newInstance( contextPath, JAXBContext.class.getClassLoader() ); return newInstance( contextPath, getContextClassLoader()); @@ -405,13 +423,7 @@ public abstract class JAXBContext { * * @return a new instance of a {@code JAXBContext} * @throws JAXBException if an error was encountered while creating the - * {@code JAXBContext} such as - *
    - *
  1. failure to locate either ObjectFactory.class or jaxb.index in the packages
  2. - *
  3. an ambiguity among global elements contained in the contextPath
  4. - *
  5. failure to locate a value for the context factory provider property
  6. - *
  7. mixing schema derived packages from different providers on the same contextPath
  8. - *
+ * {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details. */ public static JAXBContext newInstance( String contextPath, ClassLoader classLoader ) throws JAXBException { @@ -427,7 +439,7 @@ public abstract class JAXBContext { * the instantiation of {@link JAXBContext}. * *

- * The interpretation of properties is up to implementations. Implementations should + * The interpretation of properties is up to implementations. Implementations must * throw {@code JAXBException} if it finds properties that it doesn't understand. * * @param contextPath list of java package names that contain schema derived classes @@ -439,13 +451,7 @@ public abstract class JAXBContext { * * @return a new instance of a {@code JAXBContext} * @throws JAXBException if an error was encountered while creating the - * {@code JAXBContext} such as - *

    - *
  1. failure to locate either ObjectFactory.class or jaxb.index in the packages
  2. - *
  3. an ambiguity among global elements contained in the contextPath
  4. - *
  5. failure to locate a value for the context factory provider property
  6. - *
  7. mixing schema derived packages from different providers on the same contextPath
  8. - *
+ * {@code JAXBContext}. See {@link #newInstance(String, ClassLoader)} for details. * @since 1.6, JAXB 2.0 */ public static JAXBContext newInstance( String contextPath, @@ -454,14 +460,14 @@ public abstract class JAXBContext { return ContextFinder.find( /* The default property name according to the JAXB spec */ - JAXB_CONTEXT_FACTORY, + JAXB_CONTEXT_FACTORY, /* the context path supplied by the client app */ - contextPath, + contextPath, /* class loader to be used */ - classLoader, - properties ); + classLoader, + properties ); } // TODO: resurrect this once we introduce external annotations @@ -583,17 +589,8 @@ public abstract class JAXBContext { * @return * A new instance of a {@code JAXBContext}. * - * @throws JAXBException - * if an error was encountered while creating the - * {@code JAXBContext}, such as (but not limited to): - *
    - *
  1. No JAXB implementation was discovered - *
  2. Classes use JAXB annotations incorrectly - *
  3. Classes have colliding annotations (i.e., two classes with the same type name) - *
  4. The JAXB implementation was unable to locate - * provider-specific out-of-band information (such as additional - * files generated at the development time.) - *
+ * @throws JAXBException if an error was encountered while creating the + * {@code JAXBContext}. See {@link JAXBContext#newInstance(Class[], Map)} for details. * * @throws IllegalArgumentException * if the parameter contains {@code null} (i.e., {@code newInstance(null);}) @@ -601,7 +598,7 @@ public abstract class JAXBContext { * @since 1.6, JAXB 2.0 */ public static JAXBContext newInstance( Class ... classesToBeBound ) - throws JAXBException { + throws JAXBException { return newInstance(classesToBeBound,Collections.emptyMap()); } @@ -614,7 +611,7 @@ public abstract class JAXBContext { * to configure 'properties' for this instantiation of {@link JAXBContext}. * *

- * The interpretation of properties is up to implementations. Implementations should + * The interpretation of properties is up to implementations. Implementations must * throw {@code JAXBException} if it finds properties that it doesn't understand. * * @param classesToBeBound @@ -646,10 +643,10 @@ public abstract class JAXBContext { * @since 1.6, JAXB 2.0 */ public static JAXBContext newInstance( Class[] classesToBeBound, Map properties ) - throws JAXBException { + throws JAXBException { if (classesToBeBound == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(); } // but it is an error to have nulls in it. diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java index 09630484dc9..ef98dc61e4f 100644 --- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java +++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,14 +56,7 @@ public interface JAXBContextFactory { * * @throws JAXBException * if an error was encountered while creating the - * {@code JAXBContext}, such as (but not limited to): - *

    - *
  1. Classes use JAXB annotations incorrectly - *
  2. Classes have colliding annotations (i.e., two classes with the same type name) - *
  3. The JAXB implementation was unable to locate - * provider-specific out-of-band information (such as additional - * files generated at the development time.) - *
+ * {@code JAXBContext}. See {@link JAXBContext#newInstance(Class[], Map)} for details. * * @throws IllegalArgumentException * if the parameter contains {@code null} (i.e., {@code newInstance(null,someMap);}) @@ -81,7 +74,7 @@ public interface JAXBContextFactory { * For semantics see {@link javax.xml.bind.JAXBContext#newInstance(String, ClassLoader, java.util.Map)} * *

- * The interpretation of properties is up to implementations. Implementations should + * The interpretation of properties is up to implementations. Implementations must * throw {@code JAXBException} if it finds properties that it doesn't understand. * * @param contextPath list of java package names that contain schema derived classes @@ -93,13 +86,8 @@ public interface JAXBContextFactory { * * @return a new instance of a {@code JAXBContext} * @throws JAXBException if an error was encountered while creating the - * {@code JAXBContext} such as - *

    - *
  1. failure to locate either ObjectFactory.class or jaxb.index in the packages
  2. - *
  3. an ambiguity among global elements contained in the contextPath
  4. - *
  5. failure to locate a value for the context factory provider property
  6. - *
  7. mixing schema derived packages from different providers on the same contextPath
  8. - *
+ * {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details. + * * @since 9, JAXB 2.3 */ JAXBContext createContext(String contextPath, diff --git a/jdk/.hgtags b/jdk/.hgtags index 9f1d47685fb..a0ad6c5e122 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -345,3 +345,4 @@ e1a789be1535741274c9779f4d4ca3495196b5c3 jdk-9+99 3d452840f48299a36842760d17c0c8402f0e1266 jdk-9+100 5e8370fb3ed925335164afe340d1e54beab2d4d5 jdk-9+101 6eb3c8132e489dab81adde4ce29844904ce15482 jdk-9+102 +eee1ced1d8e78293f2a004af818ca474387dbebf jdk-9+103 diff --git a/jdk/make/launcher/Launcher-jdk.javadoc.gmk b/jdk/make/launcher/Launcher-jdk.javadoc.gmk index 5922c40d8da..afdb687b0ab 100644 --- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk +++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk @@ -26,7 +26,7 @@ include LauncherCommon.gmk $(eval $(call SetupBuildLauncher, javadoc, \ - MAIN_CLASS := com.sun.tools.javadoc.Main, \ + MAIN_CLASS := jdk.javadoc.internal.tool.Main, \ CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \ -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \ )) diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java index 5302091fb39..2a846c4d07f 100644 --- a/jdk/src/java.base/share/classes/java/lang/Integer.java +++ b/jdk/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -339,7 +339,6 @@ public final class Integer extends Number implements Comparable { // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { byte[] buf = new byte[chars]; formatUnsignedInt(val, shift, buf, 0, chars); @@ -477,8 +476,13 @@ public final class Integer extends Number implements Comparable { * values, to cover the Integer.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Integer.MIN_VALUE that overflows * integer. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present */ - static void getChars(int i, int index, byte[] buf) { + static int getChars(int i, int index, byte[] buf) { int q, r; int charPos = index; @@ -509,9 +513,19 @@ public final class Integer extends Number implements Comparable { if (negative) { buf[--charPos] = (byte)'-'; } + return charPos; } - static void getCharsUTF16(int i, int index, byte[] buf) { + /** + * This is a variant of {@link #getChars(int, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + static int getCharsUTF16(int i, int index, byte[] buf) { int q, r; int charPos = index; @@ -542,6 +556,7 @@ public final class Integer extends Number implements Comparable { if (negative) { StringUTF16.putChar(buf, --charPos, '-'); } + return charPos; } // Left here for compatibility reasons, see JDK-8143900. diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java index 191ae2071d4..48019cd31b3 100644 --- a/jdk/src/java.base/share/classes/java/lang/Long.java +++ b/jdk/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -379,7 +379,6 @@ public final class Long extends Number implements Comparable { // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - if (COMPACT_STRINGS) { byte[] buf = new byte[chars]; formatUnsignedLong0(val, shift, buf, 0, chars); @@ -489,8 +488,13 @@ public final class Long extends Number implements Comparable { * values, to cover the Long.MIN_VALUE case. Converting otherwise * (negative to positive) will expose -Long.MIN_VALUE that overflows * long. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, Latin1-encoded + * @return index of the most significant digit or minus sign, if present */ - static void getChars(long i, int index, byte[] buf) { + static int getChars(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -533,9 +537,19 @@ public final class Long extends Number implements Comparable { if (negative) { buf[--charPos] = (byte)'-'; } + return charPos; } - static void getCharsUTF16(long i, int index, byte[] buf) { + /** + * This is a variant of {@link #getChars(long, int, byte[])}, but for + * UTF-16 coder. + * + * @param i value to convert + * @param index next index, after the least significant digit + * @param buf target buffer, UTF16-coded. + * @return index of the most significant digit or minus sign, if present + */ + static int getCharsUTF16(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -578,6 +592,7 @@ public final class Long extends Number implements Comparable { if (negative) { StringUTF16.putChar(buf, --charPos, '-'); } + return charPos; } /** diff --git a/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java new file mode 100644 index 00000000000..d9517e5226f --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Helper for string concatenation. These methods are mostly looked up with private lookups + * from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle} + * combinators there. + */ +final class StringConcatHelper { + + private StringConcatHelper() { + // no instantiation + } + + /** + * Check for overflow, throw the exception on overflow. + * @param len String length + * @return length + */ + private static int checkOverflow(int len) { + if (len < 0) { + throw new OutOfMemoryError("Overflow: String length out of range"); + } + return len; + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, boolean value) { + return checkOverflow(current + (value ? 4 : 5)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, byte value) { + return mixLen(current, (int)value); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, char value) { + return checkOverflow(current + 1); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, short value) { + return mixLen(current, (int)value); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, int value) { + return checkOverflow(current + Integer.stringSize(value)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, long value) { + return checkOverflow(current + Long.stringSize(value)); + } + + /** + * Mix value length into current length + * @param current current length + * @param value value to mix in + * @return new length + */ + static int mixLen(int current, String value) { + return checkOverflow(current + value.length()); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, char value) { + return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1)); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, String value) { + return (byte)(current | value.coder()); + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, boolean value) { + // Booleans are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, byte value) { + // Bytes are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, short value) { + // Shorts are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, int value) { + // Ints are represented with Latin1 + return current; + } + + /** + * Mix coder into current coder + * @param current current coder + * @param value value to mix in + * @return new coder + */ + static byte mixCoder(byte current, long value) { + // Longs are represented with Latin1 + return current; + } + + /** + * Prepends the stringly representation of boolean value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value boolean value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, boolean value) { + if (coder == String.LATIN1) { + if (value) { + buf[--index] = 'e'; + buf[--index] = 'u'; + buf[--index] = 'r'; + buf[--index] = 't'; + } else { + buf[--index] = 'e'; + buf[--index] = 's'; + buf[--index] = 'l'; + buf[--index] = 'a'; + buf[--index] = 'f'; + } + } else { + if (value) { + StringUTF16.putChar(buf, --index, 'e'); + StringUTF16.putChar(buf, --index, 'u'); + StringUTF16.putChar(buf, --index, 'r'); + StringUTF16.putChar(buf, --index, 't'); + } else { + StringUTF16.putChar(buf, --index, 'e'); + StringUTF16.putChar(buf, --index, 's'); + StringUTF16.putChar(buf, --index, 'l'); + StringUTF16.putChar(buf, --index, 'a'); + StringUTF16.putChar(buf, --index, 'f'); + } + } + return index; + } + + /** + * Prepends the stringly representation of byte value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value byte value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, byte value) { + return prepend(index, buf, coder, (int)value); + } + + /** + * Prepends the stringly representation of char value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value char value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, char value) { + if (coder == String.LATIN1) { + buf[--index] = (byte) (value & 0xFF); + } else { + StringUTF16.putChar(buf, --index, value); + } + return index; + } + + /** + * Prepends the stringly representation of short value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value short value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, short value) { + return prepend(index, buf, coder, (int)value); + } + + /** + * Prepends the stringly representation of integer value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value integer value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, int value) { + if (coder == String.LATIN1) { + return Integer.getChars(value, index, buf); + } else { + return Integer.getCharsUTF16(value, index, buf); + } + } + + /** + * Prepends the stringly representation of long value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value long value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, long value) { + if (coder == String.LATIN1) { + return Long.getChars(value, index, buf); + } else { + return Long.getCharsUTF16(value, index, buf); + } + } + + /** + * Prepends the stringly representation of String value into buffer, + * given the coder and final index. Index is measured in chars, not in bytes! + * + * @param index final char index in the buffer + * @param buf buffer to append to + * @param coder coder to add with + * @param value String value to encode + * @return new index + */ + static int prepend(int index, byte[] buf, byte coder, String value) { + index -= value.length(); + value.getBytes(buf, index, coder); + return index; + } + + /** + * Instantiates the String with given buffer and coder + * @param buf buffer to use + * @param coder coder to use + * @return String resulting string + */ + static String newString(byte[] buf, byte coder) { + // Use the private, non-copying constructor (unsafe!) + return new String(buf, coder); + } + +} diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java new file mode 100644 index 00000000000..1cf0a66e370 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +/** + * StringConcatException is thrown by {@link StringConcatFactory} when linkage + * invariants are violated. + * + * @since 9 + */ +public class StringConcatException extends Exception { + private static final long serialVersionUID = 292L + 9L; + + /** + * Constructs an exception with a message + * @param msg exception message + */ + public StringConcatException(String msg) { + super(msg); + } + + /** + * Constructs an exception with a message and a linked throwable + * @param msg exception message + * @param cause throwable cause + */ + public StringConcatException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java new file mode 100644 index 00000000000..016c939a285 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -0,0 +1,1792 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.vm.annotation.ForceInline; +import sun.misc.Unsafe; + +import java.lang.invoke.MethodHandles.Lookup; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +/** + *

Methods to facilitate the creation of String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of known + * types, possibly after type adaptation and partial evaluation of arguments. + * These methods are typically used as bootstrap methods for {@code + * invokedynamic} call sites, to support the string concatenation + * feature of the Java Programming Language. + * + *

Indirect access to the behavior specified by the provided {@code + * MethodHandle} proceeds in order through two phases: + * + *

    + *
  1. Linkage occurs when the methods in this class are invoked. + * They take as arguments a method type describing the concatenated arguments + * count and types, and optionally the String recipe, plus the + * constants that participate in the String concatenation. The details on + * accepted recipe shapes are described further below. Linkage may involve + * dynamically loading a new class that implements the expected concatenation + * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the + * exact concatenation method. The concatenation methods may be shared among + * different {@code CallSite}s, e.g. if linkage methods produce them as pure + * functions.
  2. + * + *
  3. Invocation occurs when a generated concatenation method is + * invoked with the exact dynamic arguments. This may occur many times for a + * single concatenation method. The method referenced by the behavior {@code + * MethodHandle} is invoked with the static arguments and any additional dynamic + * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.
  4. + *
+ * + *

This class provides two forms of linkage methods: a simple version + * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String, + * MethodType)}) using only the dynamic arguments, and an advanced version + * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup, + * String, MethodType, String, Object...)} using the advanced forms of capturing + * the constant arguments. The advanced strategy can produce marginally better + * invocation bytecode, at the expense of exploding the number of shapes of + * string concatenation methods present at runtime, because those shapes would + * include constant static arguments as well. + * + * @author Aleksey Shipilev + * @author Remi Forax + * @author Peter Levart + * + * @apiNote + *

There is a JVM limit (classfile structural constraint): no method + * can call with more than 255 slots. This limits the number of static and + * dynamic arguments one can pass to bootstrap method. Since there are potential + * concatenation strategies that use {@code MethodHandle} combinators, we need + * to reserve a few empty slots on the parameter lists to to capture the + * temporal results. This is why bootstrap methods in this factory do not accept + * more than 200 argument slots. Users requiring more than 200 argument slots in + * concatenation are expected to split the large concatenation in smaller + * expressions. + * + * @since 9 + */ +public final class StringConcatFactory { + + /** + * Tag used to demarcate an ordinary argument. + */ + private static final char TAG_ARG = '\u0001'; + + /** + * Tag used to demarcate a constant. + */ + private static final char TAG_CONST = '\u0002'; + + /** + * Maximum number of argument slots in String Concat call. + * + * While the maximum number of argument slots that indy call can handle is 253, + * we do not use all those slots, to let the strategies with MethodHandle + * combinators to use some arguments. + */ + private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200; + + /** + * Concatenation strategy to use. See {@link Strategy} for possible options. + * This option is controllable with -Djava.lang.invoke.stringConcat JDK option. + */ + private static final Strategy STRATEGY; + + /** + * Default strategy to use for concatenation. + */ + private static final Strategy DEFAULT_STRATEGY = Strategy.BC_SB; + + private enum Strategy { + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}. + */ + BC_SB, + + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}; + * but trying to estimate the required storage. + */ + BC_SB_SIZED, + + /** + * Bytecode generator, calling into {@link java.lang.StringBuilder}; + * but computing the required storage exactly. + */ + BC_SB_SIZED_EXACT, + + /** + * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. + * This strategy also tries to estimate the required storage. + */ + MH_SB_SIZED, + + /** + * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}. + * This strategy also estimate the required storage exactly. + */ + MH_SB_SIZED_EXACT, + + /** + * MethodHandle-based generator, that constructs its own byte[] array from + * the arguments. It computes the required storage exactly. + */ + MH_INLINE_SIZED_EXACT + } + + /** + * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance) + * checks, etc. + */ + private static final boolean DEBUG; + + /** + * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated + * code, at the expense of contaminating the profiles. + */ + private static final boolean CACHE_ENABLE; + + private static final ConcurrentMap CACHE; + + static { + // Poke the privileged block once, taking everything we need: + final Object[] values = new Object[3]; + AccessController.doPrivileged((PrivilegedAction) () -> { + values[0] = System.getProperty("java.lang.invoke.stringConcat"); + values[1] = Boolean.getBoolean("java.lang.invoke.stringConcat.cache"); + values[2] = Boolean.getBoolean("java.lang.invoke.stringConcat.debug"); + return null; + }); + + final String strategy = (String) values[0]; + CACHE_ENABLE = (Boolean) values[1]; + DEBUG = (Boolean) values[2]; + + STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy); + CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null; + } + + private static final class Key { + final MethodType mt; + final Recipe recipe; + + public Key(MethodType mt, Recipe recipe) { + this.mt = mt; + this.recipe = recipe; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Key key = (Key) o; + + if (!mt.equals(key.mt)) return false; + if (!recipe.equals(key.recipe)) return false; + return true; + } + + @Override + public int hashCode() { + int result = mt.hashCode(); + result = 31 * result + recipe.hashCode(); + return result; + } + } + + /** + * Parses the recipe string, and produces the traversable collection of + * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator + * strategies. Notably, this class parses out the constants from the recipe + * and from other static arguments. + */ + private static final class Recipe { + private final List elements; + private final List elementsRev; + + public Recipe(String src, Object[] constants) { + List el = new ArrayList<>(); + + int constC = 0; + int argC = 0; + + StringBuilder acc = new StringBuilder(); + + for (int i = 0; i < src.length(); i++) { + char c = src.charAt(i); + + if (c == TAG_CONST || c == TAG_ARG) { + // Detected a special tag, flush all accumulated characters + // as a constant first: + if (acc.length() > 0) { + el.add(new RecipeElement(acc.toString())); + acc.setLength(0); + } + if (c == TAG_CONST) { + Object cnst = constants[constC++]; + el.add(new RecipeElement(cnst)); + } + if (c == TAG_ARG) { + el.add(new RecipeElement(argC++)); + } + } else { + // Not a special characters, this is a constant embedded into + // the recipe itself. + acc.append(c); + } + } + + // Flush the remaining characters as constant: + if (acc.length() > 0) { + el.add(new RecipeElement(acc.toString())); + } + + elements = new ArrayList<>(el); + Collections.reverse(el); + elementsRev = el; + } + + public Collection getElements() { + return elements; + } + + public Collection getElementsReversed() { + return elementsRev; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Recipe recipe = (Recipe) o; + return elements.equals(recipe.elements); + } + + @Override + public int hashCode() { + return elements.hashCode(); + } + } + + private static final class RecipeElement { + private final Object value; + private final int argPos; + private final Tag tag; + + public RecipeElement(Object cnst) { + this.value = Objects.requireNonNull(cnst); + this.argPos = -1; + this.tag = Tag.CONST; + } + + public RecipeElement(int arg) { + this.value = null; + this.argPos = arg; + this.tag = Tag.ARG; + } + + public Object getValue() { + assert (tag == Tag.CONST); + return value; + } + + public int getArgPos() { + assert (tag == Tag.ARG); + return argPos; + } + + public Tag getTag() { + return tag; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RecipeElement that = (RecipeElement) o; + + if (tag != that.tag) return false; + if (tag == Tag.CONST && (!value.equals(that.value))) return false; + if (tag == Tag.ARG && (argPos != that.argPos)) return false; + return true; + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + } + + private enum Tag { + CONST, ARG + } + + /** + * Facilitates the creation of optimized String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of + * known types, possibly after type adaptation and partial evaluation of + * arguments. Typically used as a bootstrap method for {@code + * invokedynamic} call sites, to support the string concatenation + * feature of the Java Programming Language. + * + *

When the target of the {@code CallSite} returned from this method is + * invoked, it returns the result of String concatenation, taking all + * function arguments passed to the linkage method as inputs for + * concatenation. The target signature is given by {@code concatType}. + * The arguments are concatenated as per requirements stated in JLS 15.18.1 + * "String Concatenation Operator +". Notably, the inputs are converted as + * per JLS 5.1.11 "String Conversion", and combined from left to right. + * + *

Assume the linkage arguments are as follows: + * + *

    + *
  • {@code concatType}, describing the {@code CallSite} signature
  • + *
+ * + *

Then the following linkage invariants must hold: + * + *

    + *
  • The parameter count in {@code concatType} is less than or equal to 200
  • + * + *
  • The return type in {@code concatType} is assignable from {@link java.lang.String}
  • + *
+ * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code + * invokedynamic}, this is stacked automatically by the VM. + * @param name The name of the method to implement. This name is + * arbitrary, and has no meaning for this linkage method. + * When used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param concatType The expected signature of the {@code CallSite}. The + * parameter types represent the types of concatenation + * arguments; the return type is always assignable from {@link + * java.lang.String}. When used with {@code invokedynamic}, + * this is provided by the {@code NameAndType} of the {@code + * InvokeDynamic} structure and is stacked automatically by + * the VM. + * @return a CallSite whose target can be used to perform String + * concatenation, with dynamic concatenation arguments described by the given + * {@code concatType}. + * @throws StringConcatException If any of the linkage invariants described + * here are violated. + * @throws NullPointerException If any of the incoming arguments is null. + * This will never happen when a bootstrap method + * is called with invokedynamic. + * + * @jls 5.1.11 String Conversion + * @jls 15.18.1 String Concatenation Operator + + */ + public static CallSite makeConcat(MethodHandles.Lookup lookup, + String name, + MethodType concatType) throws StringConcatException { + if (DEBUG) { + System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType); + } + + return doStringConcat(lookup, name, concatType, true, null); + } + + /** + * Facilitates the creation of optimized String concatenation methods, that + * can be used to efficiently concatenate a known number of arguments of + * known types, possibly after type adaptation and partial evaluation of + * arguments. Typically used as a bootstrap method for {@code + * invokedynamic} call sites, to support the string concatenation + * feature of the Java Programming Language. + * + *

When the target of the {@code CallSite} returned from this method is + * invoked, it returns the result of String concatenation, taking all + * function arguments and constants passed to the linkage method as inputs for + * concatenation. The target signature is given by {@code concatType}, and + * does not include constants. The arguments are concatenated as per requirements + * stated in JLS 15.18.1 "String Concatenation Operator +". Notably, the inputs + * are converted as per JLS 5.1.11 "String Conversion", and combined from left + * to right. + * + *

The concatenation recipe is a String description for the way to + * construct a concatenated String from the arguments and constants. The + * recipe is processed from left to right, and each character represents an + * input to concatenation. Recipe characters mean: + * + *

    + * + *
  • \1 (Unicode point 0001): an ordinary argument. This + * input is passed through dynamic argument, and is provided during the + * concatenation method invocation. This input can be null.
  • + * + *
  • \2 (Unicode point 0002): a constant. This input passed + * through static bootstrap argument. This constant can be any value + * representable in constant pool. If necessary, the factory would call + * {@code toString} to perform a one-time String conversion.
  • + * + *
  • Any other char value: a single character constant.
  • + *
+ * + *

Assume the linkage arguments are as follows: + * + *

    + *
  • {@code concatType}, describing the {@code CallSite} signature
  • + *
  • {@code recipe}, describing the String recipe
  • + *
  • {@code constants}, the vararg array of constants
  • + *
+ * + *

Then the following linkage invariants must hold: + * + *

    + *
  • The parameter count in {@code concatType} is less than or equal to + * 200
  • + * + *
  • The parameter count in {@code concatType} equals to number of \1 tags + * in {@code recipe}
  • + * + *
  • The return type in {@code concatType} is assignable + * from {@link java.lang.String}, and matches the return type of the + * returned {@link MethodHandle}
  • + * + *
  • The number of elements in {@code constants} equals to number of \2 + * tags in {@code recipe}
  • + *
+ * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. When used with {@code + * invokedynamic}, this is stacked automatically by the + * VM. + * @param name The name of the method to implement. This name is + * arbitrary, and has no meaning for this linkage method. + * When used with {@code invokedynamic}, this is provided + * by the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param concatType The expected signature of the {@code CallSite}. The + * parameter types represent the types of dynamic concatenation + * arguments; the return type is always assignable from {@link + * java.lang.String}. When used with {@code + * invokedynamic}, this is provided by the {@code + * NameAndType} of the {@code InvokeDynamic} structure and + * is stacked automatically by the VM. + * @param recipe Concatenation recipe, described above. + * @param constants A vararg parameter representing the constants passed to + * the linkage method. + * @return a CallSite whose target can be used to perform String + * concatenation, with dynamic concatenation arguments described by the given + * {@code concatType}. + * @throws StringConcatException If any of the linkage invariants described + * here are violated. + * @throws NullPointerException If any of the incoming arguments is null, or + * any constant in {@code recipe} is null. + * This will never happen when a bootstrap method + * is called with invokedynamic. + * @apiNote Code generators have three distinct ways to process a constant + * string operand S in a string concatenation expression. First, S can be + * materialized as a reference (using ldc) and passed as an ordinary argument + * (recipe '\1'). Or, S can be stored in the constant pool and passed as a + * constant (recipe '\2') . Finally, if S contains neither of the recipe + * tag characters ('\1', '\2') then S can be interpolated into the recipe + * itself, causing its characters to be inserted into the result. + * + * @jls 5.1.11 String Conversion + * @jls 15.18.1 String Concatenation Operator + + */ + public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup, + String name, + MethodType concatType, + String recipe, + Object... constants) throws StringConcatException { + if (DEBUG) { + System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants)); + } + + return doStringConcat(lookup, name, concatType, false, recipe, constants); + } + + private static CallSite doStringConcat(MethodHandles.Lookup lookup, + String name, + MethodType concatType, + boolean generateRecipe, + String recipe, + Object... constants) throws StringConcatException { + Objects.requireNonNull(lookup, "Lookup is null"); + Objects.requireNonNull(name, "Name is null"); + Objects.requireNonNull(concatType, "Concat type is null"); + Objects.requireNonNull(constants, "Constants are null"); + + for (Object o : constants) { + Objects.requireNonNull(o, "Cannot accept null constants"); + } + + int cCount = 0; + int oCount = 0; + if (generateRecipe) { + // Mock the recipe to reuse the concat generator code + char[] value = new char[concatType.parameterCount()]; + Arrays.fill(value, TAG_ARG); + recipe = new String(value); + oCount = concatType.parameterCount(); + } else { + Objects.requireNonNull(recipe, "Recipe is null"); + + for (int i = 0; i < recipe.length(); i++) { + char c = recipe.charAt(i); + if (c == TAG_CONST) cCount++; + if (c == TAG_ARG) oCount++; + } + } + + if (oCount != concatType.parameterCount()) { + throw new StringConcatException( + "Mismatched number of concat arguments: recipe wants " + + oCount + + " arguments, but signature provides " + + concatType.parameterCount()); + } + + if (cCount != constants.length) { + throw new StringConcatException( + "Mismatched number of concat constants: recipe wants " + + cCount + + " constants, but only " + + constants.length + + " are passed"); + } + + if (!concatType.returnType().isAssignableFrom(String.class)) { + throw new StringConcatException( + "The return type should be compatible with String, but it is " + + concatType.returnType()); + } + + if (concatType.parameterCount() > MAX_INDY_CONCAT_ARG_SLOTS) { + throw new StringConcatException("Too many concat argument slots: " + + concatType.parameterCount() + + ", can only accept " + + MAX_INDY_CONCAT_ARG_SLOTS); + } + + MethodType mt = adaptType(concatType); + + Recipe rec = new Recipe(recipe, constants); + + MethodHandle mh; + if (CACHE_ENABLE) { + Key key = new Key(mt, rec); + mh = CACHE.get(key); + if (mh == null) { + mh = generate(lookup, mt, rec); + CACHE.put(key, mh); + } + } else { + mh = generate(lookup, mt, rec); + } + return new ConstantCallSite(mh.asType(concatType)); + } + + /** + * Adapt method type to an API we are going to use. + * + * This strips the concrete classes from the signatures, thus preventing + * class leakage when we cache the concatenation stubs. + * + * @param args actual argument types + * @return argument types the strategy is going to use + */ + private static MethodType adaptType(MethodType args) { + Class[] ptypes = args.parameterArray(); + boolean changed = false; + for (int i = 0; i < ptypes.length; i++) { + Class ptype = ptypes[i]; + if (!ptype.isPrimitive() && + ptype != String.class && + ptype != Object.class) { // truncate to Object + ptypes[i] = Object.class; + changed = true; + } + // else other primitives or String or Object (unchanged) + } + return changed + ? MethodType.methodType(args.returnType(), ptypes) + : args; + } + + private static MethodHandle generate(Lookup lookup, MethodType mt, Recipe recipe) throws StringConcatException { + try { + switch (STRATEGY) { + case BC_SB: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.DEFAULT); + case BC_SB_SIZED: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED); + case BC_SB_SIZED_EXACT: + return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED_EXACT); + case MH_SB_SIZED: + return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED); + case MH_SB_SIZED_EXACT: + return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT); + case MH_INLINE_SIZED_EXACT: + return MethodHandleInlineCopyStrategy.generate(mt, recipe); + default: + throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented"); + } + } catch (Throwable t) { + throw new StringConcatException("Generator failed", t); + } + } + + private enum Mode { + DEFAULT(false, false), + SIZED(true, false), + SIZED_EXACT(true, true); + + private final boolean sized; + private final boolean exact; + + Mode(boolean sized, boolean exact) { + this.sized = sized; + this.exact = exact; + } + + boolean isSized() { + return sized; + } + + boolean isExact() { + return exact; + } + } + + /** + * Bytecode StringBuilder strategy. + * + *

This strategy operates in three modes, gated by {@link Mode}. + * + *

{@link Strategy#BC_SB}: "bytecode StringBuilder". + * + *

This strategy spins up the bytecode that has the same StringBuilder + * chain javac would otherwise emit. This strategy uses only the public API, + * and comes as the baseline for the current JDK behavior. On other words, + * this strategy moves the javac generated bytecode to runtime. The + * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with + * the caller class coming from the BSM -- in other words, the protection + * guarantees are inherited from the method where invokedynamic was + * originally called. This means, among other things, that the bytecode is + * verified for all non-JDK uses. + * + *

{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but + * sized". + * + *

This strategy acts similarly to {@link Strategy#BC_SB}, but it also + * tries to guess the capacity required for StringBuilder to accept all + * arguments without resizing. This strategy only makes an educated guess: + * it only guesses the space required for known types (e.g. primitives and + * Strings), but does not otherwise convert arguments. Therefore, the + * capacity estimate may be wrong, and StringBuilder may have to + * transparently resize or trim when doing the actual concatenation. While + * this does not constitute a correctness issue (in the end, that what BC_SB + * has to do anyway), this does pose a potential performance problem. + * + *

{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but + * sized exactly". + * + *

This strategy improves on @link Strategy#BC_SB_SIZED}, by first + * converting all arguments to String in order to get the exact capacity + * StringBuilder should have. The conversion is done via the public + * String.valueOf and/or Object.toString methods, and does not touch any + * private String API. + */ + private static final class BytecodeStringBuilderStrategy { + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + static final int CLASSFILE_VERSION = 52; + static final String NAME_FACTORY = "concat"; + static final String CLASS_NAME = "java/lang/String$Concat"; + + private BytecodeStringBuilderStrategy() { + // no instantiation + } + + private static MethodHandle generate(MethodHandles.Lookup lookup, MethodType args, Recipe recipe, Mode mode) throws Exception { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); + + cw.visit(CLASSFILE_VERSION, + ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, + CLASS_NAME, + null, + "java/lang/Object", + null + ); + + MethodVisitor mv = cw.visitMethod( + ACC_PUBLIC + ACC_STATIC + ACC_FINAL, + NAME_FACTORY, + args.toMethodDescriptorString(), + null, + null); + + mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true); + mv.visitCode(); + + Class[] arr = args.parameterArray(); + boolean[] guaranteedNonNull = new boolean[arr.length]; + + if (mode.isExact()) { + /* + In exact mode, we need to convert all arguments to their String representations, + as this allows to compute their String sizes exactly. We cannot use private + methods for primitives in here, therefore we need to convert those as well. + + We also record what arguments are guaranteed to be non-null as the result + of the conversion. String.valueOf does the null checks for us. The only + corner case to take care of is String.valueOf(Object) returning null itself. + + Also, if any conversion happened, then the slot indices in the incoming + arguments are not equal to the final local maps. The only case this may break + is when converting 2-slot long/double argument to 1-slot String. Therefore, + we get away with tracking modified offset, since no conversion can overwrite + the upcoming the argument. + */ + + int off = 0; + int modOff = 0; + for (int c = 0; c < arr.length; c++) { + Class cl = arr[c]; + if (cl == String.class) { + if (off != modOff) { + mv.visitIntInsn(getLoadOpcode(cl), off); + mv.visitIntInsn(ASTORE, modOff); + } + } else { + mv.visitIntInsn(getLoadOpcode(cl), off); + mv.visitMethodInsn( + INVOKESTATIC, + "java/lang/String", + "valueOf", + getStringValueOfDesc(cl), + false + ); + mv.visitIntInsn(ASTORE, modOff); + arr[c] = String.class; + guaranteedNonNull[c] = cl.isPrimitive(); + } + off += getParameterSize(cl); + modOff += getParameterSize(String.class); + } + } + + if (mode.isSized()) { + /* + When operating in sized mode (this includes exact mode), it makes sense to make + StringBuilder append chains look familiar to OptimizeStringConcat. For that, we + need to do null-checks early, not make the append chain shape simpler. + */ + + int off = 0; + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + // Guaranteed non-null, no null check required. + break; + } + case ARG: { + // Null-checks are needed only for String arguments, and when a previous stage + // did not do implicit null-checks. If a String is null, we eagerly replace it + // with "null" constant. Note, we omit Objects here, because we don't call + // .length() on them down below. + int ac = el.getArgPos(); + Class cl = arr[ac]; + if (cl == String.class && !guaranteedNonNull[ac]) { + Label l0 = new Label(); + mv.visitIntInsn(ALOAD, off); + mv.visitJumpInsn(IFNONNULL, l0); + mv.visitLdcInsn("null"); + mv.visitIntInsn(ASTORE, off); + mv.visitLabel(l0); + } + off += getParameterSize(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + } + + // Prepare StringBuilder instance + mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); + mv.visitInsn(DUP); + + if (mode.isSized()) { + /* + Sized mode requires us to walk through the arguments, and estimate the final length. + In exact mode, this will operate on Strings only. This code would accumulate the + final length on stack. + */ + int len = 0; + int off = 0; + + mv.visitInsn(ICONST_0); + + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + len += cnst.toString().length(); + break; + } + case ARG: { + /* + If an argument is String, then we can call .length() on it. Sized/Exact modes have + converted arguments for us. If an argument is primitive, we can provide a guess + for its String representation size. + */ + Class cl = arr[el.getArgPos()]; + if (cl == String.class) { + mv.visitIntInsn(ALOAD, off); + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/String", + "length", + "()I", + false + ); + mv.visitInsn(IADD); + } else if (cl.isPrimitive()) { + len += estimateSize(cl); + } + off += getParameterSize(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Constants have non-zero length, mix in + if (len > 0) { + iconst(mv, len); + mv.visitInsn(IADD); + } + + mv.visitMethodInsn( + INVOKESPECIAL, + "java/lang/StringBuilder", + "", + "(I)V", + false + ); + } else { + mv.visitMethodInsn( + INVOKESPECIAL, + "java/lang/StringBuilder", + "", + "()V", + false + ); + } + + // At this point, we have a blank StringBuilder on stack, fill it in with .append calls. + { + int off = 0; + for (RecipeElement el : recipe.getElements()) { + String desc; + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + mv.visitLdcInsn(cnst); + desc = getSBAppendDesc(cnst.getClass()); + break; + } + case ARG: { + Class cl = arr[el.getArgPos()]; + mv.visitVarInsn(getLoadOpcode(cl), off); + off += getParameterSize(cl); + desc = getSBAppendDesc(cl); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "append", + desc, + false + ); + } + } + + if (DEBUG && mode.isExact()) { + /* + Exactness checks compare the final StringBuilder.capacity() with a resulting + String.length(). If these values disagree, that means StringBuilder had to perform + storage trimming, which defeats the purpose of exact strategies. + */ + + mv.visitInsn(DUP); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "capacity", + "()I", + false + ); + + mv.visitIntInsn(ISTORE, 0); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "toString", + "()Ljava/lang/String;", + false + ); + + mv.visitInsn(DUP); + + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/String", + "length", + "()I", + false + ); + + mv.visitIntInsn(ILOAD, 0); + + Label l0 = new Label(); + mv.visitJumpInsn(IF_ICMPEQ, l0); + + mv.visitTypeInsn(NEW, "java/lang/AssertionError"); + mv.visitInsn(DUP); + mv.visitLdcInsn("Failed exactness check"); + mv.visitMethodInsn(INVOKESPECIAL, + "java/lang/AssertionError", + "", + "(Ljava/lang/Object;)V", + false); + mv.visitInsn(ATHROW); + + mv.visitLabel(l0); + } else { + mv.visitMethodInsn( + INVOKEVIRTUAL, + "java/lang/StringBuilder", + "toString", + "()Ljava/lang/String;", + false + ); + } + + mv.visitInsn(ARETURN); + + mv.visitMaxs(-1, -1); + mv.visitEnd(); + cw.visitEnd(); + + Class targetClass = lookup.lookupClass(); + final byte[] classBytes = cw.toByteArray(); + final Class innerClass = UNSAFE.defineAnonymousClass(targetClass, classBytes, null); + + try { + UNSAFE.ensureClassInitialized(innerClass); + return lookup.findStatic(innerClass, NAME_FACTORY, args); + } catch (ReflectiveOperationException e) { + throw new StringConcatException("Exception finding constructor", e); + } + } + + private static String getSBAppendDesc(Class cl) { + if (cl.isPrimitive()) { + if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { + return "(I)Ljava/lang/StringBuilder;"; + } else if (cl == Boolean.TYPE) { + return "(Z)Ljava/lang/StringBuilder;"; + } else if (cl == Character.TYPE) { + return "(C)Ljava/lang/StringBuilder;"; + } else if (cl == Double.TYPE) { + return "(D)Ljava/lang/StringBuilder;"; + } else if (cl == Float.TYPE) { + return "(F)Ljava/lang/StringBuilder;"; + } else if (cl == Long.TYPE) { + return "(J)Ljava/lang/StringBuilder;"; + } else { + throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl); + } + } else if (cl == String.class) { + return "(Ljava/lang/String;)Ljava/lang/StringBuilder;"; + } else { + return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"; + } + } + + private static String getStringValueOfDesc(Class cl) { + if (cl.isPrimitive()) { + if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) { + return "(I)Ljava/lang/String;"; + } else if (cl == Boolean.TYPE) { + return "(Z)Ljava/lang/String;"; + } else if (cl == Character.TYPE) { + return "(C)Ljava/lang/String;"; + } else if (cl == Double.TYPE) { + return "(D)Ljava/lang/String;"; + } else if (cl == Float.TYPE) { + return "(F)Ljava/lang/String;"; + } else if (cl == Long.TYPE) { + return "(J)Ljava/lang/String;"; + } else { + throw new IllegalStateException("Unhandled String.valueOf: " + cl); + } + } else if (cl == String.class) { + return "(Ljava/lang/String;)Ljava/lang/String;"; + } else { + return "(Ljava/lang/Object;)Ljava/lang/String;"; + } + } + + /** + * The following method is copied from + * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small + * and fast Java bytecode manipulation framework. + * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. + */ + private static void iconst(MethodVisitor mv, final int cst) { + if (cst >= -1 && cst <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + cst); + } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, cst); + } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, cst); + } else { + mv.visitLdcInsn(cst); + } + } + + private static int getLoadOpcode(Class c) { + if (c == Void.TYPE) { + throw new InternalError("Unexpected void type of load opcode"); + } + return ILOAD + getOpcodeOffset(c); + } + + private static int getOpcodeOffset(Class c) { + if (c.isPrimitive()) { + if (c == Long.TYPE) { + return 1; + } else if (c == Float.TYPE) { + return 2; + } else if (c == Double.TYPE) { + return 3; + } + return 0; + } else { + return 4; + } + } + + private static int getParameterSize(Class c) { + if (c == Void.TYPE) { + return 0; + } else if (c == Long.TYPE || c == Double.TYPE) { + return 2; + } + return 1; + } + } + + /** + * MethodHandle StringBuilder strategy. + * + *

This strategy operates in two modes, gated by {@link Mode}. + * + *

{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder, + * sized". + * + *

This strategy avoids spinning up the bytecode by building the + * computation on MethodHandle combinators. The computation is built with + * public MethodHandle APIs, resolved from a public Lookup sequence, and + * ends up calling the public StringBuilder API. Therefore, this strategy + * does not use any private API at all, even the Unsafe.defineAnonymousClass, + * since everything is handled under cover by java.lang.invoke APIs. + * + *

{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, + * sized exactly". + * + *

This strategy improves on @link Strategy#MH_SB_SIZED}, by first + * converting all arguments to String in order to get the exact capacity + * StringBuilder should have. The conversion is done via the public + * String.valueOf and/or Object.toString methods, and does not touch any + * private String API. + */ + private static final class MethodHandleStringBuilderStrategy { + + private MethodHandleStringBuilderStrategy() { + // no instantiation + } + + private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception { + int pc = mt.parameterCount(); + + Class[] ptypes = mt.parameterArray(); + MethodHandle[] filters = new MethodHandle[ptypes.length]; + for (int i = 0; i < ptypes.length; i++) { + MethodHandle filter; + switch (mode) { + case SIZED: + // In sized mode, we convert all references and floats/doubles + // to String: there is no specialization for different + // classes in StringBuilder API, and it will convert to + // String internally anyhow. + filter = Stringifiers.forMost(ptypes[i]); + break; + case SIZED_EXACT: + // In exact mode, we convert everything to String: + // this helps to compute the storage exactly. + filter = Stringifiers.forAny(ptypes[i]); + break; + default: + throw new StringConcatException("Not supported"); + } + if (filter != null) { + filters[i] = filter; + ptypes[i] = filter.type().returnType(); + } + } + + List> ptypesList = Arrays.asList(ptypes); + MethodHandle[] lengthers = new MethodHandle[pc]; + + // Figure out lengths: constants' lengths can be deduced on the spot. + // All reference arguments were filtered to String in the combinators below, so we can + // call the usual String.length(). Primitive values string sizes can be estimated. + int initial = 0; + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + initial += cnst.toString().length(); + break; + } + case ARG: { + final int i = el.getArgPos(); + Class type = ptypesList.get(i); + if (type.isPrimitive()) { + MethodHandle est = MethodHandles.constant(int.class, estimateSize(type)); + est = MethodHandles.dropArguments(est, 0, type); + lengthers[i] = est; + } else { + lengthers[i] = STRING_LENGTH; + } + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Create (StringBuilder, ) shape for appending: + MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList); + + // Compose append calls. This is done in reverse because the application order is + // reverse as well. + for (RecipeElement el : recipe.getElementsReversed()) { + MethodHandle appender; + switch (el.getTag()) { + case CONST: { + Object constant = el.getValue(); + MethodHandle mh = appender(adaptToStringBuilder(constant.getClass())); + appender = MethodHandles.insertArguments(mh, 1, constant); + break; + } + case ARG: { + int ac = el.getArgPos(); + appender = appender(ptypesList.get(ac)); + + // Insert dummy arguments to match the prefix in the signature. + // The actual appender argument will be the ac-ith argument. + if (ac != 0) { + appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac)); + } + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + builder = MethodHandles.foldArguments(builder, appender); + } + + // Build the sub-tree that adds the sizes and produces a StringBuilder: + + // a) Start with the reducer that accepts all arguments, plus one + // slot for the initial value. Inject the initial value right away. + // This produces ()int shape: + MethodHandle sum = getReducerFor(pc + 1); + MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial); + + // b) Apply lengthers to transform arguments to lengths, producing ()int + adder = MethodHandles.filterArguments(adder, 0, lengthers); + + // c) Instantiate StringBuilder ()int -> ()StringBuilder + MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER); + + // d) Fold in StringBuilder constructor, this produces ()StringBuilder + MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder); + + // Convert non-primitive arguments to Strings + mh = MethodHandles.filterArguments(mh, 0, filters); + + // Convert ()StringBuilder to ()String + if (DEBUG && mode.isExact()) { + mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED); + } else { + mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING); + } + + return mh; + } + + private static MethodHandle getReducerFor(int cnt) { + return SUMMERS.computeIfAbsent(cnt, SUMMER); + } + + private static MethodHandle appender(Class appendType) { + MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append", + StringBuilder.class, adaptToStringBuilder(appendType)); + + // appenders should return void, this would not modify the target signature during folding + MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType); + return appender.asType(nt); + } + + private static String toStringChecked(StringBuilder sb) { + String s = sb.toString(); + if (s.length() != sb.capacity()) { + throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity()); + } + return s; + } + + private static int sum(int v1, int v2) { + return v1 + v2; + } + + private static int sum(int v1, int v2, int v3) { + return v1 + v2 + v3; + } + + private static int sum(int v1, int v2, int v3, int v4) { + return v1 + v2 + v3 + v4; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5) { + return v1 + v2 + v3 + v4 + v5; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) { + return v1 + v2 + v3 + v4 + v5 + v6; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) { + return v1 + v2 + v3 + v4 + v5 + v6 + v7; + } + + private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) { + return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8; + } + + private static int sum(int initial, int[] vs) { + int sum = initial; + for (int v : vs) { + sum += v; + } + return sum; + } + + private static final ConcurrentMap SUMMERS; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function SUMMER = new Function() { + @Override + public MethodHandle apply(Integer cnt) { + if (cnt == 1) { + return MethodHandles.identity(int.class); + } else if (cnt <= 8) { + // Variable-arity collectors are not as efficient as small-count methods, + // unroll some initial sizes. + Class[] cls = new Class[cnt]; + Arrays.fill(cls, int.class); + return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); + } else { + return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) + .asCollector(int[].class, cnt - 1); + } + } + }; + + private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED; + + static { + SUMMERS = new ConcurrentHashMap<>(); + Lookup publicLookup = MethodHandles.publicLookup(); + NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class); + STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); + BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); + if (DEBUG) { + BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP, + MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class); + } else { + BUILDER_TO_STRING_CHECKED = null; + } + } + + } + + + /** + *

{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline, + * sized exactly". + * + *

This strategy replicates what StringBuilders are doing: it builds the + * byte[] array on its own and passes that byte[] array to String + * constructor. This strategy requires access to some private APIs in JDK, + * most notably, the read-only Integer/Long.stringSize methods that measure + * the character length of the integers, and the private String constructor + * that accepts byte[] arrays without copying. While this strategy assumes a + * particular implementation details for String, this opens the door for + * building a very optimal concatenation sequence. This is the only strategy + * that requires porting if there are private JDK changes occur. + */ + private static final class MethodHandleInlineCopyStrategy { + + private MethodHandleInlineCopyStrategy() { + // no instantiation + } + + static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { + + // Create filters and obtain filtered parameter types. Filters would be used in the beginning + // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). + // The filtered argument type list is used all over in the combinators below. + Class[] ptypes = mt.parameterArray(); + MethodHandle[] filters = null; + for (int i = 0; i < ptypes.length; i++) { + MethodHandle filter = Stringifiers.forMost(ptypes[i]); + if (filter != null) { + if (filters == null) { + filters = new MethodHandle[ptypes.length]; + } + filters[i] = filter; + ptypes[i] = filter.type().returnType(); + } + } + List> ptypesList = Arrays.asList(ptypes); + + // Start building the combinator tree. The tree "starts" with ()String, and "finishes" + // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up, + // which makes the code arguably hard to read. + + // Drop all remaining parameter types, leave only helper arguments: + MethodHandle mh; + + mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); + mh = MethodHandles.dropArguments(mh, 0, int.class); + + // In debug mode, check that remaining index is zero. + if (DEBUG) { + mh = MethodHandles.filterArgument(mh, 0, CHECK_INDEX); + } + + // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already + // known from the combinators below. We are assembling the string backwards, so "index" is the + // *ending* index. + for (RecipeElement el : recipe.getElements()) { + MethodHandle prepender; + switch (el.getTag()) { + case CONST: { + Object cnst = el.getValue(); + prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); + break; + } + case ARG: { + int pos = el.getArgPos(); + prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos); + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + + // Remove "old" index from arguments + mh = MethodHandles.dropArguments(mh, 1, int.class); + + // Do the prepend, and put "new" index at index 0 + mh = MethodHandles.foldArguments(mh, prepender); + } + + // Prepare the argument list for prepending. The tree below would instantiate + // the storage byte[] into argument 0, so we need to swap "storage" and "index". + // The index at this point equals to "size", and resides at argument 1. + { + MethodType nmt = mh.type() + .changeParameterType(0, byte[].class) + .changeParameterType(1, int.class); + mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount())); + } + + // Fold in byte[] instantiation at argument 0. + MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList); + mh = MethodHandles.foldArguments(mh, combiner); + + // Start combining length and coder mixers. + // + // Length is easy: constant lengths can be computed on the spot, and all non-constant + // shapes have been either converted to Strings, or explicit methods for getting the + // string length out of primitives are provided. + // + // Coders are more interesting. Only Object, String and char arguments (and constants) + // can have non-Latin1 encoding. It is easier to blindly convert constants to String, + // and deduce the coder from there. Arguments would be either converted to Strings + // during the initial filtering, or handled by primitive specializations in CODER_MIXERS. + // + // The method handle shape after all length and coder mixers is: + // (int, byte, )String = ("index", "coder", ) + byte initialCoder = 0; // initial coder + int initialLen = 0; // initial length, in characters + for (RecipeElement el : recipe.getElements()) { + switch (el.getTag()) { + case CONST: { + Object constant = el.getValue(); + String s = constant.toString(); + initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s); + initialLen += s.length(); + break; + } + case ARG: { + int ac = el.getArgPos(); + + Class argClass = ptypesList.get(ac); + MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac); + lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*) + lm = MethodHandles.dropArguments(lm, 2, byte.class); + + MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypesList, ac); + cm = MethodHandles.dropArguments(cm, 0, int.class); // (**) + + // Read this bottom up: + + // 4. Drop old index and coder, producing ("new-index", "new-coder", ) + mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); + + // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", ) + // Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*) + mh = MethodHandles.foldArguments(mh, lm); + + // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", ) + // Coder mixer ignores the "old-index" arg due to dropArguments above (**) + mh = MethodHandles.foldArguments(mh, cm); + + // 1. The mh shape here is ("old-index", "old-coder", ) + break; + } + default: + throw new StringConcatException("Unhandled tag: " + el.getTag()); + } + } + + // Insert initial lengths and coders here. + // The method handle shape here is (). + mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder); + + // Apply filters, converting the arguments: + if (filters != null) { + mh = MethodHandles.filterArguments(mh, 0, filters); + } + + return mh; + } + + private static int[] swap10(int count) { + int[] perm = new int[count]; + perm[0] = 1; + perm[1] = 0; + for (int i = 2; i < count; i++) { + perm[i] = i; + } + return perm; + } + + // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R + private static MethodHandle selectArgument(MethodHandle mh, int prefix, List> ptypes, int pos) { + if (pos == 0) { + return MethodHandles.dropArguments(mh, prefix + 1, ptypes.subList(1, ptypes.size())); + } else if (pos == ptypes.size() - 1) { + return MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, ptypes.size() - 1)); + } else { // 0 < pos < ptypes.size() - 1 + MethodHandle t = MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, pos)); + return MethodHandles.dropArguments(t, prefix + 1 + pos, ptypes.subList(pos + 1, ptypes.size())); + } + } + + @ForceInline + private static byte[] newArray(int length, byte coder) { + return new byte[length << coder]; + } + + @ForceInline + private static int checkIndex(int index) { + if (index != 0) { + throw new AssertionError("Exactness check failed: " + index + " characters left in the buffer."); + } + return index; + } + + private static MethodHandle prepender(Class cl) { + return PREPENDERS.computeIfAbsent(cl, PREPEND); + } + + private static MethodHandle coderMixer(Class cl) { + return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX); + } + + private static MethodHandle lengthMixer(Class cl) { + return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX); + } + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function, MethodHandle> PREPEND = new Function, MethodHandle>() { + @Override + public MethodHandle apply(Class c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, c); + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function, MethodHandle> CODER_MIX = new Function, MethodHandle>() { + @Override + public MethodHandle apply(Class c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c); + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function, MethodHandle> LENGTH_MIX = new Function, MethodHandle>() { + @Override + public MethodHandle apply(Class c) { + return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c); + } + }; + + private static final MethodHandle NEW_STRING; + private static final MethodHandle CHECK_INDEX; + private static final MethodHandle NEW_ARRAY; + private static final ConcurrentMap, MethodHandle> PREPENDERS; + private static final ConcurrentMap, MethodHandle> LENGTH_MIXERS; + private static final ConcurrentMap, MethodHandle> CODER_MIXERS; + private static final Class STRING_HELPER; + + static { + try { + STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + + PREPENDERS = new ConcurrentHashMap<>(); + LENGTH_MIXERS = new ConcurrentHashMap<>(); + CODER_MIXERS = new ConcurrentHashMap<>(); + + NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class); + NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class); + + if (DEBUG) { + CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class); + } else { + CHECK_INDEX = null; + } + } + } + + /** + * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally + * delegate to {@code String.valueOf}, depending on argument's type. + */ + private static final class Stringifiers { + private Stringifiers() { + // no instantiation + } + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function, MethodHandle> MOST = new Function, MethodHandle>() { + @Override + public MethodHandle apply(Class cl) { + MethodHandle mhObject = lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, Object.class); + + // We need the additional conversion here, because String.valueOf(Object) may return null. + // String conversion rules in Java state we need to produce "null" String in this case. + // It can be easily done with applying valueOf the second time. + MethodHandle mhObjectNoNulls = MethodHandles.filterReturnValue(mhObject, + mhObject.asType(MethodType.methodType(String.class, String.class))); + + if (cl == String.class) { + return mhObject; + } else if (cl == float.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, float.class); + } else if (cl == double.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, double.class); + } else if (!cl.isPrimitive()) { + return mhObjectNoNulls; + } + + return null; + } + }; + + // This one is deliberately non-lambdified to optimize startup time: + private static final Function, MethodHandle> ANY = new Function, MethodHandle>() { + @Override + public MethodHandle apply(Class cl) { + MethodHandle mh = MOST.apply(cl); + if (mh != null) { + return mh; + } + + if (cl == byte.class || cl == short.class || cl == int.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, int.class); + } else if (cl == boolean.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, boolean.class); + } else if (cl == char.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, char.class); + } else if (cl == long.class) { + return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, long.class); + } else { + throw new IllegalStateException("Unknown class: " + cl); + } + } + }; + + private static final ConcurrentMap, MethodHandle> STRINGIFIERS_MOST = new ConcurrentHashMap<>(); + private static final ConcurrentMap, MethodHandle> STRINGIFIERS_ANY = new ConcurrentHashMap<>(); + + /** + * Returns a stringifier for references and floats/doubles only. + * Always returns null for other primitives. + * + * @param t class to stringify + * @return stringifier; null, if not available + */ + static MethodHandle forMost(Class t) { + return STRINGIFIERS_MOST.computeIfAbsent(t, MOST); + } + + /** + * Returns a stringifier for any type. Never returns null. + * + * @param t class to stringify + * @return stringifier + */ + static MethodHandle forAny(Class t) { + return STRINGIFIERS_ANY.computeIfAbsent(t, ANY); + } + } + + /* ------------------------------- Common utilities ------------------------------------ */ + + private static MethodHandle lookupStatic(Lookup lookup, Class refc, String name, Class rtype, Class... ptypes) { + try { + return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupVirtual(Lookup lookup, Class refc, String name, Class rtype, Class... ptypes) { + try { + return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupConstructor(Lookup lookup, Class refc, Class ptypes) { + try { + return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static int estimateSize(Class cl) { + if (cl == Integer.TYPE) { + return 11; // "-2147483648" + } else if (cl == Boolean.TYPE) { + return 5; // "false" + } else if (cl == Byte.TYPE) { + return 4; // "-128" + } else if (cl == Character.TYPE) { + return 1; // duh + } else if (cl == Short.TYPE) { + return 6; // "-32768" + } else if (cl == Double.TYPE) { + return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer + } else if (cl == Float.TYPE) { + return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer + } else if (cl == Long.TYPE) { + return 20; // "-9223372036854775808" + } else { + throw new IllegalArgumentException("Cannot estimate the size for " + cl); + } + } + + private static Class adaptToStringBuilder(Class c) { + if (c.isPrimitive()) { + if (c == Byte.TYPE || c == Short.TYPE) { + return int.class; + } + } else { + if (c != String.class) { + return Object.class; + } + } + return c; + } + + private StringConcatFactory() { + // no instantiation + } + +} diff --git a/jdk/src/java.base/share/classes/java/net/URI.java b/jdk/src/java.base/share/classes/java/net/URI.java index 862588019fd..069aedb685f 100644 --- a/jdk/src/java.base/share/classes/java/net/URI.java +++ b/jdk/src/java.base/share/classes/java/net/URI.java @@ -1080,11 +1080,8 @@ public final class URI * If a protocol handler for the URL could not be found, * or if some other error occurred while constructing the URL */ - public URL toURL() - throws MalformedURLException { - if (!isAbsolute()) - throw new IllegalArgumentException("URI is not absolute"); - return new URL(toString()); + public URL toURL() throws MalformedURLException { + return URL.fromURI(this); } // -- Component access methods -- diff --git a/jdk/src/java.base/share/classes/java/net/URL.java b/jdk/src/java.base/share/classes/java/net/URL.java index 2235a4edd2a..598e4103cf7 100644 --- a/jdk/src/java.base/share/classes/java/net/URL.java +++ b/jdk/src/java.base/share/classes/java/net/URL.java @@ -36,6 +36,7 @@ import java.io.ObjectStreamException; import java.io.ObjectStreamField; import java.io.ObjectInputStream.GetField; import java.util.Iterator; +import java.util.Locale; import java.util.NoSuchElementException; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; @@ -405,7 +406,7 @@ public final class URL implements java.io.Serializable { } } - protocol = protocol.toLowerCase(); + protocol = protocol.toLowerCase(Locale.ROOT); this.protocol = protocol; if (host != null) { @@ -579,8 +580,7 @@ public final class URL implements java.io.Serializable { for (i = start ; !aRef && (i < limit) && ((c = spec.charAt(i)) != '/') ; i++) { if (c == ':') { - - String s = spec.substring(start, i).toLowerCase(); + String s = spec.substring(start, i).toLowerCase(Locale.ROOT); if (isValidProtocol(s)) { newProtocol = s; start = i + 1; @@ -659,6 +659,44 @@ public final class URL implements java.io.Serializable { } } + /** + * Creates a URL from a URI, as if by invoking {@code uri.toURL()}. + * + * @see java.net.URI#toURL() + */ + static URL fromURI(URI uri) throws MalformedURLException { + if (!uri.isAbsolute()) { + throw new IllegalArgumentException("URI is not absolute"); + } + String protocol = uri.getScheme(); + + // In general we need to go via Handler.parseURL, but for the jrt + // protocol we enforce that the Handler is not overrideable and can + // optimize URI to URL conversion. + // + // Case-sensitive comparison for performance; malformed protocols will + // be handled correctly by the slow path. + if (protocol.equals("jrt") && !uri.isOpaque() + && uri.getRawFragment() == null) { + + String query = uri.getRawQuery(); + String path = uri.getRawPath(); + String file = (query == null) ? path : path + "?" + query; + + // URL represent undefined host as empty string while URI use null + String host = uri.getHost(); + if (host == null) { + host = ""; + } + + int port = uri.getPort(); + + return new URL("jrt", host, port, file, null); + } else { + return new URL((URL)null, uri.toString(), null); + } + } + /* * Returns true if specified string is a valid protocol name. */ @@ -1275,11 +1313,28 @@ public final class URL implements java.io.Serializable { } } - private static final String[] NON_OVERRIDEABLE_PROTOCOLS = {"file", "jrt"}; - private static boolean isOverrideable(String protocol) { - for (String p : NON_OVERRIDEABLE_PROTOCOLS) - if (protocol.equalsIgnoreCase(p)) + + /** + * Non-overrideable protocols: "jrt" and "file" + * + * Character-based comparison for performance reasons; also ensures + * case-insensitive comparison in a locale-independent fashion. + */ + static boolean isOverrideable(String protocol) { + if (protocol.length() == 3) { + if ((Character.toLowerCase(protocol.charAt(0)) == 'j') && + (Character.toLowerCase(protocol.charAt(1)) == 'r') && + (Character.toLowerCase(protocol.charAt(2)) == 't')) { return false; + } + } else if (protocol.length() == 4) { + if ((Character.toLowerCase(protocol.charAt(0)) == 'f') && + (Character.toLowerCase(protocol.charAt(1)) == 'i') && + (Character.toLowerCase(protocol.charAt(2)) == 'l') && + (Character.toLowerCase(protocol.charAt(3)) == 'e')) { + return false; + } + } return true; } diff --git a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java index 9acde225339..82d0d1bce6f 100644 --- a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java +++ b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ public interface FileOwnerAttributeView /** * Read the file owner. * - *

It it implementation specific if the file owner can be a {@link + *

It is implementation specific if the file owner can be a {@link * GroupPrincipal group}. * * @return the file owner @@ -78,7 +78,7 @@ public interface FileOwnerAttributeView /** * Updates the file owner. * - *

It it implementation specific if the file owner can be a {@link + *

It is implementation specific if the file owner can be a {@link * GroupPrincipal group}. To ensure consistent and correct behavior * across platforms it is recommended that this method should only be used * to set the file owner to a user principal that is not a group. diff --git a/jdk/src/java.base/share/classes/java/util/Map.java b/jdk/src/java.base/share/classes/java/util/Map.java index 0967523eba3..615bb0a5344 100644 --- a/jdk/src/java.base/share/classes/java/util/Map.java +++ b/jdk/src/java.base/share/classes/java/util/Map.java @@ -649,7 +649,7 @@ public interface Map { try { k = entry.getKey(); v = entry.getValue(); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -704,7 +704,7 @@ public interface Map { try { k = entry.getKey(); v = entry.getValue(); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -714,7 +714,7 @@ public interface Map { try { entry.setValue(v); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } @@ -887,7 +887,7 @@ public interface Map { * or atomicity properties of this method. Any implementation providing * atomicity guarantees must override this method and document its * concurrency properties. - * + * * @param key key with which the specified value is associated * @param value value to be associated with the specified key * @return the previous value associated with the specified key, or @@ -984,6 +984,9 @@ public interface Map { * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (optional) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (optional) * @since 1.8 */ default V computeIfAbsent(K key, @@ -1058,6 +1061,9 @@ public interface Map { * @throws ClassCastException if the class of the specified key or value * prevents it from being stored in this map * (optional) + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + * (optional) * @since 1.8 */ default V computeIfPresent(K key, @@ -1103,7 +1109,7 @@ public interface Map { *

 {@code
      * V oldValue = map.get(key);
      * V newValue = remappingFunction.apply(key, oldValue);
-     * if (oldValue != null ) {
+     * if (oldValue != null) {
      *    if (newValue != null)
      *       map.put(key, newValue);
      *    else
@@ -1147,6 +1153,9 @@ public interface Map {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (optional)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (optional)
      * @since 1.8
      */
     default V compute(K key,
@@ -1239,6 +1248,9 @@ public interface Map {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (optional)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (optional)
      * @throws NullPointerException if the specified key is null and this map
      *         does not support null keys or the value or remappingFunction is
      *         null
@@ -1251,7 +1263,7 @@ public interface Map {
         V oldValue = get(key);
         V newValue = (oldValue == null) ? value :
                    remappingFunction.apply(oldValue, value);
-        if(newValue == null) {
+        if (newValue == null) {
             remove(key);
         } else {
             put(key, newValue);
diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java
index b5456b99f2f..7d5e39c7030 100644
--- a/jdk/src/java.base/share/classes/java/util/Queue.java
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java
@@ -129,14 +129,6 @@ package java.util;
  * 
  * Java Collections Framework.
  *
- * @see java.util.Collection
- * @see LinkedList
- * @see PriorityQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.BlockingQueue
- * @see java.util.concurrent.ArrayBlockingQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.PriorityBlockingQueue
  * @since 1.5
  * @author Doug Lea
  * @param  the type of elements held in this queue
diff --git a/jdk/src/java.base/share/classes/java/util/Vector.java b/jdk/src/java.base/share/classes/java/util/Vector.java
index 6e833f7dd6a..f6cdfbe8e9c 100644
--- a/jdk/src/java.base/share/classes/java/util/Vector.java
+++ b/jdk/src/java.base/share/classes/java/util/Vector.java
@@ -233,42 +233,56 @@ public class Vector
     public synchronized void ensureCapacity(int minCapacity) {
         if (minCapacity > 0) {
             modCount++;
-            ensureCapacityHelper(minCapacity);
+            if (minCapacity > elementData.length)
+                grow(minCapacity);
         }
     }
 
     /**
-     * This implements the unsynchronized semantics of ensureCapacity.
-     * Synchronized methods in this class can internally call this
-     * method for ensuring capacity without incurring the cost of an
-     * extra synchronization.
-     *
-     * @see #ensureCapacity(int)
-     */
-    private void ensureCapacityHelper(int minCapacity) {
-        // overflow-conscious code
-        if (minCapacity - elementData.length > 0)
-            grow(minCapacity);
-    }
-
-    /**
-     * The maximum size of array to allocate.
+     * The maximum size of array to allocate (unless necessary).
      * Some VMs reserve some header words in an array.
      * Attempts to allocate larger arrays may result in
      * OutOfMemoryError: Requested array size exceeds VM limit
      */
     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
-    private void grow(int minCapacity) {
+    /**
+     * Increases the capacity to ensure that it can hold at least the
+     * number of elements specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private Object[] grow(int minCapacity) {
+        return elementData = Arrays.copyOf(elementData,
+                                           newCapacity(minCapacity));
+    }
+
+    private Object[] grow() {
+        return grow(elementCount + 1);
+    }
+
+    /**
+     * Returns a capacity at least as large as the given minimum capacity.
+     * Will not return a capacity greater than MAX_ARRAY_SIZE unless
+     * the given minimum capacity is greater than MAX_ARRAY_SIZE.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private int newCapacity(int minCapacity) {
         // overflow-conscious code
         int oldCapacity = elementData.length;
         int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                          capacityIncrement : oldCapacity);
-        if (newCapacity - minCapacity < 0)
-            newCapacity = minCapacity;
-        if (newCapacity - MAX_ARRAY_SIZE > 0)
-            newCapacity = hugeCapacity(minCapacity);
-        elementData = Arrays.copyOf(elementData, newCapacity);
+        if (newCapacity - minCapacity <= 0) {
+            if (minCapacity < 0) // overflow
+                throw new OutOfMemoryError();
+            return minCapacity;
+        }
+        return (newCapacity - MAX_ARRAY_SIZE <= 0)
+            ? newCapacity
+            : hugeCapacity(minCapacity);
     }
 
     private static int hugeCapacity(int minCapacity) {
@@ -290,13 +304,10 @@ public class Vector
      */
     public synchronized void setSize(int newSize) {
         modCount++;
-        if (newSize > elementCount) {
-            ensureCapacityHelper(newSize);
-        } else {
-            for (int i = newSize ; i < elementCount ; i++) {
-                elementData[i] = null;
-            }
-        }
+        if (newSize > elementData.length)
+            grow(newSize);
+        for (int i = newSize; i < elementCount; i++)
+            elementData[i] = null;
         elementCount = newSize;
     }
 
@@ -604,11 +615,16 @@ public class Vector
             throw new ArrayIndexOutOfBoundsException(index
                                                      + " > " + elementCount);
         }
-        ensureCapacityHelper(elementCount + 1);
-        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
-        elementData[index] = obj;
         modCount++;
-        elementCount++;
+        final int s = elementCount;
+        Object[] elementData = this.elementData;
+        if (s == elementData.length)
+            elementData = grow();
+        System.arraycopy(elementData, index,
+                         elementData, index + 1,
+                         s - index);
+        elementData[index] = obj;
+        elementCount = s + 1;
     }
 
     /**
@@ -623,9 +639,8 @@ public class Vector
      * @param   obj   the component to be added
      */
     public synchronized void addElement(E obj) {
-        ensureCapacityHelper(elementCount + 1);
         modCount++;
-        elementData[elementCount++] = obj;
+        add(obj, elementData, elementCount);
     }
 
     /**
@@ -780,6 +795,18 @@ public class Vector
         return oldValue;
     }
 
+    /**
+     * This helper method split out from add(E) to keep method
+     * bytecode size under 35 (the -XX:MaxInlineSize default value),
+     * which helps when add(E) is called in a C1-compiled loop.
+     */
+    private void add(E e, Object[] elementData, int s) {
+        if (s == elementData.length)
+            elementData = grow();
+        elementData[s] = e;
+        elementCount = s + 1;
+    }
+
     /**
      * Appends the specified element to the end of this Vector.
      *
@@ -788,9 +815,8 @@ public class Vector
      * @since 1.2
      */
     public synchronized boolean add(E e) {
-        ensureCapacityHelper(elementCount + 1);
         modCount++;
-        elementData[elementCount++] = e;
+        add(e, elementData, elementCount);
         return true;
     }
 
@@ -891,16 +917,19 @@ public class Vector
      */
     public boolean addAll(Collection c) {
         Object[] a = c.toArray();
+        modCount++;
         int numNew = a.length;
-        if (numNew > 0) {
-            synchronized (this) {
-                ensureCapacityHelper(elementCount + numNew);
-                System.arraycopy(a, 0, elementData, elementCount, numNew);
-                modCount++;
-                elementCount += numNew;
-            }
+        if (numNew == 0)
+            return false;
+        synchronized (this) {
+            Object[] elementData = this.elementData;
+            final int s = elementCount;
+            if (numNew > elementData.length - s)
+                elementData = grow(s + numNew);
+            System.arraycopy(a, 0, elementData, s, numNew);
+            elementCount = s + numNew;
+            return true;
         }
-        return numNew > 0;
     }
 
     /**
@@ -969,21 +998,23 @@ public class Vector
             throw new ArrayIndexOutOfBoundsException(index);
 
         Object[] a = c.toArray();
+        modCount++;
         int numNew = a.length;
+        if (numNew == 0)
+            return false;
+        Object[] elementData = this.elementData;
+        final int s = elementCount;
+        if (numNew > elementData.length - s)
+            elementData = grow(s + numNew);
 
-        if (numNew > 0) {
-            ensureCapacityHelper(elementCount + numNew);
-
-            int numMoved = elementCount - index;
-            if (numMoved > 0)
-                System.arraycopy(elementData, index, elementData,
-                        index + numNew, numMoved);
-
-             System.arraycopy(a, 0, elementData, index, numNew);
-             elementCount += numNew;
-             modCount++;
-        }
-        return numNew > 0;
+        int numMoved = s - index;
+        if (numMoved > 0)
+            System.arraycopy(elementData, index,
+                             elementData, index + numNew,
+                             numMoved);
+        System.arraycopy(a, 0, elementData, index, numNew);
+        elementCount = s + numNew;
+        return true;
     }
 
     /**
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
index 37b97c36265..d301de9eee6 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
@@ -301,19 +301,15 @@ public interface ConcurrentMap extends Map {
      *
      * @implSpec
      * The default implementation is equivalent to the following steps for this
-     * {@code map}, then returning the current value or {@code null} if now
-     * absent:
+     * {@code map}:
      *
      * 
 {@code
-     * if (map.get(key) == null) {
-     *   V newValue = mappingFunction.apply(key);
-     *   if (newValue != null)
-     *     return map.putIfAbsent(key, newValue);
-     * }}
- * - * The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the mapping - * function multiple times. + * V oldValue, newValue; + * return ((oldValue = map.get(key)) == null + * && (newValue = mappingFunction.apply(key)) != null + * && (oldValue = map.putIfAbsent(key, newValue)) == null) + * ? newValue + * : oldValue;}
* *

This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -323,16 +319,19 @@ public interface ConcurrentMap extends Map { * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V computeIfAbsent(K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); - V v, newValue; - return ((v = get(key)) == null && - (newValue = mappingFunction.apply(key)) != null && - (v = putIfAbsent(key, newValue)) == null) ? newValue : v; + V oldValue, newValue; + return ((oldValue = get(key)) == null + && (newValue = mappingFunction.apply(key)) != null + && (oldValue = putIfAbsent(key, newValue)) == null) + ? newValue + : oldValue; } /** @@ -340,22 +339,19 @@ public interface ConcurrentMap extends Map { * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if now absent: + * steps for this {@code map}: * *

 {@code
-     * if (map.get(key) != null) {
-     *   V oldValue = map.get(key);
+     * for (V oldValue; (oldValue = map.get(key)) != null; ) {
      *   V newValue = remappingFunction.apply(key, oldValue);
-     *   if (newValue != null)
-     *     map.replace(key, oldValue, newValue);
-     *   else
-     *     map.remove(key, oldValue);
-     * }}
- * - * The default implementation may retry these steps when multiple threads - * attempt updates including potentially calling the remapping function - * multiple times. + * if ((newValue == null) + * ? map.remove(key, oldValue) + * : map.replace(key, oldValue, newValue)) + * return newValue; + * } + * return null;} + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * *

This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -365,22 +361,21 @@ public interface ConcurrentMap extends Map { * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V computeIfPresent(K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); - V oldValue; - while ((oldValue = get(key)) != null) { + for (V oldValue; (oldValue = get(key)) != null; ) { V newValue = remappingFunction.apply(key, oldValue); - if (newValue != null) { - if (replace(key, oldValue, newValue)) - return newValue; - } else if (remove(key, oldValue)) - return null; + if ((newValue == null) + ? remove(key, oldValue) + : replace(key, oldValue, newValue)) + return newValue; } - return oldValue; + return null; } /** @@ -388,27 +383,23 @@ public interface ConcurrentMap extends Map { * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if absent: + * steps for this {@code map}: * *

 {@code
-     * V oldValue = map.get(key);
-     * V newValue = remappingFunction.apply(key, oldValue);
-     * if (oldValue != null ) {
-     *   if (newValue != null)
-     *     map.replace(key, oldValue, newValue);
-     *   else
-     *     map.remove(key, oldValue);
-     * } else {
-     *   if (newValue != null)
-     *     map.putIfAbsent(key, newValue);
-     *   else
+     * for (;;) {
+     *   V oldValue = map.get(key);
+     *   V newValue = remappingFunction.apply(key, oldValue);
+     *   if (newValue != null) {
+     *     if ((oldValue != null)
+     *       ? map.replace(key, oldValue, newValue)
+     *       : map.putIfAbsent(key, newValue) == null)
+     *       return newValue;
+     *   } else if (oldValue == null || map.remove(key, oldValue)) {
      *     return null;
+     *   }
      * }}
- * - * The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the remapping - * function multiple times. + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * *

This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -418,50 +409,29 @@ public interface ConcurrentMap extends Map { * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override default V compute(K key, - BiFunction remappingFunction) { - Objects.requireNonNull(remappingFunction); - V oldValue = get(key); - for (;;) { - V newValue = remappingFunction.apply(key, oldValue); - if (newValue == null) { - // delete mapping - if (oldValue != null || containsKey(key)) { - // something to remove - if (remove(key, oldValue)) { - // removed the old value as expected - return null; + BiFunction remappingFunction) { + retry: for (;;) { + V oldValue = get(key); + // if putIfAbsent fails, opportunistically use its return value + haveOldValue: for (;;) { + V newValue = remappingFunction.apply(key, oldValue); + if (newValue != null) { + if (oldValue != null) { + if (replace(key, oldValue, newValue)) + return newValue; } - - // some other value replaced old value. try again. - oldValue = get(key); - } else { - // nothing to do. Leave things as they were. + else if ((oldValue = putIfAbsent(key, newValue)) == null) + return newValue; + else continue haveOldValue; + } else if (oldValue == null || remove(key, oldValue)) { return null; } - } else { - // add or replace old mapping - if (oldValue != null) { - // replace - if (replace(key, oldValue, newValue)) { - // replaced as expected. - return newValue; - } - - // some other value replaced old value. try again. - oldValue = get(key); - } else { - // add (replace if oldValue was null) - if ((oldValue = putIfAbsent(key, newValue)) == null) { - // replaced - return newValue; - } - - // some other value replaced old value. try again. - } + continue retry; } } } @@ -471,21 +441,25 @@ public interface ConcurrentMap extends Map { * * @implSpec * The default implementation is equivalent to performing the following - * steps for this {@code map}, then returning the current value or - * {@code null} if absent: + * steps for this {@code map}: * *

 {@code
-     * V oldValue = map.get(key);
-     * V newValue = (oldValue == null) ? value :
-     *     remappingFunction.apply(oldValue, value);
-     * if (newValue == null)
-     *   map.remove(key);
-     * else
-     *   map.put(key, newValue);}
- * - *

The default implementation may retry these steps when multiple - * threads attempt updates including potentially calling the remapping - * function multiple times. + * for (;;) { + * V oldValue = map.get(key); + * if (oldValue != null) { + * V newValue = remappingFunction.apply(oldValue, value); + * if (newValue != null) { + * if (map.replace(key, oldValue, newValue)) + * return newValue; + * } else if (map.remove(key, oldValue)) { + * return null; + * } + * } else if (map.putIfAbsent(key, value) == null) { + * return value; + * } + * }} + * When multiple threads attempt updates, map operations and the + * remapping function may be called multiple times. * *

This implementation assumes that the ConcurrentMap cannot contain null * values and {@code get()} returning null unambiguously means the key is @@ -495,6 +469,7 @@ public interface ConcurrentMap extends Map { * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} * @since 1.8 */ @Override @@ -502,20 +477,23 @@ public interface ConcurrentMap extends Map { BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); - V oldValue = get(key); - for (;;) { - if (oldValue != null) { - V newValue = remappingFunction.apply(oldValue, value); - if (newValue != null) { - if (replace(key, oldValue, newValue)) - return newValue; - } else if (remove(key, oldValue)) { - return null; - } - oldValue = get(key); - } else { - if ((oldValue = putIfAbsent(key, value)) == null) { - return value; + retry: for (;;) { + V oldValue = get(key); + // if putIfAbsent fails, opportunistically use its return value + haveOldValue: for (;;) { + if (oldValue != null) { + V newValue = remappingFunction.apply(oldValue, value); + if (newValue != null) { + if (replace(key, oldValue, newValue)) + return newValue; + } else if (remove(key, oldValue)) { + return null; + } + continue retry; + } else { + if ((oldValue = putIfAbsent(key, value)) == null) + return value; + continue haveOldValue; } } } diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java index 3b8d814bcd1..f2095540180 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,9 @@ public class Util { // The number of temp buffers in our pool private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX; + // The max size allowed for a cached temp buffer, in bytes + private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize(); + // Per-thread cache of temporary direct buffers private static ThreadLocal bufferCache = new ThreadLocal() @@ -54,6 +57,52 @@ public class Util { } }; + /** + * Returns the max size allowed for a cached temp buffers, in + * bytes. It defaults to Long.MAX_VALUE. It can be set with the + * jdk.nio.maxCachedBufferSize property. Even though + * ByteBuffer.capacity() returns an int, we're using a long here + * for potential future-proofing. + */ + private static long getMaxCachedBufferSize() { + String s = java.security.AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("jdk.nio.maxCachedBufferSize"); + } + }); + if (s != null) { + try { + long m = Long.parseLong(s); + if (m >= 0) { + return m; + } else { + // if it's negative, ignore the system property + } + } catch (NumberFormatException e) { + // if the string is not well formed, ignore the system property + } + } + return Long.MAX_VALUE; + } + + /** + * Returns true if a buffer of this size is too large to be + * added to the buffer cache, false otherwise. + */ + private static boolean isBufferTooLarge(int size) { + return size > MAX_CACHED_BUFFER_SIZE; + } + + /** + * Returns true if the buffer is too large to be added to the + * buffer cache, false otherwise. + */ + private static boolean isBufferTooLarge(ByteBuffer buf) { + return isBufferTooLarge(buf.capacity()); + } + /** * A simple cache of direct buffers. */ @@ -80,6 +129,9 @@ public class Util { * size (or null if no suitable buffer is found). */ ByteBuffer get(int size) { + // Don't call this if the buffer would be too large. + assert !isBufferTooLarge(size); + if (count == 0) return null; // cache is empty @@ -117,6 +169,9 @@ public class Util { } boolean offerFirst(ByteBuffer buf) { + // Don't call this if the buffer is too large. + assert !isBufferTooLarge(buf); + if (count >= TEMP_BUF_POOL_SIZE) { return false; } else { @@ -128,6 +183,9 @@ public class Util { } boolean offerLast(ByteBuffer buf) { + // Don't call this if the buffer is too large. + assert !isBufferTooLarge(buf); + if (count >= TEMP_BUF_POOL_SIZE) { return false; } else { @@ -156,6 +214,15 @@ public class Util { * Returns a temporary buffer of at least the given size */ public static ByteBuffer getTemporaryDirectBuffer(int size) { + // If a buffer of this size is too large for the cache, there + // should not be a buffer in the cache that is at least as + // large. So we'll just create a new one. Also, we don't have + // to remove the buffer from the cache (as this method does + // below) given that we won't put the new buffer in the cache. + if (isBufferTooLarge(size)) { + return ByteBuffer.allocateDirect(size); + } + BufferCache cache = bufferCache.get(); ByteBuffer buf = cache.get(size); if (buf != null) { @@ -185,6 +252,13 @@ public class Util { * likely to be returned by a subsequent call to getTemporaryDirectBuffer. */ static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { + // If the buffer is too large for the cache we don't have to + // check the cache. We'll just free it. + if (isBufferTooLarge(buf)) { + free(buf); + return; + } + assert buf != null; BufferCache cache = bufferCache.get(); if (!cache.offerFirst(buf)) { @@ -200,6 +274,13 @@ public class Util { * cache in same order that they were obtained. */ static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { + // If the buffer is too large for the cache we don't have to + // check the cache. We'll just free it. + if (isBufferTooLarge(buf)) { + free(buf); + return; + } + assert buf != null; BufferCache cache = bufferCache.get(); if (!cache.offerLast(buf)) { diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java index 84fbae5dddd..fa2d9a90991 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java @@ -59,7 +59,11 @@ abstract class AbstractPoller implements Runnable { AccessController.doPrivileged(new PrivilegedAction<>() { @Override public Object run() { - Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false); + Thread thr = new Thread(null, + thisRunnable, + "FileSystemWatchService", + 0, + false); thr.setDaemon(true); thr.start(); return null; @@ -216,10 +220,10 @@ abstract class AbstractPoller implements Runnable { throw new ClosedWatchServiceException(); } requestList.add(req); - } - // wakeup thread - wakeup(); + // wakeup thread + wakeup(); + } // wait for result Object result = req.awaitResult(); @@ -244,6 +248,7 @@ abstract class AbstractPoller implements Runnable { // if in process of shutdown then reject request if (shutdown) { req.release(new ClosedWatchServiceException()); + continue; } switch (req.type()) { diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java new file mode 100644 index 00000000000..0d39af447f7 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.*; + +import jdk.internal.joptsimple.internal.Reflection; +import jdk.internal.joptsimple.internal.ReflectionException; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * @param represents the type of the arguments this option accepts + * @author Paul Holser + */ +abstract class AbstractOptionSpec implements OptionSpec, OptionDescriptor { + private final List options = new ArrayList(); + private final String description; + private boolean forHelp; + + protected AbstractOptionSpec( String option ) { + this( singletonList( option ), EMPTY ); + } + + protected AbstractOptionSpec( Collection options, String description ) { + arrangeOptions( options ); + + this.description = description; + } + + public final Collection options() { + return unmodifiableList( options ); + } + + public final List values( OptionSet detectedOptions ) { + return detectedOptions.valuesOf( this ); + } + + public final V value( OptionSet detectedOptions ) { + return detectedOptions.valueOf( this ); + } + + public String description() { + return description; + } + + public final AbstractOptionSpec forHelp() { + forHelp = true; + return this; + } + + public final boolean isForHelp() { + return forHelp; + } + + public boolean representsNonOptions() { + return false; + } + + protected abstract V convert( String argument ); + + protected V convertWith( ValueConverter converter, String argument ) { + try { + return Reflection.convertWith( converter, argument ); + } + catch ( ReflectionException ex ) { + throw new OptionArgumentConversionException( options(), argument, ex ); + } + catch ( ValueConversionException ex ) { + throw new OptionArgumentConversionException( options(), argument, ex ); + } + } + + protected String argumentTypeIndicatorFrom( ValueConverter converter ) { + if ( converter == null ) + return null; + + String pattern = converter.valuePattern(); + return pattern == null ? converter.valueType().getName() : pattern; + } + + abstract void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ); + + private void arrangeOptions( Collection unarranged ) { + if ( unarranged.size() == 1 ) { + options.addAll( unarranged ); + return; + } + + List shortOptions = new ArrayList(); + List longOptions = new ArrayList(); + + for ( String each : unarranged ) { + if ( each.length() == 1 ) + shortOptions.add( each ); + else + longOptions.add( each ); + } + + sort( shortOptions ); + sort( longOptions ); + + options.addAll( shortOptions ); + options.addAll( longOptions ); + } + + @Override + public boolean equals( Object that ) { + if ( !( that instanceof AbstractOptionSpec ) ) + return false; + + AbstractOptionSpec other = (AbstractOptionSpec) that; + return options.equals( other.options ); + } + + @Override + public int hashCode() { + return options.hashCode(); + } + + @Override + public String toString() { + return options.toString(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java new file mode 100644 index 00000000000..12532c6b3bc --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Represents the "-W" form of long option specification. + * + * @author Paul Holser + */ +class AlternativeLongOptionSpec extends ArgumentAcceptingOptionSpec { + AlternativeLongOptionSpec() { + super( singletonList( RESERVED_FOR_EXTENSIONS ), true, "Alternative form of long options" ); + + describedAs( "opt=value" ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( !arguments.hasMore() ) + throw new OptionMissingRequiredArgumentException( options() ); + + arguments.treatNextAsLongOption(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java new file mode 100644 index 00000000000..ffb083746e9 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Objects.*; +import static jdk.internal.joptsimple.internal.Reflection.*; +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + *

Specification of an option that accepts an argument.

+ * + *

Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:

+ * + *
+ *   
+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+ *   
+ * 
+ * + *

If no methods are invoked on an instance of this class, then that instance's option will treat its argument as + * a {@link String}.

+ * + * @param represents the type of the arguments this option accepts + * @author Paul Holser + */ +public abstract class ArgumentAcceptingOptionSpec extends AbstractOptionSpec { + private static final char NIL_VALUE_SEPARATOR = '\u0000'; + + private boolean optionRequired; + private final boolean argumentRequired; + private ValueConverter converter; + private String argumentDescription = ""; + private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR ); + private final List defaultValues = new ArrayList(); + + ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) { + super( option ); + + this.argumentRequired = argumentRequired; + } + + ArgumentAcceptingOptionSpec( Collection options, boolean argumentRequired, String description ) { + super( options, description ); + + this.argumentRequired = argumentRequired; + } + + /** + *

Specifies a type to which arguments of this spec's option are to be converted.

+ * + *

JOpt Simple accepts types that have either:

+ * + *
    + *
  1. a public static method called {@code valueOf} which accepts a single argument of type {@link String} + * and whose return type is the same as the class on which the method is declared. The {@code java.lang} + * primitive wrapper classes have such methods.
  2. + * + *
  3. a public constructor which accepts a single argument of type {@link String}.
  4. + *
+ * + *

This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked + * before a one-{@link String}-arg constructor would.

+ * + *

Invoking this method will trump any previous calls to this method or to + * {@link #withValuesConvertedBy(ValueConverter)}.

+ * + * @param represents the runtime class of the desired option argument type + * @param argumentType desired type of arguments to this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the type is {@code null} + * @throws IllegalArgumentException if the type does not have the standard conversion methods + */ + public final ArgumentAcceptingOptionSpec ofType( Class argumentType ) { + return withValuesConvertedBy( findConverter( argumentType ) ); + } + + /** + *

Specifies a converter to use to translate arguments of this spec's option into Java objects. This is useful + * when converting to types that do not have the requisite factory method or constructor for + * {@link #ofType(Class)}.

+ * + *

Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}. + * + * @param represents the runtime class of the desired option argument type + * @param aConverter the converter to use + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the converter is {@code null} + */ + @SuppressWarnings( "unchecked" ) + public final ArgumentAcceptingOptionSpec withValuesConvertedBy( ValueConverter aConverter ) { + if ( aConverter == null ) + throw new NullPointerException( "illegal null converter" ); + + converter = (ValueConverter) aConverter; + return (ArgumentAcceptingOptionSpec) this; + } + + /** + *

Specifies a description for the argument of the option that this spec represents. This description is used + * when generating help information about the parser.

+ * + * @param description describes the nature of the argument of this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public final ArgumentAcceptingOptionSpec describedAs( String description ) { + argumentDescription = description; + return this; + } + + /** + *

Specifies a value separator for the argument of the option that this spec represents. This allows a single + * option argument to represent multiple values for the option. For example:

+ * + *
+     *   
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .withValuesSeparatedBy( ',' );
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   
+     * 
+ * + *

Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.

+ * + *

You cannot use Unicode U+0000 as the separator.

+ * + * @param separator a character separator + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws IllegalArgumentException if the separator is Unicode U+0000 + */ + public final ArgumentAcceptingOptionSpec withValuesSeparatedBy( char separator ) { + if ( separator == NIL_VALUE_SEPARATOR ) + throw new IllegalArgumentException( "cannot use U+0000 as separator" ); + + valueSeparator = String.valueOf( separator ); + return this; + } + + /** + *

Specifies a value separator for the argument of the option that this spec represents. This allows a single + * option argument to represent multiple values for the option. For example:

+ * + *
+     *   
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .withValuesSeparatedBy( ":::" );
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   
+     * 
+ * + *

Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.

+ * + *

You cannot use Unicode U+0000 in the separator.

+ * + * @param separator a string separator + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws IllegalArgumentException if the separator contains Unicode U+0000 + */ + public final ArgumentAcceptingOptionSpec withValuesSeparatedBy( String separator ) { + if ( separator.indexOf( NIL_VALUE_SEPARATOR ) != -1 ) + throw new IllegalArgumentException( "cannot use U+0000 in separator" ); + + valueSeparator = separator; + return this; + } + + /** + * Specifies a set of default values for the argument of the option that this spec represents. + * + * @param value the first in the set of default argument values for this spec's option + * @param values the (optional) remainder of the set of default argument values for this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are + * {@code null} + */ + @SuppressWarnings("unchecked") + public ArgumentAcceptingOptionSpec defaultsTo( V value, V... values ) { + addDefaultValue( value ); + defaultsTo( values ); + + return this; + } + + /** + * Specifies a set of default values for the argument of the option that this spec represents. + * + * @param values the set of default argument values for this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if {@code values} or any elements of {@code values} are {@code null} + */ + public ArgumentAcceptingOptionSpec defaultsTo( V[] values ) { + for ( V each : values ) + addDefaultValue( each ); + + return this; + } + + /** + * Marks this option as required. An {@link OptionException} will be thrown when + * {@link OptionParser#parse(java.lang.String...)} is called, if an option is marked as required and not specified + * on the command line. + * + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public ArgumentAcceptingOptionSpec required() { + optionRequired = true; + return this; + } + + public boolean isRequired() { + return optionRequired; + } + + private void addDefaultValue( V value ) { + ensureNotNull( value ); + defaultValues.add( value ); + } + + @Override + final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + if ( isNullOrEmpty( detectedArgument ) ) + detectOptionArgument( parser, arguments, detectedOptions ); + else + addArguments( detectedOptions, detectedArgument ); + } + + protected void addArguments( OptionSet detectedOptions, String detectedArgument ) { + StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator ); + if ( !lexer.hasMoreTokens() ) + detectedOptions.addWithArgument( this, detectedArgument ); + else { + while ( lexer.hasMoreTokens() ) + detectedOptions.addWithArgument( this, lexer.nextToken() ); + } + } + + protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments, + OptionSet detectedOptions ); + + @Override + protected final V convert( String argument ) { + return convertWith( converter, argument ); + } + + protected boolean canConvertArgument( String argument ) { + StringTokenizer lexer = new StringTokenizer( argument, valueSeparator ); + + try { + while ( lexer.hasMoreTokens() ) + convert( lexer.nextToken() ); + return true; + } + catch ( OptionException ignored ) { + return false; + } + } + + protected boolean isArgumentOfNumberType() { + return converter != null && Number.class.isAssignableFrom( converter.valueType() ); + } + + public boolean acceptsArguments() { + return true; + } + + public boolean requiresArgument() { + return argumentRequired; + } + + public String argumentDescription() { + return argumentDescription; + } + + public String argumentTypeIndicator() { + return argumentTypeIndicatorFrom( converter ); + } + + public List defaultValues() { + return unmodifiableList( defaultValues ); + } + + @Override + public boolean equals( Object that ) { + if ( !super.equals( that ) ) + return false; + + ArgumentAcceptingOptionSpec other = (ArgumentAcceptingOptionSpec) that; + return requiresArgument() == other.requiresArgument(); + } + + @Override + public int hashCode() { + return super.hashCode() ^ ( argumentRequired ? 0 : 1 ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java new file mode 100644 index 00000000000..e8cf157f99c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + *

Wrapper for an array of command line arguments.

+ * + * @author Paul Holser + */ +class ArgumentList { + private final String[] arguments; + private int currentIndex; + + ArgumentList( String... arguments ) { + this.arguments = arguments.clone(); + } + + boolean hasMore() { + return currentIndex < arguments.length; + } + + String next() { + return arguments[ currentIndex++ ]; + } + + String peek() { + return arguments[ currentIndex ]; + } + + void treatNextAsLongOption() { + if ( HYPHEN_CHAR != arguments[ currentIndex ].charAt( 0 ) ) + arguments[ currentIndex ] = DOUBLE_HYPHEN + arguments[ currentIndex ]; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java new file mode 100644 index 00000000000..ba72af36f90 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import jdk.internal.joptsimple.internal.Rows; +import jdk.internal.joptsimple.internal.Strings; + +import static jdk.internal.joptsimple.ParserRules.*; +import static jdk.internal.joptsimple.internal.Classes.*; +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + *

A help formatter that allows configuration of overall row width and column separator width.

+ * + *

The formatter produces a two-column output. The left column is for the options, and the right column for their + * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option + * column's width, no greater than slightly less than half the overall desired width.

+ * + * @author Paul Holser + */ +public class BuiltinHelpFormatter implements HelpFormatter { + private final Rows nonOptionRows; + private final Rows optionRows; + + /** + * Makes a formatter with a pre-configured overall row width and column separator width. + */ + BuiltinHelpFormatter() { + this( 80, 2 ); + } + + /** + * Makes a formatter with a given overall row width and column separator width. + * + * @param desiredOverallWidth how many characters wide to make the overall help display + * @param desiredColumnSeparatorWidth how many characters wide to make the separation between option column and + * description column + */ + public BuiltinHelpFormatter( int desiredOverallWidth, int desiredColumnSeparatorWidth ) { + nonOptionRows = new Rows( desiredOverallWidth * 2, 0 ); + optionRows = new Rows( desiredOverallWidth, desiredColumnSeparatorWidth ); + } + + public String format( Map options ) { + Comparator comparator = + new Comparator() { + public int compare( OptionDescriptor first, OptionDescriptor second ) { + return first.options().iterator().next().compareTo( second.options().iterator().next() ); + } + }; + + Set sorted = new TreeSet( comparator ); + sorted.addAll( options.values() ); + + addRows( sorted ); + + return formattedHelpOutput(); + } + + private String formattedHelpOutput() { + StringBuilder formatted = new StringBuilder(); + String nonOptionDisplay = nonOptionRows.render(); + if ( !Strings.isNullOrEmpty( nonOptionDisplay ) ) + formatted.append( nonOptionDisplay ).append( LINE_SEPARATOR ); + formatted.append( optionRows.render() ); + + return formatted.toString(); + } + + private void addRows( Collection options ) { + addNonOptionsDescription( options ); + + if ( options.isEmpty() ) + optionRows.add( "No options specified", "" ); + else { + addHeaders( options ); + addOptions( options ); + } + + fitRowsToWidth(); + } + + private void addNonOptionsDescription( Collection options ) { + OptionDescriptor nonOptions = findAndRemoveNonOptionsSpec( options ); + if ( shouldShowNonOptionArgumentDisplay( nonOptions ) ) { + nonOptionRows.add( "Non-option arguments:", "" ); + nonOptionRows.add(createNonOptionArgumentsDisplay(nonOptions), ""); + } + } + + private boolean shouldShowNonOptionArgumentDisplay( OptionDescriptor nonOptions ) { + return !Strings.isNullOrEmpty( nonOptions.description() ) + || !Strings.isNullOrEmpty( nonOptions.argumentTypeIndicator() ) + || !Strings.isNullOrEmpty( nonOptions.argumentDescription() ); + } + + private String createNonOptionArgumentsDisplay(OptionDescriptor nonOptions) { + StringBuilder buffer = new StringBuilder(); + maybeAppendOptionInfo( buffer, nonOptions ); + maybeAppendNonOptionsDescription( buffer, nonOptions ); + + return buffer.toString(); + } + + private void maybeAppendNonOptionsDescription( StringBuilder buffer, OptionDescriptor nonOptions ) { + buffer.append( buffer.length() > 0 && !Strings.isNullOrEmpty( nonOptions.description() ) ? " -- " : "" ) + .append( nonOptions.description() ); + } + + private OptionDescriptor findAndRemoveNonOptionsSpec( Collection options ) { + for ( Iterator it = options.iterator(); it.hasNext(); ) { + OptionDescriptor next = it.next(); + if ( next.representsNonOptions() ) { + it.remove(); + return next; + } + } + + throw new AssertionError( "no non-options argument spec" ); + } + + private void addHeaders( Collection options ) { + if ( hasRequiredOption( options ) ) { + optionRows.add("Option (* = required)", "Description"); + optionRows.add("---------------------", "-----------"); + } else { + optionRows.add("Option", "Description"); + optionRows.add("------", "-----------"); + } + } + + private boolean hasRequiredOption( Collection options ) { + for ( OptionDescriptor each : options ) { + if ( each.isRequired() ) + return true; + } + + return false; + } + + private void addOptions( Collection options ) { + for ( OptionDescriptor each : options ) { + if ( !each.representsNonOptions() ) + optionRows.add( createOptionDisplay( each ), createDescriptionDisplay( each ) ); + } + } + + private String createOptionDisplay( OptionDescriptor descriptor ) { + StringBuilder buffer = new StringBuilder( descriptor.isRequired() ? "* " : "" ); + + for ( Iterator i = descriptor.options().iterator(); i.hasNext(); ) { + String option = i.next(); + buffer.append( option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN ); + buffer.append( option ); + + if ( i.hasNext() ) + buffer.append( ", " ); + } + + maybeAppendOptionInfo( buffer, descriptor ); + + return buffer.toString(); + } + + private void maybeAppendOptionInfo( StringBuilder buffer, OptionDescriptor descriptor ) { + String indicator = extractTypeIndicator( descriptor ); + String description = descriptor.argumentDescription(); + if ( indicator != null || !isNullOrEmpty( description ) ) + appendOptionHelp( buffer, indicator, description, descriptor.requiresArgument() ); + } + + private String extractTypeIndicator( OptionDescriptor descriptor ) { + String indicator = descriptor.argumentTypeIndicator(); + + if ( !isNullOrEmpty( indicator ) && !String.class.getName().equals( indicator ) ) + return shortNameOf( indicator ); + + return null; + } + + private void appendOptionHelp( StringBuilder buffer, String typeIndicator, String description, boolean required ) { + if ( required ) + appendTypeIndicator( buffer, typeIndicator, description, '<', '>' ); + else + appendTypeIndicator( buffer, typeIndicator, description, '[', ']' ); + } + + private void appendTypeIndicator( StringBuilder buffer, String typeIndicator, String description, + char start, char end ) { + buffer.append( ' ' ).append( start ); + if ( typeIndicator != null ) + buffer.append( typeIndicator ); + + if ( !Strings.isNullOrEmpty( description ) ) { + if ( typeIndicator != null ) + buffer.append( ": " ); + + buffer.append( description ); + } + + buffer.append( end ); + } + + private String createDescriptionDisplay( OptionDescriptor descriptor ) { + List defaultValues = descriptor.defaultValues(); + if ( defaultValues.isEmpty() ) + return descriptor.description(); + + String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues ); + return ( descriptor.description() + ' ' + surround( "default: " + defaultValuesDisplay, '(', ')' ) ).trim(); + } + + private String createDefaultValuesDisplay( List defaultValues ) { + return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString(); + } + + private void fitRowsToWidth() { + nonOptionRows.fitToWidth(); + optionRows.fitToWidth(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java new file mode 100644 index 00000000000..e5dd316f1d7 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Map; + +/** + *

Represents objects charged with taking a set of option descriptions and producing some help text from them.

+ * + * @author Paul Holser + */ +public interface HelpFormatter { + /** + * Produces help text, given a set of option descriptors. + * + * @param options descriptors for the configured options of a parser + * @return text to be used as help + * @see OptionParser#printHelpOn(java.io.Writer) + * @see OptionParser#formatHelpWith(HelpFormatter) + */ + String format( Map options ); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java new file mode 100644 index 00000000000..8696bf1a150 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +/** + * Thrown when the option parser is asked to recognize an option with illegal characters in it. + * + * @author Paul Holser + */ +class IllegalOptionSpecificationException extends OptionException { + private static final long serialVersionUID = -1L; + + IllegalOptionSpecificationException( String option ) { + super( singletonList( option ) ); + } + + @Override + public String getMessage() { + return singleOptionMessage() + " is not a legal option character"; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java new file mode 100644 index 00000000000..2c997e2052c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when an option is marked as required, but not specified on the command line. + * + * @author Emils Solmanis + */ +class MissingRequiredOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + protected MissingRequiredOptionException( Collection options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Missing required option(s) " + multipleOptionMessage(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java new file mode 100644 index 00000000000..e96cea33918 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when asking an {@link OptionSet} for a single argument of an option when many have been specified. + * + * @author Paul Holser + */ +class MultipleArgumentsForOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + MultipleArgumentsForOptionException( Collection options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Found multiple arguments for option " + multipleOptionMessage() + ", but you asked for only one"; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java new file mode 100644 index 00000000000..9647ff1bad2 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.*; + +/** + * A specification for an option that does not accept arguments. + * + * @author Paul Holser + */ +class NoArgumentOptionSpec extends AbstractOptionSpec { + NoArgumentOptionSpec( String option ) { + this( singletonList( option ), "" ); + } + + NoArgumentOptionSpec( Collection options, String description ) { + super( options, description ); + } + + @Override + void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + detectedOptions.add( this ); + } + + public boolean acceptsArguments() { + return false; + } + + public boolean requiresArgument() { + return false; + } + + public boolean isRequired() { + return false; + } + + public String argumentDescription() { + return ""; + } + + public String argumentTypeIndicator() { + return ""; + } + + @Override + protected Void convert( String argument ) { + return null; + } + + public List defaultValues() { + return emptyList(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java new file mode 100644 index 00000000000..8124a05e366 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.List; + +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static jdk.internal.joptsimple.internal.Reflection.*; + +/** + *

Specification of a command line's non-option arguments.

+ * + *

Instances are returned from {@link OptionParser} methods to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:

+ * + *
+ *   
+ *   OptionParser parser = new OptionParser();
+ *   parser.nonOptions( "files to be processed" ).ofType( File.class );
+ *   
+ * 
+ * + *

If no methods are invoked on an instance of this class, then that instance's option will treat the non-option + * arguments as {@link String}s.

+ * + * @param represents the type of the non-option arguments + * @author Paul Holser + */ +public class NonOptionArgumentSpec extends AbstractOptionSpec { + static final String NAME = "[arguments]"; + + private ValueConverter converter; + private String argumentDescription = ""; + + NonOptionArgumentSpec() { + this(""); + } + + NonOptionArgumentSpec( String description ) { + super( asList( NAME ), description ); + } + + /** + *

Specifies a type to which the non-option arguments are to be converted.

+ * + *

JOpt Simple accepts types that have either:

+ * + *
    + *
  1. a public static method called {@code valueOf} which accepts a single argument of type {@link String} + * and whose return type is the same as the class on which the method is declared. The {@code java.lang} + * primitive wrapper classes have such methods.
  2. + * + *
  3. a public constructor which accepts a single argument of type {@link String}.
  4. + *
+ * + *

This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked + * before a one-{@link String}-arg constructor would.

+ * + *

Invoking this method will trump any previous calls to this method or to + * {@link #withValuesConvertedBy(ValueConverter)}.

+ * + * @param represents the runtime class of the desired option argument type + * @param argumentType desired type of arguments to this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the type is {@code null} + * @throws IllegalArgumentException if the type does not have the standard conversion methods + */ + @SuppressWarnings( "unchecked" ) + public NonOptionArgumentSpec ofType( Class argumentType ) { + converter = (ValueConverter) findConverter( argumentType ); + return (NonOptionArgumentSpec) this; + } + + /** + *

Specifies a converter to use to translate non-option arguments into Java objects. This is useful + * when converting to types that do not have the requisite factory method or constructor for + * {@link #ofType(Class)}.

+ * + *

Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}. + * + * @param represents the runtime class of the desired non-option argument type + * @param aConverter the converter to use + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws NullPointerException if the converter is {@code null} + */ + @SuppressWarnings( "unchecked" ) + public final NonOptionArgumentSpec withValuesConvertedBy( ValueConverter aConverter ) { + if ( aConverter == null ) + throw new NullPointerException( "illegal null converter" ); + + converter = (ValueConverter) aConverter; + return (NonOptionArgumentSpec) this; + } + + /** + *

Specifies a description for the non-option arguments that this spec represents. This description is used + * when generating help information about the parser.

+ * + * @param description describes the nature of the argument of this spec's option + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public NonOptionArgumentSpec describedAs( String description ) { + argumentDescription = description; + return this; + } + + @Override + protected final V convert( String argument ) { + return convertWith( converter, argument ); + } + + @Override + void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions, + String detectedArgument ) { + + detectedOptions.addWithArgument( this, detectedArgument ); + } + + public List defaultValues() { + return emptyList(); + } + + public boolean isRequired() { + return false; + } + + public boolean acceptsArguments() { + return false; + } + + public boolean requiresArgument() { + return false; + } + + public String argumentDescription() { + return argumentDescription; + } + + public String argumentTypeIndicator() { + return argumentTypeIndicatorFrom( converter ); + } + + public boolean representsNonOptions() { + return true; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java new file mode 100644 index 00000000000..063b97200f8 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when a problem occurs converting an argument of an option from {@link String} to another type. + * + * @author Paul Holser + */ +class OptionArgumentConversionException extends OptionException { + private static final long serialVersionUID = -1L; + + private final String argument; + + OptionArgumentConversionException( Collection options, String argument, Throwable cause ) { + super( options, cause ); + + this.argument = argument; + } + + @Override + public String getMessage() { + return "Cannot parse argument '" + argument + "' of option " + multipleOptionMessage(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java new file mode 100644 index 00000000000..2a85e25901a --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009, 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.internal.joptsimple; + +import java.util.Collection; + +/** + * Trains the option parser. This interface aids integration with other code which may expose declaration of options but + * not actual command-line parsing. + * + * @author Paul Holser + * @see OptionParser + */ +public interface OptionDeclarer { + /** + * Tells the parser to recognize the given option. + * + *

This method returns an instance of {@link OptionSpecBuilder} to allow the formation of parser directives + * as sentences in a fluent interface language. For example:

+ * + *

+     *   OptionDeclarer parser = new OptionParser();
+     *   parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+     * 
+ * + *

If no methods are invoked on the returned {@link OptionSpecBuilder}, then the parser treats the option as + * accepting no argument.

+ * + * @param option the option to recognize + * @return an object that can be used to flesh out more detail about the option + * @throws OptionException if the option contains illegal characters + * @throws NullPointerException if the option is {@code null} + */ + OptionSpecBuilder accepts( String option ); + + /** + * Tells the parser to recognize the given option. + * + * @see #accepts(String) + * @param option the option to recognize + * @param description a string that describes the purpose of the option. This is used when generating help + * information about the parser. + * @return an object that can be used to flesh out more detail about the option + * @throws OptionException if the option contains illegal characters + * @throws NullPointerException if the option is {@code null} + */ + OptionSpecBuilder accepts( String option, String description ); + + /** + * Tells the parser to recognize the given options, and treat them as synonymous. + * + * @see #accepts(String) + * @param options the options to recognize and treat as synonymous + * @return an object that can be used to flesh out more detail about the options + * @throws OptionException if any of the options contain illegal characters + * @throws NullPointerException if the option list or any of its elements are {@code null} + */ + OptionSpecBuilder acceptsAll( Collection options ); + + /** + * Tells the parser to recognize the given options, and treat them as synonymous. + * + * @see #acceptsAll(Collection) + * @param options the options to recognize and treat as synonymous + * @param description a string that describes the purpose of the option. This is used when generating help + * information about the parser. + * @return an object that can be used to flesh out more detail about the options + * @throws OptionException if any of the options contain illegal characters + * @throws NullPointerException if the option list or any of its elements are {@code null} + * @throws IllegalArgumentException if the option list is empty + */ + OptionSpecBuilder acceptsAll( Collection options, String description ); + + /** + * Gives an object that represents an access point for non-option arguments on a command line. + * + * @return an object that can be used to flesh out more detail about the non-option arguments + */ + NonOptionArgumentSpec nonOptions(); + + /** + * Gives an object that represents an access point for non-option arguments on a command line. + * + * @see #nonOptions() + * @param description a string that describes the purpose of the non-option arguments. This is used when generating + * help information about the parser. + * @return an object that can be used to flesh out more detail about the non-option arguments + */ + NonOptionArgumentSpec nonOptions( String description ); + + /** + * Tells the parser whether or not to behave "POSIX-ly correct"-ly. + * + * @param setting {@code true} if the parser should behave "POSIX-ly correct"-ly + */ + void posixlyCorrect( boolean setting ); + + /** + *

Tells the parser to treat unrecognized options as non-option arguments.

+ * + *

If not called, then the parser raises an {@link OptionException} when it encounters an unrecognized + * option.

+ */ + void allowsUnrecognizedOptions(); + + /** + * Tells the parser either to recognize or ignore "-W"-style long options. + * + * @param recognize {@code true} if the parser is to recognize the special style of long options + */ + void recognizeAlternativeLongOptions( boolean recognize ); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java new file mode 100644 index 00000000000..4a6694814f3 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +/** + * Describes options that an option parser recognizes, in ways that might be useful to {@linkplain HelpFormatter + * help screens}. + * + * @author Paul Holser + */ +public interface OptionDescriptor { + /** + * A set of options that are mutually synonymous. + * + * @return synonymous options + */ + Collection options(); + + /** + * Description of this option's purpose. + * + * @return a description for the option + */ + String description(); + + /** + * What values will the option take if none are specified on the command line? + * + * @return any default values for the option + */ + List defaultValues(); + + /** + * Is this option {@linkplain ArgumentAcceptingOptionSpec#required() required} on a command line? + * + * @return whether the option is required + */ + boolean isRequired(); + + /** + * Does this option {@linkplain ArgumentAcceptingOptionSpec accept arguments}? + * + * @return whether the option accepts arguments + */ + boolean acceptsArguments(); + + /** + * Does this option {@linkplain OptionSpecBuilder#withRequiredArg() require an argument}? + * + * @return whether the option requires an argument + */ + boolean requiresArgument(); + + /** + * Gives a short {@linkplain ArgumentAcceptingOptionSpec#describedAs(String) description} of the option's argument. + * + * @return a description for the option's argument + */ + String argumentDescription(); + + /** + * Gives an indication of the {@linkplain ArgumentAcceptingOptionSpec#ofType(Class) expected type} of the option's + * argument. + * + * @return a description for the option's argument type + */ + String argumentTypeIndicator(); + + /** + * Tells whether this object represents the non-option arguments of a command line. + * + * @return {@code true} if this represents non-option arguments + */ + boolean representsNonOptions(); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java new file mode 100644 index 00000000000..15977b63801 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * Thrown when a problem occurs during option parsing. + * + * @author Paul Holser + */ +public abstract class OptionException extends RuntimeException { + private static final long serialVersionUID = -1L; + + private final List options = new ArrayList(); + + protected OptionException( Collection options ) { + this.options.addAll( options ); + } + + protected OptionException( Collection options, Throwable cause ) { + super( cause ); + + this.options.addAll( options ); + } + + /** + * Gives the option being considered when the exception was created. + * + * @return the option being considered when the exception was created + */ + public Collection options() { + return unmodifiableCollection( options ); + } + + protected final String singleOptionMessage() { + return singleOptionMessage( options.get( 0 ) ); + } + + protected final String singleOptionMessage( String option ) { + return SINGLE_QUOTE + option + SINGLE_QUOTE; + } + + protected final String multipleOptionMessage() { + StringBuilder buffer = new StringBuilder( "[" ); + + for ( Iterator iter = options.iterator(); iter.hasNext(); ) { + buffer.append( singleOptionMessage( iter.next() ) ); + if ( iter.hasNext() ) + buffer.append( ", " ); + } + + buffer.append( ']' ); + + return buffer.toString(); + } + + static OptionException unrecognizedOption( String option ) { + return new UnrecognizedOptionException( option ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java new file mode 100644 index 00000000000..f667b446527 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Thrown when the option parser discovers an option that requires an argument, but that argument is missing. + * + * @author Paul Holser + */ +class OptionMissingRequiredArgumentException extends OptionException { + private static final long serialVersionUID = -1L; + + OptionMissingRequiredArgumentException( Collection options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Option " + multipleOptionMessage() + " requires an argument"; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java new file mode 100644 index 00000000000..c6c319a329a --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import jdk.internal.joptsimple.internal.AbbreviationMap; +import jdk.internal.joptsimple.util.KeyValuePair; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.*; +import static jdk.internal.joptsimple.OptionException.*; +import static jdk.internal.joptsimple.OptionParserState.*; +import static jdk.internal.joptsimple.ParserRules.*; + +/** + *

Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()} + * and GNU {@code getopt_long()}.

+ * + *

This parser supports short options and long options.

+ * + *
    + *
  • Short options begin with a single hyphen ("-") followed by a single letter or digit, + * or question mark ("?"), or dot (".").
  • + * + *
  • Short options can accept single arguments. The argument can be made required or optional. The option's + * argument can occur: + *
      + *
    • in the slot after the option, as in -d /tmp
    • + *
    • right up against the option, as in -d/tmp
    • + *
    • right up against the option separated by an equals sign ("="), as in -d=/tmp
    • + *
    + * To specify n arguments for an option, specify the option n times, once for each argument, + * as in -d /tmp -d /var -d /opt; or, when using the + * {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent + * interface" (see below), give multiple values separated by a given character as a single argument to the + * option.
  • + * + *
  • Short options can be clustered, so that -abc is treated as -a -b -c. If a short option + * in the cluster can accept an argument, the remaining characters are interpreted as the argument for that + * option.
  • + * + *
  • An argument consisting only of two hyphens ("--") signals that the remaining arguments are to be + * treated as non-options.
  • + * + *
  • An argument consisting only of a single hyphen is considered a non-option argument (though it can be an + * argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard + * output streams.
  • + * + *
  • Long options begin with two hyphens ("--"), followed by multiple letters, digits, + * hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when + * configuring the parser.
  • + * + *
  • You can abbreviate long options, so long as the abbreviation is unique.
  • + * + *
  • Long options can accept single arguments. The argument can be made required or optional. The option's + * argument can occur: + *
      + *
    • in the slot after the option, as in --directory /tmp
    • + *
    • right up against the option separated by an equals sign ("="), as in + * --directory=/tmp + *
    + * Specify multiple arguments for a long option in the same manner as for short options (see above).
  • + * + *
  • You can use a single hyphen ("-") instead of a double hyphen ("--") for a long + * option.
  • + * + *
  • The option -W is reserved. If you tell the parser to {@linkplain + * #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example, + * -W foo=bar as the long option foo with argument bar, as though you had written + * --foo=bar.
  • + * + *
  • You can specify -W as a valid short option, or use it as an abbreviation for a long option, but + * {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede + * this behavior.
  • + * + *
  • You can specify a given short or long option multiple times on a single command line. The parser collects + * any arguments specified for those options as a list.
  • + * + *
  • If the parser detects an option whose argument is optional, and the next argument "looks like" an option, + * that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other + * hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the + * negative number argument of the option, even if the parser recognizes the corresponding numeric option. + * For example: + *
    
    + *     OptionParser parser = new OptionParser();
    + *     parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
    + *     parser.accepts( "2" );
    + *     OptionSet options = parser.parse( "-a", "-2" );
    + *   
    + * In this case, the option set contains "a" with argument -2, not both "a" and + * "2". Swapping the elements in the args array gives the latter.
  • + *
+ * + *

There are two ways to tell the parser what options to recognize:

+ * + *
    + *
  1. A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent + * interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(Collection) + * acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument, + * whether the argument is required or optional, to what type arguments of the options should be converted if any, + * etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to + * retrieve the arguments of the associated option in a type-safe manner.
  2. + * + *
  3. Since version 1, a more concise way of specifying short options has been to use the special {@linkplain + * #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}. + * Here are the rules for the format of the specification strings this constructor accepts: + * + *
      + *
    • Any letter or digit is treated as an option character.
    • + * + *
    • An option character can be immediately followed by an asterisk (*) to indicate that the option is a + * "help" option.
    • + * + *
    • If an option character (with possible trailing asterisk) is followed by a single colon (":"), + * then the option requires an argument.
    • + * + *
    • If an option character (with possible trailing asterisk) is followed by two colons ("::"), + * then the option accepts an optional argument.
    • + * + *
    • Otherwise, the option character accepts no argument.
    • + * + *
    • If the option specification string begins with a plus sign ("+"), the parser will behave + * "POSIX-ly correct".
    • + * + *
    • If the option specification string contains the sequence "W;" (capital W followed by a + * semicolon), the parser will recognize the alternative form of long options.
    • + *
    + *
  4. + *
+ * + *

Each of the options in a list of options given to {@link #acceptsAll(Collection) acceptsAll} is treated as a + * synonym of the others. For example: + *

+ *     
+ *     OptionParser parser = new OptionParser();
+ *     parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
+ *     OptionSet options = parser.parse( "-w" );
+ *     
+ *   
+ * In this case, options.{@link OptionSet#has(String) has} would answer {@code true} when given arguments + * "w", "interactive", and "confirmation". The {@link OptionSet} would give the same + * responses to these arguments for its other methods as well.

+ * + *

By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however, + * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an + * option, and is not a required argument of a preceding option, signals the end of options. You can still bind + * optional arguments to their options using the abutting (for short options) or = syntax.

+ * + *

Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}. + * "POSIX-ly correct" parsers are configured by either:

+ * + *
    + *
  1. using the method {@link #posixlyCorrect(boolean)}, or
  2. + * + *
  3. using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign + * ("+")
  4. + *
+ * + * @author Paul Holser + * @see The GNU C Library + */ +public class OptionParser implements OptionDeclarer { + private final AbbreviationMap> recognizedOptions; + private final Map, Set>> requiredIf; + private final Map, Set>> requiredUnless; + private OptionParserState state; + private boolean posixlyCorrect; + private boolean allowsUnrecognizedOptions; + private HelpFormatter helpFormatter = new BuiltinHelpFormatter(); + + /** + * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct" + * behavior. + */ + public OptionParser() { + recognizedOptions = new AbbreviationMap>(); + requiredIf = new HashMap, Set>>(); + requiredUnless = new HashMap, Set>>(); + state = moreOptions( false ); + + recognize( new NonOptionArgumentSpec() ); + } + + /** + * Creates an option parser and configures it to recognize the short options specified in the given string. + * + * Arguments of options specified this way will be of type {@link String}. + * + * @param optionSpecification an option specification + * @throws NullPointerException if {@code optionSpecification} is {@code null} + * @throws OptionException if the option specification contains illegal characters or otherwise cannot be + * recognized + */ + public OptionParser( String optionSpecification ) { + this(); + + new OptionSpecTokenizer( optionSpecification ).configure( this ); + } + + public OptionSpecBuilder accepts( String option ) { + return acceptsAll( singletonList( option ) ); + } + + public OptionSpecBuilder accepts( String option, String description ) { + return acceptsAll( singletonList( option ), description ); + } + + public OptionSpecBuilder acceptsAll( Collection options ) { + return acceptsAll( options, "" ); + } + + public OptionSpecBuilder acceptsAll( Collection options, String description ) { + if ( options.isEmpty() ) + throw new IllegalArgumentException( "need at least one option" ); + + ensureLegalOptions( options ); + + return new OptionSpecBuilder( this, options, description ); + } + + public NonOptionArgumentSpec nonOptions() { + NonOptionArgumentSpec spec = new NonOptionArgumentSpec(); + + recognize( spec ); + + return spec; + } + + public NonOptionArgumentSpec nonOptions( String description ) { + NonOptionArgumentSpec spec = new NonOptionArgumentSpec( description ); + + recognize( spec ); + + return spec; + } + + public void posixlyCorrect( boolean setting ) { + posixlyCorrect = setting; + state = moreOptions( setting ); + } + + boolean posixlyCorrect() { + return posixlyCorrect; + } + + public void allowsUnrecognizedOptions() { + allowsUnrecognizedOptions = true; + } + + boolean doesAllowsUnrecognizedOptions() { + return allowsUnrecognizedOptions; + } + + public void recognizeAlternativeLongOptions( boolean recognize ) { + if ( recognize ) + recognize( new AlternativeLongOptionSpec() ); + else + recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) ); + } + + void recognize( AbstractOptionSpec spec ) { + recognizedOptions.putAll( spec.options(), spec ); + } + + /** + * Writes information about the options this parser recognizes to the given output sink. + * + * The output sink is flushed, but not closed. + * + * @param sink the sink to write information to + * @throws IOException if there is a problem writing to the sink + * @throws NullPointerException if {@code sink} is {@code null} + * @see #printHelpOn(Writer) + */ + public void printHelpOn( OutputStream sink ) throws IOException { + printHelpOn( new OutputStreamWriter( sink ) ); + } + + /** + * Writes information about the options this parser recognizes to the given output sink. + * + * The output sink is flushed, but not closed. + * + * @param sink the sink to write information to + * @throws IOException if there is a problem writing to the sink + * @throws NullPointerException if {@code sink} is {@code null} + * @see #printHelpOn(OutputStream) + */ + public void printHelpOn( Writer sink ) throws IOException { + sink.write( helpFormatter.format( recognizedOptions.toJavaUtilMap() ) ); + sink.flush(); + } + + /** + * Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}. + * + * @param formatter the formatter to use for printing help + * @throws NullPointerException if the formatter is {@code null} + */ + public void formatHelpWith( HelpFormatter formatter ) { + if ( formatter == null ) + throw new NullPointerException(); + + helpFormatter = formatter; + } + + /** + * Retrieves all the options which have been configured for the parser. + * + * @return a {@link Map} containing all the configured options and their corresponding {@link OptionSpec} + */ + public Map> recognizedOptions() { + return new HashMap>( recognizedOptions.toJavaUtilMap() ); + } + + /** + * Parses the given command line arguments according to the option specifications given to the parser. + * + * @param arguments arguments to parse + * @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found + * @throws OptionException if problems are detected while parsing + * @throws NullPointerException if the argument list is {@code null} + */ + public OptionSet parse( String... arguments ) { + ArgumentList argumentList = new ArgumentList( arguments ); + OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() ); + detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) ); + + while ( argumentList.hasMore() ) + state.handleArgument( this, argumentList, detected ); + + reset(); + + ensureRequiredOptions( detected ); + + return detected; + } + + private void ensureRequiredOptions( OptionSet options ) { + Collection missingRequiredOptions = missingRequiredOptions( options ); + boolean helpOptionPresent = isHelpOptionPresent( options ); + + if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent ) + throw new MissingRequiredOptionException( missingRequiredOptions ); + } + + private Collection missingRequiredOptions( OptionSet options ) { + Collection missingRequiredOptions = new HashSet(); + + for ( AbstractOptionSpec each : recognizedOptions.toJavaUtilMap().values() ) { + if ( each.isRequired() && !options.has( each ) ) + missingRequiredOptions.addAll( each.options() ); + } + + for ( Map.Entry, Set>> eachEntry : requiredIf.entrySet() ) { + AbstractOptionSpec required = specFor( eachEntry.getKey().iterator().next() ); + + if ( optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) { + missingRequiredOptions.addAll( required.options() ); + } + } + + for ( Map.Entry, Set>> eachEntry : requiredUnless.entrySet() ) { + AbstractOptionSpec required = specFor( eachEntry.getKey().iterator().next() ); + + if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) { + missingRequiredOptions.addAll( required.options() ); + } + } + + return missingRequiredOptions; + } + + private boolean optionsHasAnyOf( OptionSet options, Collection> specs ) { + for ( OptionSpec each : specs ) { + if ( options.has( each ) ) + return true; + } + + return false; + } + + private boolean isHelpOptionPresent( OptionSet options ) { + boolean helpOptionPresent = false; + for ( AbstractOptionSpec each : recognizedOptions.toJavaUtilMap().values() ) { + if ( each.isForHelp() && options.has( each ) ) { + helpOptionPresent = true; + break; + } + } + return helpOptionPresent; + } + + void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) { + KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate ); + + if ( !isRecognized( optionAndArgument.key ) ) + throw unrecognizedOption( optionAndArgument.key ); + + AbstractOptionSpec optionSpec = specFor( optionAndArgument.key ); + optionSpec.handleOption( this, arguments, detected, optionAndArgument.value ); + } + + void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) { + KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate ); + + if ( isRecognized( optionAndArgument.key ) ) { + specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value ); + } + else + handleShortOptionCluster( candidate, arguments, detected ); + } + + private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) { + char[] options = extractShortOptionsFrom( candidate ); + validateOptionCharacters( options ); + + for ( int i = 0; i < options.length; i++ ) { + AbstractOptionSpec optionSpec = specFor( options[ i ] ); + + if ( optionSpec.acceptsArguments() && options.length > i + 1 ) { + String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i ); + optionSpec.handleOption( this, arguments, detected, detectedArgument ); + break; + } + + optionSpec.handleOption( this, arguments, detected, null ); + } + } + + void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) { + specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate ); + } + + void noMoreOptions() { + state = OptionParserState.noMoreOptions(); + } + + boolean looksLikeAnOption( String argument ) { + return isShortOptionToken( argument ) || isLongOptionToken( argument ); + } + + boolean isRecognized( String option ) { + return recognizedOptions.contains( option ); + } + + void requiredIf( Collection precedentSynonyms, String required ) { + requiredIf( precedentSynonyms, specFor( required ) ); + } + + void requiredIf( Collection precedentSynonyms, OptionSpec required ) { + putRequiredOption( precedentSynonyms, required, requiredIf ); + } + + void requiredUnless( Collection precedentSynonyms, String required ) { + requiredUnless( precedentSynonyms, specFor( required ) ); + } + + void requiredUnless( Collection precedentSynonyms, OptionSpec required ) { + putRequiredOption( precedentSynonyms, required, requiredUnless ); + } + + private void putRequiredOption( Collection precedentSynonyms, OptionSpec required, + Map, Set>> target ) { + + for ( String each : precedentSynonyms ) { + AbstractOptionSpec spec = specFor( each ); + if ( spec == null ) + throw new UnconfiguredOptionException( precedentSynonyms ); + } + + Set> associated = target.get( precedentSynonyms ); + if ( associated == null ) { + associated = new HashSet>(); + target.put( precedentSynonyms, associated ); + } + + associated.add( required ); + } + + private AbstractOptionSpec specFor( char option ) { + return specFor( String.valueOf( option ) ); + } + + private AbstractOptionSpec specFor( String option ) { + return recognizedOptions.get( option ); + } + + private void reset() { + state = moreOptions( posixlyCorrect ); + } + + private static char[] extractShortOptionsFrom( String argument ) { + char[] options = new char[ argument.length() - 1 ]; + argument.getChars( 1, argument.length(), options, 0 ); + + return options; + } + + private void validateOptionCharacters( char[] options ) { + for ( char each : options ) { + String option = String.valueOf( each ); + + if ( !isRecognized( option ) ) + throw unrecognizedOption( option ); + + if ( specFor( option ).acceptsArguments() ) + return; + } + } + + private static KeyValuePair parseLongOptionWithArgument( String argument ) { + return KeyValuePair.valueOf( argument.substring( 2 ) ); + } + + private static KeyValuePair parseShortOptionWithArgument( String argument ) { + return KeyValuePair.valueOf( argument.substring( 1 ) ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java new file mode 100644 index 00000000000..7fdbcbf3618 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Abstraction of parser state; mostly serves to model how a parser behaves depending on whether end-of-options + * has been detected. + * + * @author Paul Holser + */ +abstract class OptionParserState { + static OptionParserState noMoreOptions() { + return new OptionParserState() { + @Override + protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + parser.handleNonOptionArgument( arguments.next(), arguments, detectedOptions ); + } + }; + } + + static OptionParserState moreOptions( final boolean posixlyCorrect ) { + return new OptionParserState() { + @Override + protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + String candidate = arguments.next(); + try { + if ( isOptionTerminator( candidate ) ) { + parser.noMoreOptions(); + return; + } else if ( isLongOptionToken( candidate ) ) { + parser.handleLongOptionToken( candidate, arguments, detectedOptions ); + return; + } else if ( isShortOptionToken( candidate ) ) { + parser.handleShortOptionToken( candidate, arguments, detectedOptions ); + return; + } + } catch ( UnrecognizedOptionException e ) { + if ( !parser.doesAllowsUnrecognizedOptions() ) + throw e; + } + + if ( posixlyCorrect ) + parser.noMoreOptions(); + + parser.handleNonOptionArgument( candidate, arguments, detectedOptions ); + } + }; + } + + protected abstract void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java new file mode 100644 index 00000000000..71061156fa0 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.*; + +import static java.util.Collections.*; + +import static jdk.internal.joptsimple.internal.Objects.*; + +/** + * Representation of a group of detected command line options, their arguments, and non-option arguments. + * + * @author Paul Holser + */ +public class OptionSet { + private final List> detectedSpecs; + private final Map> detectedOptions; + private final Map, List> optionsToArguments; + private final Map> recognizedSpecs; + private final Map> defaultValues; + + /* + * Package-private because clients don't create these. + */ + OptionSet( Map> recognizedSpecs ) { + detectedSpecs = new ArrayList>(); + detectedOptions = new HashMap>(); + optionsToArguments = new IdentityHashMap, List>(); + defaultValues = defaultValues( recognizedSpecs ); + this.recognizedSpecs = recognizedSpecs; + } + + /** + * Tells whether any options were detected. + * + * @return {@code true} if any options were detected + */ + public boolean hasOptions() { + return !detectedOptions.isEmpty(); + } + + /** + * Tells whether the given option was detected. + * + * @param option the option to search for + * @return {@code true} if the option was detected + * @see #has(OptionSpec) + */ + public boolean has( String option ) { + return detectedOptions.containsKey( option ); + } + + /** + * Tells whether the given option was detected. + * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + *

Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value} + * for an option does not cause this method to return {@code true} if the option was not detected on the command + * line.

+ * + * @param option the option to search for + * @return {@code true} if the option was detected + * @see #has(String) + */ + public boolean has( OptionSpec option ) { + return optionsToArguments.containsKey( option ); + } + + /** + * Tells whether there are any arguments associated with the given option. + * + * @param option the option to search for + * @return {@code true} if the option was detected and at least one argument was detected for the option + * @see #hasArgument(OptionSpec) + */ + public boolean hasArgument( String option ) { + AbstractOptionSpec spec = detectedOptions.get( option ); + return spec != null && hasArgument( spec ); + } + + /** + * Tells whether there are any arguments associated with the given option. + * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + *

Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for an option does not cause this method to return {@code true} if the option was not detected on the command + * line, or if the option can take an optional argument but did not have one on the command line.

+ * + * @param option the option to search for + * @return {@code true} if the option was detected and at least one argument was detected for the option + * @throws NullPointerException if {@code option} is {@code null} + * @see #hasArgument(String) + */ + public boolean hasArgument( OptionSpec option ) { + ensureNotNull( option ); + + List values = optionsToArguments.get( option ); + return values != null && !values.isEmpty(); + } + + /** + * Gives the argument associated with the given option. If the option was given an argument type, the argument + * will take on that type; otherwise, it will be a {@link String}. + * + *

Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for an option will cause this method to return that default value even if the option was not detected on the + * command line, or if the option can take an optional argument but did not have one on the command line.

+ * + * @param option the option to search for + * @return the argument of the given option; {@code null} if no argument is present, or that option was not + * detected + * @throws NullPointerException if {@code option} is {@code null} + * @throws OptionException if more than one argument was detected for the option + */ + public Object valueOf( String option ) { + ensureNotNull( option ); + + AbstractOptionSpec spec = detectedOptions.get( option ); + if ( spec == null ) { + List defaults = defaultValuesFor( option ); + return defaults.isEmpty() ? null : defaults.get( 0 ); + } + + return valueOf( spec ); + } + + /** + * Gives the argument associated with the given option. + * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + * @param represents the type of the arguments the given option accepts + * @param option the option to search for + * @return the argument of the given option; {@code null} if no argument is present, or that option was not + * detected + * @throws OptionException if more than one argument was detected for the option + * @throws NullPointerException if {@code option} is {@code null} + * @throws ClassCastException if the arguments of this option are not of the expected type + */ + public V valueOf( OptionSpec option ) { + ensureNotNull( option ); + + List values = valuesOf( option ); + switch ( values.size() ) { + case 0: + return null; + case 1: + return values.get( 0 ); + default: + throw new MultipleArgumentsForOptionException( option.options() ); + } + } + + /** + *

Gives any arguments associated with the given option. If the option was given an argument type, the + * arguments will take on that type; otherwise, they will be {@link String}s.

+ * + * @param option the option to search for + * @return the arguments associated with the option, as a list of objects of the type given to the arguments; an + * empty list if no such arguments are present, or if the option was not detected + * @throws NullPointerException if {@code option} is {@code null} + */ + public List valuesOf( String option ) { + ensureNotNull( option ); + + AbstractOptionSpec spec = detectedOptions.get( option ); + return spec == null ? defaultValuesFor( option ) : valuesOf( spec ); + } + + /** + *

Gives any arguments associated with the given option. If the option was given an argument type, the + * arguments will take on that type; otherwise, they will be {@link String}s.

+ * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + * @param represents the type of the arguments the given option accepts + * @param option the option to search for + * @return the arguments associated with the option; an empty list if no such arguments are present, or if the + * option was not detected + * @throws NullPointerException if {@code option} is {@code null} + * @throws OptionException if there is a problem converting the option's arguments to the desired type; for + * example, if the type does not implement a correct conversion constructor or method + */ + public List valuesOf( OptionSpec option ) { + ensureNotNull( option ); + + List values = optionsToArguments.get( option ); + if ( values == null || values.isEmpty() ) + return defaultValueFor( option ); + + AbstractOptionSpec spec = (AbstractOptionSpec) option; + List convertedValues = new ArrayList(); + for ( String each : values ) + convertedValues.add( spec.convert( each ) ); + + return unmodifiableList( convertedValues ); + } + + /** + * Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the + * options were found on the command line. + * + * @return the set of detected command line options + */ + public List> specs() { + List> specs = detectedSpecs; + specs.remove( detectedOptions.get( NonOptionArgumentSpec.NAME ) ); + + return unmodifiableList( specs ); + } + + /** + * Gives all declared options as a map of string to {@linkplain OptionSpec}. + * + * @return the declared options as a map + */ + public Map, List> asMap() { + Map, List> map = new HashMap, List>(); + for ( AbstractOptionSpec spec : recognizedSpecs.values() ) + if ( !spec.representsNonOptions() ) + map.put( spec, valuesOf( spec ) ); + return unmodifiableMap( map ); + } + + /** + * @return the detected non-option arguments + */ + public List nonOptionArguments() { + return unmodifiableList( valuesOf( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) ); + } + + void add( AbstractOptionSpec spec ) { + addWithArgument( spec, null ); + } + + void addWithArgument( AbstractOptionSpec spec, String argument ) { + detectedSpecs.add( spec ); + + for ( String each : spec.options() ) + detectedOptions.put( each, spec ); + + List optionArguments = optionsToArguments.get( spec ); + + if ( optionArguments == null ) { + optionArguments = new ArrayList(); + optionsToArguments.put( spec, optionArguments ); + } + + if ( argument != null ) + optionArguments.add( argument ); + } + + @Override + public boolean equals( Object that ) { + if ( this == that ) + return true; + + if ( that == null || !getClass().equals( that.getClass() ) ) + return false; + + OptionSet other = (OptionSet) that; + Map, List> thisOptionsToArguments = + new HashMap, List>( optionsToArguments ); + Map, List> otherOptionsToArguments = + new HashMap, List>( other.optionsToArguments ); + return detectedOptions.equals( other.detectedOptions ) + && thisOptionsToArguments.equals( otherOptionsToArguments ); + } + + @Override + public int hashCode() { + Map, List> thisOptionsToArguments = + new HashMap, List>( optionsToArguments ); + return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode(); + } + + @SuppressWarnings( "unchecked" ) + private List defaultValuesFor( String option ) { + if ( defaultValues.containsKey( option ) ) + return (List) defaultValues.get( option ); + + return emptyList(); + } + + private List defaultValueFor( OptionSpec option ) { + return defaultValuesFor( option.options().iterator().next() ); + } + + private static Map> defaultValues( Map> recognizedSpecs ) { + Map> defaults = new HashMap>(); + for ( Map.Entry> each : recognizedSpecs.entrySet() ) + defaults.put( each.getKey(), each.getValue().defaultValues() ); + return defaults; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java new file mode 100644 index 00000000000..666230b05d4 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; +import java.util.List; + +/** + * Describes options that an option parser recognizes. + * + *

Instances of this interface are returned by the "fluent interface" methods to allow retrieval of option arguments + * in a type-safe manner. Here's an example:

+ * + *

+ *     OptionParser parser = new OptionParser();
+ *     OptionSpec<Integer> count =
+ *         parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+ *     OptionSet options = parser.parse( "--count", "2" );
+ *     assert options.has( count );
+ *     int countValue = options.valueOf( count );
+ *     assert countValue == count.value( options );
+ *     List<Integer> countValues = options.valuesOf( count );
+ *     assert countValues.equals( count.values( options ) );
+ * 
+ * + * @param represents the type of the arguments this option accepts + * @author Paul Holser + */ +public interface OptionSpec { + /** + * Gives any arguments associated with the given option in the given set of detected options. + * + *

Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for this option will cause this method to return that default value even if this option was not detected on the + * command line, or if this option can take an optional argument but did not have one on the command line.

+ * + * @param detectedOptions the detected options to search in + * @return the arguments associated with this option; an empty list if no such arguments are present, or if this + * option was not detected + * @throws OptionException if there is a problem converting this option's arguments to the desired type; for + * example, if the type does not implement a correct conversion constructor or method + * @throws NullPointerException if {@code detectedOptions} is {@code null} + * @see OptionSet#valuesOf(OptionSpec) + */ + List values( OptionSet detectedOptions ); + + /** + * Gives the argument associated with the given option in the given set of detected options. + * + *

Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value} + * for this option will cause this method to return that default value even if this option was not detected on the + * command line, or if this option can take an optional argument but did not have one on the command line.

+ * + * @param detectedOptions the detected options to search in + * @return the argument of the this option; {@code null} if no argument is present, or that option was not detected + * @throws OptionException if more than one argument was detected for the option + * @throws NullPointerException if {@code detectedOptions} is {@code null} + * @throws ClassCastException if the arguments of this option are not of the expected type + * @see OptionSet#valueOf(OptionSpec) + */ + V value( OptionSet detectedOptions ); + + /** + * @return the string representations of this option + */ + Collection options(); + + /** + * Tells whether this option is designated as a "help" option. The presence of a "help" option on a command line + * means that missing "required" options will not cause parsing to fail. + * + * @return whether this option is designated as a "help" option + */ + boolean isForHelp(); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java new file mode 100644 index 00000000000..4f171091ec0 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Allows callers to specify whether a given option accepts arguments (required or optional). + * + *

Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as + * sentences in a "fluent interface" language. For example:

+ * + *

+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).withRequiredArg().ofType( Integer.class );
+ * 
+ * + *

If no methods are invoked on an instance of this class, then that instance's option will accept no argument.

+ * + *

Note that you should not use the fluent interface clauses in a way that would defeat the typing of option + * arguments:

+ * + *

+ *   OptionParser parser = new OptionParser();
+ *   ArgumentAcceptingOptionSpec<String> optionC =
+ *       parser.accepts( "c" ).withRequiredArg();
+ *   optionC.ofType( Integer.class );  // DON'T THROW AWAY THE TYPE!
+ *
+ *   String value = parser.parse( "-c", "2" ).valueOf( optionC );  // ClassCastException
+ * 
+ * + * @author Paul Holser + */ +public class OptionSpecBuilder extends NoArgumentOptionSpec { + private final OptionParser parser; + + OptionSpecBuilder( OptionParser parser, Collection options, String description ) { + super( options, description ); + + this.parser = parser; + attachToParser(); + } + + private void attachToParser() { + parser.recognize( this ); + } + + /** + * Informs an option parser that this builder's option requires an argument. + * + * @return a specification for the option + */ + public ArgumentAcceptingOptionSpec withRequiredArg() { + ArgumentAcceptingOptionSpec newSpec = + new RequiredArgumentOptionSpec( options(), description() ); + parser.recognize( newSpec ); + + return newSpec; + } + + /** + * Informs an option parser that this builder's option accepts an optional argument. + * + * @return a specification for the option + */ + public ArgumentAcceptingOptionSpec withOptionalArg() { + ArgumentAcceptingOptionSpec newSpec = + new OptionalArgumentOptionSpec( options(), description() ); + parser.recognize( newSpec ); + + return newSpec; + } + + /** + *

Informs an option parser that this builder's option is required if the given option is present on the command + * line.

+ * + *

For a given option, you should not mix this with {@link #requiredUnless(String, String...) + * requiredUnless} to avoid conflicts.

+ * + * @param dependent an option whose presence on a command line makes this builder's option required + * @param otherDependents other options whose presence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws OptionException if any of the dependent options haven't been configured in the parser yet + */ + public OptionSpecBuilder requiredIf( String dependent, String... otherDependents ) { + List dependents = validatedDependents( dependent, otherDependents ); + for ( String each : dependents ) { + parser.requiredIf( options(), each ); + } + + return this; + } + + /** + *

Informs an option parser that this builder's option is required if the given option is present on the command + * line.

+ * + *

For a given option, you should not mix this with {@link #requiredUnless(OptionSpec, OptionSpec[]) + * requiredUnless} to avoid conflicts.

+ * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + * @param dependent the option whose presence on a command line makes this builder's option required + * @param otherDependents other options whose presence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public OptionSpecBuilder requiredIf( OptionSpec dependent, OptionSpec... otherDependents ) { + parser.requiredIf( options(), dependent ); + for ( OptionSpec each : otherDependents ) + parser.requiredIf( options(), each ); + + return this; + } + + /** + *

Informs an option parser that this builder's option is required if the given option is absent on the command + * line.

+ * + *

For a given option, you should not mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) + * requiredIf} to avoid conflicts.

+ * + * @param dependent an option whose absence on a command line makes this builder's option required + * @param otherDependents other options whose absence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + * @throws OptionException if any of the dependent options haven't been configured in the parser yet + */ + public OptionSpecBuilder requiredUnless( String dependent, String... otherDependents ) { + List dependents = validatedDependents( dependent, otherDependents ); + for ( String each : dependents ) { + parser.requiredUnless( options(), each ); + } + return this; + } + + /** + *

Informs an option parser that this builder's option is required if the given option is absent on the command + * line.

+ * + *

For a given option, you should not mix this with {@link #requiredIf(OptionSpec, OptionSpec[]) + * requiredIf} to avoid conflicts.

+ * + *

This method recognizes only instances of options returned from the fluent interface methods.

+ * + * @param dependent the option whose absence on a command line makes this builder's option required + * @param otherDependents other options whose absence on a command line makes this builder's option required + * @return self, so that the caller can add clauses to the fluent interface sentence + */ + public OptionSpecBuilder requiredUnless( OptionSpec dependent, OptionSpec... otherDependents ) { + parser.requiredUnless( options(), dependent ); + for ( OptionSpec each : otherDependents ) + parser.requiredUnless( options(), each ); + + return this; + } + + private List validatedDependents( String dependent, String... otherDependents ) { + List dependents = new ArrayList(); + dependents.add( dependent ); + Collections.addAll( dependents, otherDependents ); + + for ( String each : dependents ) { + if ( !parser.isRecognized( each ) ) + throw new UnconfiguredOptionException( each ); + } + + return dependents; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java new file mode 100644 index 00000000000..d878daba6eb --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.NoSuchElementException; + +import static jdk.internal.joptsimple.ParserRules.*; + +/** + * Tokenizes a short option specification string. + * + * @author Paul Holser + */ +class OptionSpecTokenizer { + private static final char POSIXLY_CORRECT_MARKER = '+'; + private static final char HELP_MARKER = '*'; + + private String specification; + private int index; + + OptionSpecTokenizer( String specification ) { + if ( specification == null ) + throw new NullPointerException( "null option specification" ); + + this.specification = specification; + } + + boolean hasMore() { + return index < specification.length(); + } + + AbstractOptionSpec next() { + if ( !hasMore() ) + throw new NoSuchElementException(); + + + String optionCandidate = String.valueOf( specification.charAt( index ) ); + index++; + + AbstractOptionSpec spec; + if ( RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) { + spec = handleReservedForExtensionsToken(); + + if ( spec != null ) + return spec; + } + + ensureLegalOption( optionCandidate ); + + if ( hasMore() ) { + boolean forHelp = false; + if ( specification.charAt( index ) == HELP_MARKER ) { + forHelp = true; + ++index; + } + spec = hasMore() && specification.charAt( index ) == ':' + ? handleArgumentAcceptingOption( optionCandidate ) + : new NoArgumentOptionSpec( optionCandidate ); + if ( forHelp ) + spec.forHelp(); + } else + spec = new NoArgumentOptionSpec( optionCandidate ); + + return spec; + } + + void configure( OptionParser parser ) { + adjustForPosixlyCorrect( parser ); + + while ( hasMore() ) + parser.recognize( next() ); + } + + private void adjustForPosixlyCorrect( OptionParser parser ) { + if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) { + parser.posixlyCorrect( true ); + specification = specification.substring( 1 ); + } + } + + private AbstractOptionSpec handleReservedForExtensionsToken() { + if ( !hasMore() ) + return new NoArgumentOptionSpec( RESERVED_FOR_EXTENSIONS ); + + if ( specification.charAt( index ) == ';' ) { + ++index; + return new AlternativeLongOptionSpec(); + } + + return null; + } + + private AbstractOptionSpec handleArgumentAcceptingOption( String candidate ) { + index++; + + if ( hasMore() && specification.charAt( index ) == ':' ) { + index++; + return new OptionalArgumentOptionSpec( candidate ); + } + + return new RequiredArgumentOptionSpec( candidate ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java new file mode 100644 index 00000000000..894e00e3a29 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Specification of an option that accepts an optional argument. + * + * @param represents the type of the arguments this option accepts + * @author Paul Holser + */ +class OptionalArgumentOptionSpec extends ArgumentAcceptingOptionSpec { + OptionalArgumentOptionSpec( String option ) { + super( option, false ); + } + + OptionalArgumentOptionSpec( Collection options, String description ) { + super( options, false, description ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( arguments.hasMore() ) { + String nextArgument = arguments.peek(); + + if ( !parser.looksLikeAnOption( nextArgument ) ) + handleOptionArgument( parser, detectedOptions, arguments ); + else if ( isArgumentOfNumberType() && canConvertArgument( nextArgument ) ) + addArguments( detectedOptions, arguments.next() ); + else + detectedOptions.add( this ); + } + else + detectedOptions.add( this ); + } + + private void handleOptionArgument( OptionParser parser, OptionSet detectedOptions, ArgumentList arguments ) { + if ( parser.posixlyCorrect() ) { + detectedOptions.add( this ); + parser.noMoreOptions(); + } + else + addArguments( detectedOptions, arguments.next() ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java new file mode 100644 index 00000000000..e981d878adc --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +import static java.lang.Character.*; + +/** + * Can tell whether or not options are well-formed. + * + * @author Paul Holser + */ +final class ParserRules { + static final char HYPHEN_CHAR = '-'; + static final String HYPHEN = String.valueOf( HYPHEN_CHAR ); + static final String DOUBLE_HYPHEN = "--"; + static final String OPTION_TERMINATOR = DOUBLE_HYPHEN; + static final String RESERVED_FOR_EXTENSIONS = "W"; + + private ParserRules() { + throw new UnsupportedOperationException(); + } + + static boolean isShortOptionToken( String argument ) { + return argument.startsWith( HYPHEN ) + && !HYPHEN.equals( argument ) + && !isLongOptionToken( argument ); + } + + static boolean isLongOptionToken( String argument ) { + return argument.startsWith( DOUBLE_HYPHEN ) && !isOptionTerminator( argument ); + } + + static boolean isOptionTerminator( String argument ) { + return OPTION_TERMINATOR.equals( argument ); + } + + static void ensureLegalOption( String option ) { + if ( option.startsWith( HYPHEN ) ) + throw new IllegalOptionSpecificationException( String.valueOf( option ) ); + + for ( int i = 0; i < option.length(); ++i ) + ensureLegalOptionCharacter( option.charAt( i ) ); + } + + static void ensureLegalOptions( Collection options ) { + for ( String each : options ) + ensureLegalOption( each ); + } + + private static void ensureLegalOptionCharacter( char option ) { + if ( !( isLetterOrDigit( option ) || isAllowedPunctuation( option ) ) ) + throw new IllegalOptionSpecificationException( String.valueOf( option ) ); + } + + private static boolean isAllowedPunctuation( char option ) { + String allowedPunctuation = "?." + HYPHEN_CHAR; + return allowedPunctuation.indexOf( option ) != -1; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README new file mode 100644 index 00000000000..ad0d0f64db5 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README @@ -0,0 +1,3 @@ +JOpt Simple, Version 4.6 +https://pholser.github.io/jopt-simple/ + diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java new file mode 100644 index 00000000000..9972f8d1cff --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +/** + * Specification of an option that accepts a required argument. + * + * @param represents the type of the arguments this option accepts + * @author Paul Holser + */ +class RequiredArgumentOptionSpec extends ArgumentAcceptingOptionSpec { + RequiredArgumentOptionSpec( String option ) { + super( option, true ); + } + + RequiredArgumentOptionSpec( Collection options, String description ) { + super( options, true, description ); + } + + @Override + protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) { + if ( !arguments.hasMore() ) + throw new OptionMissingRequiredArgumentException( options() ); + + addArguments( detectedOptions, arguments.next() ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java new file mode 100644 index 00000000000..c5775fb3458 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +/** + * Thrown when the option parser detects an unacceptable number of {@code linkplain NonOptionArgumentSpec + * non-option arguments}. + * + * @author Paul Holser + */ +class UnacceptableNumberOfNonOptionsException extends OptionException { + private static final long serialVersionUID = -1L; + private final int minimum; + private final int maximum; + private final int actual; + + UnacceptableNumberOfNonOptionsException( int minimum, int maximum, int actual ) { + super( singletonList( NonOptionArgumentSpec.NAME ) ); + + this.minimum = minimum; + this.maximum = maximum; + this.actual = actual; + } + + @Override + public String getMessage() { + return String.format( "actual = %d, minimum = %d, maximum = %d", actual, minimum, maximum ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java new file mode 100644 index 00000000000..0783b7a684b --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import java.util.Collection; + +import static java.util.Collections.*; + +/** + * Thrown when an option parser refers to an option that is not in fact configured already on the parser. + * + * @author Paul Holser + */ +class UnconfiguredOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + UnconfiguredOptionException( String option ) { + this( singletonList( option ) ); + } + + UnconfiguredOptionException( Collection options ) { + super( options ); + } + + @Override + public String getMessage() { + return "Option " + multipleOptionMessage() + " has not been configured on this parser"; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java new file mode 100644 index 00000000000..b9ec16abb0a --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +import static java.util.Collections.*; + +/** + * Thrown when the option parser encounters an unrecognized option. + * + * @author Paul Holser + */ +class UnrecognizedOptionException extends OptionException { + private static final long serialVersionUID = -1L; + + UnrecognizedOptionException( String option ) { + super( singletonList( option ) ); + } + + @Override + public String getMessage() { + return singleOptionMessage() + " is not a recognized option"; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java new file mode 100644 index 00000000000..3ea4938d21c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +/** + * Thrown by {@link ValueConverter}s when problems occur in converting string values to other Java types. + * + * @author Paul Holser + */ +public class ValueConversionException extends RuntimeException { + private static final long serialVersionUID = -1L; + + /** + * Creates a new exception with the specified detail message. + * + * @param message the detail message + */ + public ValueConversionException( String message ) { + this( message, null ); + } + + /** + * Creates a new exception with the specified detail message and cause. + * + * @param message the detail message + * @param cause the original exception + */ + public ValueConversionException( String message, Throwable cause ) { + super( message, cause ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java new file mode 100644 index 00000000000..db08b2550d1 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple; + +/** + * Instances of this interface are used to convert arguments of options into specific Java types. + * + * @param constraint on the type of values being converted to + * @author Paul Holser + */ +public interface ValueConverter { + /** + * Converts the given string value into a Java type. + * + * @param value the string to convert + * @return the converted value + * @throws ValueConversionException if a problem occurs while converting the value + */ + V convert( String value ); + + /** + * Gives the class of the type of values this converter converts to. + * + * @return the target class for conversion + */ + Class valueType(); + + /** + * Gives a string that describes the pattern of the values this converter expects, if any. For example, a date + * converter can respond with a {@link java.text.SimpleDateFormat date format string}. + * + * @return a value pattern, or {@code null} if there's nothing interesting here + */ + String valuePattern(); +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java new file mode 100644 index 00000000000..2646b7372b7 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.util.Map; +import java.util.TreeMap; + +/** + *

A map whose keys are strings; when a key/value pair is added to the map, the longest unique abbreviations of that + * key are added as well, and associated with the value. Thus:

+ * + *
+ *   
+ *   abbreviations.put( "good", "bye" );
+ *   
+ * 
+ * + *

would make it such that you could retrieve the value {@code "bye"} from the map using the keys {@code "good"}, + * {@code "goo"}, {@code "go"}, and {@code "g"}. A subsequent invocation of:

+ *
+ *   
+ *   abbreviations.put( "go", "fish" );
+ *   
+ * 
+ * + *

would make it such that you could retrieve the value {@code "bye"} using the keys {@code "good"} and + * {@code "goo"}, and the value {@code "fish"} using the key {@code "go"}. The key {@code "g"} would yield + * {@code null}, since it would no longer be a unique abbreviation.

+ * + *

The data structure is much like a "trie".

+ * + * @param a constraint on the types of the values in the map + * @author Paul Holser + * @see Perl's Text::Abbrev module + */ +public class AbbreviationMap { + private String key; + private V value; + private final Map> children = new TreeMap>(); + private int keysBeyond; + + /** + *

Tells whether the given key is in the map, or whether the given key is a unique + * abbreviation of a key that is in the map.

+ * + * @param aKey key to look up + * @return {@code true} if {@code key} is present in the map + * @throws NullPointerException if {@code key} is {@code null} + */ + public boolean contains( String aKey ) { + return get( aKey ) != null; + } + + /** + *

Answers the value associated with the given key. The key can be a unique + * abbreviation of a key that is in the map.

+ * + * @param aKey key to look up + * @return the value associated with {@code aKey}; or {@code null} if there is no + * such value or {@code aKey} is not a unique abbreviation of a key in the map + * @throws NullPointerException if {@code aKey} is {@code null} + */ + public V get( String aKey ) { + char[] chars = charsOf( aKey ); + + AbbreviationMap child = this; + for ( char each : chars ) { + child = child.children.get( each ); + if ( child == null ) + return null; + } + + return child.value; + } + + /** + *

Associates a given value with a given key. If there was a previous + * association, the old value is replaced with the new one.

+ * + * @param aKey key to create in the map + * @param newValue value to associate with the key + * @throws NullPointerException if {@code aKey} or {@code newValue} is {@code null} + * @throws IllegalArgumentException if {@code aKey} is a zero-length string + */ + public void put( String aKey, V newValue ) { + if ( newValue == null ) + throw new NullPointerException(); + if ( aKey.length() == 0 ) + throw new IllegalArgumentException(); + + char[] chars = charsOf( aKey ); + add( chars, newValue, 0, chars.length ); + } + + /** + *

Associates a given value with a given set of keys. If there was a previous + * association, the old value is replaced with the new one.

+ * + * @param keys keys to create in the map + * @param newValue value to associate with the key + * @throws NullPointerException if {@code keys} or {@code newValue} is {@code null} + * @throws IllegalArgumentException if any of {@code keys} is a zero-length string + */ + public void putAll( Iterable keys, V newValue ) { + for ( String each : keys ) + put( each, newValue ); + } + + private boolean add( char[] chars, V newValue, int offset, int length ) { + if ( offset == length ) { + value = newValue; + boolean wasAlreadyAKey = key != null; + key = new String( chars ); + return !wasAlreadyAKey; + } + + char nextChar = chars[ offset ]; + AbbreviationMap child = children.get( nextChar ); + if ( child == null ) { + child = new AbbreviationMap(); + children.put( nextChar, child ); + } + + boolean newKeyAdded = child.add( chars, newValue, offset + 1, length ); + + if ( newKeyAdded ) + ++keysBeyond; + + if ( key == null ) + value = keysBeyond > 1 ? null : newValue; + + return newKeyAdded; + } + + /** + *

If the map contains the given key, dissociates the key from its value.

+ * + * @param aKey key to remove + * @throws NullPointerException if {@code aKey} is {@code null} + * @throws IllegalArgumentException if {@code aKey} is a zero-length string + */ + public void remove( String aKey ) { + if ( aKey.length() == 0 ) + throw new IllegalArgumentException(); + + char[] keyChars = charsOf( aKey ); + remove( keyChars, 0, keyChars.length ); + } + + private boolean remove( char[] aKey, int offset, int length ) { + if ( offset == length ) + return removeAtEndOfKey(); + + char nextChar = aKey[ offset ]; + AbbreviationMap child = children.get( nextChar ); + if ( child == null || !child.remove( aKey, offset + 1, length ) ) + return false; + + --keysBeyond; + if ( child.keysBeyond == 0 ) + children.remove( nextChar ); + if ( keysBeyond == 1 && key == null ) + setValueToThatOfOnlyChild(); + + return true; + } + + private void setValueToThatOfOnlyChild() { + Map.Entry> entry = children.entrySet().iterator().next(); + AbbreviationMap onlyChild = entry.getValue(); + value = onlyChild.value; + } + + private boolean removeAtEndOfKey() { + if ( key == null ) + return false; + + key = null; + if ( keysBeyond == 1 ) + setValueToThatOfOnlyChild(); + else + value = null; + + return true; + } + + /** + * Gives a Java map representation of this abbreviation map. + * + * @return a Java map corresponding to this abbreviation map + */ + public Map toJavaUtilMap() { + Map mappings = new TreeMap(); + addToMappings( mappings ); + return mappings; + } + + private void addToMappings( Map mappings ) { + if ( key != null ) + mappings.put( key, value ); + + for ( AbbreviationMap each : children.values() ) + each.addToMappings( mappings ); + } + + private static char[] charsOf( String aKey ) { + char[] chars = new char[ aKey.length() ]; + aKey.getChars( 0, aKey.length(), chars, 0 ); + return chars; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java new file mode 100644 index 00000000000..f75025ccd19 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Paul Holser + */ +public final class Classes { + private static final Map, Class> WRAPPERS = new HashMap, Class>( 13 ); + + static { + WRAPPERS.put( boolean.class, Boolean.class ); + WRAPPERS.put( byte.class, Byte.class ); + WRAPPERS.put( char.class, Character.class ); + WRAPPERS.put( double.class, Double.class ); + WRAPPERS.put( float.class, Float.class ); + WRAPPERS.put( int.class, Integer.class ); + WRAPPERS.put( long.class, Long.class ); + WRAPPERS.put( short.class, Short.class ); + WRAPPERS.put( void.class, Void.class ); + } + + private Classes() { + throw new UnsupportedOperationException(); + } + + /** + * Gives the "short version" of the given class name. Somewhat naive to inner classes. + * + * @param className class name to chew on + * @return the short name of the class + */ + public static String shortNameOf( String className ) { + return className.substring( className.lastIndexOf( '.' ) + 1 ); + } + + /** + * Gives the primitive wrapper class for the given class. If the given class is not + * {@linkplain Class#isPrimitive() primitive}, returns the class itself. + * + * @param generic class type + * @param clazz the class to check + * @return primitive wrapper type if {@code clazz} is primitive, otherwise {@code clazz} + */ + @SuppressWarnings( "unchecked" ) + public static Class wrapperOf( Class clazz ) { + return clazz.isPrimitive() ? (Class) WRAPPERS.get( clazz ) : clazz; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java new file mode 100644 index 00000000000..a3859f7d1cf --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java @@ -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. 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 is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.text.BreakIterator; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static java.text.BreakIterator.*; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * @author Paul Holser + */ +class Columns { + private static final int INDENT_WIDTH = 2; + + private final int optionWidth; + private final int descriptionWidth; + + Columns( int optionWidth, int descriptionWidth ) { + this.optionWidth = optionWidth; + this.descriptionWidth = descriptionWidth; + } + + List fit( Row row ) { + List options = piecesOf( row.option, optionWidth ); + List descriptions = piecesOf( row.description, descriptionWidth ); + + List rows = new ArrayList(); + for ( int i = 0; i < Math.max( options.size(), descriptions.size() ); ++i ) + rows.add( new Row( itemOrEmpty( options, i ), itemOrEmpty( descriptions, i ) ) ); + + return rows; + } + + private static String itemOrEmpty( List items, int index ) { + return index >= items.size() ? "" : items.get( index ); + } + + private List piecesOf( String raw, int width ) { + List pieces = new ArrayList(); + + for ( String each : raw.trim().split( LINE_SEPARATOR ) ) + pieces.addAll( piecesOfEmbeddedLine( each, width ) ); + + return pieces; + } + + private List piecesOfEmbeddedLine( String line, int width ) { + List pieces = new ArrayList(); + + BreakIterator words = BreakIterator.getLineInstance( Locale.US ); + words.setText( line ); + + StringBuilder nextPiece = new StringBuilder(); + + int start = words.first(); + for ( int end = words.next(); end != DONE; start = end, end = words.next() ) + nextPiece = processNextWord( line, nextPiece, start, end, width, pieces ); + + if ( nextPiece.length() > 0 ) + pieces.add( nextPiece.toString() ); + + return pieces; + } + + private StringBuilder processNextWord( String source, StringBuilder nextPiece, int start, int end, int width, + List pieces ) { + StringBuilder augmented = nextPiece; + + String word = source.substring( start, end ); + if ( augmented.length() + word.length() > width ) { + pieces.add( augmented.toString().replaceAll( "\\s+$", "" ) ); + augmented = new StringBuilder( repeat( ' ', INDENT_WIDTH ) ).append( word ); + } + else + augmented.append( word ); + + return augmented; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java new file mode 100644 index 00000000000..720c6e4b8c2 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.lang.reflect.Constructor; + +import jdk.internal.joptsimple.ValueConverter; + +import static jdk.internal.joptsimple.internal.Reflection.*; + +/** + * @param constraint on the type of values being converted to + * @author Paul Holser + */ +class ConstructorInvokingValueConverter implements ValueConverter { + private final Constructor ctor; + + ConstructorInvokingValueConverter( Constructor ctor ) { + this.ctor = ctor; + } + + public V convert( String value ) { + return instantiate( ctor, value ); + } + + public Class valueType() { + return ctor.getDeclaringClass(); + } + + public String valuePattern() { + return null; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java new file mode 100644 index 00000000000..599728bb67b --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.lang.reflect.Method; + +import jdk.internal.joptsimple.ValueConverter; + +import static jdk.internal.joptsimple.internal.Reflection.*; + +/** + * @param constraint on the type of values being converted to + * @author Paul Holser + */ +class MethodInvokingValueConverter implements ValueConverter { + private final Method method; + private final Class clazz; + + MethodInvokingValueConverter( Method method, Class clazz ) { + this.method = method; + this.clazz = clazz; + } + + public V convert( String value ) { + return clazz.cast( invoke( method, value ) ); + } + + public Class valueType() { + return clazz; + } + + public String valuePattern() { + return null; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java new file mode 100644 index 00000000000..2c2b696cd0a --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +/** + * @author Paul Holser + */ +public final class Objects { + private Objects() { + throw new UnsupportedOperationException(); + } + + /** + * Rejects {@code null} references. + * + * @param target reference to check + * @throws NullPointerException if {@code target} is {@code null} + */ + public static void ensureNotNull( Object target ) { + if ( target == null ) + throw new NullPointerException(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java new file mode 100644 index 00000000000..240ad24394c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static java.lang.reflect.Modifier.*; + +import jdk.internal.joptsimple.ValueConverter; + +import static jdk.internal.joptsimple.internal.Classes.*; + +/** + * Helper methods for reflection. + * + * @author Paul Holser + */ +public final class Reflection { + private Reflection() { + throw new UnsupportedOperationException(); + } + + /** + * Finds an appropriate value converter for the given class. + * + * @param a constraint on the class object to introspect + * @param clazz class to introspect on + * @return a converter method or constructor + */ + public static ValueConverter findConverter( Class clazz ) { + Class maybeWrapper = wrapperOf( clazz ); + + ValueConverter valueOf = valueOfConverter( maybeWrapper ); + if ( valueOf != null ) + return valueOf; + + ValueConverter constructor = constructorConverter( maybeWrapper ); + if ( constructor != null ) + return constructor; + + throw new IllegalArgumentException( clazz + " is not a value type" ); + } + + private static ValueConverter valueOfConverter( Class clazz ) { + try { + Method valueOf = clazz.getDeclaredMethod( "valueOf", String.class ); + if ( meetsConverterRequirements( valueOf, clazz ) ) + return new MethodInvokingValueConverter( valueOf, clazz ); + + return null; + } + catch ( NoSuchMethodException ignored ) { + return null; + } + } + + private static ValueConverter constructorConverter( Class clazz ) { + try { + return new ConstructorInvokingValueConverter( clazz.getConstructor( String.class ) ); + } + catch ( NoSuchMethodException ignored ) { + return null; + } + } + + /** + * Invokes the given constructor with the given arguments. + * + * @param constraint on the type of the objects yielded by the constructor + * @param constructor constructor to invoke + * @param args arguments to hand to the constructor + * @return the result of invoking the constructor + * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions + */ + public static T instantiate( Constructor constructor, Object... args ) { + try { + return constructor.newInstance( args ); + } + catch ( Exception ex ) { + throw reflectionException( ex ); + } + } + + /** + * Invokes the given static method with the given arguments. + * + * @param method method to invoke + * @param args arguments to hand to the method + * @return the result of invoking the method + * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions + */ + public static Object invoke( Method method, Object... args ) { + try { + return method.invoke( null, args ); + } + catch ( Exception ex ) { + throw reflectionException( ex ); + } + } + + @SuppressWarnings( "unchecked" ) + public static V convertWith( ValueConverter converter, String raw ) { + return converter == null ? (V) raw : converter.convert( raw ); + } + + private static boolean meetsConverterRequirements( Method method, Class expectedReturnType ) { + int modifiers = method.getModifiers(); + return isPublic( modifiers ) && isStatic( modifiers ) && expectedReturnType.equals( method.getReturnType() ); + } + + private static RuntimeException reflectionException( Exception ex ) { + if ( ex instanceof IllegalArgumentException ) + return new ReflectionException( ex ); + if ( ex instanceof InvocationTargetException ) + return new ReflectionException( ex.getCause() ); + if ( ex instanceof RuntimeException ) + return (RuntimeException) ex; + + return new ReflectionException( ex ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java new file mode 100644 index 00000000000..3e98d8f244c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +/** + * This unchecked exception wraps reflection-oriented exceptions. + * + * @author Paul Holser + */ +public class ReflectionException extends RuntimeException { + private static final long serialVersionUID = -2L; + + ReflectionException( Throwable cause ) { + super( cause ); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java new file mode 100644 index 00000000000..0ce8bcf0cc9 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +/** + * @author Paul Holser + */ +class Row { + final String option; + final String description; + + Row( String option, String description ) { + this.option = option; + this.description = description; + } + + @Override + public boolean equals( Object that ) { + if ( that == this ) + return true; + if ( that == null || !getClass().equals( that.getClass() ) ) + return false; + + Row other = (Row) that; + return option.equals( other.option ) && description.equals( other.description ); + } + + @Override + public int hashCode() { + return option.hashCode() ^ description.hashCode(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java new file mode 100644 index 00000000000..0d0e4c2d0e0 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java @@ -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. 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 is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.lang.Math.*; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + * @author Paul Holser + */ +public class Rows { + private final int overallWidth; + private final int columnSeparatorWidth; + private final Set rows = new LinkedHashSet(); + private int widthOfWidestOption; + private int widthOfWidestDescription; + + public Rows( int overallWidth, int columnSeparatorWidth ) { + this.overallWidth = overallWidth; + this.columnSeparatorWidth = columnSeparatorWidth; + } + + public void add( String option, String description ) { + add( new Row( option, description ) ); + } + + private void add( Row row ) { + rows.add( row ); + widthOfWidestOption = max( widthOfWidestOption, row.option.length() ); + widthOfWidestDescription = max( widthOfWidestDescription, row.description.length() ); + } + + private void reset() { + rows.clear(); + widthOfWidestOption = 0; + widthOfWidestDescription = 0; + } + + public void fitToWidth() { + Columns columns = new Columns( optionWidth(), descriptionWidth() ); + + Set fitted = new LinkedHashSet(); + for ( Row each : rows ) + fitted.addAll( columns.fit( each ) ); + + reset(); + + for ( Row each : fitted ) + add( each ); + } + + public String render() { + StringBuilder buffer = new StringBuilder(); + + for ( Row each : rows ) { + pad( buffer, each.option, optionWidth() ).append( repeat( ' ', columnSeparatorWidth ) ); + pad( buffer, each.description, descriptionWidth() ).append( LINE_SEPARATOR ); + } + + return buffer.toString(); + } + + private int optionWidth() { + return min( ( overallWidth - columnSeparatorWidth ) / 2, widthOfWidestOption ); + } + + private int descriptionWidth() { + return min( ( overallWidth - columnSeparatorWidth ) / 2, widthOfWidestDescription ); + } + + private StringBuilder pad( StringBuilder buffer, String s, int length ) { + buffer.append( s ).append( repeat( ' ', length - s.length() ) ); + return buffer; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java new file mode 100644 index 00000000000..038b3f0ce6b --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.internal; + +import java.util.Iterator; +import java.util.List; + +import static java.lang.System.*; +import static java.util.Arrays.*; + +/** + * @author Paul Holser + */ +public final class Strings { + public static final String EMPTY = ""; + public static final String SINGLE_QUOTE = "'"; + public static final String LINE_SEPARATOR = getProperty( "line.separator" ); + + private Strings() { + throw new UnsupportedOperationException(); + } + + /** + * Gives a string consisting of the given character repeated the given number of times. + * + * @param ch the character to repeat + * @param count how many times to repeat the character + * @return the resultant string + */ + public static String repeat( char ch, int count ) { + StringBuilder buffer = new StringBuilder(); + + for ( int i = 0; i < count; ++i ) + buffer.append( ch ); + + return buffer.toString(); + } + + /** + * Tells whether the given string is either {@code} or consists solely of whitespace characters. + * + * @param target string to check + * @return {@code true} if the target string is null or empty + */ + public static boolean isNullOrEmpty( String target ) { + return target == null || EMPTY.equals( target ); + } + + + /** + * Gives a string consisting of a given string prepended and appended with surrounding characters. + * + * @param target a string + * @param begin character to prepend + * @param end character to append + * @return the surrounded string + */ + public static String surround( String target, char begin, char end ) { + return begin + target + end; + } + + /** + * Gives a string consisting of the elements of a given array of strings, each separated by a given separator + * string. + * + * @param pieces the strings to join + * @param separator the separator + * @return the joined string + */ + public static String join( String[] pieces, String separator ) { + return join( asList( pieces ), separator ); + } + + /** + * Gives a string consisting of the string representations of the elements of a given array of objects, + * each separated by a given separator string. + * + * @param pieces the elements whose string representations are to be joined + * @param separator the separator + * @return the joined string + */ + public static String join( List pieces, String separator ) { + StringBuilder buffer = new StringBuilder(); + + for ( Iterator iter = pieces.iterator(); iter.hasNext(); ) { + buffer.append( iter.next() ); + + if ( iter.hasNext() ) + buffer.append( separator ); + } + + return buffer.toString(); + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java new file mode 100644 index 00000000000..f90585300d1 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java @@ -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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.util; + +import java.text.DateFormat; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +import jdk.internal.joptsimple.ValueConversionException; +import jdk.internal.joptsimple.ValueConverter; + +/** + * Converts values to {@link Date}s using a {@link DateFormat} object. + * + * @author Paul Holser + */ +public class DateConverter implements ValueConverter { + private final DateFormat formatter; + + /** + * Creates a converter that uses the given date formatter/parser. + * + * @param formatter the formatter/parser to use + * @throws NullPointerException if {@code formatter} is {@code null} + */ + public DateConverter( DateFormat formatter ) { + if ( formatter == null ) + throw new NullPointerException( "illegal null formatter" ); + + this.formatter = formatter; + } + + /** + * Creates a converter that uses a {@link SimpleDateFormat} with the given date/time pattern. The date formatter + * created is not {@link SimpleDateFormat#setLenient(boolean) lenient}. + * + * @param pattern expected date/time pattern + * @return the new converter + * @throws NullPointerException if {@code pattern} is {@code null} + * @throws IllegalArgumentException if {@code pattern} is invalid + */ + public static DateConverter datePattern( String pattern ) { + SimpleDateFormat formatter = new SimpleDateFormat( pattern ); + formatter.setLenient( false ); + + return new DateConverter( formatter ); + } + + public Date convert( String value ) { + ParsePosition position = new ParsePosition( 0 ); + + Date date = formatter.parse( value, position ); + if ( position.getIndex() != value.length() ) + throw new ValueConversionException( message( value ) ); + + return date; + } + + public Class valueType() { + return Date.class; + } + + public String valuePattern() { + return formatter instanceof SimpleDateFormat + ? ( (SimpleDateFormat) formatter ).toPattern() + : ""; + } + + private String message( String value ) { + String message = "Value [" + value + "] does not match date/time pattern"; + if ( formatter instanceof SimpleDateFormat ) + message += " [" + ( (SimpleDateFormat) formatter ).toPattern() + ']'; + + return message; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java new file mode 100644 index 00000000000..c6c7366509c --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.util; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import jdk.internal.joptsimple.ValueConversionException; +import jdk.internal.joptsimple.ValueConverter; + +/** + * Converts values to {@link java.net.InetAddress} using {@link InetAddress#getByName(String) getByName}. + * + * @author Raymund F\u00FCl\u00F6p + */ +public class InetAddressConverter implements ValueConverter { + public InetAddress convert( String value ) { + try { + return InetAddress.getByName( value ); + } catch ( UnknownHostException e ) { + throw new ValueConversionException( "Cannot convert value [" + value + " into an InetAddress", e ); + } + } + + public Class valueType() { + return InetAddress.class; + } + + public String valuePattern() { + return null; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java new file mode 100644 index 00000000000..430e0c89b75 --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009, 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.util; + +import static jdk.internal.joptsimple.internal.Strings.*; + +/** + *

A simple string key/string value pair.

+ * + *

This is useful as an argument type for options whose values take on the form key=value, such as JVM + * command line system properties.

+ * + * @author Paul Holser + */ +public final class KeyValuePair { + public final String key; + public final String value; + + private KeyValuePair( String key, String value ) { + this.key = key; + this.value = value; + } + + /** + * Parses a string assumed to be of the form key=value into its parts. + * + * @param asString key-value string + * @return a key-value pair + * @throws NullPointerException if {@code stringRepresentation} is {@code null} + */ + public static KeyValuePair valueOf( String asString ) { + int equalsIndex = asString.indexOf( '=' ); + if ( equalsIndex == -1 ) + return new KeyValuePair( asString, EMPTY ); + + String aKey = asString.substring( 0, equalsIndex ); + String aValue = equalsIndex == asString.length() - 1 ? EMPTY : asString.substring( equalsIndex + 1 ); + + return new KeyValuePair( aKey, aValue ); + } + + @Override + public boolean equals( Object that ) { + if ( !( that instanceof KeyValuePair ) ) + return false; + + KeyValuePair other = (KeyValuePair) that; + return key.equals( other.key ) && value.equals( other.value ); + } + + @Override + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + @Override + public String toString() { + return key + '=' + value; + } +} diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java new file mode 100644 index 00000000000..b6ceee5557e --- /dev/null +++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java @@ -0,0 +1,113 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * The MIT License + * + * Copyright (c) 2004-2014 Paul R. Holser, Jr. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package jdk.internal.joptsimple.util; + +import java.util.regex.Pattern; + +import static java.util.regex.Pattern.*; + +import jdk.internal.joptsimple.ValueConversionException; +import jdk.internal.joptsimple.ValueConverter; + +/** + * Ensures that values entirely match a regular expression. + * + * @author Paul Holser + */ +public class RegexMatcher implements ValueConverter { + private final Pattern pattern; + + /** + * Creates a matcher that uses the given regular expression, modified by the given flags. + * + * @param pattern the regular expression pattern + * @param flags modifying regex flags + * @throws IllegalArgumentException if bit values other than those corresponding to the defined match flags are + * set in {@code flags} + * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid + */ + public RegexMatcher( String pattern, int flags ) { + this.pattern = compile( pattern, flags ); + } + + /** + * Gives a matcher that uses the given regular expression. + * + * @param pattern the regular expression pattern + * @return the new converter + * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid + */ + public static ValueConverter regex( String pattern ) { + return new RegexMatcher( pattern, 0 ); + } + + public String convert( String value ) { + if ( !pattern.matcher( value ).matches() ) { + throw new ValueConversionException( + "Value [" + value + "] did not match regex [" + pattern.pattern() + ']' ); + } + + return value; + } + + public Class valueType() { + return String.class; + } + + public String valuePattern() { + return pattern.pattern(); + } +} diff --git a/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java b/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java index 02a82d941f9..f8083997c09 100644 --- a/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java +++ b/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java @@ -65,8 +65,11 @@ import javax.swing.border.EmptyBorder; * * @see java.security.Policy * @since 1.2 + * @deprecated The policytool tool has been deprecated and + * is planned to be removed in a future release. */ +@Deprecated public class PolicyTool { // for i18n @@ -738,6 +741,8 @@ public class PolicyTool { * run the PolicyTool */ public static void main(String args[]) { + System.out.println("Note: The policytool tool has been deprecated and" + + " is planned to be removed in a future release.\n"); parseArgs(args); SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -873,6 +878,7 @@ public class PolicyTool { * The Permission contains the (Type, Name, Action) triplet. * */ +@SuppressWarnings("deprecation") class PolicyEntry { private CodeSource codesource; @@ -1012,6 +1018,7 @@ class PolicyEntry { /** * The main window for the PolicyTool */ +@SuppressWarnings("deprecation") class ToolWindow extends JFrame { // use serialVersionUID from JDK 1.2.2 for interoperability private static final long serialVersionUID = 5682568601210376777L; @@ -1549,6 +1556,7 @@ class ToolWindow extends JFrame { /** * General dialog window */ +@SuppressWarnings("deprecation") class ToolDialog extends JDialog { // use serialVersionUID from JDK 1.2.2 for interoperability private static final long serialVersionUID = -372244357011301190L; @@ -2912,6 +2920,7 @@ class ToolDialog extends JDialog { /** * Event handler for the PolicyTool window */ +@SuppressWarnings("deprecation") class ToolWindowListener implements WindowListener { private PolicyTool tool; @@ -2956,6 +2965,7 @@ class ToolWindowListener implements WindowListener { /** * Event handler for the Policy List */ +@SuppressWarnings("deprecation") class PolicyListListener extends MouseAdapter implements ActionListener { private PolicyTool tool; @@ -2985,6 +2995,7 @@ class PolicyListListener extends MouseAdapter implements ActionListener { /** * Event handler for the File Menu */ +@SuppressWarnings("deprecation") class FileMenuListener implements ActionListener { private PolicyTool tool; @@ -3083,6 +3094,7 @@ class FileMenuListener implements ActionListener { /** * Event handler for the main window buttons and Edit Menu */ +@SuppressWarnings("deprecation") class MainWindowListener implements ActionListener { private PolicyTool tool; @@ -3158,6 +3170,7 @@ class MainWindowListener implements ActionListener { * if edit is FALSE, then we are ADDing a new PolicyEntry, * so we only need to update the GUI listing. */ +@SuppressWarnings("deprecation") class AddEntryDoneButtonListener implements ActionListener { private PolicyTool tool; @@ -3224,6 +3237,7 @@ class AddEntryDoneButtonListener implements ActionListener { /** * Event handler for ChangeKeyStoreOKButton button */ +@SuppressWarnings("deprecation") class ChangeKeyStoreOKButtonListener implements ActionListener { private PolicyTool tool; @@ -3270,6 +3284,7 @@ class ChangeKeyStoreOKButtonListener implements ActionListener { /** * Event handler for AddPrinButton button */ +@SuppressWarnings("deprecation") class AddPrinButtonListener implements ActionListener { private PolicyTool tool; @@ -3295,6 +3310,7 @@ class AddPrinButtonListener implements ActionListener { /** * Event handler for AddPermButton button */ +@SuppressWarnings("deprecation") class AddPermButtonListener implements ActionListener { private PolicyTool tool; @@ -3320,6 +3336,7 @@ class AddPermButtonListener implements ActionListener { /** * Event handler for AddPrinOKButton button */ +@SuppressWarnings("deprecation") class NewPolicyPrinOKButtonListener implements ActionListener { private PolicyTool tool; @@ -3383,6 +3400,7 @@ class NewPolicyPrinOKButtonListener implements ActionListener { /** * Event handler for AddPermOKButton button */ +@SuppressWarnings("deprecation") class NewPolicyPermOKButtonListener implements ActionListener { private PolicyTool tool; @@ -3446,6 +3464,7 @@ class NewPolicyPermOKButtonListener implements ActionListener { /** * Event handler for RemovePrinButton button */ +@SuppressWarnings("deprecation") class RemovePrinButtonListener implements ActionListener { private PolicyTool tool; @@ -3481,6 +3500,7 @@ class RemovePrinButtonListener implements ActionListener { /** * Event handler for RemovePermButton button */ +@SuppressWarnings("deprecation") class RemovePermButtonListener implements ActionListener { private PolicyTool tool; @@ -3523,6 +3543,7 @@ class RemovePermButtonListener implements ActionListener { * GUI listing. If the user is editing an existing PolicyEntry, we * update both the GUI listing and the actual PolicyEntry. */ +@SuppressWarnings("deprecation") class EditPrinButtonListener extends MouseAdapter implements ActionListener { private PolicyTool tool; @@ -3569,6 +3590,7 @@ class EditPrinButtonListener extends MouseAdapter implements ActionListener { * GUI listing. If the user is editing an existing PolicyEntry, we * update both the GUI listing and the actual PolicyEntry. */ +@SuppressWarnings("deprecation") class EditPermButtonListener extends MouseAdapter implements ActionListener { private PolicyTool tool; @@ -3609,6 +3631,7 @@ class EditPermButtonListener extends MouseAdapter implements ActionListener { /** * Event handler for Principal Popup Menu */ +@SuppressWarnings("deprecation") class PrincipalTypeMenuListener implements ItemListener { private ToolDialog td; @@ -3660,6 +3683,7 @@ class PrincipalTypeMenuListener implements ItemListener { /** * Event handler for Permission Popup Menu */ +@SuppressWarnings("deprecation") class PermissionMenuListener implements ItemListener { private ToolDialog td; @@ -3734,6 +3758,7 @@ class PermissionMenuListener implements ItemListener { /** * Event handler for Permission Name Popup Menu */ +@SuppressWarnings("deprecation") class PermissionNameMenuListener implements ItemListener { private ToolDialog td; @@ -3887,6 +3912,7 @@ class StatusOKButtonListener implements ActionListener { /** * Event handler for UserSaveYes button */ +@SuppressWarnings("deprecation") class UserSaveYesButtonListener implements ActionListener { private ToolDialog us; @@ -3941,6 +3967,7 @@ class UserSaveYesButtonListener implements ActionListener { /** * Event handler for UserSaveNoButton */ +@SuppressWarnings("deprecation") class UserSaveNoButtonListener implements ActionListener { private PolicyTool tool; @@ -3989,6 +4016,7 @@ class UserSaveCancelButtonListener implements ActionListener { /** * Event handler for ConfirmRemovePolicyEntryOKButtonListener */ +@SuppressWarnings("deprecation") class ConfirmRemovePolicyEntryOKButtonListener implements ActionListener { private PolicyTool tool; @@ -4144,6 +4172,7 @@ class AudioPerm extends Perm { } } +@SuppressWarnings("deprecation") class AuthPerm extends Perm { AuthPerm() { super(javax.security.auth.AuthPermission.class, @@ -4216,6 +4245,7 @@ class FilePerm extends Perm { } } +@SuppressWarnings("deprecation") class URLPerm extends Perm { URLPerm() { super(java.net.URLPermission.class, @@ -4380,6 +4410,7 @@ class ReflectPerm extends Perm { } } +@SuppressWarnings("deprecation") class RuntimePerm extends Perm { RuntimePerm() { super(java.lang.RuntimePermission.class, @@ -4420,6 +4451,7 @@ class RuntimePerm extends Perm { } } +@SuppressWarnings("deprecation") class SecurityPerm extends Perm { SecurityPerm() { super(java.security.SecurityPermission.class, diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index fbe25e8224f..e608d9d98b5 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -77,6 +77,7 @@ jdk_lang = \ sun/misc \ sun/reflect \ jdk/lambda \ + jdk/internal/misc \ vm # All of the java.util package @@ -144,7 +145,8 @@ jdk_stream = \ java/util/stream jdk_math = \ - java/math + java/math \ + jdk/internal/math jdk_io = \ java/io diff --git a/jdk/test/com/sun/jdi/LineNumberInfo.java b/jdk/test/com/sun/jdi/LineNumberInfo.java index c5166da0c43..5b7ff743efa 100644 --- a/jdk/test/com/sun/jdi/LineNumberInfo.java +++ b/jdk/test/com/sun/jdi/LineNumberInfo.java @@ -29,7 +29,7 @@ * * @modules jdk.jdi * @run build TestScaffold VMConnection TargetListener TargetAdapter - * @run compile -g LineNumberInfo.java ControlFlow.java + * @run compile -XDstringConcat=inline -g LineNumberInfo.java ControlFlow.java * * @run driver LineNumberInfo */ diff --git a/jdk/test/com/sun/jdi/sde/InstallSDE.java b/jdk/test/com/sun/jdi/sde/InstallSDE.java index 81bb3052778..17f6558760d 100644 --- a/jdk/test/com/sun/jdi/sde/InstallSDE.java +++ b/jdk/test/com/sun/jdi/sde/InstallSDE.java @@ -253,12 +253,16 @@ class InstallSDE { case 3: // Integer case 4: // Float case 12: // NameAndType + case 18: // InvokeDynamic copy(4); break; case 5: // Long case 6: // Double copy(8); break; + case 15: // MethodHandle + copy(3); + break; case 1: // Utf8 int len = readU2(); writeU2(len); diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcat.java b/jdk/test/java/lang/String/concat/ImplicitStringConcat.java new file mode 100644 index 00000000000..5eb78602498 --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcat.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary test implicit String concatenations + * + * @compile ImplicitStringConcat.java + * @run main/othervm ImplicitStringConcat + * + * @compile -XDstringConcat=inline ImplicitStringConcat.java + * @run main/othervm ImplicitStringConcat + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcat.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcat + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcat.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcat + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcat + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcat +*/ +import java.lang.StringBuilder; + +public class ImplicitStringConcat { + + static boolean b = true; + static byte by = 42; + static short sh = 42; + static char ch = 'a'; + static int i = 42; + static float fl = 42.0f; + static long l = 42; + static double d = 42.0d; + static String s = "foo"; + static String sNull = null; + static Object o = "bar"; + static Object oNull = null; + static CharSequence cs = "bar"; + static char[] chars = new char[] {'a'}; + + static MyClass myCl = new MyClass(); + static MyClassNull myClNull = new MyClassNull(); + static Object myCl2 = new MyClass(); + static Object[] myArr = new Object[] { myCl }; + static final Object[] s_myArr = new Object[] { myCl }; + + static StringBuffer sb = new StringBuffer("a"); + + public static void main(String[] args) throws Exception { + + test("footrue", s + b); + test("foo42", s + by); + test("foo42", s + sh); + test("fooa", s + ch); + test("foo42", s + i); + test("foo42", s + l); + test("foo42.0", s + fl); + test("foo42.0", s + d); + test("foofoo", s + s); + test("foonull", s + sNull); + test("foobar", s + o); + test("foonull", s + oNull); + test("foobar", s + cs); + + { + StringBuilder sb = new StringBuilder(); + sb.append("foo"); + sb.append(myArr.toString()); + test(sb.toString(), s + myArr); + } + + { + StringBuilder sb = new StringBuilder(); + sb.append("foo"); + sb.append(s_myArr.toString()); + test(sb.toString(), s + s_myArr); + } + + { + StringBuilder sb = new StringBuilder(); + sb.append("foo[C@"); + sb.append(Integer.toHexString(System.identityHashCode(chars))); + test(sb.toString(), s + chars); + } + + test("fooa", s + ImplicitStringConcat.sb); + test("foonull", s + null); + test("fooMyClass", s + myCl); + test("foonull", s + myClNull); + test("fooMyClass", s + myCl2); + + s = "foo"; s += b; test("footrue", s); + s = "foo"; s += by; test("foo42", s); + s = "foo"; s += sh; test("foo42", s); + s = "foo"; s += ch; test("fooa", s); + s = "foo"; s += i; test("foo42", s); + s = "foo"; s += l; test("foo42", s); + s = "foo"; s += fl; test("foo42.0", s); + s = "foo"; s += d; test("foo42.0", s); + s = "foo"; s += s; test("foofoo", s); + s = "foo"; s += sNull; test("foonull", s); + s = "foo"; s += o; test("foobar", s); + s = "foo"; s += oNull; test("foonull", s); + s = "foo"; s += cs; test("foobar", s); + + { + StringBuilder sb = new StringBuilder(); + sb.append("foo[C@"); + sb.append(Integer.toHexString(System.identityHashCode(chars))); + s = "foo"; + s += chars; + test(sb.toString(), s); + } + + s = "foo"; s += ImplicitStringConcat.sb; test("fooa", s); + s = "foo"; s += null; test("foonull", s); + s = "foo"; s += myCl; test("fooMyClass", s); + s = "foo"; s += myCl2; test("fooMyClass", s); + } + + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } + + static class MyClass { + public String toString() { + return "MyClass"; + } + } + + static class MyClassNull { + public String toString() { + return null; + } + } +} diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java new file mode 100644 index 00000000000..0cc0a2a7887 --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test multiple number of arguments to concatenate. + * + * @compile ImplicitStringConcatArgCount.java + * @run main/othervm ImplicitStringConcatArgCount + * + * @compile -XDallowStringFolding=false -XDstringConcat=inline ImplicitStringConcatArgCount.java + * @run main/othervm ImplicitStringConcatArgCount + * + * @compile -XDallowStringFolding=false -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatArgCount.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatArgCount + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * + * @compile -XDallowStringFolding=false -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatArgCount.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatArgCount + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatArgCount + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatArgCount +*/ +public class ImplicitStringConcatArgCount { + static final String s = "f"; + static final String s1 = "o"; + static String s2 = "o"; + static int i = 7; + + public static void main(String[] args) throws Exception { + test("fo", s + s1); + test("foo", s + s1 + s2); + test("foo7", s + s1 + s2 + i); + test("foo77", s + s1 + s2 + i + i); + test("foo777", s + s1 + s2 + i + i + i); + test("foo7777", s + s1 + s2 + i + i + i + i); + test("foo77777", s + s1 + s2 + i + i + i + i + i); + test("foo777777", s + s1 + s2 + i + i + i + i + i + i); + test("foo7777777", s + s1 + s2 + i + i + i + i + i + i + i); + test("foo77777777", s + s1 + s2 + i + i + i + i + i + i + i + i); + } + + public static void test(String expected, String actual) { + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } +} diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java new file mode 100644 index 00000000000..f8e677b12bb --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test the boundary values for concatenation arguments. + * + * @compile ImplicitStringConcatBoundaries.java + * @run main/othervm ImplicitStringConcatBoundaries + * + * @compile -XDstringConcat=inline ImplicitStringConcatBoundaries.java + * @run main/othervm ImplicitStringConcatBoundaries + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatBoundaries.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatBoundaries + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatBoundaries.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatBoundaries + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatBoundaries + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatBoundaries + +*/ + +public class ImplicitStringConcatBoundaries { + + public static final boolean BOOL_TRUE_1 = true; + public static boolean BOOL_TRUE_2 = true; + public static final boolean BOOL_FALSE_1 = false; + public static boolean BOOL_FALSE_2 = false; + + public static final byte BYTE_MIN_1 = Byte.MIN_VALUE; + public static byte BYTE_MIN_2 = Byte.MIN_VALUE; + public static final byte BYTE_MAX_1 = Byte.MAX_VALUE; + public static byte BYTE_MAX_2 = Byte.MAX_VALUE; + + public static final short SHORT_MIN_1 = Short.MIN_VALUE; + public static short SHORT_MIN_2 = Short.MIN_VALUE; + public static final short SHORT_MAX_1 = Short.MAX_VALUE; + public static short SHORT_MAX_2 = Short.MAX_VALUE; + + public static final char CHAR_MIN_1 = Character.MIN_VALUE; + public static char CHAR_MIN_2 = Character.MIN_VALUE; + public static final char CHAR_MAX_1 = Character.MAX_VALUE; + public static char CHAR_MAX_2 = Character.MAX_VALUE; + + public static final int INT_MIN_1 = Integer.MIN_VALUE; + public static int INT_MIN_2 = Integer.MIN_VALUE; + public static final int INT_MAX_1 = Integer.MAX_VALUE; + public static int INT_MAX_2 = Integer.MAX_VALUE; + + public static final float FLOAT_MIN_EXP_1 = Float.MIN_EXPONENT; + public static float FLOAT_MIN_EXP_2 = Float.MIN_EXPONENT; + public static final float FLOAT_MIN_NORM_1 = Float.MIN_NORMAL; + public static float FLOAT_MIN_NORM_2 = Float.MIN_NORMAL; + public static final float FLOAT_MIN_1 = Float.MIN_VALUE; + public static float FLOAT_MIN_2 = Float.MIN_VALUE; + public static final float FLOAT_MAX_1 = Float.MAX_VALUE; + public static float FLOAT_MAX_2 = Float.MAX_VALUE; + + public static final long LONG_MIN_1 = Long.MIN_VALUE; + public static long LONG_MIN_2 = Long.MIN_VALUE; + public static final long LONG_MAX_1 = Long.MAX_VALUE; + public static long LONG_MAX_2 = Long.MAX_VALUE; + + public static final double DOUBLE_MIN_EXP_1 = Double.MIN_EXPONENT; + public static double DOUBLE_MIN_EXP_2 = Double.MIN_EXPONENT; + public static final double DOUBLE_MIN_NORM_1 = Double.MIN_NORMAL; + public static double DOUBLE_MIN_NORM_2 = Double.MIN_NORMAL; + public static final double DOUBLE_MIN_1 = Double.MIN_VALUE; + public static double DOUBLE_MIN_2 = Double.MIN_VALUE; + public static final double DOUBLE_MAX_1 = Double.MAX_VALUE; + public static double DOUBLE_MAX_2 = Double.MAX_VALUE; + + public static void main(String[] args) throws Exception { + test("foofalse", "foo" + BOOL_FALSE_1); + test("foofalse", "foo" + BOOL_FALSE_2); + test("footrue", "foo" + BOOL_TRUE_1); + test("footrue", "foo" + BOOL_TRUE_2); + + test("foo127", "foo" + BYTE_MAX_1); + test("foo127", "foo" + BYTE_MAX_2); + test("foo-128", "foo" + BYTE_MIN_1); + test("foo-128", "foo" + BYTE_MIN_2); + + test("foo32767", "foo" + SHORT_MAX_1); + test("foo32767", "foo" + SHORT_MAX_2); + test("foo-32768", "foo" + SHORT_MIN_1); + test("foo-32768", "foo" + SHORT_MIN_2); + + test("foo\u0000", "foo" + CHAR_MIN_1); + test("foo\u0000", "foo" + CHAR_MIN_2); + test("foo\uFFFF", "foo" + CHAR_MAX_1); + test("foo\uFFFF", "foo" + CHAR_MAX_2); + + test("foo2147483647", "foo" + INT_MAX_1); + test("foo2147483647", "foo" + INT_MAX_2); + test("foo-2147483648", "foo" + INT_MIN_1); + test("foo-2147483648", "foo" + INT_MIN_2); + + test("foo1.17549435E-38", "foo" + FLOAT_MIN_NORM_1); + test("foo1.17549435E-38", "foo" + FLOAT_MIN_NORM_2); + test("foo-126.0", "foo" + FLOAT_MIN_EXP_1); + test("foo-126.0", "foo" + FLOAT_MIN_EXP_2); + test("foo1.4E-45", "foo" + FLOAT_MIN_1); + test("foo1.4E-45", "foo" + FLOAT_MIN_2); + test("foo3.4028235E38", "foo" + FLOAT_MAX_1); + test("foo3.4028235E38", "foo" + FLOAT_MAX_2); + + test("foo-9223372036854775808", "foo" + LONG_MIN_1); + test("foo-9223372036854775808", "foo" + LONG_MIN_2); + test("foo9223372036854775807", "foo" + LONG_MAX_1); + test("foo9223372036854775807", "foo" + LONG_MAX_2); + + test("foo2.2250738585072014E-308", "foo" + DOUBLE_MIN_NORM_1); + test("foo2.2250738585072014E-308", "foo" + DOUBLE_MIN_NORM_2); + test("foo-1022.0", "foo" + DOUBLE_MIN_EXP_1); + test("foo-1022.0", "foo" + DOUBLE_MIN_EXP_2); + test("foo4.9E-324", "foo" + DOUBLE_MIN_1); + test("foo4.9E-324", "foo" + DOUBLE_MIN_2); + test("foo1.7976931348623157E308", "foo" + DOUBLE_MAX_1); + test("foo1.7976931348623157E308", "foo" + DOUBLE_MAX_2); + } + + public static void test(String expected, String actual) { + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } +} diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java new file mode 100644 index 00000000000..7af7e7d5e05 --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test implicit String concatenations with lots of arguments. + * + * @compile ImplicitStringConcatMany.java + * @run main/othervm ImplicitStringConcatMany + * + * @compile -XDstringConcat=inline ImplicitStringConcatMany.java + * @run main/othervm ImplicitStringConcatMany + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatMany.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatMany + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatMany.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatMany + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatMany + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatMany +*/ + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ImplicitStringConcatMany { + + static String s000, s001, s002, s003, s004, s005, s006, s007, s008, s009; + static String s010, s011, s012, s013, s014, s015, s016, s017, s018, s019; + static String s020, s021, s022, s023, s024, s025, s026, s027, s028, s029; + static String s030, s031, s032, s033, s034, s035, s036, s037, s038, s039; + static String s040, s041, s042, s043, s044, s045, s046, s047, s048, s049; + static String s050, s051, s052, s053, s054, s055, s056, s057, s058, s059; + static String s060, s061, s062, s063, s064, s065, s066, s067, s068, s069; + static String s070, s071, s072, s073, s074, s075, s076, s077, s078, s079; + static String s080, s081, s082, s083, s084, s085, s086, s087, s088, s089; + static String s090, s091, s092, s093, s094, s095, s096, s097, s098, s099; + + static String s100, s101, s102, s103, s104, s105, s106, s107, s108, s109; + static String s110, s111, s112, s113, s114, s115, s116, s117, s118, s119; + static String s120, s121, s122, s123, s124, s125, s126, s127, s128, s129; + static String s130, s131, s132, s133, s134, s135, s136, s137, s138, s139; + static String s140, s141, s142, s143, s144, s145, s146, s147, s148, s149; + static String s150, s151, s152, s153, s154, s155, s156, s157, s158, s159; + static String s160, s161, s162, s163, s164, s165, s166, s167, s168, s169; + static String s170, s171, s172, s173, s174, s175, s176, s177, s178, s179; + static String s180, s181, s182, s183, s184, s185, s186, s187, s188, s189; + static String s190, s191, s192, s193, s194, s195, s196, s197, s198, s199; + + static String s200, s201, s202, s203, s204, s205, s206, s207, s208, s209; + static String s210, s211, s212, s213, s214, s215, s216, s217, s218, s219; + static String s220, s221, s222, s223, s224, s225, s226, s227, s228, s229; + static String s230, s231, s232, s233, s234, s235, s236, s237, s238, s239; + static String s240, s241, s242, s243, s244, s245, s246, s247, s248, s249; + static String s250, s251, s252, s253, s254, s255, s256, s257, s258, s259; + static String s260, s261, s262, s263, s264, s265, s266, s267, s268, s269; + static String s270, s271, s272, s273, s274, s275, s276, s277, s278, s279; + static String s280, s281, s282, s283, s284, s285, s286, s287, s288, s289; + static String s290, s291, s292, s293, s294, s295, s296, s297, s298, s299; + + static { + for (Field f : ImplicitStringConcatMany.class.getDeclaredFields()) { + if (Modifier.isStatic(f.getModifiers())) { + String name = f.getName(); + try { + f.set(null, name); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + } + + public static void main(String[] args) throws Exception { + String res = + s000 + s001 + s002 + s003 + s004 + s005 + s006 + s007 + s008 + s009 + + s010 + s011 + s012 + s013 + s014 + s015 + s016 + s017 + s018 + s019 + + s020 + s021 + s022 + s023 + s024 + s025 + s026 + s027 + s028 + s029 + + s030 + s031 + s032 + s033 + s034 + s035 + s036 + s037 + s038 + s039 + + s040 + s041 + s042 + s043 + s044 + s045 + s046 + s047 + s048 + s049 + + s050 + s051 + s052 + s053 + s054 + s055 + s056 + s057 + s058 + s059 + + s060 + s061 + s062 + s063 + s064 + s065 + s066 + s067 + s068 + s069 + + s070 + s071 + s072 + s073 + s074 + s075 + s076 + s077 + s078 + s079 + + s080 + s081 + s082 + s083 + s084 + s085 + s086 + s087 + s088 + s089 + + s090 + s091 + s092 + s093 + s094 + s095 + s096 + s097 + s098 + s099 + + + s100 + s101 + s102 + s103 + s104 + s105 + s106 + s107 + s108 + s109 + + s110 + s111 + s112 + s113 + s114 + s115 + s116 + s117 + s118 + s119 + + s120 + s121 + s122 + s123 + s124 + s125 + s126 + s127 + s128 + s129 + + s130 + s131 + s132 + s133 + s134 + s135 + s136 + s137 + s138 + s139 + + s140 + s141 + s142 + s143 + s144 + s145 + s146 + s147 + s148 + s149 + + s150 + s151 + s152 + s153 + s154 + s155 + s156 + s157 + s158 + s159 + + s160 + s161 + s162 + s163 + s164 + s165 + s166 + s167 + s168 + s169 + + s170 + s171 + s172 + s173 + s174 + s175 + s176 + s177 + s178 + s179 + + s180 + s181 + s182 + s183 + s184 + s185 + s186 + s187 + s188 + s189 + + s190 + s191 + s192 + s193 + s194 + s195 + s196 + s197 + s198 + s199 + + + s200 + s201 + s202 + s203 + s204 + s205 + s206 + s207 + s208 + s209 + + s210 + s211 + s212 + s213 + s214 + s215 + s216 + s217 + s218 + s219 + + s220 + s221 + s222 + s223 + s224 + s225 + s226 + s227 + s228 + s229 + + s230 + s231 + s232 + s233 + s234 + s235 + s236 + s237 + s238 + s239 + + s240 + s241 + s242 + s243 + s244 + s245 + s246 + s247 + s248 + s249 + + s250 + s251 + s252 + s253 + s254 + s255 + s256 + s257 + s258 + s259 + + s260 + s261 + s262 + s263 + s264 + s265 + s266 + s267 + s268 + s269 + + s270 + s271 + s272 + s273 + s274 + s275 + s276 + s277 + s278 + s279 + + s280 + s281 + s282 + s283 + s284 + s285 + s286 + s287 + s288 + s289 + + s290 + s291 + s292 + s293 + s294 + s295 + s296 + s297 + s298 + s299; + + StringBuilder sb = new StringBuilder(); + for (int c = 0; c < 300; c++) { + sb.append(String.format("s%03d", c)); + } + test(sb.toString(), res); + } + + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + throw new IllegalStateException("Expected = " + expected + ", actual = " + actual); + } + } +} + diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java new file mode 100644 index 00000000000..cc14a100cec --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test implicit String concatenations with lots of arguments (two-slot version) + * + * @compile ImplicitStringConcatManyLongs.java + * @run main/othervm ImplicitStringConcatManyLongs + * + * @compile -XDstringConcat=inline ImplicitStringConcatManyLongs.java + * @run main/othervm ImplicitStringConcatManyLongs + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatManyLongs.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatManyLongs + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatManyLongs.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatManyLongs + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatManyLongs + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatManyLongs +*/ + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ImplicitStringConcatManyLongs { + + static long s000, s001, s002, s003, s004, s005, s006, s007, s008, s009; + static long s010, s011, s012, s013, s014, s015, s016, s017, s018, s019; + static long s020, s021, s022, s023, s024, s025, s026, s027, s028, s029; + static long s030, s031, s032, s033, s034, s035, s036, s037, s038, s039; + static long s040, s041, s042, s043, s044, s045, s046, s047, s048, s049; + static long s050, s051, s052, s053, s054, s055, s056, s057, s058, s059; + static long s060, s061, s062, s063, s064, s065, s066, s067, s068, s069; + static long s070, s071, s072, s073, s074, s075, s076, s077, s078, s079; + static long s080, s081, s082, s083, s084, s085, s086, s087, s088, s089; + static long s090, s091, s092, s093, s094, s095, s096, s097, s098, s099; + + static long s100, s101, s102, s103, s104, s105, s106, s107, s108, s109; + static long s110, s111, s112, s113, s114, s115, s116, s117, s118, s119; + static long s120, s121, s122, s123, s124, s125, s126, s127, s128, s129; + static long s130, s131, s132, s133, s134, s135, s136, s137, s138, s139; + static long s140, s141, s142, s143, s144, s145, s146, s147, s148, s149; + static long s150, s151, s152, s153, s154, s155, s156, s157, s158, s159; + static long s160, s161, s162, s163, s164, s165, s166, s167, s168, s169; + static long s170, s171, s172, s173, s174, s175, s176, s177, s178, s179; + static long s180, s181, s182, s183, s184, s185, s186, s187, s188, s189; + static long s190, s191, s192, s193, s194, s195, s196, s197, s198, s199; + + static long s200, s201, s202, s203, s204, s205, s206, s207, s208, s209; + static long s210, s211, s212, s213, s214, s215, s216, s217, s218, s219; + static long s220, s221, s222, s223, s224, s225, s226, s227, s228, s229; + static long s230, s231, s232, s233, s234, s235, s236, s237, s238, s239; + static long s240, s241, s242, s243, s244, s245, s246, s247, s248, s249; + static long s250, s251, s252, s253, s254, s255, s256, s257, s258, s259; + static long s260, s261, s262, s263, s264, s265, s266, s267, s268, s269; + static long s270, s271, s272, s273, s274, s275, s276, s277, s278, s279; + static long s280, s281, s282, s283, s284, s285, s286, s287, s288, s289; + static long s290, s291, s292, s293, s294, s295, s296, s297, s298, s299; + + static { + for (Field f : ImplicitStringConcatManyLongs.class.getDeclaredFields()) { + if (Modifier.isStatic(f.getModifiers())) { + String name = f.getName(); + try { + f.set(null, Long.valueOf(name.substring(1))); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + } + + public static void main(String[] args) throws Exception { + String res = "" + + s000 + s001 + s002 + s003 + s004 + s005 + s006 + s007 + s008 + s009 + + s010 + s011 + s012 + s013 + s014 + s015 + s016 + s017 + s018 + s019 + + s020 + s021 + s022 + s023 + s024 + s025 + s026 + s027 + s028 + s029 + + s030 + s031 + s032 + s033 + s034 + s035 + s036 + s037 + s038 + s039 + + s040 + s041 + s042 + s043 + s044 + s045 + s046 + s047 + s048 + s049 + + s050 + s051 + s052 + s053 + s054 + s055 + s056 + s057 + s058 + s059 + + s060 + s061 + s062 + s063 + s064 + s065 + s066 + s067 + s068 + s069 + + s070 + s071 + s072 + s073 + s074 + s075 + s076 + s077 + s078 + s079 + + s080 + s081 + s082 + s083 + s084 + s085 + s086 + s087 + s088 + s089 + + s090 + s091 + s092 + s093 + s094 + s095 + s096 + s097 + s098 + s099 + + + s100 + s101 + s102 + s103 + s104 + s105 + s106 + s107 + s108 + s109 + + s110 + s111 + s112 + s113 + s114 + s115 + s116 + s117 + s118 + s119 + + s120 + s121 + s122 + s123 + s124 + s125 + s126 + s127 + s128 + s129 + + s130 + s131 + s132 + s133 + s134 + s135 + s136 + s137 + s138 + s139 + + s140 + s141 + s142 + s143 + s144 + s145 + s146 + s147 + s148 + s149 + + s150 + s151 + s152 + s153 + s154 + s155 + s156 + s157 + s158 + s159 + + s160 + s161 + s162 + s163 + s164 + s165 + s166 + s167 + s168 + s169 + + s170 + s171 + s172 + s173 + s174 + s175 + s176 + s177 + s178 + s179 + + s180 + s181 + s182 + s183 + s184 + s185 + s186 + s187 + s188 + s189 + + s190 + s191 + s192 + s193 + s194 + s195 + s196 + s197 + s198 + s199 + + + s200 + s201 + s202 + s203 + s204 + s205 + s206 + s207 + s208 + s209 + + s210 + s211 + s212 + s213 + s214 + s215 + s216 + s217 + s218 + s219 + + s220 + s221 + s222 + s223 + s224 + s225 + s226 + s227 + s228 + s229 + + s230 + s231 + s232 + s233 + s234 + s235 + s236 + s237 + s238 + s239 + + s240 + s241 + s242 + s243 + s244 + s245 + s246 + s247 + s248 + s249 + + s250 + s251 + s252 + s253 + s254 + s255 + s256 + s257 + s258 + s259 + + s260 + s261 + s262 + s263 + s264 + s265 + s266 + s267 + s268 + s269 + + s270 + s271 + s272 + s273 + s274 + s275 + s276 + s277 + s278 + s279 + + s280 + s281 + s282 + s283 + s284 + s285 + s286 + s287 + s288 + s289 + + s290 + s291 + s292 + s293 + s294 + s295 + s296 + s297 + s298 + s299; + + StringBuilder sb = new StringBuilder(); + for (int c = 0; c < 300; c++) { + sb.append(c); + } + test(sb.toString(), res); + } + + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + throw new IllegalStateException("Expected = " + expected + ", actual = " + actual); + } + } +} + diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template new file mode 100644 index 00000000000..10fd57f0616 --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test implicit String concatenations, multiple shapes. + * + * @compile ImplicitStringConcatShapes.java + * @run main/othervm ImplicitStringConcatShapes + * + * @compile -XDstringConcat=inline ImplicitStringConcatShapes.java + * @run main/othervm ImplicitStringConcatShapes + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatShapes.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatShapes.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes +*/ +public class ImplicitStringConcatShapes { + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } + + static class MyClass { + private final int i; + + public MyClass(int i) { + this.i = i; + } + + public String toString() { + return new StringBuilder("C(").append(i).append(")").toString(); + } + } + + static class MyClassNullToString { + public String toString() { + return null; + } + } + + public static void main(String[] args) throws Exception { + new ImplicitStringConcatShapes().run(); + } diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java new file mode 100644 index 00000000000..c4a673721ae --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java @@ -0,0 +1,5931 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Test implicit String concatenations, multiple shapes. + * + * @compile ImplicitStringConcatShapes.java + * @run main/othervm ImplicitStringConcatShapes + * + * @compile -XDstringConcat=inline ImplicitStringConcatShapes.java + * @run main/othervm ImplicitStringConcatShapes + * + * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatShapes.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * + * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatShapes.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true ImplicitStringConcatShapes +*/ +public class ImplicitStringConcatShapes { + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } + + static class MyClass { + private final int i; + + public MyClass(int i) { + this.i = i; + } + + public String toString() { + return new StringBuilder("C(").append(i).append(")").toString(); + } + } + + static class MyClassNullToString { + public String toString() { + return null; + } + } + + public static void main(String[] args) throws Exception { + new ImplicitStringConcatShapes().run(); + } + static final boolean sf_bl = true; + static final byte sf_b = 80; + static final byte sf_bM = -41; + static final char sf_c = 'C'; + static final short sf_s = 5500; + static final short sf_sM = -8400; + static final int sf_i = 75000000; + static final int sf_iM = -2000000; + static final Integer sf_I = 1000000; + static final Integer sf_IN = null; + static final float sf_f = 17.0f; + static final float sf_fM = -42.0f; + static final long sf_l = -194313216L; + static final long sf_lM = -1705032704L; + static final double sf_d = 12.0d; + static final double sf_dM = -84.0d; + static final Object sf_o = new MyClass(87); + static final Object sf_oN = null; + static final Object sf_oNtS = new MyClassNullToString(); + static final String sf_str = "75"; + static final String sf_strU = "\u04511"; + static final String sf_strU1 = "\u000151"; + static final String sf_strU2 = "\u000292"; + static final int[] sf_iAN = null; + static final Object[] sf_oAN = null; + static boolean s_bl = true; + static byte s_b = 25; + static byte s_bM = -43; + static char s_c = 'T'; + static short s_s = 3900; + static short s_sM = -2900; + static int s_i = 97000000; + static int s_iM = -1000000; + static Integer s_I = 25000000; + static Integer s_IN = null; + static float s_f = 55.0f; + static float s_fM = -52.0f; + static long s_l = 935228928L; + static long s_lM = -1410065408L; + static double s_d = 8.0d; + static double s_dM = -96.0d; + static Object s_o = new MyClass(82); + static Object s_oN = null; + static Object s_oNtS = new MyClassNullToString(); + static String s_str = "18"; + static String s_strU = "\u045180"; + static String s_strU1 = "\u000112"; + static String s_strU2 = "\u000291"; + static int[] s_iAN = null; + static Object[] s_oAN = null; + final boolean f_bl = false; + final byte f_b = 44; + final byte f_bM = -54; + final char f_c = 'I'; + final short f_s = 8000; + final short f_sM = -9900; + final int f_i = 58000000; + final int f_iM = -55000000; + final Integer f_I = 94000000; + final Integer f_IN = null; + final float f_f = 94.0f; + final float f_fM = -87.0f; + final long f_l = 1460392448L; + final long f_lM = -820130816L; + final double f_d = 83.0d; + final double f_dM = -99.0d; + final Object f_o = new MyClass(70); + final Object f_oN = null; + final Object f_oNtS = new MyClassNullToString(); + final String f_str = "19"; + final String f_strU = "\u045176"; + final String f_strU1 = "\u000121"; + final String f_strU2 = "\u000218"; + final int[] f_iAN = null; + final Object[] f_oAN = null; + + public void run() { + run0(); + run1(); + run2(); + run3(); + run4(); + run5(); + } + + public void run0() { + test("-96.0", "" + s_dM); + test("null", "" + s_oNtS); + test("\u045176", "" + f_strU); + test("92", "" + sf_strU2); + test("51", "" + sf_strU1); + test("null", "" + s_iAN); + test("-54", "" + f_bM); + test("-87.0", "" + f_fM); + test("null", "" + s_oAN); + test("19", "" + f_str); + test("-41", "" + sf_bM); + test("null", "" + sf_IN); + test("T", "" + s_c); + test("-42.0", "" + sf_fM); + test("25", "" + s_b); + test("null", "" + f_oN); + test("-1410065408", "" + s_lM); + test("8.0", "" + s_d); + test("55.0", "" + s_f); + test("97000000", "" + s_i); + test("-9900", "" + f_sM); + test("935228928", "" + s_l); + test("-8400", "" + sf_sM); + test("C(82)", "" + s_o); + test("null", "" + sf_oNtS); + test("true", "" + s_bl); + test("3900", "" + s_s); + test("null", "" + sf_oN); + test("94000000", "" + f_I); + test("null", "" + f_IN); + test("true", "" + sf_bl); + test("5500", "" + sf_s); + test("-2900", "" + s_sM); + test("-194313216", "" + sf_l); + test("12", "" + s_strU1); + test("C(87)", "" + sf_o); + test("91", "" + s_strU2); + test("21", "" + f_strU1); + test("18", "" + f_strU2); + test("null", "" + f_iAN); + test("null", "" + s_oN); + test("\u045180", "" + s_strU); + test("C", "" + sf_c); + test("75", "" + sf_str); + test("-43", "" + s_bM); + test("80", "" + sf_b); + test("null", "" + s_IN); + test("-52.0", "" + s_fM); + test("75000000", "" + sf_i); + test("44", "" + f_b); + test("-1705032704", "" + sf_lM); + test("null", "" + f_oAN); + test("83.0", "" + f_d); + test("I", "" + f_c); + test("94.0", "" + f_f); + test("12.0", "" + sf_d); + test("-99.0", "" + f_dM); + test("17.0", "" + sf_f); + test("-84.0", "" + sf_dM); + test("58000000", "" + f_i); + test("-55000000", "" + f_iM); + test("1460392448", "" + f_l); + test("C(70)", "" + f_o); + test("\u04511", "" + sf_strU); + test("8000", "" + f_s); + test("18", "" + s_str); + test("-1000000", "" + s_iM); + test("1000000", "" + sf_I); + test("null", "" + f_oNtS); + test("false", "" + f_bl); + test("null", "" + sf_iAN); + test("-2000000", "" + sf_iM); + test("-820130816", "" + f_lM); + test("null", "" + sf_oAN); + test("25000000", "" + s_I); + test("-96.0-96.0", "" + s_dM + s_dM); + test("-96.0null", "" + s_dM + s_oNtS); + test("-96.0\u045176", "" + s_dM + f_strU); + test("-96.092", "" + s_dM + sf_strU2); + test("-96.051", "" + s_dM + sf_strU1); + test("-96.0null", "" + s_dM + s_iAN); + test("-96.0-54", "" + s_dM + f_bM); + test("-96.0-87.0", "" + s_dM + f_fM); + test("-96.0null", "" + s_dM + s_oAN); + test("-96.019", "" + s_dM + f_str); + test("-96.0-41", "" + s_dM + sf_bM); + test("-96.0null", "" + s_dM + sf_IN); + test("-96.0T", "" + s_dM + s_c); + test("-96.0-42.0", "" + s_dM + sf_fM); + test("-96.025", "" + s_dM + s_b); + test("-96.0null", "" + s_dM + f_oN); + test("-96.0-1410065408", "" + s_dM + s_lM); + test("-96.08.0", "" + s_dM + s_d); + test("-96.055.0", "" + s_dM + s_f); + test("-96.097000000", "" + s_dM + s_i); + test("-96.0-9900", "" + s_dM + f_sM); + test("-96.0935228928", "" + s_dM + s_l); + test("-96.0-8400", "" + s_dM + sf_sM); + test("-96.0C(82)", "" + s_dM + s_o); + test("-96.0null", "" + s_dM + sf_oNtS); + test("-96.0true", "" + s_dM + s_bl); + test("-96.03900", "" + s_dM + s_s); + test("-96.0null", "" + s_dM + sf_oN); + test("-96.094000000", "" + s_dM + f_I); + test("-96.0null", "" + s_dM + f_IN); + test("-96.0true", "" + s_dM + sf_bl); + test("-96.05500", "" + s_dM + sf_s); + test("-96.0-2900", "" + s_dM + s_sM); + test("-96.0-194313216", "" + s_dM + sf_l); + test("-96.012", "" + s_dM + s_strU1); + test("-96.0C(87)", "" + s_dM + sf_o); + test("-96.091", "" + s_dM + s_strU2); + test("-96.021", "" + s_dM + f_strU1); + test("-96.018", "" + s_dM + f_strU2); + test("-96.0null", "" + s_dM + f_iAN); + test("-96.0null", "" + s_dM + s_oN); + test("-96.0\u045180", "" + s_dM + s_strU); + test("-96.0C", "" + s_dM + sf_c); + test("-96.075", "" + s_dM + sf_str); + test("-96.0-43", "" + s_dM + s_bM); + test("-96.080", "" + s_dM + sf_b); + test("-96.0null", "" + s_dM + s_IN); + test("-96.0-52.0", "" + s_dM + s_fM); + test("-96.075000000", "" + s_dM + sf_i); + test("-96.044", "" + s_dM + f_b); + test("-96.0-1705032704", "" + s_dM + sf_lM); + test("-96.0null", "" + s_dM + f_oAN); + test("-96.083.0", "" + s_dM + f_d); + test("-96.0I", "" + s_dM + f_c); + test("-96.094.0", "" + s_dM + f_f); + test("-96.012.0", "" + s_dM + sf_d); + test("-96.0-99.0", "" + s_dM + f_dM); + test("-96.017.0", "" + s_dM + sf_f); + test("-96.0-84.0", "" + s_dM + sf_dM); + test("-96.058000000", "" + s_dM + f_i); + test("-96.0-55000000", "" + s_dM + f_iM); + test("-96.01460392448", "" + s_dM + f_l); + test("-96.0C(70)", "" + s_dM + f_o); + test("-96.0\u04511", "" + s_dM + sf_strU); + test("-96.08000", "" + s_dM + f_s); + test("-96.018", "" + s_dM + s_str); + test("-96.0-1000000", "" + s_dM + s_iM); + test("-96.01000000", "" + s_dM + sf_I); + test("-96.0null", "" + s_dM + f_oNtS); + test("-96.0false", "" + s_dM + f_bl); + test("-96.0null", "" + s_dM + sf_iAN); + test("-96.0-2000000", "" + s_dM + sf_iM); + test("-96.0-820130816", "" + s_dM + f_lM); + test("-96.0null", "" + s_dM + sf_oAN); + test("-96.025000000", "" + s_dM + s_I); + test("null-96.0", "" + s_oNtS + s_dM); + test("nullnull", "" + s_oNtS + s_oNtS); + test("null\u045176", "" + s_oNtS + f_strU); + test("null92", "" + s_oNtS + sf_strU2); + test("null51", "" + s_oNtS + sf_strU1); + test("nullnull", "" + s_oNtS + s_iAN); + test("null-54", "" + s_oNtS + f_bM); + test("null-87.0", "" + s_oNtS + f_fM); + test("nullnull", "" + s_oNtS + s_oAN); + test("null19", "" + s_oNtS + f_str); + test("null-41", "" + s_oNtS + sf_bM); + test("nullnull", "" + s_oNtS + sf_IN); + test("nullT", "" + s_oNtS + s_c); + test("null-42.0", "" + s_oNtS + sf_fM); + test("null25", "" + s_oNtS + s_b); + test("nullnull", "" + s_oNtS + f_oN); + test("null-1410065408", "" + s_oNtS + s_lM); + test("null8.0", "" + s_oNtS + s_d); + test("null55.0", "" + s_oNtS + s_f); + test("null97000000", "" + s_oNtS + s_i); + test("null-9900", "" + s_oNtS + f_sM); + test("null935228928", "" + s_oNtS + s_l); + test("null-8400", "" + s_oNtS + sf_sM); + test("nullC(82)", "" + s_oNtS + s_o); + test("nullnull", "" + s_oNtS + sf_oNtS); + test("nulltrue", "" + s_oNtS + s_bl); + test("null3900", "" + s_oNtS + s_s); + test("nullnull", "" + s_oNtS + sf_oN); + test("null94000000", "" + s_oNtS + f_I); + test("nullnull", "" + s_oNtS + f_IN); + test("nulltrue", "" + s_oNtS + sf_bl); + test("null5500", "" + s_oNtS + sf_s); + test("null-2900", "" + s_oNtS + s_sM); + test("null-194313216", "" + s_oNtS + sf_l); + test("null12", "" + s_oNtS + s_strU1); + test("nullC(87)", "" + s_oNtS + sf_o); + test("null91", "" + s_oNtS + s_strU2); + test("null21", "" + s_oNtS + f_strU1); + test("null18", "" + s_oNtS + f_strU2); + test("nullnull", "" + s_oNtS + f_iAN); + test("nullnull", "" + s_oNtS + s_oN); + test("null\u045180", "" + s_oNtS + s_strU); + test("nullC", "" + s_oNtS + sf_c); + test("null75", "" + s_oNtS + sf_str); + test("null-43", "" + s_oNtS + s_bM); + test("null80", "" + s_oNtS + sf_b); + test("nullnull", "" + s_oNtS + s_IN); + test("null-52.0", "" + s_oNtS + s_fM); + test("null75000000", "" + s_oNtS + sf_i); + test("null44", "" + s_oNtS + f_b); + test("null-1705032704", "" + s_oNtS + sf_lM); + test("nullnull", "" + s_oNtS + f_oAN); + test("null83.0", "" + s_oNtS + f_d); + test("nullI", "" + s_oNtS + f_c); + test("null94.0", "" + s_oNtS + f_f); + test("null12.0", "" + s_oNtS + sf_d); + test("null-99.0", "" + s_oNtS + f_dM); + test("null17.0", "" + s_oNtS + sf_f); + test("null-84.0", "" + s_oNtS + sf_dM); + test("null58000000", "" + s_oNtS + f_i); + test("null-55000000", "" + s_oNtS + f_iM); + test("null1460392448", "" + s_oNtS + f_l); + test("nullC(70)", "" + s_oNtS + f_o); + test("null\u04511", "" + s_oNtS + sf_strU); + test("null8000", "" + s_oNtS + f_s); + test("null18", "" + s_oNtS + s_str); + test("null-1000000", "" + s_oNtS + s_iM); + test("null1000000", "" + s_oNtS + sf_I); + test("nullnull", "" + s_oNtS + f_oNtS); + test("nullfalse", "" + s_oNtS + f_bl); + test("nullnull", "" + s_oNtS + sf_iAN); + test("null-2000000", "" + s_oNtS + sf_iM); + test("null-820130816", "" + s_oNtS + f_lM); + test("nullnull", "" + s_oNtS + sf_oAN); + test("null25000000", "" + s_oNtS + s_I); + test("\u045176-96.0", "" + f_strU + s_dM); + test("\u045176null", "" + f_strU + s_oNtS); + test("\u045176\u045176", "" + f_strU + f_strU); + test("\u04517692", "" + f_strU + sf_strU2); + test("\u04517651", "" + f_strU + sf_strU1); + test("\u045176null", "" + f_strU + s_iAN); + test("\u045176-54", "" + f_strU + f_bM); + test("\u045176-87.0", "" + f_strU + f_fM); + test("\u045176null", "" + f_strU + s_oAN); + test("\u04517619", "" + f_strU + f_str); + test("\u045176-41", "" + f_strU + sf_bM); + test("\u045176null", "" + f_strU + sf_IN); + test("\u045176T", "" + f_strU + s_c); + test("\u045176-42.0", "" + f_strU + sf_fM); + test("\u04517625", "" + f_strU + s_b); + test("\u045176null", "" + f_strU + f_oN); + test("\u045176-1410065408", "" + f_strU + s_lM); + test("\u0451768.0", "" + f_strU + s_d); + test("\u04517655.0", "" + f_strU + s_f); + test("\u04517697000000", "" + f_strU + s_i); + test("\u045176-9900", "" + f_strU + f_sM); + test("\u045176935228928", "" + f_strU + s_l); + test("\u045176-8400", "" + f_strU + sf_sM); + test("\u045176C(82)", "" + f_strU + s_o); + test("\u045176null", "" + f_strU + sf_oNtS); + test("\u045176true", "" + f_strU + s_bl); + test("\u0451763900", "" + f_strU + s_s); + test("\u045176null", "" + f_strU + sf_oN); + test("\u04517694000000", "" + f_strU + f_I); + test("\u045176null", "" + f_strU + f_IN); + test("\u045176true", "" + f_strU + sf_bl); + test("\u0451765500", "" + f_strU + sf_s); + test("\u045176-2900", "" + f_strU + s_sM); + test("\u045176-194313216", "" + f_strU + sf_l); + test("\u04517612", "" + f_strU + s_strU1); + test("\u045176C(87)", "" + f_strU + sf_o); + test("\u04517691", "" + f_strU + s_strU2); + test("\u04517621", "" + f_strU + f_strU1); + test("\u04517618", "" + f_strU + f_strU2); + test("\u045176null", "" + f_strU + f_iAN); + test("\u045176null", "" + f_strU + s_oN); + test("\u045176\u045180", "" + f_strU + s_strU); + test("\u045176C", "" + f_strU + sf_c); + test("\u04517675", "" + f_strU + sf_str); + test("\u045176-43", "" + f_strU + s_bM); + test("\u04517680", "" + f_strU + sf_b); + test("\u045176null", "" + f_strU + s_IN); + test("\u045176-52.0", "" + f_strU + s_fM); + test("\u04517675000000", "" + f_strU + sf_i); + test("\u04517644", "" + f_strU + f_b); + test("\u045176-1705032704", "" + f_strU + sf_lM); + test("\u045176null", "" + f_strU + f_oAN); + test("\u04517683.0", "" + f_strU + f_d); + test("\u045176I", "" + f_strU + f_c); + test("\u04517694.0", "" + f_strU + f_f); + test("\u04517612.0", "" + f_strU + sf_d); + test("\u045176-99.0", "" + f_strU + f_dM); + test("\u04517617.0", "" + f_strU + sf_f); + test("\u045176-84.0", "" + f_strU + sf_dM); + test("\u04517658000000", "" + f_strU + f_i); + test("\u045176-55000000", "" + f_strU + f_iM); + test("\u0451761460392448", "" + f_strU + f_l); + test("\u045176C(70)", "" + f_strU + f_o); + test("\u045176\u04511", "" + f_strU + sf_strU); + test("\u0451768000", "" + f_strU + f_s); + test("\u04517618", "" + f_strU + s_str); + test("\u045176-1000000", "" + f_strU + s_iM); + test("\u0451761000000", "" + f_strU + sf_I); + test("\u045176null", "" + f_strU + f_oNtS); + test("\u045176false", "" + f_strU + f_bl); + test("\u045176null", "" + f_strU + sf_iAN); + test("\u045176-2000000", "" + f_strU + sf_iM); + test("\u045176-820130816", "" + f_strU + f_lM); + test("\u045176null", "" + f_strU + sf_oAN); + test("\u04517625000000", "" + f_strU + s_I); + test("92-96.0", "" + sf_strU2 + s_dM); + test("92null", "" + sf_strU2 + s_oNtS); + test("92\u045176", "" + sf_strU2 + f_strU); + test("9292", "" + sf_strU2 + sf_strU2); + test("9251", "" + sf_strU2 + sf_strU1); + test("92null", "" + sf_strU2 + s_iAN); + test("92-54", "" + sf_strU2 + f_bM); + test("92-87.0", "" + sf_strU2 + f_fM); + test("92null", "" + sf_strU2 + s_oAN); + test("9219", "" + sf_strU2 + f_str); + test("92-41", "" + sf_strU2 + sf_bM); + test("92null", "" + sf_strU2 + sf_IN); + test("92T", "" + sf_strU2 + s_c); + test("92-42.0", "" + sf_strU2 + sf_fM); + test("9225", "" + sf_strU2 + s_b); + test("92null", "" + sf_strU2 + f_oN); + test("92-1410065408", "" + sf_strU2 + s_lM); + test("928.0", "" + sf_strU2 + s_d); + test("9255.0", "" + sf_strU2 + s_f); + test("9297000000", "" + sf_strU2 + s_i); + test("92-9900", "" + sf_strU2 + f_sM); + test("92935228928", "" + sf_strU2 + s_l); + test("92-8400", "" + sf_strU2 + sf_sM); + test("92C(82)", "" + sf_strU2 + s_o); + test("92null", "" + sf_strU2 + sf_oNtS); + test("92true", "" + sf_strU2 + s_bl); + test("923900", "" + sf_strU2 + s_s); + test("92null", "" + sf_strU2 + sf_oN); + test("9294000000", "" + sf_strU2 + f_I); + test("92null", "" + sf_strU2 + f_IN); + test("92true", "" + sf_strU2 + sf_bl); + test("925500", "" + sf_strU2 + sf_s); + test("92-2900", "" + sf_strU2 + s_sM); + test("92-194313216", "" + sf_strU2 + sf_l); + test("9212", "" + sf_strU2 + s_strU1); + test("92C(87)", "" + sf_strU2 + sf_o); + test("9291", "" + sf_strU2 + s_strU2); + test("9221", "" + sf_strU2 + f_strU1); + test("9218", "" + sf_strU2 + f_strU2); + test("92null", "" + sf_strU2 + f_iAN); + test("92null", "" + sf_strU2 + s_oN); + test("92\u045180", "" + sf_strU2 + s_strU); + test("92C", "" + sf_strU2 + sf_c); + test("9275", "" + sf_strU2 + sf_str); + test("92-43", "" + sf_strU2 + s_bM); + test("9280", "" + sf_strU2 + sf_b); + test("92null", "" + sf_strU2 + s_IN); + test("92-52.0", "" + sf_strU2 + s_fM); + test("9275000000", "" + sf_strU2 + sf_i); + test("9244", "" + sf_strU2 + f_b); + test("92-1705032704", "" + sf_strU2 + sf_lM); + test("92null", "" + sf_strU2 + f_oAN); + test("9283.0", "" + sf_strU2 + f_d); + test("92I", "" + sf_strU2 + f_c); + test("9294.0", "" + sf_strU2 + f_f); + test("9212.0", "" + sf_strU2 + sf_d); + test("92-99.0", "" + sf_strU2 + f_dM); + test("9217.0", "" + sf_strU2 + sf_f); + test("92-84.0", "" + sf_strU2 + sf_dM); + test("9258000000", "" + sf_strU2 + f_i); + test("92-55000000", "" + sf_strU2 + f_iM); + test("921460392448", "" + sf_strU2 + f_l); + test("92C(70)", "" + sf_strU2 + f_o); + test("92\u04511", "" + sf_strU2 + sf_strU); + test("928000", "" + sf_strU2 + f_s); + test("9218", "" + sf_strU2 + s_str); + test("92-1000000", "" + sf_strU2 + s_iM); + test("921000000", "" + sf_strU2 + sf_I); + test("92null", "" + sf_strU2 + f_oNtS); + test("92false", "" + sf_strU2 + f_bl); + test("92null", "" + sf_strU2 + sf_iAN); + test("92-2000000", "" + sf_strU2 + sf_iM); + test("92-820130816", "" + sf_strU2 + f_lM); + test("92null", "" + sf_strU2 + sf_oAN); + test("9225000000", "" + sf_strU2 + s_I); + test("51-96.0", "" + sf_strU1 + s_dM); + test("51null", "" + sf_strU1 + s_oNtS); + test("51\u045176", "" + sf_strU1 + f_strU); + test("5192", "" + sf_strU1 + sf_strU2); + test("5151", "" + sf_strU1 + sf_strU1); + test("51null", "" + sf_strU1 + s_iAN); + test("51-54", "" + sf_strU1 + f_bM); + test("51-87.0", "" + sf_strU1 + f_fM); + test("51null", "" + sf_strU1 + s_oAN); + test("5119", "" + sf_strU1 + f_str); + test("51-41", "" + sf_strU1 + sf_bM); + test("51null", "" + sf_strU1 + sf_IN); + test("51T", "" + sf_strU1 + s_c); + test("51-42.0", "" + sf_strU1 + sf_fM); + test("5125", "" + sf_strU1 + s_b); + test("51null", "" + sf_strU1 + f_oN); + test("51-1410065408", "" + sf_strU1 + s_lM); + test("518.0", "" + sf_strU1 + s_d); + test("5155.0", "" + sf_strU1 + s_f); + test("5197000000", "" + sf_strU1 + s_i); + test("51-9900", "" + sf_strU1 + f_sM); + test("51935228928", "" + sf_strU1 + s_l); + test("51-8400", "" + sf_strU1 + sf_sM); + test("51C(82)", "" + sf_strU1 + s_o); + test("51null", "" + sf_strU1 + sf_oNtS); + test("51true", "" + sf_strU1 + s_bl); + test("513900", "" + sf_strU1 + s_s); + test("51null", "" + sf_strU1 + sf_oN); + test("5194000000", "" + sf_strU1 + f_I); + test("51null", "" + sf_strU1 + f_IN); + test("51true", "" + sf_strU1 + sf_bl); + test("515500", "" + sf_strU1 + sf_s); + test("51-2900", "" + sf_strU1 + s_sM); + test("51-194313216", "" + sf_strU1 + sf_l); + test("5112", "" + sf_strU1 + s_strU1); + test("51C(87)", "" + sf_strU1 + sf_o); + test("5191", "" + sf_strU1 + s_strU2); + test("5121", "" + sf_strU1 + f_strU1); + test("5118", "" + sf_strU1 + f_strU2); + test("51null", "" + sf_strU1 + f_iAN); + test("51null", "" + sf_strU1 + s_oN); + test("51\u045180", "" + sf_strU1 + s_strU); + test("51C", "" + sf_strU1 + sf_c); + test("5175", "" + sf_strU1 + sf_str); + test("51-43", "" + sf_strU1 + s_bM); + test("5180", "" + sf_strU1 + sf_b); + test("51null", "" + sf_strU1 + s_IN); + test("51-52.0", "" + sf_strU1 + s_fM); + test("5175000000", "" + sf_strU1 + sf_i); + test("5144", "" + sf_strU1 + f_b); + test("51-1705032704", "" + sf_strU1 + sf_lM); + test("51null", "" + sf_strU1 + f_oAN); + test("5183.0", "" + sf_strU1 + f_d); + test("51I", "" + sf_strU1 + f_c); + test("5194.0", "" + sf_strU1 + f_f); + test("5112.0", "" + sf_strU1 + sf_d); + test("51-99.0", "" + sf_strU1 + f_dM); + test("5117.0", "" + sf_strU1 + sf_f); + test("51-84.0", "" + sf_strU1 + sf_dM); + test("5158000000", "" + sf_strU1 + f_i); + test("51-55000000", "" + sf_strU1 + f_iM); + test("511460392448", "" + sf_strU1 + f_l); + test("51C(70)", "" + sf_strU1 + f_o); + test("51\u04511", "" + sf_strU1 + sf_strU); + test("518000", "" + sf_strU1 + f_s); + test("5118", "" + sf_strU1 + s_str); + test("51-1000000", "" + sf_strU1 + s_iM); + test("511000000", "" + sf_strU1 + sf_I); + test("51null", "" + sf_strU1 + f_oNtS); + test("51false", "" + sf_strU1 + f_bl); + test("51null", "" + sf_strU1 + sf_iAN); + test("51-2000000", "" + sf_strU1 + sf_iM); + test("51-820130816", "" + sf_strU1 + f_lM); + test("51null", "" + sf_strU1 + sf_oAN); + test("5125000000", "" + sf_strU1 + s_I); + test("null-96.0", "" + s_iAN + s_dM); + test("nullnull", "" + s_iAN + s_oNtS); + test("null\u045176", "" + s_iAN + f_strU); + test("null92", "" + s_iAN + sf_strU2); + test("null51", "" + s_iAN + sf_strU1); + test("nullnull", "" + s_iAN + s_iAN); + test("null-54", "" + s_iAN + f_bM); + test("null-87.0", "" + s_iAN + f_fM); + test("nullnull", "" + s_iAN + s_oAN); + test("null19", "" + s_iAN + f_str); + test("null-41", "" + s_iAN + sf_bM); + test("nullnull", "" + s_iAN + sf_IN); + test("nullT", "" + s_iAN + s_c); + test("null-42.0", "" + s_iAN + sf_fM); + test("null25", "" + s_iAN + s_b); + test("nullnull", "" + s_iAN + f_oN); + test("null-1410065408", "" + s_iAN + s_lM); + test("null8.0", "" + s_iAN + s_d); + test("null55.0", "" + s_iAN + s_f); + test("null97000000", "" + s_iAN + s_i); + test("null-9900", "" + s_iAN + f_sM); + test("null935228928", "" + s_iAN + s_l); + test("null-8400", "" + s_iAN + sf_sM); + test("nullC(82)", "" + s_iAN + s_o); + test("nullnull", "" + s_iAN + sf_oNtS); + test("nulltrue", "" + s_iAN + s_bl); + test("null3900", "" + s_iAN + s_s); + test("nullnull", "" + s_iAN + sf_oN); + test("null94000000", "" + s_iAN + f_I); + test("nullnull", "" + s_iAN + f_IN); + test("nulltrue", "" + s_iAN + sf_bl); + test("null5500", "" + s_iAN + sf_s); + test("null-2900", "" + s_iAN + s_sM); + test("null-194313216", "" + s_iAN + sf_l); + test("null12", "" + s_iAN + s_strU1); + test("nullC(87)", "" + s_iAN + sf_o); + test("null91", "" + s_iAN + s_strU2); + test("null21", "" + s_iAN + f_strU1); + test("null18", "" + s_iAN + f_strU2); + test("nullnull", "" + s_iAN + f_iAN); + test("nullnull", "" + s_iAN + s_oN); + test("null\u045180", "" + s_iAN + s_strU); + test("nullC", "" + s_iAN + sf_c); + test("null75", "" + s_iAN + sf_str); + test("null-43", "" + s_iAN + s_bM); + test("null80", "" + s_iAN + sf_b); + test("nullnull", "" + s_iAN + s_IN); + test("null-52.0", "" + s_iAN + s_fM); + test("null75000000", "" + s_iAN + sf_i); + test("null44", "" + s_iAN + f_b); + test("null-1705032704", "" + s_iAN + sf_lM); + test("nullnull", "" + s_iAN + f_oAN); + test("null83.0", "" + s_iAN + f_d); + test("nullI", "" + s_iAN + f_c); + test("null94.0", "" + s_iAN + f_f); + test("null12.0", "" + s_iAN + sf_d); + test("null-99.0", "" + s_iAN + f_dM); + test("null17.0", "" + s_iAN + sf_f); + test("null-84.0", "" + s_iAN + sf_dM); + test("null58000000", "" + s_iAN + f_i); + test("null-55000000", "" + s_iAN + f_iM); + test("null1460392448", "" + s_iAN + f_l); + test("nullC(70)", "" + s_iAN + f_o); + test("null\u04511", "" + s_iAN + sf_strU); + test("null8000", "" + s_iAN + f_s); + test("null18", "" + s_iAN + s_str); + test("null-1000000", "" + s_iAN + s_iM); + test("null1000000", "" + s_iAN + sf_I); + test("nullnull", "" + s_iAN + f_oNtS); + test("nullfalse", "" + s_iAN + f_bl); + test("nullnull", "" + s_iAN + sf_iAN); + test("null-2000000", "" + s_iAN + sf_iM); + test("null-820130816", "" + s_iAN + f_lM); + test("nullnull", "" + s_iAN + sf_oAN); + test("null25000000", "" + s_iAN + s_I); + test("-54-96.0", "" + f_bM + s_dM); + test("-54null", "" + f_bM + s_oNtS); + test("-54\u045176", "" + f_bM + f_strU); + test("-5492", "" + f_bM + sf_strU2); + test("-5451", "" + f_bM + sf_strU1); + test("-54null", "" + f_bM + s_iAN); + test("-54-54", "" + f_bM + f_bM); + test("-54-87.0", "" + f_bM + f_fM); + test("-54null", "" + f_bM + s_oAN); + test("-5419", "" + f_bM + f_str); + test("-54-41", "" + f_bM + sf_bM); + test("-54null", "" + f_bM + sf_IN); + test("-54T", "" + f_bM + s_c); + test("-54-42.0", "" + f_bM + sf_fM); + test("-5425", "" + f_bM + s_b); + test("-54null", "" + f_bM + f_oN); + test("-54-1410065408", "" + f_bM + s_lM); + test("-548.0", "" + f_bM + s_d); + test("-5455.0", "" + f_bM + s_f); + test("-5497000000", "" + f_bM + s_i); + test("-54-9900", "" + f_bM + f_sM); + test("-54935228928", "" + f_bM + s_l); + test("-54-8400", "" + f_bM + sf_sM); + test("-54C(82)", "" + f_bM + s_o); + test("-54null", "" + f_bM + sf_oNtS); + test("-54true", "" + f_bM + s_bl); + test("-543900", "" + f_bM + s_s); + test("-54null", "" + f_bM + sf_oN); + test("-5494000000", "" + f_bM + f_I); + test("-54null", "" + f_bM + f_IN); + test("-54true", "" + f_bM + sf_bl); + test("-545500", "" + f_bM + sf_s); + test("-54-2900", "" + f_bM + s_sM); + test("-54-194313216", "" + f_bM + sf_l); + test("-5412", "" + f_bM + s_strU1); + test("-54C(87)", "" + f_bM + sf_o); + test("-5491", "" + f_bM + s_strU2); + test("-5421", "" + f_bM + f_strU1); + test("-5418", "" + f_bM + f_strU2); + test("-54null", "" + f_bM + f_iAN); + test("-54null", "" + f_bM + s_oN); + test("-54\u045180", "" + f_bM + s_strU); + test("-54C", "" + f_bM + sf_c); + test("-5475", "" + f_bM + sf_str); + test("-54-43", "" + f_bM + s_bM); + test("-5480", "" + f_bM + sf_b); + test("-54null", "" + f_bM + s_IN); + test("-54-52.0", "" + f_bM + s_fM); + test("-5475000000", "" + f_bM + sf_i); + test("-5444", "" + f_bM + f_b); + test("-54-1705032704", "" + f_bM + sf_lM); + test("-54null", "" + f_bM + f_oAN); + test("-5483.0", "" + f_bM + f_d); + test("-54I", "" + f_bM + f_c); + test("-5494.0", "" + f_bM + f_f); + test("-5412.0", "" + f_bM + sf_d); + test("-54-99.0", "" + f_bM + f_dM); + test("-5417.0", "" + f_bM + sf_f); + test("-54-84.0", "" + f_bM + sf_dM); + test("-5458000000", "" + f_bM + f_i); + test("-54-55000000", "" + f_bM + f_iM); + test("-541460392448", "" + f_bM + f_l); + test("-54C(70)", "" + f_bM + f_o); + test("-54\u04511", "" + f_bM + sf_strU); + test("-548000", "" + f_bM + f_s); + test("-5418", "" + f_bM + s_str); + test("-54-1000000", "" + f_bM + s_iM); + test("-541000000", "" + f_bM + sf_I); + test("-54null", "" + f_bM + f_oNtS); + test("-54false", "" + f_bM + f_bl); + test("-54null", "" + f_bM + sf_iAN); + test("-54-2000000", "" + f_bM + sf_iM); + test("-54-820130816", "" + f_bM + f_lM); + test("-54null", "" + f_bM + sf_oAN); + test("-5425000000", "" + f_bM + s_I); + test("-87.0-96.0", "" + f_fM + s_dM); + test("-87.0null", "" + f_fM + s_oNtS); + test("-87.0\u045176", "" + f_fM + f_strU); + test("-87.092", "" + f_fM + sf_strU2); + test("-87.051", "" + f_fM + sf_strU1); + test("-87.0null", "" + f_fM + s_iAN); + test("-87.0-54", "" + f_fM + f_bM); + test("-87.0-87.0", "" + f_fM + f_fM); + test("-87.0null", "" + f_fM + s_oAN); + test("-87.019", "" + f_fM + f_str); + test("-87.0-41", "" + f_fM + sf_bM); + test("-87.0null", "" + f_fM + sf_IN); + test("-87.0T", "" + f_fM + s_c); + test("-87.0-42.0", "" + f_fM + sf_fM); + test("-87.025", "" + f_fM + s_b); + test("-87.0null", "" + f_fM + f_oN); + test("-87.0-1410065408", "" + f_fM + s_lM); + test("-87.08.0", "" + f_fM + s_d); + test("-87.055.0", "" + f_fM + s_f); + test("-87.097000000", "" + f_fM + s_i); + test("-87.0-9900", "" + f_fM + f_sM); + test("-87.0935228928", "" + f_fM + s_l); + test("-87.0-8400", "" + f_fM + sf_sM); + test("-87.0C(82)", "" + f_fM + s_o); + test("-87.0null", "" + f_fM + sf_oNtS); + test("-87.0true", "" + f_fM + s_bl); + test("-87.03900", "" + f_fM + s_s); + test("-87.0null", "" + f_fM + sf_oN); + test("-87.094000000", "" + f_fM + f_I); + test("-87.0null", "" + f_fM + f_IN); + test("-87.0true", "" + f_fM + sf_bl); + test("-87.05500", "" + f_fM + sf_s); + test("-87.0-2900", "" + f_fM + s_sM); + test("-87.0-194313216", "" + f_fM + sf_l); + test("-87.012", "" + f_fM + s_strU1); + test("-87.0C(87)", "" + f_fM + sf_o); + test("-87.091", "" + f_fM + s_strU2); + test("-87.021", "" + f_fM + f_strU1); + test("-87.018", "" + f_fM + f_strU2); + test("-87.0null", "" + f_fM + f_iAN); + test("-87.0null", "" + f_fM + s_oN); + test("-87.0\u045180", "" + f_fM + s_strU); + test("-87.0C", "" + f_fM + sf_c); + test("-87.075", "" + f_fM + sf_str); + test("-87.0-43", "" + f_fM + s_bM); + test("-87.080", "" + f_fM + sf_b); + test("-87.0null", "" + f_fM + s_IN); + test("-87.0-52.0", "" + f_fM + s_fM); + test("-87.075000000", "" + f_fM + sf_i); + test("-87.044", "" + f_fM + f_b); + test("-87.0-1705032704", "" + f_fM + sf_lM); + test("-87.0null", "" + f_fM + f_oAN); + test("-87.083.0", "" + f_fM + f_d); + test("-87.0I", "" + f_fM + f_c); + test("-87.094.0", "" + f_fM + f_f); + test("-87.012.0", "" + f_fM + sf_d); + test("-87.0-99.0", "" + f_fM + f_dM); + test("-87.017.0", "" + f_fM + sf_f); + test("-87.0-84.0", "" + f_fM + sf_dM); + test("-87.058000000", "" + f_fM + f_i); + test("-87.0-55000000", "" + f_fM + f_iM); + test("-87.01460392448", "" + f_fM + f_l); + test("-87.0C(70)", "" + f_fM + f_o); + test("-87.0\u04511", "" + f_fM + sf_strU); + test("-87.08000", "" + f_fM + f_s); + test("-87.018", "" + f_fM + s_str); + test("-87.0-1000000", "" + f_fM + s_iM); + test("-87.01000000", "" + f_fM + sf_I); + test("-87.0null", "" + f_fM + f_oNtS); + test("-87.0false", "" + f_fM + f_bl); + test("-87.0null", "" + f_fM + sf_iAN); + test("-87.0-2000000", "" + f_fM + sf_iM); + test("-87.0-820130816", "" + f_fM + f_lM); + test("-87.0null", "" + f_fM + sf_oAN); + test("-87.025000000", "" + f_fM + s_I); + test("null-96.0", "" + s_oAN + s_dM); + test("nullnull", "" + s_oAN + s_oNtS); + test("null\u045176", "" + s_oAN + f_strU); + test("null92", "" + s_oAN + sf_strU2); + test("null51", "" + s_oAN + sf_strU1); + test("nullnull", "" + s_oAN + s_iAN); + test("null-54", "" + s_oAN + f_bM); + test("null-87.0", "" + s_oAN + f_fM); + test("nullnull", "" + s_oAN + s_oAN); + test("null19", "" + s_oAN + f_str); + test("null-41", "" + s_oAN + sf_bM); + test("nullnull", "" + s_oAN + sf_IN); + test("nullT", "" + s_oAN + s_c); + test("null-42.0", "" + s_oAN + sf_fM); + test("null25", "" + s_oAN + s_b); + test("nullnull", "" + s_oAN + f_oN); + test("null-1410065408", "" + s_oAN + s_lM); + test("null8.0", "" + s_oAN + s_d); + test("null55.0", "" + s_oAN + s_f); + test("null97000000", "" + s_oAN + s_i); + test("null-9900", "" + s_oAN + f_sM); + test("null935228928", "" + s_oAN + s_l); + test("null-8400", "" + s_oAN + sf_sM); + test("nullC(82)", "" + s_oAN + s_o); + test("nullnull", "" + s_oAN + sf_oNtS); + test("nulltrue", "" + s_oAN + s_bl); + test("null3900", "" + s_oAN + s_s); + test("nullnull", "" + s_oAN + sf_oN); + test("null94000000", "" + s_oAN + f_I); + test("nullnull", "" + s_oAN + f_IN); + test("nulltrue", "" + s_oAN + sf_bl); + test("null5500", "" + s_oAN + sf_s); + test("null-2900", "" + s_oAN + s_sM); + test("null-194313216", "" + s_oAN + sf_l); + test("null12", "" + s_oAN + s_strU1); + test("nullC(87)", "" + s_oAN + sf_o); + test("null91", "" + s_oAN + s_strU2); + test("null21", "" + s_oAN + f_strU1); + test("null18", "" + s_oAN + f_strU2); + test("nullnull", "" + s_oAN + f_iAN); + test("nullnull", "" + s_oAN + s_oN); + test("null\u045180", "" + s_oAN + s_strU); + test("nullC", "" + s_oAN + sf_c); + test("null75", "" + s_oAN + sf_str); + test("null-43", "" + s_oAN + s_bM); + test("null80", "" + s_oAN + sf_b); + test("nullnull", "" + s_oAN + s_IN); + test("null-52.0", "" + s_oAN + s_fM); + test("null75000000", "" + s_oAN + sf_i); + test("null44", "" + s_oAN + f_b); + test("null-1705032704", "" + s_oAN + sf_lM); + test("nullnull", "" + s_oAN + f_oAN); + test("null83.0", "" + s_oAN + f_d); + test("nullI", "" + s_oAN + f_c); + test("null94.0", "" + s_oAN + f_f); + test("null12.0", "" + s_oAN + sf_d); + test("null-99.0", "" + s_oAN + f_dM); + test("null17.0", "" + s_oAN + sf_f); + test("null-84.0", "" + s_oAN + sf_dM); + test("null58000000", "" + s_oAN + f_i); + test("null-55000000", "" + s_oAN + f_iM); + test("null1460392448", "" + s_oAN + f_l); + test("nullC(70)", "" + s_oAN + f_o); + test("null\u04511", "" + s_oAN + sf_strU); + test("null8000", "" + s_oAN + f_s); + test("null18", "" + s_oAN + s_str); + test("null-1000000", "" + s_oAN + s_iM); + test("null1000000", "" + s_oAN + sf_I); + test("nullnull", "" + s_oAN + f_oNtS); + test("nullfalse", "" + s_oAN + f_bl); + test("nullnull", "" + s_oAN + sf_iAN); + test("null-2000000", "" + s_oAN + sf_iM); + test("null-820130816", "" + s_oAN + f_lM); + test("nullnull", "" + s_oAN + sf_oAN); + test("null25000000", "" + s_oAN + s_I); + test("19-96.0", "" + f_str + s_dM); + test("19null", "" + f_str + s_oNtS); + test("19\u045176", "" + f_str + f_strU); + test("1992", "" + f_str + sf_strU2); + test("1951", "" + f_str + sf_strU1); + test("19null", "" + f_str + s_iAN); + test("19-54", "" + f_str + f_bM); + test("19-87.0", "" + f_str + f_fM); + test("19null", "" + f_str + s_oAN); + test("1919", "" + f_str + f_str); + test("19-41", "" + f_str + sf_bM); + test("19null", "" + f_str + sf_IN); + test("19T", "" + f_str + s_c); + test("19-42.0", "" + f_str + sf_fM); + test("1925", "" + f_str + s_b); + test("19null", "" + f_str + f_oN); + test("19-1410065408", "" + f_str + s_lM); + test("198.0", "" + f_str + s_d); + test("1955.0", "" + f_str + s_f); + test("1997000000", "" + f_str + s_i); + test("19-9900", "" + f_str + f_sM); + test("19935228928", "" + f_str + s_l); + test("19-8400", "" + f_str + sf_sM); + test("19C(82)", "" + f_str + s_o); + test("19null", "" + f_str + sf_oNtS); + test("19true", "" + f_str + s_bl); + test("193900", "" + f_str + s_s); + test("19null", "" + f_str + sf_oN); + test("1994000000", "" + f_str + f_I); + test("19null", "" + f_str + f_IN); + test("19true", "" + f_str + sf_bl); + test("195500", "" + f_str + sf_s); + test("19-2900", "" + f_str + s_sM); + test("19-194313216", "" + f_str + sf_l); + test("1912", "" + f_str + s_strU1); + test("19C(87)", "" + f_str + sf_o); + test("1991", "" + f_str + s_strU2); + test("1921", "" + f_str + f_strU1); + test("1918", "" + f_str + f_strU2); + test("19null", "" + f_str + f_iAN); + test("19null", "" + f_str + s_oN); + test("19\u045180", "" + f_str + s_strU); + test("19C", "" + f_str + sf_c); + test("1975", "" + f_str + sf_str); + test("19-43", "" + f_str + s_bM); + test("1980", "" + f_str + sf_b); + test("19null", "" + f_str + s_IN); + test("19-52.0", "" + f_str + s_fM); + test("1975000000", "" + f_str + sf_i); + test("1944", "" + f_str + f_b); + test("19-1705032704", "" + f_str + sf_lM); + test("19null", "" + f_str + f_oAN); + test("1983.0", "" + f_str + f_d); + test("19I", "" + f_str + f_c); + test("1994.0", "" + f_str + f_f); + test("1912.0", "" + f_str + sf_d); + test("19-99.0", "" + f_str + f_dM); + test("1917.0", "" + f_str + sf_f); + test("19-84.0", "" + f_str + sf_dM); + test("1958000000", "" + f_str + f_i); + test("19-55000000", "" + f_str + f_iM); + test("191460392448", "" + f_str + f_l); + test("19C(70)", "" + f_str + f_o); + test("19\u04511", "" + f_str + sf_strU); + test("198000", "" + f_str + f_s); + test("1918", "" + f_str + s_str); + test("19-1000000", "" + f_str + s_iM); + test("191000000", "" + f_str + sf_I); + test("19null", "" + f_str + f_oNtS); + test("19false", "" + f_str + f_bl); + test("19null", "" + f_str + sf_iAN); + test("19-2000000", "" + f_str + sf_iM); + test("19-820130816", "" + f_str + f_lM); + test("19null", "" + f_str + sf_oAN); + test("1925000000", "" + f_str + s_I); + test("-41-96.0", "" + sf_bM + s_dM); + test("-41null", "" + sf_bM + s_oNtS); + test("-41\u045176", "" + sf_bM + f_strU); + test("-4192", "" + sf_bM + sf_strU2); + test("-4151", "" + sf_bM + sf_strU1); + test("-41null", "" + sf_bM + s_iAN); + test("-41-54", "" + sf_bM + f_bM); + test("-41-87.0", "" + sf_bM + f_fM); + test("-41null", "" + sf_bM + s_oAN); + test("-4119", "" + sf_bM + f_str); + test("-41-41", "" + sf_bM + sf_bM); + test("-41null", "" + sf_bM + sf_IN); + test("-41T", "" + sf_bM + s_c); + test("-41-42.0", "" + sf_bM + sf_fM); + test("-4125", "" + sf_bM + s_b); + test("-41null", "" + sf_bM + f_oN); + test("-41-1410065408", "" + sf_bM + s_lM); + test("-418.0", "" + sf_bM + s_d); + test("-4155.0", "" + sf_bM + s_f); + test("-4197000000", "" + sf_bM + s_i); + test("-41-9900", "" + sf_bM + f_sM); + test("-41935228928", "" + sf_bM + s_l); + test("-41-8400", "" + sf_bM + sf_sM); + test("-41C(82)", "" + sf_bM + s_o); + test("-41null", "" + sf_bM + sf_oNtS); + test("-41true", "" + sf_bM + s_bl); + test("-413900", "" + sf_bM + s_s); + test("-41null", "" + sf_bM + sf_oN); + test("-4194000000", "" + sf_bM + f_I); + test("-41null", "" + sf_bM + f_IN); + test("-41true", "" + sf_bM + sf_bl); + test("-415500", "" + sf_bM + sf_s); + test("-41-2900", "" + sf_bM + s_sM); + test("-41-194313216", "" + sf_bM + sf_l); + test("-4112", "" + sf_bM + s_strU1); + test("-41C(87)", "" + sf_bM + sf_o); + test("-4191", "" + sf_bM + s_strU2); + test("-4121", "" + sf_bM + f_strU1); + test("-4118", "" + sf_bM + f_strU2); + test("-41null", "" + sf_bM + f_iAN); + test("-41null", "" + sf_bM + s_oN); + test("-41\u045180", "" + sf_bM + s_strU); + test("-41C", "" + sf_bM + sf_c); + test("-4175", "" + sf_bM + sf_str); + test("-41-43", "" + sf_bM + s_bM); + test("-4180", "" + sf_bM + sf_b); + test("-41null", "" + sf_bM + s_IN); + test("-41-52.0", "" + sf_bM + s_fM); + test("-4175000000", "" + sf_bM + sf_i); + test("-4144", "" + sf_bM + f_b); + test("-41-1705032704", "" + sf_bM + sf_lM); + test("-41null", "" + sf_bM + f_oAN); + test("-4183.0", "" + sf_bM + f_d); + test("-41I", "" + sf_bM + f_c); + test("-4194.0", "" + sf_bM + f_f); + test("-4112.0", "" + sf_bM + sf_d); + test("-41-99.0", "" + sf_bM + f_dM); + test("-4117.0", "" + sf_bM + sf_f); + test("-41-84.0", "" + sf_bM + sf_dM); + test("-4158000000", "" + sf_bM + f_i); + test("-41-55000000", "" + sf_bM + f_iM); + test("-411460392448", "" + sf_bM + f_l); + test("-41C(70)", "" + sf_bM + f_o); + test("-41\u04511", "" + sf_bM + sf_strU); + test("-418000", "" + sf_bM + f_s); + test("-4118", "" + sf_bM + s_str); + test("-41-1000000", "" + sf_bM + s_iM); + test("-411000000", "" + sf_bM + sf_I); + test("-41null", "" + sf_bM + f_oNtS); + test("-41false", "" + sf_bM + f_bl); + test("-41null", "" + sf_bM + sf_iAN); + test("-41-2000000", "" + sf_bM + sf_iM); + test("-41-820130816", "" + sf_bM + f_lM); + test("-41null", "" + sf_bM + sf_oAN); + test("-4125000000", "" + sf_bM + s_I); + test("null-96.0", "" + sf_IN + s_dM); + test("nullnull", "" + sf_IN + s_oNtS); + test("null\u045176", "" + sf_IN + f_strU); + test("null92", "" + sf_IN + sf_strU2); + test("null51", "" + sf_IN + sf_strU1); + test("nullnull", "" + sf_IN + s_iAN); + test("null-54", "" + sf_IN + f_bM); + test("null-87.0", "" + sf_IN + f_fM); + test("nullnull", "" + sf_IN + s_oAN); + test("null19", "" + sf_IN + f_str); + test("null-41", "" + sf_IN + sf_bM); + test("nullnull", "" + sf_IN + sf_IN); + test("nullT", "" + sf_IN + s_c); + test("null-42.0", "" + sf_IN + sf_fM); + test("null25", "" + sf_IN + s_b); + test("nullnull", "" + sf_IN + f_oN); + test("null-1410065408", "" + sf_IN + s_lM); + test("null8.0", "" + sf_IN + s_d); + test("null55.0", "" + sf_IN + s_f); + test("null97000000", "" + sf_IN + s_i); + test("null-9900", "" + sf_IN + f_sM); + test("null935228928", "" + sf_IN + s_l); + test("null-8400", "" + sf_IN + sf_sM); + test("nullC(82)", "" + sf_IN + s_o); + test("nullnull", "" + sf_IN + sf_oNtS); + test("nulltrue", "" + sf_IN + s_bl); + test("null3900", "" + sf_IN + s_s); + test("nullnull", "" + sf_IN + sf_oN); + test("null94000000", "" + sf_IN + f_I); + test("nullnull", "" + sf_IN + f_IN); + test("nulltrue", "" + sf_IN + sf_bl); + test("null5500", "" + sf_IN + sf_s); + test("null-2900", "" + sf_IN + s_sM); + test("null-194313216", "" + sf_IN + sf_l); + test("null12", "" + sf_IN + s_strU1); + test("nullC(87)", "" + sf_IN + sf_o); + test("null91", "" + sf_IN + s_strU2); + test("null21", "" + sf_IN + f_strU1); + test("null18", "" + sf_IN + f_strU2); + test("nullnull", "" + sf_IN + f_iAN); + test("nullnull", "" + sf_IN + s_oN); + test("null\u045180", "" + sf_IN + s_strU); + test("nullC", "" + sf_IN + sf_c); + test("null75", "" + sf_IN + sf_str); + test("null-43", "" + sf_IN + s_bM); + test("null80", "" + sf_IN + sf_b); + test("nullnull", "" + sf_IN + s_IN); + test("null-52.0", "" + sf_IN + s_fM); + test("null75000000", "" + sf_IN + sf_i); + test("null44", "" + sf_IN + f_b); + test("null-1705032704", "" + sf_IN + sf_lM); + test("nullnull", "" + sf_IN + f_oAN); + test("null83.0", "" + sf_IN + f_d); + test("nullI", "" + sf_IN + f_c); + test("null94.0", "" + sf_IN + f_f); + test("null12.0", "" + sf_IN + sf_d); + test("null-99.0", "" + sf_IN + f_dM); + test("null17.0", "" + sf_IN + sf_f); + test("null-84.0", "" + sf_IN + sf_dM); + test("null58000000", "" + sf_IN + f_i); + test("null-55000000", "" + sf_IN + f_iM); + test("null1460392448", "" + sf_IN + f_l); + test("nullC(70)", "" + sf_IN + f_o); + test("null\u04511", "" + sf_IN + sf_strU); + test("null8000", "" + sf_IN + f_s); + test("null18", "" + sf_IN + s_str); + test("null-1000000", "" + sf_IN + s_iM); + test("null1000000", "" + sf_IN + sf_I); + test("nullnull", "" + sf_IN + f_oNtS); + test("nullfalse", "" + sf_IN + f_bl); + test("nullnull", "" + sf_IN + sf_iAN); + test("null-2000000", "" + sf_IN + sf_iM); + test("null-820130816", "" + sf_IN + f_lM); + test("nullnull", "" + sf_IN + sf_oAN); + test("null25000000", "" + sf_IN + s_I); + test("T-96.0", "" + s_c + s_dM); + test("Tnull", "" + s_c + s_oNtS); + test("T\u045176", "" + s_c + f_strU); + test("T92", "" + s_c + sf_strU2); + test("T51", "" + s_c + sf_strU1); + test("Tnull", "" + s_c + s_iAN); + test("T-54", "" + s_c + f_bM); + test("T-87.0", "" + s_c + f_fM); + test("Tnull", "" + s_c + s_oAN); + test("T19", "" + s_c + f_str); + test("T-41", "" + s_c + sf_bM); + test("Tnull", "" + s_c + sf_IN); + test("TT", "" + s_c + s_c); + test("T-42.0", "" + s_c + sf_fM); + test("T25", "" + s_c + s_b); + test("Tnull", "" + s_c + f_oN); + test("T-1410065408", "" + s_c + s_lM); + test("T8.0", "" + s_c + s_d); + test("T55.0", "" + s_c + s_f); + test("T97000000", "" + s_c + s_i); + test("T-9900", "" + s_c + f_sM); + test("T935228928", "" + s_c + s_l); + test("T-8400", "" + s_c + sf_sM); + test("TC(82)", "" + s_c + s_o); + test("Tnull", "" + s_c + sf_oNtS); + } + + public void run1() { + test("Ttrue", "" + s_c + s_bl); + test("T3900", "" + s_c + s_s); + test("Tnull", "" + s_c + sf_oN); + test("T94000000", "" + s_c + f_I); + test("Tnull", "" + s_c + f_IN); + test("Ttrue", "" + s_c + sf_bl); + test("T5500", "" + s_c + sf_s); + test("T-2900", "" + s_c + s_sM); + test("T-194313216", "" + s_c + sf_l); + test("T12", "" + s_c + s_strU1); + test("TC(87)", "" + s_c + sf_o); + test("T91", "" + s_c + s_strU2); + test("T21", "" + s_c + f_strU1); + test("T18", "" + s_c + f_strU2); + test("Tnull", "" + s_c + f_iAN); + test("Tnull", "" + s_c + s_oN); + test("T\u045180", "" + s_c + s_strU); + test("TC", "" + s_c + sf_c); + test("T75", "" + s_c + sf_str); + test("T-43", "" + s_c + s_bM); + test("T80", "" + s_c + sf_b); + test("Tnull", "" + s_c + s_IN); + test("T-52.0", "" + s_c + s_fM); + test("T75000000", "" + s_c + sf_i); + test("T44", "" + s_c + f_b); + test("T-1705032704", "" + s_c + sf_lM); + test("Tnull", "" + s_c + f_oAN); + test("T83.0", "" + s_c + f_d); + test("TI", "" + s_c + f_c); + test("T94.0", "" + s_c + f_f); + test("T12.0", "" + s_c + sf_d); + test("T-99.0", "" + s_c + f_dM); + test("T17.0", "" + s_c + sf_f); + test("T-84.0", "" + s_c + sf_dM); + test("T58000000", "" + s_c + f_i); + test("T-55000000", "" + s_c + f_iM); + test("T1460392448", "" + s_c + f_l); + test("TC(70)", "" + s_c + f_o); + test("T\u04511", "" + s_c + sf_strU); + test("T8000", "" + s_c + f_s); + test("T18", "" + s_c + s_str); + test("T-1000000", "" + s_c + s_iM); + test("T1000000", "" + s_c + sf_I); + test("Tnull", "" + s_c + f_oNtS); + test("Tfalse", "" + s_c + f_bl); + test("Tnull", "" + s_c + sf_iAN); + test("T-2000000", "" + s_c + sf_iM); + test("T-820130816", "" + s_c + f_lM); + test("Tnull", "" + s_c + sf_oAN); + test("T25000000", "" + s_c + s_I); + test("-42.0-96.0", "" + sf_fM + s_dM); + test("-42.0null", "" + sf_fM + s_oNtS); + test("-42.0\u045176", "" + sf_fM + f_strU); + test("-42.092", "" + sf_fM + sf_strU2); + test("-42.051", "" + sf_fM + sf_strU1); + test("-42.0null", "" + sf_fM + s_iAN); + test("-42.0-54", "" + sf_fM + f_bM); + test("-42.0-87.0", "" + sf_fM + f_fM); + test("-42.0null", "" + sf_fM + s_oAN); + test("-42.019", "" + sf_fM + f_str); + test("-42.0-41", "" + sf_fM + sf_bM); + test("-42.0null", "" + sf_fM + sf_IN); + test("-42.0T", "" + sf_fM + s_c); + test("-42.0-42.0", "" + sf_fM + sf_fM); + test("-42.025", "" + sf_fM + s_b); + test("-42.0null", "" + sf_fM + f_oN); + test("-42.0-1410065408", "" + sf_fM + s_lM); + test("-42.08.0", "" + sf_fM + s_d); + test("-42.055.0", "" + sf_fM + s_f); + test("-42.097000000", "" + sf_fM + s_i); + test("-42.0-9900", "" + sf_fM + f_sM); + test("-42.0935228928", "" + sf_fM + s_l); + test("-42.0-8400", "" + sf_fM + sf_sM); + test("-42.0C(82)", "" + sf_fM + s_o); + test("-42.0null", "" + sf_fM + sf_oNtS); + test("-42.0true", "" + sf_fM + s_bl); + test("-42.03900", "" + sf_fM + s_s); + test("-42.0null", "" + sf_fM + sf_oN); + test("-42.094000000", "" + sf_fM + f_I); + test("-42.0null", "" + sf_fM + f_IN); + test("-42.0true", "" + sf_fM + sf_bl); + test("-42.05500", "" + sf_fM + sf_s); + test("-42.0-2900", "" + sf_fM + s_sM); + test("-42.0-194313216", "" + sf_fM + sf_l); + test("-42.012", "" + sf_fM + s_strU1); + test("-42.0C(87)", "" + sf_fM + sf_o); + test("-42.091", "" + sf_fM + s_strU2); + test("-42.021", "" + sf_fM + f_strU1); + test("-42.018", "" + sf_fM + f_strU2); + test("-42.0null", "" + sf_fM + f_iAN); + test("-42.0null", "" + sf_fM + s_oN); + test("-42.0\u045180", "" + sf_fM + s_strU); + test("-42.0C", "" + sf_fM + sf_c); + test("-42.075", "" + sf_fM + sf_str); + test("-42.0-43", "" + sf_fM + s_bM); + test("-42.080", "" + sf_fM + sf_b); + test("-42.0null", "" + sf_fM + s_IN); + test("-42.0-52.0", "" + sf_fM + s_fM); + test("-42.075000000", "" + sf_fM + sf_i); + test("-42.044", "" + sf_fM + f_b); + test("-42.0-1705032704", "" + sf_fM + sf_lM); + test("-42.0null", "" + sf_fM + f_oAN); + test("-42.083.0", "" + sf_fM + f_d); + test("-42.0I", "" + sf_fM + f_c); + test("-42.094.0", "" + sf_fM + f_f); + test("-42.012.0", "" + sf_fM + sf_d); + test("-42.0-99.0", "" + sf_fM + f_dM); + test("-42.017.0", "" + sf_fM + sf_f); + test("-42.0-84.0", "" + sf_fM + sf_dM); + test("-42.058000000", "" + sf_fM + f_i); + test("-42.0-55000000", "" + sf_fM + f_iM); + test("-42.01460392448", "" + sf_fM + f_l); + test("-42.0C(70)", "" + sf_fM + f_o); + test("-42.0\u04511", "" + sf_fM + sf_strU); + test("-42.08000", "" + sf_fM + f_s); + test("-42.018", "" + sf_fM + s_str); + test("-42.0-1000000", "" + sf_fM + s_iM); + test("-42.01000000", "" + sf_fM + sf_I); + test("-42.0null", "" + sf_fM + f_oNtS); + test("-42.0false", "" + sf_fM + f_bl); + test("-42.0null", "" + sf_fM + sf_iAN); + test("-42.0-2000000", "" + sf_fM + sf_iM); + test("-42.0-820130816", "" + sf_fM + f_lM); + test("-42.0null", "" + sf_fM + sf_oAN); + test("-42.025000000", "" + sf_fM + s_I); + test("25-96.0", "" + s_b + s_dM); + test("25null", "" + s_b + s_oNtS); + test("25\u045176", "" + s_b + f_strU); + test("2592", "" + s_b + sf_strU2); + test("2551", "" + s_b + sf_strU1); + test("25null", "" + s_b + s_iAN); + test("25-54", "" + s_b + f_bM); + test("25-87.0", "" + s_b + f_fM); + test("25null", "" + s_b + s_oAN); + test("2519", "" + s_b + f_str); + test("25-41", "" + s_b + sf_bM); + test("25null", "" + s_b + sf_IN); + test("25T", "" + s_b + s_c); + test("25-42.0", "" + s_b + sf_fM); + test("2525", "" + s_b + s_b); + test("25null", "" + s_b + f_oN); + test("25-1410065408", "" + s_b + s_lM); + test("258.0", "" + s_b + s_d); + test("2555.0", "" + s_b + s_f); + test("2597000000", "" + s_b + s_i); + test("25-9900", "" + s_b + f_sM); + test("25935228928", "" + s_b + s_l); + test("25-8400", "" + s_b + sf_sM); + test("25C(82)", "" + s_b + s_o); + test("25null", "" + s_b + sf_oNtS); + test("25true", "" + s_b + s_bl); + test("253900", "" + s_b + s_s); + test("25null", "" + s_b + sf_oN); + test("2594000000", "" + s_b + f_I); + test("25null", "" + s_b + f_IN); + test("25true", "" + s_b + sf_bl); + test("255500", "" + s_b + sf_s); + test("25-2900", "" + s_b + s_sM); + test("25-194313216", "" + s_b + sf_l); + test("2512", "" + s_b + s_strU1); + test("25C(87)", "" + s_b + sf_o); + test("2591", "" + s_b + s_strU2); + test("2521", "" + s_b + f_strU1); + test("2518", "" + s_b + f_strU2); + test("25null", "" + s_b + f_iAN); + test("25null", "" + s_b + s_oN); + test("25\u045180", "" + s_b + s_strU); + test("25C", "" + s_b + sf_c); + test("2575", "" + s_b + sf_str); + test("25-43", "" + s_b + s_bM); + test("2580", "" + s_b + sf_b); + test("25null", "" + s_b + s_IN); + test("25-52.0", "" + s_b + s_fM); + test("2575000000", "" + s_b + sf_i); + test("2544", "" + s_b + f_b); + test("25-1705032704", "" + s_b + sf_lM); + test("25null", "" + s_b + f_oAN); + test("2583.0", "" + s_b + f_d); + test("25I", "" + s_b + f_c); + test("2594.0", "" + s_b + f_f); + test("2512.0", "" + s_b + sf_d); + test("25-99.0", "" + s_b + f_dM); + test("2517.0", "" + s_b + sf_f); + test("25-84.0", "" + s_b + sf_dM); + test("2558000000", "" + s_b + f_i); + test("25-55000000", "" + s_b + f_iM); + test("251460392448", "" + s_b + f_l); + test("25C(70)", "" + s_b + f_o); + test("25\u04511", "" + s_b + sf_strU); + test("258000", "" + s_b + f_s); + test("2518", "" + s_b + s_str); + test("25-1000000", "" + s_b + s_iM); + test("251000000", "" + s_b + sf_I); + test("25null", "" + s_b + f_oNtS); + test("25false", "" + s_b + f_bl); + test("25null", "" + s_b + sf_iAN); + test("25-2000000", "" + s_b + sf_iM); + test("25-820130816", "" + s_b + f_lM); + test("25null", "" + s_b + sf_oAN); + test("2525000000", "" + s_b + s_I); + test("null-96.0", "" + f_oN + s_dM); + test("nullnull", "" + f_oN + s_oNtS); + test("null\u045176", "" + f_oN + f_strU); + test("null92", "" + f_oN + sf_strU2); + test("null51", "" + f_oN + sf_strU1); + test("nullnull", "" + f_oN + s_iAN); + test("null-54", "" + f_oN + f_bM); + test("null-87.0", "" + f_oN + f_fM); + test("nullnull", "" + f_oN + s_oAN); + test("null19", "" + f_oN + f_str); + test("null-41", "" + f_oN + sf_bM); + test("nullnull", "" + f_oN + sf_IN); + test("nullT", "" + f_oN + s_c); + test("null-42.0", "" + f_oN + sf_fM); + test("null25", "" + f_oN + s_b); + test("nullnull", "" + f_oN + f_oN); + test("null-1410065408", "" + f_oN + s_lM); + test("null8.0", "" + f_oN + s_d); + test("null55.0", "" + f_oN + s_f); + test("null97000000", "" + f_oN + s_i); + test("null-9900", "" + f_oN + f_sM); + test("null935228928", "" + f_oN + s_l); + test("null-8400", "" + f_oN + sf_sM); + test("nullC(82)", "" + f_oN + s_o); + test("nullnull", "" + f_oN + sf_oNtS); + test("nulltrue", "" + f_oN + s_bl); + test("null3900", "" + f_oN + s_s); + test("nullnull", "" + f_oN + sf_oN); + test("null94000000", "" + f_oN + f_I); + test("nullnull", "" + f_oN + f_IN); + test("nulltrue", "" + f_oN + sf_bl); + test("null5500", "" + f_oN + sf_s); + test("null-2900", "" + f_oN + s_sM); + test("null-194313216", "" + f_oN + sf_l); + test("null12", "" + f_oN + s_strU1); + test("nullC(87)", "" + f_oN + sf_o); + test("null91", "" + f_oN + s_strU2); + test("null21", "" + f_oN + f_strU1); + test("null18", "" + f_oN + f_strU2); + test("nullnull", "" + f_oN + f_iAN); + test("nullnull", "" + f_oN + s_oN); + test("null\u045180", "" + f_oN + s_strU); + test("nullC", "" + f_oN + sf_c); + test("null75", "" + f_oN + sf_str); + test("null-43", "" + f_oN + s_bM); + test("null80", "" + f_oN + sf_b); + test("nullnull", "" + f_oN + s_IN); + test("null-52.0", "" + f_oN + s_fM); + test("null75000000", "" + f_oN + sf_i); + test("null44", "" + f_oN + f_b); + test("null-1705032704", "" + f_oN + sf_lM); + test("nullnull", "" + f_oN + f_oAN); + test("null83.0", "" + f_oN + f_d); + test("nullI", "" + f_oN + f_c); + test("null94.0", "" + f_oN + f_f); + test("null12.0", "" + f_oN + sf_d); + test("null-99.0", "" + f_oN + f_dM); + test("null17.0", "" + f_oN + sf_f); + test("null-84.0", "" + f_oN + sf_dM); + test("null58000000", "" + f_oN + f_i); + test("null-55000000", "" + f_oN + f_iM); + test("null1460392448", "" + f_oN + f_l); + test("nullC(70)", "" + f_oN + f_o); + test("null\u04511", "" + f_oN + sf_strU); + test("null8000", "" + f_oN + f_s); + test("null18", "" + f_oN + s_str); + test("null-1000000", "" + f_oN + s_iM); + test("null1000000", "" + f_oN + sf_I); + test("nullnull", "" + f_oN + f_oNtS); + test("nullfalse", "" + f_oN + f_bl); + test("nullnull", "" + f_oN + sf_iAN); + test("null-2000000", "" + f_oN + sf_iM); + test("null-820130816", "" + f_oN + f_lM); + test("nullnull", "" + f_oN + sf_oAN); + test("null25000000", "" + f_oN + s_I); + test("-1410065408-96.0", "" + s_lM + s_dM); + test("-1410065408null", "" + s_lM + s_oNtS); + test("-1410065408\u045176", "" + s_lM + f_strU); + test("-141006540892", "" + s_lM + sf_strU2); + test("-141006540851", "" + s_lM + sf_strU1); + test("-1410065408null", "" + s_lM + s_iAN); + test("-1410065408-54", "" + s_lM + f_bM); + test("-1410065408-87.0", "" + s_lM + f_fM); + test("-1410065408null", "" + s_lM + s_oAN); + test("-141006540819", "" + s_lM + f_str); + test("-1410065408-41", "" + s_lM + sf_bM); + test("-1410065408null", "" + s_lM + sf_IN); + test("-1410065408T", "" + s_lM + s_c); + test("-1410065408-42.0", "" + s_lM + sf_fM); + test("-141006540825", "" + s_lM + s_b); + test("-1410065408null", "" + s_lM + f_oN); + test("-1410065408-1410065408", "" + s_lM + s_lM); + test("-14100654088.0", "" + s_lM + s_d); + test("-141006540855.0", "" + s_lM + s_f); + test("-141006540897000000", "" + s_lM + s_i); + test("-1410065408-9900", "" + s_lM + f_sM); + test("-1410065408935228928", "" + s_lM + s_l); + test("-1410065408-8400", "" + s_lM + sf_sM); + test("-1410065408C(82)", "" + s_lM + s_o); + test("-1410065408null", "" + s_lM + sf_oNtS); + test("-1410065408true", "" + s_lM + s_bl); + test("-14100654083900", "" + s_lM + s_s); + test("-1410065408null", "" + s_lM + sf_oN); + test("-141006540894000000", "" + s_lM + f_I); + test("-1410065408null", "" + s_lM + f_IN); + test("-1410065408true", "" + s_lM + sf_bl); + test("-14100654085500", "" + s_lM + sf_s); + test("-1410065408-2900", "" + s_lM + s_sM); + test("-1410065408-194313216", "" + s_lM + sf_l); + test("-141006540812", "" + s_lM + s_strU1); + test("-1410065408C(87)", "" + s_lM + sf_o); + test("-141006540891", "" + s_lM + s_strU2); + test("-141006540821", "" + s_lM + f_strU1); + test("-141006540818", "" + s_lM + f_strU2); + test("-1410065408null", "" + s_lM + f_iAN); + test("-1410065408null", "" + s_lM + s_oN); + test("-1410065408\u045180", "" + s_lM + s_strU); + test("-1410065408C", "" + s_lM + sf_c); + test("-141006540875", "" + s_lM + sf_str); + test("-1410065408-43", "" + s_lM + s_bM); + test("-141006540880", "" + s_lM + sf_b); + test("-1410065408null", "" + s_lM + s_IN); + test("-1410065408-52.0", "" + s_lM + s_fM); + test("-141006540875000000", "" + s_lM + sf_i); + test("-141006540844", "" + s_lM + f_b); + test("-1410065408-1705032704", "" + s_lM + sf_lM); + test("-1410065408null", "" + s_lM + f_oAN); + test("-141006540883.0", "" + s_lM + f_d); + test("-1410065408I", "" + s_lM + f_c); + test("-141006540894.0", "" + s_lM + f_f); + test("-141006540812.0", "" + s_lM + sf_d); + test("-1410065408-99.0", "" + s_lM + f_dM); + test("-141006540817.0", "" + s_lM + sf_f); + test("-1410065408-84.0", "" + s_lM + sf_dM); + test("-141006540858000000", "" + s_lM + f_i); + test("-1410065408-55000000", "" + s_lM + f_iM); + test("-14100654081460392448", "" + s_lM + f_l); + test("-1410065408C(70)", "" + s_lM + f_o); + test("-1410065408\u04511", "" + s_lM + sf_strU); + test("-14100654088000", "" + s_lM + f_s); + test("-141006540818", "" + s_lM + s_str); + test("-1410065408-1000000", "" + s_lM + s_iM); + test("-14100654081000000", "" + s_lM + sf_I); + test("-1410065408null", "" + s_lM + f_oNtS); + test("-1410065408false", "" + s_lM + f_bl); + test("-1410065408null", "" + s_lM + sf_iAN); + test("-1410065408-2000000", "" + s_lM + sf_iM); + test("-1410065408-820130816", "" + s_lM + f_lM); + test("-1410065408null", "" + s_lM + sf_oAN); + test("-141006540825000000", "" + s_lM + s_I); + test("8.0-96.0", "" + s_d + s_dM); + test("8.0null", "" + s_d + s_oNtS); + test("8.0\u045176", "" + s_d + f_strU); + test("8.092", "" + s_d + sf_strU2); + test("8.051", "" + s_d + sf_strU1); + test("8.0null", "" + s_d + s_iAN); + test("8.0-54", "" + s_d + f_bM); + test("8.0-87.0", "" + s_d + f_fM); + test("8.0null", "" + s_d + s_oAN); + test("8.019", "" + s_d + f_str); + test("8.0-41", "" + s_d + sf_bM); + test("8.0null", "" + s_d + sf_IN); + test("8.0T", "" + s_d + s_c); + test("8.0-42.0", "" + s_d + sf_fM); + test("8.025", "" + s_d + s_b); + test("8.0null", "" + s_d + f_oN); + test("8.0-1410065408", "" + s_d + s_lM); + test("8.08.0", "" + s_d + s_d); + test("8.055.0", "" + s_d + s_f); + test("8.097000000", "" + s_d + s_i); + test("8.0-9900", "" + s_d + f_sM); + test("8.0935228928", "" + s_d + s_l); + test("8.0-8400", "" + s_d + sf_sM); + test("8.0C(82)", "" + s_d + s_o); + test("8.0null", "" + s_d + sf_oNtS); + test("8.0true", "" + s_d + s_bl); + test("8.03900", "" + s_d + s_s); + test("8.0null", "" + s_d + sf_oN); + test("8.094000000", "" + s_d + f_I); + test("8.0null", "" + s_d + f_IN); + test("8.0true", "" + s_d + sf_bl); + test("8.05500", "" + s_d + sf_s); + test("8.0-2900", "" + s_d + s_sM); + test("8.0-194313216", "" + s_d + sf_l); + test("8.012", "" + s_d + s_strU1); + test("8.0C(87)", "" + s_d + sf_o); + test("8.091", "" + s_d + s_strU2); + test("8.021", "" + s_d + f_strU1); + test("8.018", "" + s_d + f_strU2); + test("8.0null", "" + s_d + f_iAN); + test("8.0null", "" + s_d + s_oN); + test("8.0\u045180", "" + s_d + s_strU); + test("8.0C", "" + s_d + sf_c); + test("8.075", "" + s_d + sf_str); + test("8.0-43", "" + s_d + s_bM); + test("8.080", "" + s_d + sf_b); + test("8.0null", "" + s_d + s_IN); + test("8.0-52.0", "" + s_d + s_fM); + test("8.075000000", "" + s_d + sf_i); + test("8.044", "" + s_d + f_b); + test("8.0-1705032704", "" + s_d + sf_lM); + test("8.0null", "" + s_d + f_oAN); + test("8.083.0", "" + s_d + f_d); + test("8.0I", "" + s_d + f_c); + test("8.094.0", "" + s_d + f_f); + test("8.012.0", "" + s_d + sf_d); + test("8.0-99.0", "" + s_d + f_dM); + test("8.017.0", "" + s_d + sf_f); + test("8.0-84.0", "" + s_d + sf_dM); + test("8.058000000", "" + s_d + f_i); + test("8.0-55000000", "" + s_d + f_iM); + test("8.01460392448", "" + s_d + f_l); + test("8.0C(70)", "" + s_d + f_o); + test("8.0\u04511", "" + s_d + sf_strU); + test("8.08000", "" + s_d + f_s); + test("8.018", "" + s_d + s_str); + test("8.0-1000000", "" + s_d + s_iM); + test("8.01000000", "" + s_d + sf_I); + test("8.0null", "" + s_d + f_oNtS); + test("8.0false", "" + s_d + f_bl); + test("8.0null", "" + s_d + sf_iAN); + test("8.0-2000000", "" + s_d + sf_iM); + test("8.0-820130816", "" + s_d + f_lM); + test("8.0null", "" + s_d + sf_oAN); + test("8.025000000", "" + s_d + s_I); + test("55.0-96.0", "" + s_f + s_dM); + test("55.0null", "" + s_f + s_oNtS); + test("55.0\u045176", "" + s_f + f_strU); + test("55.092", "" + s_f + sf_strU2); + test("55.051", "" + s_f + sf_strU1); + test("55.0null", "" + s_f + s_iAN); + test("55.0-54", "" + s_f + f_bM); + test("55.0-87.0", "" + s_f + f_fM); + test("55.0null", "" + s_f + s_oAN); + test("55.019", "" + s_f + f_str); + test("55.0-41", "" + s_f + sf_bM); + test("55.0null", "" + s_f + sf_IN); + test("55.0T", "" + s_f + s_c); + test("55.0-42.0", "" + s_f + sf_fM); + test("55.025", "" + s_f + s_b); + test("55.0null", "" + s_f + f_oN); + test("55.0-1410065408", "" + s_f + s_lM); + test("55.08.0", "" + s_f + s_d); + test("55.055.0", "" + s_f + s_f); + test("55.097000000", "" + s_f + s_i); + test("55.0-9900", "" + s_f + f_sM); + test("55.0935228928", "" + s_f + s_l); + test("55.0-8400", "" + s_f + sf_sM); + test("55.0C(82)", "" + s_f + s_o); + test("55.0null", "" + s_f + sf_oNtS); + test("55.0true", "" + s_f + s_bl); + test("55.03900", "" + s_f + s_s); + test("55.0null", "" + s_f + sf_oN); + test("55.094000000", "" + s_f + f_I); + test("55.0null", "" + s_f + f_IN); + test("55.0true", "" + s_f + sf_bl); + test("55.05500", "" + s_f + sf_s); + test("55.0-2900", "" + s_f + s_sM); + test("55.0-194313216", "" + s_f + sf_l); + test("55.012", "" + s_f + s_strU1); + test("55.0C(87)", "" + s_f + sf_o); + test("55.091", "" + s_f + s_strU2); + test("55.021", "" + s_f + f_strU1); + test("55.018", "" + s_f + f_strU2); + test("55.0null", "" + s_f + f_iAN); + test("55.0null", "" + s_f + s_oN); + test("55.0\u045180", "" + s_f + s_strU); + test("55.0C", "" + s_f + sf_c); + test("55.075", "" + s_f + sf_str); + test("55.0-43", "" + s_f + s_bM); + test("55.080", "" + s_f + sf_b); + test("55.0null", "" + s_f + s_IN); + test("55.0-52.0", "" + s_f + s_fM); + test("55.075000000", "" + s_f + sf_i); + test("55.044", "" + s_f + f_b); + test("55.0-1705032704", "" + s_f + sf_lM); + test("55.0null", "" + s_f + f_oAN); + test("55.083.0", "" + s_f + f_d); + test("55.0I", "" + s_f + f_c); + test("55.094.0", "" + s_f + f_f); + test("55.012.0", "" + s_f + sf_d); + test("55.0-99.0", "" + s_f + f_dM); + test("55.017.0", "" + s_f + sf_f); + test("55.0-84.0", "" + s_f + sf_dM); + test("55.058000000", "" + s_f + f_i); + test("55.0-55000000", "" + s_f + f_iM); + test("55.01460392448", "" + s_f + f_l); + test("55.0C(70)", "" + s_f + f_o); + test("55.0\u04511", "" + s_f + sf_strU); + test("55.08000", "" + s_f + f_s); + test("55.018", "" + s_f + s_str); + test("55.0-1000000", "" + s_f + s_iM); + test("55.01000000", "" + s_f + sf_I); + test("55.0null", "" + s_f + f_oNtS); + test("55.0false", "" + s_f + f_bl); + test("55.0null", "" + s_f + sf_iAN); + test("55.0-2000000", "" + s_f + sf_iM); + test("55.0-820130816", "" + s_f + f_lM); + test("55.0null", "" + s_f + sf_oAN); + test("55.025000000", "" + s_f + s_I); + test("97000000-96.0", "" + s_i + s_dM); + test("97000000null", "" + s_i + s_oNtS); + test("97000000\u045176", "" + s_i + f_strU); + test("9700000092", "" + s_i + sf_strU2); + test("9700000051", "" + s_i + sf_strU1); + test("97000000null", "" + s_i + s_iAN); + test("97000000-54", "" + s_i + f_bM); + test("97000000-87.0", "" + s_i + f_fM); + test("97000000null", "" + s_i + s_oAN); + test("9700000019", "" + s_i + f_str); + test("97000000-41", "" + s_i + sf_bM); + test("97000000null", "" + s_i + sf_IN); + test("97000000T", "" + s_i + s_c); + test("97000000-42.0", "" + s_i + sf_fM); + test("9700000025", "" + s_i + s_b); + test("97000000null", "" + s_i + f_oN); + test("97000000-1410065408", "" + s_i + s_lM); + test("970000008.0", "" + s_i + s_d); + test("9700000055.0", "" + s_i + s_f); + test("9700000097000000", "" + s_i + s_i); + test("97000000-9900", "" + s_i + f_sM); + test("97000000935228928", "" + s_i + s_l); + test("97000000-8400", "" + s_i + sf_sM); + test("97000000C(82)", "" + s_i + s_o); + test("97000000null", "" + s_i + sf_oNtS); + test("97000000true", "" + s_i + s_bl); + test("970000003900", "" + s_i + s_s); + test("97000000null", "" + s_i + sf_oN); + test("9700000094000000", "" + s_i + f_I); + test("97000000null", "" + s_i + f_IN); + test("97000000true", "" + s_i + sf_bl); + test("970000005500", "" + s_i + sf_s); + test("97000000-2900", "" + s_i + s_sM); + test("97000000-194313216", "" + s_i + sf_l); + test("9700000012", "" + s_i + s_strU1); + test("97000000C(87)", "" + s_i + sf_o); + test("9700000091", "" + s_i + s_strU2); + test("9700000021", "" + s_i + f_strU1); + test("9700000018", "" + s_i + f_strU2); + test("97000000null", "" + s_i + f_iAN); + test("97000000null", "" + s_i + s_oN); + test("97000000\u045180", "" + s_i + s_strU); + test("97000000C", "" + s_i + sf_c); + test("9700000075", "" + s_i + sf_str); + test("97000000-43", "" + s_i + s_bM); + test("9700000080", "" + s_i + sf_b); + test("97000000null", "" + s_i + s_IN); + test("97000000-52.0", "" + s_i + s_fM); + test("9700000075000000", "" + s_i + sf_i); + test("9700000044", "" + s_i + f_b); + test("97000000-1705032704", "" + s_i + sf_lM); + test("97000000null", "" + s_i + f_oAN); + test("9700000083.0", "" + s_i + f_d); + test("97000000I", "" + s_i + f_c); + test("9700000094.0", "" + s_i + f_f); + test("9700000012.0", "" + s_i + sf_d); + test("97000000-99.0", "" + s_i + f_dM); + test("9700000017.0", "" + s_i + sf_f); + test("97000000-84.0", "" + s_i + sf_dM); + test("9700000058000000", "" + s_i + f_i); + test("97000000-55000000", "" + s_i + f_iM); + test("970000001460392448", "" + s_i + f_l); + test("97000000C(70)", "" + s_i + f_o); + test("97000000\u04511", "" + s_i + sf_strU); + test("970000008000", "" + s_i + f_s); + test("9700000018", "" + s_i + s_str); + test("97000000-1000000", "" + s_i + s_iM); + test("970000001000000", "" + s_i + sf_I); + test("97000000null", "" + s_i + f_oNtS); + test("97000000false", "" + s_i + f_bl); + test("97000000null", "" + s_i + sf_iAN); + test("97000000-2000000", "" + s_i + sf_iM); + test("97000000-820130816", "" + s_i + f_lM); + test("97000000null", "" + s_i + sf_oAN); + test("9700000025000000", "" + s_i + s_I); + test("-9900-96.0", "" + f_sM + s_dM); + test("-9900null", "" + f_sM + s_oNtS); + test("-9900\u045176", "" + f_sM + f_strU); + test("-990092", "" + f_sM + sf_strU2); + test("-990051", "" + f_sM + sf_strU1); + test("-9900null", "" + f_sM + s_iAN); + test("-9900-54", "" + f_sM + f_bM); + test("-9900-87.0", "" + f_sM + f_fM); + test("-9900null", "" + f_sM + s_oAN); + test("-990019", "" + f_sM + f_str); + test("-9900-41", "" + f_sM + sf_bM); + test("-9900null", "" + f_sM + sf_IN); + test("-9900T", "" + f_sM + s_c); + test("-9900-42.0", "" + f_sM + sf_fM); + test("-990025", "" + f_sM + s_b); + test("-9900null", "" + f_sM + f_oN); + test("-9900-1410065408", "" + f_sM + s_lM); + test("-99008.0", "" + f_sM + s_d); + test("-990055.0", "" + f_sM + s_f); + test("-990097000000", "" + f_sM + s_i); + test("-9900-9900", "" + f_sM + f_sM); + test("-9900935228928", "" + f_sM + s_l); + test("-9900-8400", "" + f_sM + sf_sM); + test("-9900C(82)", "" + f_sM + s_o); + test("-9900null", "" + f_sM + sf_oNtS); + test("-9900true", "" + f_sM + s_bl); + test("-99003900", "" + f_sM + s_s); + test("-9900null", "" + f_sM + sf_oN); + test("-990094000000", "" + f_sM + f_I); + test("-9900null", "" + f_sM + f_IN); + test("-9900true", "" + f_sM + sf_bl); + test("-99005500", "" + f_sM + sf_s); + test("-9900-2900", "" + f_sM + s_sM); + test("-9900-194313216", "" + f_sM + sf_l); + test("-990012", "" + f_sM + s_strU1); + test("-9900C(87)", "" + f_sM + sf_o); + test("-990091", "" + f_sM + s_strU2); + test("-990021", "" + f_sM + f_strU1); + test("-990018", "" + f_sM + f_strU2); + test("-9900null", "" + f_sM + f_iAN); + test("-9900null", "" + f_sM + s_oN); + test("-9900\u045180", "" + f_sM + s_strU); + test("-9900C", "" + f_sM + sf_c); + test("-990075", "" + f_sM + sf_str); + test("-9900-43", "" + f_sM + s_bM); + test("-990080", "" + f_sM + sf_b); + test("-9900null", "" + f_sM + s_IN); + test("-9900-52.0", "" + f_sM + s_fM); + test("-990075000000", "" + f_sM + sf_i); + test("-990044", "" + f_sM + f_b); + test("-9900-1705032704", "" + f_sM + sf_lM); + test("-9900null", "" + f_sM + f_oAN); + test("-990083.0", "" + f_sM + f_d); + test("-9900I", "" + f_sM + f_c); + test("-990094.0", "" + f_sM + f_f); + test("-990012.0", "" + f_sM + sf_d); + test("-9900-99.0", "" + f_sM + f_dM); + test("-990017.0", "" + f_sM + sf_f); + test("-9900-84.0", "" + f_sM + sf_dM); + test("-990058000000", "" + f_sM + f_i); + test("-9900-55000000", "" + f_sM + f_iM); + test("-99001460392448", "" + f_sM + f_l); + test("-9900C(70)", "" + f_sM + f_o); + test("-9900\u04511", "" + f_sM + sf_strU); + test("-99008000", "" + f_sM + f_s); + test("-990018", "" + f_sM + s_str); + test("-9900-1000000", "" + f_sM + s_iM); + test("-99001000000", "" + f_sM + sf_I); + test("-9900null", "" + f_sM + f_oNtS); + test("-9900false", "" + f_sM + f_bl); + test("-9900null", "" + f_sM + sf_iAN); + test("-9900-2000000", "" + f_sM + sf_iM); + test("-9900-820130816", "" + f_sM + f_lM); + test("-9900null", "" + f_sM + sf_oAN); + test("-990025000000", "" + f_sM + s_I); + test("935228928-96.0", "" + s_l + s_dM); + test("935228928null", "" + s_l + s_oNtS); + test("935228928\u045176", "" + s_l + f_strU); + test("93522892892", "" + s_l + sf_strU2); + test("93522892851", "" + s_l + sf_strU1); + test("935228928null", "" + s_l + s_iAN); + test("935228928-54", "" + s_l + f_bM); + test("935228928-87.0", "" + s_l + f_fM); + test("935228928null", "" + s_l + s_oAN); + test("93522892819", "" + s_l + f_str); + test("935228928-41", "" + s_l + sf_bM); + test("935228928null", "" + s_l + sf_IN); + test("935228928T", "" + s_l + s_c); + test("935228928-42.0", "" + s_l + sf_fM); + test("93522892825", "" + s_l + s_b); + test("935228928null", "" + s_l + f_oN); + test("935228928-1410065408", "" + s_l + s_lM); + test("9352289288.0", "" + s_l + s_d); + test("93522892855.0", "" + s_l + s_f); + test("93522892897000000", "" + s_l + s_i); + test("935228928-9900", "" + s_l + f_sM); + test("935228928935228928", "" + s_l + s_l); + test("935228928-8400", "" + s_l + sf_sM); + test("935228928C(82)", "" + s_l + s_o); + test("935228928null", "" + s_l + sf_oNtS); + test("935228928true", "" + s_l + s_bl); + test("9352289283900", "" + s_l + s_s); + test("935228928null", "" + s_l + sf_oN); + test("93522892894000000", "" + s_l + f_I); + test("935228928null", "" + s_l + f_IN); + test("935228928true", "" + s_l + sf_bl); + test("9352289285500", "" + s_l + sf_s); + test("935228928-2900", "" + s_l + s_sM); + test("935228928-194313216", "" + s_l + sf_l); + test("93522892812", "" + s_l + s_strU1); + test("935228928C(87)", "" + s_l + sf_o); + test("93522892891", "" + s_l + s_strU2); + test("93522892821", "" + s_l + f_strU1); + test("93522892818", "" + s_l + f_strU2); + test("935228928null", "" + s_l + f_iAN); + test("935228928null", "" + s_l + s_oN); + test("935228928\u045180", "" + s_l + s_strU); + test("935228928C", "" + s_l + sf_c); + test("93522892875", "" + s_l + sf_str); + test("935228928-43", "" + s_l + s_bM); + test("93522892880", "" + s_l + sf_b); + test("935228928null", "" + s_l + s_IN); + test("935228928-52.0", "" + s_l + s_fM); + test("93522892875000000", "" + s_l + sf_i); + test("93522892844", "" + s_l + f_b); + test("935228928-1705032704", "" + s_l + sf_lM); + test("935228928null", "" + s_l + f_oAN); + test("93522892883.0", "" + s_l + f_d); + test("935228928I", "" + s_l + f_c); + test("93522892894.0", "" + s_l + f_f); + test("93522892812.0", "" + s_l + sf_d); + test("935228928-99.0", "" + s_l + f_dM); + test("93522892817.0", "" + s_l + sf_f); + test("935228928-84.0", "" + s_l + sf_dM); + test("93522892858000000", "" + s_l + f_i); + test("935228928-55000000", "" + s_l + f_iM); + test("9352289281460392448", "" + s_l + f_l); + test("935228928C(70)", "" + s_l + f_o); + test("935228928\u04511", "" + s_l + sf_strU); + test("9352289288000", "" + s_l + f_s); + test("93522892818", "" + s_l + s_str); + test("935228928-1000000", "" + s_l + s_iM); + test("9352289281000000", "" + s_l + sf_I); + test("935228928null", "" + s_l + f_oNtS); + test("935228928false", "" + s_l + f_bl); + test("935228928null", "" + s_l + sf_iAN); + test("935228928-2000000", "" + s_l + sf_iM); + test("935228928-820130816", "" + s_l + f_lM); + test("935228928null", "" + s_l + sf_oAN); + test("93522892825000000", "" + s_l + s_I); + test("-8400-96.0", "" + sf_sM + s_dM); + test("-8400null", "" + sf_sM + s_oNtS); + test("-8400\u045176", "" + sf_sM + f_strU); + test("-840092", "" + sf_sM + sf_strU2); + test("-840051", "" + sf_sM + sf_strU1); + test("-8400null", "" + sf_sM + s_iAN); + test("-8400-54", "" + sf_sM + f_bM); + test("-8400-87.0", "" + sf_sM + f_fM); + test("-8400null", "" + sf_sM + s_oAN); + test("-840019", "" + sf_sM + f_str); + test("-8400-41", "" + sf_sM + sf_bM); + test("-8400null", "" + sf_sM + sf_IN); + test("-8400T", "" + sf_sM + s_c); + test("-8400-42.0", "" + sf_sM + sf_fM); + test("-840025", "" + sf_sM + s_b); + test("-8400null", "" + sf_sM + f_oN); + test("-8400-1410065408", "" + sf_sM + s_lM); + test("-84008.0", "" + sf_sM + s_d); + test("-840055.0", "" + sf_sM + s_f); + test("-840097000000", "" + sf_sM + s_i); + test("-8400-9900", "" + sf_sM + f_sM); + test("-8400935228928", "" + sf_sM + s_l); + test("-8400-8400", "" + sf_sM + sf_sM); + test("-8400C(82)", "" + sf_sM + s_o); + test("-8400null", "" + sf_sM + sf_oNtS); + test("-8400true", "" + sf_sM + s_bl); + test("-84003900", "" + sf_sM + s_s); + test("-8400null", "" + sf_sM + sf_oN); + test("-840094000000", "" + sf_sM + f_I); + test("-8400null", "" + sf_sM + f_IN); + test("-8400true", "" + sf_sM + sf_bl); + test("-84005500", "" + sf_sM + sf_s); + test("-8400-2900", "" + sf_sM + s_sM); + test("-8400-194313216", "" + sf_sM + sf_l); + test("-840012", "" + sf_sM + s_strU1); + test("-8400C(87)", "" + sf_sM + sf_o); + test("-840091", "" + sf_sM + s_strU2); + test("-840021", "" + sf_sM + f_strU1); + test("-840018", "" + sf_sM + f_strU2); + test("-8400null", "" + sf_sM + f_iAN); + test("-8400null", "" + sf_sM + s_oN); + test("-8400\u045180", "" + sf_sM + s_strU); + test("-8400C", "" + sf_sM + sf_c); + test("-840075", "" + sf_sM + sf_str); + test("-8400-43", "" + sf_sM + s_bM); + test("-840080", "" + sf_sM + sf_b); + test("-8400null", "" + sf_sM + s_IN); + test("-8400-52.0", "" + sf_sM + s_fM); + test("-840075000000", "" + sf_sM + sf_i); + test("-840044", "" + sf_sM + f_b); + test("-8400-1705032704", "" + sf_sM + sf_lM); + test("-8400null", "" + sf_sM + f_oAN); + test("-840083.0", "" + sf_sM + f_d); + test("-8400I", "" + sf_sM + f_c); + test("-840094.0", "" + sf_sM + f_f); + test("-840012.0", "" + sf_sM + sf_d); + test("-8400-99.0", "" + sf_sM + f_dM); + test("-840017.0", "" + sf_sM + sf_f); + test("-8400-84.0", "" + sf_sM + sf_dM); + test("-840058000000", "" + sf_sM + f_i); + test("-8400-55000000", "" + sf_sM + f_iM); + test("-84001460392448", "" + sf_sM + f_l); + test("-8400C(70)", "" + sf_sM + f_o); + test("-8400\u04511", "" + sf_sM + sf_strU); + test("-84008000", "" + sf_sM + f_s); + test("-840018", "" + sf_sM + s_str); + test("-8400-1000000", "" + sf_sM + s_iM); + test("-84001000000", "" + sf_sM + sf_I); + test("-8400null", "" + sf_sM + f_oNtS); + test("-8400false", "" + sf_sM + f_bl); + test("-8400null", "" + sf_sM + sf_iAN); + test("-8400-2000000", "" + sf_sM + sf_iM); + test("-8400-820130816", "" + sf_sM + f_lM); + test("-8400null", "" + sf_sM + sf_oAN); + test("-840025000000", "" + sf_sM + s_I); + test("C(82)-96.0", "" + s_o + s_dM); + test("C(82)null", "" + s_o + s_oNtS); + test("C(82)\u045176", "" + s_o + f_strU); + test("C(82)92", "" + s_o + sf_strU2); + test("C(82)51", "" + s_o + sf_strU1); + test("C(82)null", "" + s_o + s_iAN); + test("C(82)-54", "" + s_o + f_bM); + test("C(82)-87.0", "" + s_o + f_fM); + test("C(82)null", "" + s_o + s_oAN); + test("C(82)19", "" + s_o + f_str); + test("C(82)-41", "" + s_o + sf_bM); + test("C(82)null", "" + s_o + sf_IN); + test("C(82)T", "" + s_o + s_c); + test("C(82)-42.0", "" + s_o + sf_fM); + test("C(82)25", "" + s_o + s_b); + test("C(82)null", "" + s_o + f_oN); + test("C(82)-1410065408", "" + s_o + s_lM); + test("C(82)8.0", "" + s_o + s_d); + test("C(82)55.0", "" + s_o + s_f); + test("C(82)97000000", "" + s_o + s_i); + test("C(82)-9900", "" + s_o + f_sM); + test("C(82)935228928", "" + s_o + s_l); + test("C(82)-8400", "" + s_o + sf_sM); + test("C(82)C(82)", "" + s_o + s_o); + test("C(82)null", "" + s_o + sf_oNtS); + test("C(82)true", "" + s_o + s_bl); + test("C(82)3900", "" + s_o + s_s); + test("C(82)null", "" + s_o + sf_oN); + test("C(82)94000000", "" + s_o + f_I); + test("C(82)null", "" + s_o + f_IN); + test("C(82)true", "" + s_o + sf_bl); + test("C(82)5500", "" + s_o + sf_s); + test("C(82)-2900", "" + s_o + s_sM); + test("C(82)-194313216", "" + s_o + sf_l); + test("C(82)12", "" + s_o + s_strU1); + test("C(82)C(87)", "" + s_o + sf_o); + test("C(82)91", "" + s_o + s_strU2); + test("C(82)21", "" + s_o + f_strU1); + test("C(82)18", "" + s_o + f_strU2); + test("C(82)null", "" + s_o + f_iAN); + test("C(82)null", "" + s_o + s_oN); + test("C(82)\u045180", "" + s_o + s_strU); + test("C(82)C", "" + s_o + sf_c); + test("C(82)75", "" + s_o + sf_str); + test("C(82)-43", "" + s_o + s_bM); + test("C(82)80", "" + s_o + sf_b); + test("C(82)null", "" + s_o + s_IN); + test("C(82)-52.0", "" + s_o + s_fM); + test("C(82)75000000", "" + s_o + sf_i); + test("C(82)44", "" + s_o + f_b); + test("C(82)-1705032704", "" + s_o + sf_lM); + test("C(82)null", "" + s_o + f_oAN); + test("C(82)83.0", "" + s_o + f_d); + test("C(82)I", "" + s_o + f_c); + test("C(82)94.0", "" + s_o + f_f); + test("C(82)12.0", "" + s_o + sf_d); + test("C(82)-99.0", "" + s_o + f_dM); + test("C(82)17.0", "" + s_o + sf_f); + test("C(82)-84.0", "" + s_o + sf_dM); + test("C(82)58000000", "" + s_o + f_i); + test("C(82)-55000000", "" + s_o + f_iM); + test("C(82)1460392448", "" + s_o + f_l); + test("C(82)C(70)", "" + s_o + f_o); + test("C(82)\u04511", "" + s_o + sf_strU); + test("C(82)8000", "" + s_o + f_s); + test("C(82)18", "" + s_o + s_str); + test("C(82)-1000000", "" + s_o + s_iM); + test("C(82)1000000", "" + s_o + sf_I); + test("C(82)null", "" + s_o + f_oNtS); + test("C(82)false", "" + s_o + f_bl); + test("C(82)null", "" + s_o + sf_iAN); + test("C(82)-2000000", "" + s_o + sf_iM); + test("C(82)-820130816", "" + s_o + f_lM); + test("C(82)null", "" + s_o + sf_oAN); + test("C(82)25000000", "" + s_o + s_I); + test("null-96.0", "" + sf_oNtS + s_dM); + test("nullnull", "" + sf_oNtS + s_oNtS); + test("null\u045176", "" + sf_oNtS + f_strU); + test("null92", "" + sf_oNtS + sf_strU2); + test("null51", "" + sf_oNtS + sf_strU1); + test("nullnull", "" + sf_oNtS + s_iAN); + test("null-54", "" + sf_oNtS + f_bM); + test("null-87.0", "" + sf_oNtS + f_fM); + test("nullnull", "" + sf_oNtS + s_oAN); + test("null19", "" + sf_oNtS + f_str); + test("null-41", "" + sf_oNtS + sf_bM); + test("nullnull", "" + sf_oNtS + sf_IN); + test("nullT", "" + sf_oNtS + s_c); + test("null-42.0", "" + sf_oNtS + sf_fM); + test("null25", "" + sf_oNtS + s_b); + test("nullnull", "" + sf_oNtS + f_oN); + test("null-1410065408", "" + sf_oNtS + s_lM); + test("null8.0", "" + sf_oNtS + s_d); + test("null55.0", "" + sf_oNtS + s_f); + test("null97000000", "" + sf_oNtS + s_i); + test("null-9900", "" + sf_oNtS + f_sM); + test("null935228928", "" + sf_oNtS + s_l); + test("null-8400", "" + sf_oNtS + sf_sM); + test("nullC(82)", "" + sf_oNtS + s_o); + test("nullnull", "" + sf_oNtS + sf_oNtS); + test("nulltrue", "" + sf_oNtS + s_bl); + test("null3900", "" + sf_oNtS + s_s); + test("nullnull", "" + sf_oNtS + sf_oN); + test("null94000000", "" + sf_oNtS + f_I); + test("nullnull", "" + sf_oNtS + f_IN); + test("nulltrue", "" + sf_oNtS + sf_bl); + test("null5500", "" + sf_oNtS + sf_s); + test("null-2900", "" + sf_oNtS + s_sM); + test("null-194313216", "" + sf_oNtS + sf_l); + test("null12", "" + sf_oNtS + s_strU1); + test("nullC(87)", "" + sf_oNtS + sf_o); + test("null91", "" + sf_oNtS + s_strU2); + test("null21", "" + sf_oNtS + f_strU1); + test("null18", "" + sf_oNtS + f_strU2); + test("nullnull", "" + sf_oNtS + f_iAN); + test("nullnull", "" + sf_oNtS + s_oN); + test("null\u045180", "" + sf_oNtS + s_strU); + test("nullC", "" + sf_oNtS + sf_c); + test("null75", "" + sf_oNtS + sf_str); + test("null-43", "" + sf_oNtS + s_bM); + test("null80", "" + sf_oNtS + sf_b); + test("nullnull", "" + sf_oNtS + s_IN); + test("null-52.0", "" + sf_oNtS + s_fM); + test("null75000000", "" + sf_oNtS + sf_i); + test("null44", "" + sf_oNtS + f_b); + test("null-1705032704", "" + sf_oNtS + sf_lM); + test("nullnull", "" + sf_oNtS + f_oAN); + test("null83.0", "" + sf_oNtS + f_d); + test("nullI", "" + sf_oNtS + f_c); + test("null94.0", "" + sf_oNtS + f_f); + test("null12.0", "" + sf_oNtS + sf_d); + test("null-99.0", "" + sf_oNtS + f_dM); + test("null17.0", "" + sf_oNtS + sf_f); + test("null-84.0", "" + sf_oNtS + sf_dM); + test("null58000000", "" + sf_oNtS + f_i); + test("null-55000000", "" + sf_oNtS + f_iM); + test("null1460392448", "" + sf_oNtS + f_l); + test("nullC(70)", "" + sf_oNtS + f_o); + test("null\u04511", "" + sf_oNtS + sf_strU); + test("null8000", "" + sf_oNtS + f_s); + test("null18", "" + sf_oNtS + s_str); + test("null-1000000", "" + sf_oNtS + s_iM); + test("null1000000", "" + sf_oNtS + sf_I); + test("nullnull", "" + sf_oNtS + f_oNtS); + test("nullfalse", "" + sf_oNtS + f_bl); + test("nullnull", "" + sf_oNtS + sf_iAN); + test("null-2000000", "" + sf_oNtS + sf_iM); + test("null-820130816", "" + sf_oNtS + f_lM); + test("nullnull", "" + sf_oNtS + sf_oAN); + test("null25000000", "" + sf_oNtS + s_I); + test("true-96.0", "" + s_bl + s_dM); + test("truenull", "" + s_bl + s_oNtS); + test("true\u045176", "" + s_bl + f_strU); + test("true92", "" + s_bl + sf_strU2); + test("true51", "" + s_bl + sf_strU1); + test("truenull", "" + s_bl + s_iAN); + test("true-54", "" + s_bl + f_bM); + test("true-87.0", "" + s_bl + f_fM); + test("truenull", "" + s_bl + s_oAN); + test("true19", "" + s_bl + f_str); + test("true-41", "" + s_bl + sf_bM); + test("truenull", "" + s_bl + sf_IN); + test("trueT", "" + s_bl + s_c); + test("true-42.0", "" + s_bl + sf_fM); + test("true25", "" + s_bl + s_b); + test("truenull", "" + s_bl + f_oN); + test("true-1410065408", "" + s_bl + s_lM); + test("true8.0", "" + s_bl + s_d); + test("true55.0", "" + s_bl + s_f); + test("true97000000", "" + s_bl + s_i); + test("true-9900", "" + s_bl + f_sM); + test("true935228928", "" + s_bl + s_l); + test("true-8400", "" + s_bl + sf_sM); + test("trueC(82)", "" + s_bl + s_o); + test("truenull", "" + s_bl + sf_oNtS); + test("truetrue", "" + s_bl + s_bl); + test("true3900", "" + s_bl + s_s); + test("truenull", "" + s_bl + sf_oN); + test("true94000000", "" + s_bl + f_I); + test("truenull", "" + s_bl + f_IN); + test("truetrue", "" + s_bl + sf_bl); + test("true5500", "" + s_bl + sf_s); + test("true-2900", "" + s_bl + s_sM); + test("true-194313216", "" + s_bl + sf_l); + test("true12", "" + s_bl + s_strU1); + test("trueC(87)", "" + s_bl + sf_o); + test("true91", "" + s_bl + s_strU2); + test("true21", "" + s_bl + f_strU1); + test("true18", "" + s_bl + f_strU2); + test("truenull", "" + s_bl + f_iAN); + test("truenull", "" + s_bl + s_oN); + test("true\u045180", "" + s_bl + s_strU); + test("trueC", "" + s_bl + sf_c); + test("true75", "" + s_bl + sf_str); + test("true-43", "" + s_bl + s_bM); + test("true80", "" + s_bl + sf_b); + test("truenull", "" + s_bl + s_IN); + test("true-52.0", "" + s_bl + s_fM); + test("true75000000", "" + s_bl + sf_i); + test("true44", "" + s_bl + f_b); + } + + public void run2() { + test("true-1705032704", "" + s_bl + sf_lM); + test("truenull", "" + s_bl + f_oAN); + test("true83.0", "" + s_bl + f_d); + test("trueI", "" + s_bl + f_c); + test("true94.0", "" + s_bl + f_f); + test("true12.0", "" + s_bl + sf_d); + test("true-99.0", "" + s_bl + f_dM); + test("true17.0", "" + s_bl + sf_f); + test("true-84.0", "" + s_bl + sf_dM); + test("true58000000", "" + s_bl + f_i); + test("true-55000000", "" + s_bl + f_iM); + test("true1460392448", "" + s_bl + f_l); + test("trueC(70)", "" + s_bl + f_o); + test("true\u04511", "" + s_bl + sf_strU); + test("true8000", "" + s_bl + f_s); + test("true18", "" + s_bl + s_str); + test("true-1000000", "" + s_bl + s_iM); + test("true1000000", "" + s_bl + sf_I); + test("truenull", "" + s_bl + f_oNtS); + test("truefalse", "" + s_bl + f_bl); + test("truenull", "" + s_bl + sf_iAN); + test("true-2000000", "" + s_bl + sf_iM); + test("true-820130816", "" + s_bl + f_lM); + test("truenull", "" + s_bl + sf_oAN); + test("true25000000", "" + s_bl + s_I); + test("3900-96.0", "" + s_s + s_dM); + test("3900null", "" + s_s + s_oNtS); + test("3900\u045176", "" + s_s + f_strU); + test("390092", "" + s_s + sf_strU2); + test("390051", "" + s_s + sf_strU1); + test("3900null", "" + s_s + s_iAN); + test("3900-54", "" + s_s + f_bM); + test("3900-87.0", "" + s_s + f_fM); + test("3900null", "" + s_s + s_oAN); + test("390019", "" + s_s + f_str); + test("3900-41", "" + s_s + sf_bM); + test("3900null", "" + s_s + sf_IN); + test("3900T", "" + s_s + s_c); + test("3900-42.0", "" + s_s + sf_fM); + test("390025", "" + s_s + s_b); + test("3900null", "" + s_s + f_oN); + test("3900-1410065408", "" + s_s + s_lM); + test("39008.0", "" + s_s + s_d); + test("390055.0", "" + s_s + s_f); + test("390097000000", "" + s_s + s_i); + test("3900-9900", "" + s_s + f_sM); + test("3900935228928", "" + s_s + s_l); + test("3900-8400", "" + s_s + sf_sM); + test("3900C(82)", "" + s_s + s_o); + test("3900null", "" + s_s + sf_oNtS); + test("3900true", "" + s_s + s_bl); + test("39003900", "" + s_s + s_s); + test("3900null", "" + s_s + sf_oN); + test("390094000000", "" + s_s + f_I); + test("3900null", "" + s_s + f_IN); + test("3900true", "" + s_s + sf_bl); + test("39005500", "" + s_s + sf_s); + test("3900-2900", "" + s_s + s_sM); + test("3900-194313216", "" + s_s + sf_l); + test("390012", "" + s_s + s_strU1); + test("3900C(87)", "" + s_s + sf_o); + test("390091", "" + s_s + s_strU2); + test("390021", "" + s_s + f_strU1); + test("390018", "" + s_s + f_strU2); + test("3900null", "" + s_s + f_iAN); + test("3900null", "" + s_s + s_oN); + test("3900\u045180", "" + s_s + s_strU); + test("3900C", "" + s_s + sf_c); + test("390075", "" + s_s + sf_str); + test("3900-43", "" + s_s + s_bM); + test("390080", "" + s_s + sf_b); + test("3900null", "" + s_s + s_IN); + test("3900-52.0", "" + s_s + s_fM); + test("390075000000", "" + s_s + sf_i); + test("390044", "" + s_s + f_b); + test("3900-1705032704", "" + s_s + sf_lM); + test("3900null", "" + s_s + f_oAN); + test("390083.0", "" + s_s + f_d); + test("3900I", "" + s_s + f_c); + test("390094.0", "" + s_s + f_f); + test("390012.0", "" + s_s + sf_d); + test("3900-99.0", "" + s_s + f_dM); + test("390017.0", "" + s_s + sf_f); + test("3900-84.0", "" + s_s + sf_dM); + test("390058000000", "" + s_s + f_i); + test("3900-55000000", "" + s_s + f_iM); + test("39001460392448", "" + s_s + f_l); + test("3900C(70)", "" + s_s + f_o); + test("3900\u04511", "" + s_s + sf_strU); + test("39008000", "" + s_s + f_s); + test("390018", "" + s_s + s_str); + test("3900-1000000", "" + s_s + s_iM); + test("39001000000", "" + s_s + sf_I); + test("3900null", "" + s_s + f_oNtS); + test("3900false", "" + s_s + f_bl); + test("3900null", "" + s_s + sf_iAN); + test("3900-2000000", "" + s_s + sf_iM); + test("3900-820130816", "" + s_s + f_lM); + test("3900null", "" + s_s + sf_oAN); + test("390025000000", "" + s_s + s_I); + test("null-96.0", "" + sf_oN + s_dM); + test("nullnull", "" + sf_oN + s_oNtS); + test("null\u045176", "" + sf_oN + f_strU); + test("null92", "" + sf_oN + sf_strU2); + test("null51", "" + sf_oN + sf_strU1); + test("nullnull", "" + sf_oN + s_iAN); + test("null-54", "" + sf_oN + f_bM); + test("null-87.0", "" + sf_oN + f_fM); + test("nullnull", "" + sf_oN + s_oAN); + test("null19", "" + sf_oN + f_str); + test("null-41", "" + sf_oN + sf_bM); + test("nullnull", "" + sf_oN + sf_IN); + test("nullT", "" + sf_oN + s_c); + test("null-42.0", "" + sf_oN + sf_fM); + test("null25", "" + sf_oN + s_b); + test("nullnull", "" + sf_oN + f_oN); + test("null-1410065408", "" + sf_oN + s_lM); + test("null8.0", "" + sf_oN + s_d); + test("null55.0", "" + sf_oN + s_f); + test("null97000000", "" + sf_oN + s_i); + test("null-9900", "" + sf_oN + f_sM); + test("null935228928", "" + sf_oN + s_l); + test("null-8400", "" + sf_oN + sf_sM); + test("nullC(82)", "" + sf_oN + s_o); + test("nullnull", "" + sf_oN + sf_oNtS); + test("nulltrue", "" + sf_oN + s_bl); + test("null3900", "" + sf_oN + s_s); + test("nullnull", "" + sf_oN + sf_oN); + test("null94000000", "" + sf_oN + f_I); + test("nullnull", "" + sf_oN + f_IN); + test("nulltrue", "" + sf_oN + sf_bl); + test("null5500", "" + sf_oN + sf_s); + test("null-2900", "" + sf_oN + s_sM); + test("null-194313216", "" + sf_oN + sf_l); + test("null12", "" + sf_oN + s_strU1); + test("nullC(87)", "" + sf_oN + sf_o); + test("null91", "" + sf_oN + s_strU2); + test("null21", "" + sf_oN + f_strU1); + test("null18", "" + sf_oN + f_strU2); + test("nullnull", "" + sf_oN + f_iAN); + test("nullnull", "" + sf_oN + s_oN); + test("null\u045180", "" + sf_oN + s_strU); + test("nullC", "" + sf_oN + sf_c); + test("null75", "" + sf_oN + sf_str); + test("null-43", "" + sf_oN + s_bM); + test("null80", "" + sf_oN + sf_b); + test("nullnull", "" + sf_oN + s_IN); + test("null-52.0", "" + sf_oN + s_fM); + test("null75000000", "" + sf_oN + sf_i); + test("null44", "" + sf_oN + f_b); + test("null-1705032704", "" + sf_oN + sf_lM); + test("nullnull", "" + sf_oN + f_oAN); + test("null83.0", "" + sf_oN + f_d); + test("nullI", "" + sf_oN + f_c); + test("null94.0", "" + sf_oN + f_f); + test("null12.0", "" + sf_oN + sf_d); + test("null-99.0", "" + sf_oN + f_dM); + test("null17.0", "" + sf_oN + sf_f); + test("null-84.0", "" + sf_oN + sf_dM); + test("null58000000", "" + sf_oN + f_i); + test("null-55000000", "" + sf_oN + f_iM); + test("null1460392448", "" + sf_oN + f_l); + test("nullC(70)", "" + sf_oN + f_o); + test("null\u04511", "" + sf_oN + sf_strU); + test("null8000", "" + sf_oN + f_s); + test("null18", "" + sf_oN + s_str); + test("null-1000000", "" + sf_oN + s_iM); + test("null1000000", "" + sf_oN + sf_I); + test("nullnull", "" + sf_oN + f_oNtS); + test("nullfalse", "" + sf_oN + f_bl); + test("nullnull", "" + sf_oN + sf_iAN); + test("null-2000000", "" + sf_oN + sf_iM); + test("null-820130816", "" + sf_oN + f_lM); + test("nullnull", "" + sf_oN + sf_oAN); + test("null25000000", "" + sf_oN + s_I); + test("94000000-96.0", "" + f_I + s_dM); + test("94000000null", "" + f_I + s_oNtS); + test("94000000\u045176", "" + f_I + f_strU); + test("9400000092", "" + f_I + sf_strU2); + test("9400000051", "" + f_I + sf_strU1); + test("94000000null", "" + f_I + s_iAN); + test("94000000-54", "" + f_I + f_bM); + test("94000000-87.0", "" + f_I + f_fM); + test("94000000null", "" + f_I + s_oAN); + test("9400000019", "" + f_I + f_str); + test("94000000-41", "" + f_I + sf_bM); + test("94000000null", "" + f_I + sf_IN); + test("94000000T", "" + f_I + s_c); + test("94000000-42.0", "" + f_I + sf_fM); + test("9400000025", "" + f_I + s_b); + test("94000000null", "" + f_I + f_oN); + test("94000000-1410065408", "" + f_I + s_lM); + test("940000008.0", "" + f_I + s_d); + test("9400000055.0", "" + f_I + s_f); + test("9400000097000000", "" + f_I + s_i); + test("94000000-9900", "" + f_I + f_sM); + test("94000000935228928", "" + f_I + s_l); + test("94000000-8400", "" + f_I + sf_sM); + test("94000000C(82)", "" + f_I + s_o); + test("94000000null", "" + f_I + sf_oNtS); + test("94000000true", "" + f_I + s_bl); + test("940000003900", "" + f_I + s_s); + test("94000000null", "" + f_I + sf_oN); + test("9400000094000000", "" + f_I + f_I); + test("94000000null", "" + f_I + f_IN); + test("94000000true", "" + f_I + sf_bl); + test("940000005500", "" + f_I + sf_s); + test("94000000-2900", "" + f_I + s_sM); + test("94000000-194313216", "" + f_I + sf_l); + test("9400000012", "" + f_I + s_strU1); + test("94000000C(87)", "" + f_I + sf_o); + test("9400000091", "" + f_I + s_strU2); + test("9400000021", "" + f_I + f_strU1); + test("9400000018", "" + f_I + f_strU2); + test("94000000null", "" + f_I + f_iAN); + test("94000000null", "" + f_I + s_oN); + test("94000000\u045180", "" + f_I + s_strU); + test("94000000C", "" + f_I + sf_c); + test("9400000075", "" + f_I + sf_str); + test("94000000-43", "" + f_I + s_bM); + test("9400000080", "" + f_I + sf_b); + test("94000000null", "" + f_I + s_IN); + test("94000000-52.0", "" + f_I + s_fM); + test("9400000075000000", "" + f_I + sf_i); + test("9400000044", "" + f_I + f_b); + test("94000000-1705032704", "" + f_I + sf_lM); + test("94000000null", "" + f_I + f_oAN); + test("9400000083.0", "" + f_I + f_d); + test("94000000I", "" + f_I + f_c); + test("9400000094.0", "" + f_I + f_f); + test("9400000012.0", "" + f_I + sf_d); + test("94000000-99.0", "" + f_I + f_dM); + test("9400000017.0", "" + f_I + sf_f); + test("94000000-84.0", "" + f_I + sf_dM); + test("9400000058000000", "" + f_I + f_i); + test("94000000-55000000", "" + f_I + f_iM); + test("940000001460392448", "" + f_I + f_l); + test("94000000C(70)", "" + f_I + f_o); + test("94000000\u04511", "" + f_I + sf_strU); + test("940000008000", "" + f_I + f_s); + test("9400000018", "" + f_I + s_str); + test("94000000-1000000", "" + f_I + s_iM); + test("940000001000000", "" + f_I + sf_I); + test("94000000null", "" + f_I + f_oNtS); + test("94000000false", "" + f_I + f_bl); + test("94000000null", "" + f_I + sf_iAN); + test("94000000-2000000", "" + f_I + sf_iM); + test("94000000-820130816", "" + f_I + f_lM); + test("94000000null", "" + f_I + sf_oAN); + test("9400000025000000", "" + f_I + s_I); + test("null-96.0", "" + f_IN + s_dM); + test("nullnull", "" + f_IN + s_oNtS); + test("null\u045176", "" + f_IN + f_strU); + test("null92", "" + f_IN + sf_strU2); + test("null51", "" + f_IN + sf_strU1); + test("nullnull", "" + f_IN + s_iAN); + test("null-54", "" + f_IN + f_bM); + test("null-87.0", "" + f_IN + f_fM); + test("nullnull", "" + f_IN + s_oAN); + test("null19", "" + f_IN + f_str); + test("null-41", "" + f_IN + sf_bM); + test("nullnull", "" + f_IN + sf_IN); + test("nullT", "" + f_IN + s_c); + test("null-42.0", "" + f_IN + sf_fM); + test("null25", "" + f_IN + s_b); + test("nullnull", "" + f_IN + f_oN); + test("null-1410065408", "" + f_IN + s_lM); + test("null8.0", "" + f_IN + s_d); + test("null55.0", "" + f_IN + s_f); + test("null97000000", "" + f_IN + s_i); + test("null-9900", "" + f_IN + f_sM); + test("null935228928", "" + f_IN + s_l); + test("null-8400", "" + f_IN + sf_sM); + test("nullC(82)", "" + f_IN + s_o); + test("nullnull", "" + f_IN + sf_oNtS); + test("nulltrue", "" + f_IN + s_bl); + test("null3900", "" + f_IN + s_s); + test("nullnull", "" + f_IN + sf_oN); + test("null94000000", "" + f_IN + f_I); + test("nullnull", "" + f_IN + f_IN); + test("nulltrue", "" + f_IN + sf_bl); + test("null5500", "" + f_IN + sf_s); + test("null-2900", "" + f_IN + s_sM); + test("null-194313216", "" + f_IN + sf_l); + test("null12", "" + f_IN + s_strU1); + test("nullC(87)", "" + f_IN + sf_o); + test("null91", "" + f_IN + s_strU2); + test("null21", "" + f_IN + f_strU1); + test("null18", "" + f_IN + f_strU2); + test("nullnull", "" + f_IN + f_iAN); + test("nullnull", "" + f_IN + s_oN); + test("null\u045180", "" + f_IN + s_strU); + test("nullC", "" + f_IN + sf_c); + test("null75", "" + f_IN + sf_str); + test("null-43", "" + f_IN + s_bM); + test("null80", "" + f_IN + sf_b); + test("nullnull", "" + f_IN + s_IN); + test("null-52.0", "" + f_IN + s_fM); + test("null75000000", "" + f_IN + sf_i); + test("null44", "" + f_IN + f_b); + test("null-1705032704", "" + f_IN + sf_lM); + test("nullnull", "" + f_IN + f_oAN); + test("null83.0", "" + f_IN + f_d); + test("nullI", "" + f_IN + f_c); + test("null94.0", "" + f_IN + f_f); + test("null12.0", "" + f_IN + sf_d); + test("null-99.0", "" + f_IN + f_dM); + test("null17.0", "" + f_IN + sf_f); + test("null-84.0", "" + f_IN + sf_dM); + test("null58000000", "" + f_IN + f_i); + test("null-55000000", "" + f_IN + f_iM); + test("null1460392448", "" + f_IN + f_l); + test("nullC(70)", "" + f_IN + f_o); + test("null\u04511", "" + f_IN + sf_strU); + test("null8000", "" + f_IN + f_s); + test("null18", "" + f_IN + s_str); + test("null-1000000", "" + f_IN + s_iM); + test("null1000000", "" + f_IN + sf_I); + test("nullnull", "" + f_IN + f_oNtS); + test("nullfalse", "" + f_IN + f_bl); + test("nullnull", "" + f_IN + sf_iAN); + test("null-2000000", "" + f_IN + sf_iM); + test("null-820130816", "" + f_IN + f_lM); + test("nullnull", "" + f_IN + sf_oAN); + test("null25000000", "" + f_IN + s_I); + test("true-96.0", "" + sf_bl + s_dM); + test("truenull", "" + sf_bl + s_oNtS); + test("true\u045176", "" + sf_bl + f_strU); + test("true92", "" + sf_bl + sf_strU2); + test("true51", "" + sf_bl + sf_strU1); + test("truenull", "" + sf_bl + s_iAN); + test("true-54", "" + sf_bl + f_bM); + test("true-87.0", "" + sf_bl + f_fM); + test("truenull", "" + sf_bl + s_oAN); + test("true19", "" + sf_bl + f_str); + test("true-41", "" + sf_bl + sf_bM); + test("truenull", "" + sf_bl + sf_IN); + test("trueT", "" + sf_bl + s_c); + test("true-42.0", "" + sf_bl + sf_fM); + test("true25", "" + sf_bl + s_b); + test("truenull", "" + sf_bl + f_oN); + test("true-1410065408", "" + sf_bl + s_lM); + test("true8.0", "" + sf_bl + s_d); + test("true55.0", "" + sf_bl + s_f); + test("true97000000", "" + sf_bl + s_i); + test("true-9900", "" + sf_bl + f_sM); + test("true935228928", "" + sf_bl + s_l); + test("true-8400", "" + sf_bl + sf_sM); + test("trueC(82)", "" + sf_bl + s_o); + test("truenull", "" + sf_bl + sf_oNtS); + test("truetrue", "" + sf_bl + s_bl); + test("true3900", "" + sf_bl + s_s); + test("truenull", "" + sf_bl + sf_oN); + test("true94000000", "" + sf_bl + f_I); + test("truenull", "" + sf_bl + f_IN); + test("truetrue", "" + sf_bl + sf_bl); + test("true5500", "" + sf_bl + sf_s); + test("true-2900", "" + sf_bl + s_sM); + test("true-194313216", "" + sf_bl + sf_l); + test("true12", "" + sf_bl + s_strU1); + test("trueC(87)", "" + sf_bl + sf_o); + test("true91", "" + sf_bl + s_strU2); + test("true21", "" + sf_bl + f_strU1); + test("true18", "" + sf_bl + f_strU2); + test("truenull", "" + sf_bl + f_iAN); + test("truenull", "" + sf_bl + s_oN); + test("true\u045180", "" + sf_bl + s_strU); + test("trueC", "" + sf_bl + sf_c); + test("true75", "" + sf_bl + sf_str); + test("true-43", "" + sf_bl + s_bM); + test("true80", "" + sf_bl + sf_b); + test("truenull", "" + sf_bl + s_IN); + test("true-52.0", "" + sf_bl + s_fM); + test("true75000000", "" + sf_bl + sf_i); + test("true44", "" + sf_bl + f_b); + test("true-1705032704", "" + sf_bl + sf_lM); + test("truenull", "" + sf_bl + f_oAN); + test("true83.0", "" + sf_bl + f_d); + test("trueI", "" + sf_bl + f_c); + test("true94.0", "" + sf_bl + f_f); + test("true12.0", "" + sf_bl + sf_d); + test("true-99.0", "" + sf_bl + f_dM); + test("true17.0", "" + sf_bl + sf_f); + test("true-84.0", "" + sf_bl + sf_dM); + test("true58000000", "" + sf_bl + f_i); + test("true-55000000", "" + sf_bl + f_iM); + test("true1460392448", "" + sf_bl + f_l); + test("trueC(70)", "" + sf_bl + f_o); + test("true\u04511", "" + sf_bl + sf_strU); + test("true8000", "" + sf_bl + f_s); + test("true18", "" + sf_bl + s_str); + test("true-1000000", "" + sf_bl + s_iM); + test("true1000000", "" + sf_bl + sf_I); + test("truenull", "" + sf_bl + f_oNtS); + test("truefalse", "" + sf_bl + f_bl); + test("truenull", "" + sf_bl + sf_iAN); + test("true-2000000", "" + sf_bl + sf_iM); + test("true-820130816", "" + sf_bl + f_lM); + test("truenull", "" + sf_bl + sf_oAN); + test("true25000000", "" + sf_bl + s_I); + test("5500-96.0", "" + sf_s + s_dM); + test("5500null", "" + sf_s + s_oNtS); + test("5500\u045176", "" + sf_s + f_strU); + test("550092", "" + sf_s + sf_strU2); + test("550051", "" + sf_s + sf_strU1); + test("5500null", "" + sf_s + s_iAN); + test("5500-54", "" + sf_s + f_bM); + test("5500-87.0", "" + sf_s + f_fM); + test("5500null", "" + sf_s + s_oAN); + test("550019", "" + sf_s + f_str); + test("5500-41", "" + sf_s + sf_bM); + test("5500null", "" + sf_s + sf_IN); + test("5500T", "" + sf_s + s_c); + test("5500-42.0", "" + sf_s + sf_fM); + test("550025", "" + sf_s + s_b); + test("5500null", "" + sf_s + f_oN); + test("5500-1410065408", "" + sf_s + s_lM); + test("55008.0", "" + sf_s + s_d); + test("550055.0", "" + sf_s + s_f); + test("550097000000", "" + sf_s + s_i); + test("5500-9900", "" + sf_s + f_sM); + test("5500935228928", "" + sf_s + s_l); + test("5500-8400", "" + sf_s + sf_sM); + test("5500C(82)", "" + sf_s + s_o); + test("5500null", "" + sf_s + sf_oNtS); + test("5500true", "" + sf_s + s_bl); + test("55003900", "" + sf_s + s_s); + test("5500null", "" + sf_s + sf_oN); + test("550094000000", "" + sf_s + f_I); + test("5500null", "" + sf_s + f_IN); + test("5500true", "" + sf_s + sf_bl); + test("55005500", "" + sf_s + sf_s); + test("5500-2900", "" + sf_s + s_sM); + test("5500-194313216", "" + sf_s + sf_l); + test("550012", "" + sf_s + s_strU1); + test("5500C(87)", "" + sf_s + sf_o); + test("550091", "" + sf_s + s_strU2); + test("550021", "" + sf_s + f_strU1); + test("550018", "" + sf_s + f_strU2); + test("5500null", "" + sf_s + f_iAN); + test("5500null", "" + sf_s + s_oN); + test("5500\u045180", "" + sf_s + s_strU); + test("5500C", "" + sf_s + sf_c); + test("550075", "" + sf_s + sf_str); + test("5500-43", "" + sf_s + s_bM); + test("550080", "" + sf_s + sf_b); + test("5500null", "" + sf_s + s_IN); + test("5500-52.0", "" + sf_s + s_fM); + test("550075000000", "" + sf_s + sf_i); + test("550044", "" + sf_s + f_b); + test("5500-1705032704", "" + sf_s + sf_lM); + test("5500null", "" + sf_s + f_oAN); + test("550083.0", "" + sf_s + f_d); + test("5500I", "" + sf_s + f_c); + test("550094.0", "" + sf_s + f_f); + test("550012.0", "" + sf_s + sf_d); + test("5500-99.0", "" + sf_s + f_dM); + test("550017.0", "" + sf_s + sf_f); + test("5500-84.0", "" + sf_s + sf_dM); + test("550058000000", "" + sf_s + f_i); + test("5500-55000000", "" + sf_s + f_iM); + test("55001460392448", "" + sf_s + f_l); + test("5500C(70)", "" + sf_s + f_o); + test("5500\u04511", "" + sf_s + sf_strU); + test("55008000", "" + sf_s + f_s); + test("550018", "" + sf_s + s_str); + test("5500-1000000", "" + sf_s + s_iM); + test("55001000000", "" + sf_s + sf_I); + test("5500null", "" + sf_s + f_oNtS); + test("5500false", "" + sf_s + f_bl); + test("5500null", "" + sf_s + sf_iAN); + test("5500-2000000", "" + sf_s + sf_iM); + test("5500-820130816", "" + sf_s + f_lM); + test("5500null", "" + sf_s + sf_oAN); + test("550025000000", "" + sf_s + s_I); + test("-2900-96.0", "" + s_sM + s_dM); + test("-2900null", "" + s_sM + s_oNtS); + test("-2900\u045176", "" + s_sM + f_strU); + test("-290092", "" + s_sM + sf_strU2); + test("-290051", "" + s_sM + sf_strU1); + test("-2900null", "" + s_sM + s_iAN); + test("-2900-54", "" + s_sM + f_bM); + test("-2900-87.0", "" + s_sM + f_fM); + test("-2900null", "" + s_sM + s_oAN); + test("-290019", "" + s_sM + f_str); + test("-2900-41", "" + s_sM + sf_bM); + test("-2900null", "" + s_sM + sf_IN); + test("-2900T", "" + s_sM + s_c); + test("-2900-42.0", "" + s_sM + sf_fM); + test("-290025", "" + s_sM + s_b); + test("-2900null", "" + s_sM + f_oN); + test("-2900-1410065408", "" + s_sM + s_lM); + test("-29008.0", "" + s_sM + s_d); + test("-290055.0", "" + s_sM + s_f); + test("-290097000000", "" + s_sM + s_i); + test("-2900-9900", "" + s_sM + f_sM); + test("-2900935228928", "" + s_sM + s_l); + test("-2900-8400", "" + s_sM + sf_sM); + test("-2900C(82)", "" + s_sM + s_o); + test("-2900null", "" + s_sM + sf_oNtS); + test("-2900true", "" + s_sM + s_bl); + test("-29003900", "" + s_sM + s_s); + test("-2900null", "" + s_sM + sf_oN); + test("-290094000000", "" + s_sM + f_I); + test("-2900null", "" + s_sM + f_IN); + test("-2900true", "" + s_sM + sf_bl); + test("-29005500", "" + s_sM + sf_s); + test("-2900-2900", "" + s_sM + s_sM); + test("-2900-194313216", "" + s_sM + sf_l); + test("-290012", "" + s_sM + s_strU1); + test("-2900C(87)", "" + s_sM + sf_o); + test("-290091", "" + s_sM + s_strU2); + test("-290021", "" + s_sM + f_strU1); + test("-290018", "" + s_sM + f_strU2); + test("-2900null", "" + s_sM + f_iAN); + test("-2900null", "" + s_sM + s_oN); + test("-2900\u045180", "" + s_sM + s_strU); + test("-2900C", "" + s_sM + sf_c); + test("-290075", "" + s_sM + sf_str); + test("-2900-43", "" + s_sM + s_bM); + test("-290080", "" + s_sM + sf_b); + test("-2900null", "" + s_sM + s_IN); + test("-2900-52.0", "" + s_sM + s_fM); + test("-290075000000", "" + s_sM + sf_i); + test("-290044", "" + s_sM + f_b); + test("-2900-1705032704", "" + s_sM + sf_lM); + test("-2900null", "" + s_sM + f_oAN); + test("-290083.0", "" + s_sM + f_d); + test("-2900I", "" + s_sM + f_c); + test("-290094.0", "" + s_sM + f_f); + test("-290012.0", "" + s_sM + sf_d); + test("-2900-99.0", "" + s_sM + f_dM); + test("-290017.0", "" + s_sM + sf_f); + test("-2900-84.0", "" + s_sM + sf_dM); + test("-290058000000", "" + s_sM + f_i); + test("-2900-55000000", "" + s_sM + f_iM); + test("-29001460392448", "" + s_sM + f_l); + test("-2900C(70)", "" + s_sM + f_o); + test("-2900\u04511", "" + s_sM + sf_strU); + test("-29008000", "" + s_sM + f_s); + test("-290018", "" + s_sM + s_str); + test("-2900-1000000", "" + s_sM + s_iM); + test("-29001000000", "" + s_sM + sf_I); + test("-2900null", "" + s_sM + f_oNtS); + test("-2900false", "" + s_sM + f_bl); + test("-2900null", "" + s_sM + sf_iAN); + test("-2900-2000000", "" + s_sM + sf_iM); + test("-2900-820130816", "" + s_sM + f_lM); + test("-2900null", "" + s_sM + sf_oAN); + test("-290025000000", "" + s_sM + s_I); + test("-194313216-96.0", "" + sf_l + s_dM); + test("-194313216null", "" + sf_l + s_oNtS); + test("-194313216\u045176", "" + sf_l + f_strU); + test("-19431321692", "" + sf_l + sf_strU2); + test("-19431321651", "" + sf_l + sf_strU1); + test("-194313216null", "" + sf_l + s_iAN); + test("-194313216-54", "" + sf_l + f_bM); + test("-194313216-87.0", "" + sf_l + f_fM); + test("-194313216null", "" + sf_l + s_oAN); + test("-19431321619", "" + sf_l + f_str); + test("-194313216-41", "" + sf_l + sf_bM); + test("-194313216null", "" + sf_l + sf_IN); + test("-194313216T", "" + sf_l + s_c); + test("-194313216-42.0", "" + sf_l + sf_fM); + test("-19431321625", "" + sf_l + s_b); + test("-194313216null", "" + sf_l + f_oN); + test("-194313216-1410065408", "" + sf_l + s_lM); + test("-1943132168.0", "" + sf_l + s_d); + test("-19431321655.0", "" + sf_l + s_f); + test("-19431321697000000", "" + sf_l + s_i); + test("-194313216-9900", "" + sf_l + f_sM); + test("-194313216935228928", "" + sf_l + s_l); + test("-194313216-8400", "" + sf_l + sf_sM); + test("-194313216C(82)", "" + sf_l + s_o); + test("-194313216null", "" + sf_l + sf_oNtS); + test("-194313216true", "" + sf_l + s_bl); + test("-1943132163900", "" + sf_l + s_s); + test("-194313216null", "" + sf_l + sf_oN); + test("-19431321694000000", "" + sf_l + f_I); + test("-194313216null", "" + sf_l + f_IN); + test("-194313216true", "" + sf_l + sf_bl); + test("-1943132165500", "" + sf_l + sf_s); + test("-194313216-2900", "" + sf_l + s_sM); + test("-194313216-194313216", "" + sf_l + sf_l); + test("-19431321612", "" + sf_l + s_strU1); + test("-194313216C(87)", "" + sf_l + sf_o); + test("-19431321691", "" + sf_l + s_strU2); + test("-19431321621", "" + sf_l + f_strU1); + test("-19431321618", "" + sf_l + f_strU2); + test("-194313216null", "" + sf_l + f_iAN); + test("-194313216null", "" + sf_l + s_oN); + test("-194313216\u045180", "" + sf_l + s_strU); + test("-194313216C", "" + sf_l + sf_c); + test("-19431321675", "" + sf_l + sf_str); + test("-194313216-43", "" + sf_l + s_bM); + test("-19431321680", "" + sf_l + sf_b); + test("-194313216null", "" + sf_l + s_IN); + test("-194313216-52.0", "" + sf_l + s_fM); + test("-19431321675000000", "" + sf_l + sf_i); + test("-19431321644", "" + sf_l + f_b); + test("-194313216-1705032704", "" + sf_l + sf_lM); + test("-194313216null", "" + sf_l + f_oAN); + test("-19431321683.0", "" + sf_l + f_d); + test("-194313216I", "" + sf_l + f_c); + test("-19431321694.0", "" + sf_l + f_f); + test("-19431321612.0", "" + sf_l + sf_d); + test("-194313216-99.0", "" + sf_l + f_dM); + test("-19431321617.0", "" + sf_l + sf_f); + test("-194313216-84.0", "" + sf_l + sf_dM); + test("-19431321658000000", "" + sf_l + f_i); + test("-194313216-55000000", "" + sf_l + f_iM); + test("-1943132161460392448", "" + sf_l + f_l); + test("-194313216C(70)", "" + sf_l + f_o); + test("-194313216\u04511", "" + sf_l + sf_strU); + test("-1943132168000", "" + sf_l + f_s); + test("-19431321618", "" + sf_l + s_str); + test("-194313216-1000000", "" + sf_l + s_iM); + test("-1943132161000000", "" + sf_l + sf_I); + test("-194313216null", "" + sf_l + f_oNtS); + test("-194313216false", "" + sf_l + f_bl); + test("-194313216null", "" + sf_l + sf_iAN); + test("-194313216-2000000", "" + sf_l + sf_iM); + test("-194313216-820130816", "" + sf_l + f_lM); + test("-194313216null", "" + sf_l + sf_oAN); + test("-19431321625000000", "" + sf_l + s_I); + test("12-96.0", "" + s_strU1 + s_dM); + test("12null", "" + s_strU1 + s_oNtS); + test("12\u045176", "" + s_strU1 + f_strU); + test("1292", "" + s_strU1 + sf_strU2); + test("1251", "" + s_strU1 + sf_strU1); + test("12null", "" + s_strU1 + s_iAN); + test("12-54", "" + s_strU1 + f_bM); + test("12-87.0", "" + s_strU1 + f_fM); + test("12null", "" + s_strU1 + s_oAN); + test("1219", "" + s_strU1 + f_str); + test("12-41", "" + s_strU1 + sf_bM); + test("12null", "" + s_strU1 + sf_IN); + test("12T", "" + s_strU1 + s_c); + test("12-42.0", "" + s_strU1 + sf_fM); + test("1225", "" + s_strU1 + s_b); + test("12null", "" + s_strU1 + f_oN); + test("12-1410065408", "" + s_strU1 + s_lM); + test("128.0", "" + s_strU1 + s_d); + test("1255.0", "" + s_strU1 + s_f); + test("1297000000", "" + s_strU1 + s_i); + test("12-9900", "" + s_strU1 + f_sM); + test("12935228928", "" + s_strU1 + s_l); + test("12-8400", "" + s_strU1 + sf_sM); + test("12C(82)", "" + s_strU1 + s_o); + test("12null", "" + s_strU1 + sf_oNtS); + test("12true", "" + s_strU1 + s_bl); + test("123900", "" + s_strU1 + s_s); + test("12null", "" + s_strU1 + sf_oN); + test("1294000000", "" + s_strU1 + f_I); + test("12null", "" + s_strU1 + f_IN); + test("12true", "" + s_strU1 + sf_bl); + test("125500", "" + s_strU1 + sf_s); + test("12-2900", "" + s_strU1 + s_sM); + test("12-194313216", "" + s_strU1 + sf_l); + test("1212", "" + s_strU1 + s_strU1); + test("12C(87)", "" + s_strU1 + sf_o); + test("1291", "" + s_strU1 + s_strU2); + test("1221", "" + s_strU1 + f_strU1); + test("1218", "" + s_strU1 + f_strU2); + test("12null", "" + s_strU1 + f_iAN); + test("12null", "" + s_strU1 + s_oN); + test("12\u045180", "" + s_strU1 + s_strU); + test("12C", "" + s_strU1 + sf_c); + test("1275", "" + s_strU1 + sf_str); + test("12-43", "" + s_strU1 + s_bM); + test("1280", "" + s_strU1 + sf_b); + test("12null", "" + s_strU1 + s_IN); + test("12-52.0", "" + s_strU1 + s_fM); + test("1275000000", "" + s_strU1 + sf_i); + test("1244", "" + s_strU1 + f_b); + test("12-1705032704", "" + s_strU1 + sf_lM); + test("12null", "" + s_strU1 + f_oAN); + test("1283.0", "" + s_strU1 + f_d); + test("12I", "" + s_strU1 + f_c); + test("1294.0", "" + s_strU1 + f_f); + test("1212.0", "" + s_strU1 + sf_d); + test("12-99.0", "" + s_strU1 + f_dM); + test("1217.0", "" + s_strU1 + sf_f); + test("12-84.0", "" + s_strU1 + sf_dM); + test("1258000000", "" + s_strU1 + f_i); + test("12-55000000", "" + s_strU1 + f_iM); + test("121460392448", "" + s_strU1 + f_l); + test("12C(70)", "" + s_strU1 + f_o); + test("12\u04511", "" + s_strU1 + sf_strU); + test("128000", "" + s_strU1 + f_s); + test("1218", "" + s_strU1 + s_str); + test("12-1000000", "" + s_strU1 + s_iM); + test("121000000", "" + s_strU1 + sf_I); + test("12null", "" + s_strU1 + f_oNtS); + test("12false", "" + s_strU1 + f_bl); + test("12null", "" + s_strU1 + sf_iAN); + test("12-2000000", "" + s_strU1 + sf_iM); + test("12-820130816", "" + s_strU1 + f_lM); + test("12null", "" + s_strU1 + sf_oAN); + test("1225000000", "" + s_strU1 + s_I); + test("C(87)-96.0", "" + sf_o + s_dM); + test("C(87)null", "" + sf_o + s_oNtS); + test("C(87)\u045176", "" + sf_o + f_strU); + test("C(87)92", "" + sf_o + sf_strU2); + test("C(87)51", "" + sf_o + sf_strU1); + test("C(87)null", "" + sf_o + s_iAN); + test("C(87)-54", "" + sf_o + f_bM); + test("C(87)-87.0", "" + sf_o + f_fM); + test("C(87)null", "" + sf_o + s_oAN); + test("C(87)19", "" + sf_o + f_str); + test("C(87)-41", "" + sf_o + sf_bM); + test("C(87)null", "" + sf_o + sf_IN); + test("C(87)T", "" + sf_o + s_c); + test("C(87)-42.0", "" + sf_o + sf_fM); + test("C(87)25", "" + sf_o + s_b); + test("C(87)null", "" + sf_o + f_oN); + test("C(87)-1410065408", "" + sf_o + s_lM); + test("C(87)8.0", "" + sf_o + s_d); + test("C(87)55.0", "" + sf_o + s_f); + test("C(87)97000000", "" + sf_o + s_i); + test("C(87)-9900", "" + sf_o + f_sM); + test("C(87)935228928", "" + sf_o + s_l); + test("C(87)-8400", "" + sf_o + sf_sM); + test("C(87)C(82)", "" + sf_o + s_o); + test("C(87)null", "" + sf_o + sf_oNtS); + test("C(87)true", "" + sf_o + s_bl); + test("C(87)3900", "" + sf_o + s_s); + test("C(87)null", "" + sf_o + sf_oN); + test("C(87)94000000", "" + sf_o + f_I); + test("C(87)null", "" + sf_o + f_IN); + test("C(87)true", "" + sf_o + sf_bl); + test("C(87)5500", "" + sf_o + sf_s); + test("C(87)-2900", "" + sf_o + s_sM); + test("C(87)-194313216", "" + sf_o + sf_l); + test("C(87)12", "" + sf_o + s_strU1); + test("C(87)C(87)", "" + sf_o + sf_o); + test("C(87)91", "" + sf_o + s_strU2); + test("C(87)21", "" + sf_o + f_strU1); + test("C(87)18", "" + sf_o + f_strU2); + test("C(87)null", "" + sf_o + f_iAN); + test("C(87)null", "" + sf_o + s_oN); + test("C(87)\u045180", "" + sf_o + s_strU); + test("C(87)C", "" + sf_o + sf_c); + test("C(87)75", "" + sf_o + sf_str); + test("C(87)-43", "" + sf_o + s_bM); + test("C(87)80", "" + sf_o + sf_b); + test("C(87)null", "" + sf_o + s_IN); + test("C(87)-52.0", "" + sf_o + s_fM); + test("C(87)75000000", "" + sf_o + sf_i); + test("C(87)44", "" + sf_o + f_b); + test("C(87)-1705032704", "" + sf_o + sf_lM); + test("C(87)null", "" + sf_o + f_oAN); + test("C(87)83.0", "" + sf_o + f_d); + test("C(87)I", "" + sf_o + f_c); + test("C(87)94.0", "" + sf_o + f_f); + test("C(87)12.0", "" + sf_o + sf_d); + test("C(87)-99.0", "" + sf_o + f_dM); + test("C(87)17.0", "" + sf_o + sf_f); + test("C(87)-84.0", "" + sf_o + sf_dM); + test("C(87)58000000", "" + sf_o + f_i); + test("C(87)-55000000", "" + sf_o + f_iM); + test("C(87)1460392448", "" + sf_o + f_l); + test("C(87)C(70)", "" + sf_o + f_o); + test("C(87)\u04511", "" + sf_o + sf_strU); + test("C(87)8000", "" + sf_o + f_s); + test("C(87)18", "" + sf_o + s_str); + test("C(87)-1000000", "" + sf_o + s_iM); + test("C(87)1000000", "" + sf_o + sf_I); + test("C(87)null", "" + sf_o + f_oNtS); + test("C(87)false", "" + sf_o + f_bl); + test("C(87)null", "" + sf_o + sf_iAN); + test("C(87)-2000000", "" + sf_o + sf_iM); + test("C(87)-820130816", "" + sf_o + f_lM); + test("C(87)null", "" + sf_o + sf_oAN); + test("C(87)25000000", "" + sf_o + s_I); + test("91-96.0", "" + s_strU2 + s_dM); + test("91null", "" + s_strU2 + s_oNtS); + test("91\u045176", "" + s_strU2 + f_strU); + test("9192", "" + s_strU2 + sf_strU2); + test("9151", "" + s_strU2 + sf_strU1); + test("91null", "" + s_strU2 + s_iAN); + test("91-54", "" + s_strU2 + f_bM); + test("91-87.0", "" + s_strU2 + f_fM); + test("91null", "" + s_strU2 + s_oAN); + test("9119", "" + s_strU2 + f_str); + test("91-41", "" + s_strU2 + sf_bM); + test("91null", "" + s_strU2 + sf_IN); + test("91T", "" + s_strU2 + s_c); + test("91-42.0", "" + s_strU2 + sf_fM); + test("9125", "" + s_strU2 + s_b); + test("91null", "" + s_strU2 + f_oN); + test("91-1410065408", "" + s_strU2 + s_lM); + test("918.0", "" + s_strU2 + s_d); + test("9155.0", "" + s_strU2 + s_f); + test("9197000000", "" + s_strU2 + s_i); + test("91-9900", "" + s_strU2 + f_sM); + test("91935228928", "" + s_strU2 + s_l); + test("91-8400", "" + s_strU2 + sf_sM); + test("91C(82)", "" + s_strU2 + s_o); + test("91null", "" + s_strU2 + sf_oNtS); + test("91true", "" + s_strU2 + s_bl); + test("913900", "" + s_strU2 + s_s); + test("91null", "" + s_strU2 + sf_oN); + test("9194000000", "" + s_strU2 + f_I); + test("91null", "" + s_strU2 + f_IN); + test("91true", "" + s_strU2 + sf_bl); + test("915500", "" + s_strU2 + sf_s); + test("91-2900", "" + s_strU2 + s_sM); + test("91-194313216", "" + s_strU2 + sf_l); + test("9112", "" + s_strU2 + s_strU1); + test("91C(87)", "" + s_strU2 + sf_o); + test("9191", "" + s_strU2 + s_strU2); + test("9121", "" + s_strU2 + f_strU1); + test("9118", "" + s_strU2 + f_strU2); + test("91null", "" + s_strU2 + f_iAN); + test("91null", "" + s_strU2 + s_oN); + test("91\u045180", "" + s_strU2 + s_strU); + test("91C", "" + s_strU2 + sf_c); + test("9175", "" + s_strU2 + sf_str); + test("91-43", "" + s_strU2 + s_bM); + test("9180", "" + s_strU2 + sf_b); + test("91null", "" + s_strU2 + s_IN); + test("91-52.0", "" + s_strU2 + s_fM); + test("9175000000", "" + s_strU2 + sf_i); + test("9144", "" + s_strU2 + f_b); + test("91-1705032704", "" + s_strU2 + sf_lM); + test("91null", "" + s_strU2 + f_oAN); + test("9183.0", "" + s_strU2 + f_d); + test("91I", "" + s_strU2 + f_c); + test("9194.0", "" + s_strU2 + f_f); + test("9112.0", "" + s_strU2 + sf_d); + test("91-99.0", "" + s_strU2 + f_dM); + test("9117.0", "" + s_strU2 + sf_f); + test("91-84.0", "" + s_strU2 + sf_dM); + test("9158000000", "" + s_strU2 + f_i); + test("91-55000000", "" + s_strU2 + f_iM); + test("911460392448", "" + s_strU2 + f_l); + test("91C(70)", "" + s_strU2 + f_o); + test("91\u04511", "" + s_strU2 + sf_strU); + test("918000", "" + s_strU2 + f_s); + test("9118", "" + s_strU2 + s_str); + test("91-1000000", "" + s_strU2 + s_iM); + test("911000000", "" + s_strU2 + sf_I); + test("91null", "" + s_strU2 + f_oNtS); + test("91false", "" + s_strU2 + f_bl); + test("91null", "" + s_strU2 + sf_iAN); + test("91-2000000", "" + s_strU2 + sf_iM); + test("91-820130816", "" + s_strU2 + f_lM); + test("91null", "" + s_strU2 + sf_oAN); + test("9125000000", "" + s_strU2 + s_I); + test("21-96.0", "" + f_strU1 + s_dM); + test("21null", "" + f_strU1 + s_oNtS); + test("21\u045176", "" + f_strU1 + f_strU); + test("2192", "" + f_strU1 + sf_strU2); + test("2151", "" + f_strU1 + sf_strU1); + test("21null", "" + f_strU1 + s_iAN); + test("21-54", "" + f_strU1 + f_bM); + test("21-87.0", "" + f_strU1 + f_fM); + test("21null", "" + f_strU1 + s_oAN); + test("2119", "" + f_strU1 + f_str); + test("21-41", "" + f_strU1 + sf_bM); + test("21null", "" + f_strU1 + sf_IN); + test("21T", "" + f_strU1 + s_c); + test("21-42.0", "" + f_strU1 + sf_fM); + test("2125", "" + f_strU1 + s_b); + test("21null", "" + f_strU1 + f_oN); + test("21-1410065408", "" + f_strU1 + s_lM); + test("218.0", "" + f_strU1 + s_d); + test("2155.0", "" + f_strU1 + s_f); + test("2197000000", "" + f_strU1 + s_i); + test("21-9900", "" + f_strU1 + f_sM); + test("21935228928", "" + f_strU1 + s_l); + test("21-8400", "" + f_strU1 + sf_sM); + test("21C(82)", "" + f_strU1 + s_o); + test("21null", "" + f_strU1 + sf_oNtS); + test("21true", "" + f_strU1 + s_bl); + test("213900", "" + f_strU1 + s_s); + test("21null", "" + f_strU1 + sf_oN); + test("2194000000", "" + f_strU1 + f_I); + test("21null", "" + f_strU1 + f_IN); + test("21true", "" + f_strU1 + sf_bl); + test("215500", "" + f_strU1 + sf_s); + test("21-2900", "" + f_strU1 + s_sM); + test("21-194313216", "" + f_strU1 + sf_l); + test("2112", "" + f_strU1 + s_strU1); + test("21C(87)", "" + f_strU1 + sf_o); + test("2191", "" + f_strU1 + s_strU2); + test("2121", "" + f_strU1 + f_strU1); + test("2118", "" + f_strU1 + f_strU2); + test("21null", "" + f_strU1 + f_iAN); + test("21null", "" + f_strU1 + s_oN); + test("21\u045180", "" + f_strU1 + s_strU); + test("21C", "" + f_strU1 + sf_c); + test("2175", "" + f_strU1 + sf_str); + test("21-43", "" + f_strU1 + s_bM); + test("2180", "" + f_strU1 + sf_b); + test("21null", "" + f_strU1 + s_IN); + test("21-52.0", "" + f_strU1 + s_fM); + test("2175000000", "" + f_strU1 + sf_i); + test("2144", "" + f_strU1 + f_b); + test("21-1705032704", "" + f_strU1 + sf_lM); + test("21null", "" + f_strU1 + f_oAN); + test("2183.0", "" + f_strU1 + f_d); + test("21I", "" + f_strU1 + f_c); + test("2194.0", "" + f_strU1 + f_f); + test("2112.0", "" + f_strU1 + sf_d); + test("21-99.0", "" + f_strU1 + f_dM); + test("2117.0", "" + f_strU1 + sf_f); + test("21-84.0", "" + f_strU1 + sf_dM); + test("2158000000", "" + f_strU1 + f_i); + test("21-55000000", "" + f_strU1 + f_iM); + test("211460392448", "" + f_strU1 + f_l); + test("21C(70)", "" + f_strU1 + f_o); + test("21\u04511", "" + f_strU1 + sf_strU); + test("218000", "" + f_strU1 + f_s); + test("2118", "" + f_strU1 + s_str); + test("21-1000000", "" + f_strU1 + s_iM); + test("211000000", "" + f_strU1 + sf_I); + test("21null", "" + f_strU1 + f_oNtS); + test("21false", "" + f_strU1 + f_bl); + test("21null", "" + f_strU1 + sf_iAN); + test("21-2000000", "" + f_strU1 + sf_iM); + test("21-820130816", "" + f_strU1 + f_lM); + test("21null", "" + f_strU1 + sf_oAN); + test("2125000000", "" + f_strU1 + s_I); + test("18-96.0", "" + f_strU2 + s_dM); + test("18null", "" + f_strU2 + s_oNtS); + test("18\u045176", "" + f_strU2 + f_strU); + test("1892", "" + f_strU2 + sf_strU2); + test("1851", "" + f_strU2 + sf_strU1); + test("18null", "" + f_strU2 + s_iAN); + test("18-54", "" + f_strU2 + f_bM); + test("18-87.0", "" + f_strU2 + f_fM); + test("18null", "" + f_strU2 + s_oAN); + test("1819", "" + f_strU2 + f_str); + test("18-41", "" + f_strU2 + sf_bM); + test("18null", "" + f_strU2 + sf_IN); + test("18T", "" + f_strU2 + s_c); + test("18-42.0", "" + f_strU2 + sf_fM); + test("1825", "" + f_strU2 + s_b); + test("18null", "" + f_strU2 + f_oN); + test("18-1410065408", "" + f_strU2 + s_lM); + test("188.0", "" + f_strU2 + s_d); + test("1855.0", "" + f_strU2 + s_f); + test("1897000000", "" + f_strU2 + s_i); + test("18-9900", "" + f_strU2 + f_sM); + test("18935228928", "" + f_strU2 + s_l); + test("18-8400", "" + f_strU2 + sf_sM); + test("18C(82)", "" + f_strU2 + s_o); + test("18null", "" + f_strU2 + sf_oNtS); + test("18true", "" + f_strU2 + s_bl); + test("183900", "" + f_strU2 + s_s); + test("18null", "" + f_strU2 + sf_oN); + test("1894000000", "" + f_strU2 + f_I); + test("18null", "" + f_strU2 + f_IN); + test("18true", "" + f_strU2 + sf_bl); + test("185500", "" + f_strU2 + sf_s); + test("18-2900", "" + f_strU2 + s_sM); + test("18-194313216", "" + f_strU2 + sf_l); + test("1812", "" + f_strU2 + s_strU1); + test("18C(87)", "" + f_strU2 + sf_o); + test("1891", "" + f_strU2 + s_strU2); + test("1821", "" + f_strU2 + f_strU1); + test("1818", "" + f_strU2 + f_strU2); + test("18null", "" + f_strU2 + f_iAN); + test("18null", "" + f_strU2 + s_oN); + test("18\u045180", "" + f_strU2 + s_strU); + test("18C", "" + f_strU2 + sf_c); + test("1875", "" + f_strU2 + sf_str); + test("18-43", "" + f_strU2 + s_bM); + test("1880", "" + f_strU2 + sf_b); + test("18null", "" + f_strU2 + s_IN); + test("18-52.0", "" + f_strU2 + s_fM); + test("1875000000", "" + f_strU2 + sf_i); + test("1844", "" + f_strU2 + f_b); + test("18-1705032704", "" + f_strU2 + sf_lM); + test("18null", "" + f_strU2 + f_oAN); + test("1883.0", "" + f_strU2 + f_d); + test("18I", "" + f_strU2 + f_c); + test("1894.0", "" + f_strU2 + f_f); + test("1812.0", "" + f_strU2 + sf_d); + test("18-99.0", "" + f_strU2 + f_dM); + test("1817.0", "" + f_strU2 + sf_f); + test("18-84.0", "" + f_strU2 + sf_dM); + test("1858000000", "" + f_strU2 + f_i); + test("18-55000000", "" + f_strU2 + f_iM); + test("181460392448", "" + f_strU2 + f_l); + test("18C(70)", "" + f_strU2 + f_o); + test("18\u04511", "" + f_strU2 + sf_strU); + test("188000", "" + f_strU2 + f_s); + test("1818", "" + f_strU2 + s_str); + test("18-1000000", "" + f_strU2 + s_iM); + test("181000000", "" + f_strU2 + sf_I); + test("18null", "" + f_strU2 + f_oNtS); + test("18false", "" + f_strU2 + f_bl); + test("18null", "" + f_strU2 + sf_iAN); + test("18-2000000", "" + f_strU2 + sf_iM); + test("18-820130816", "" + f_strU2 + f_lM); + test("18null", "" + f_strU2 + sf_oAN); + test("1825000000", "" + f_strU2 + s_I); + } + + public void run3() { + test("null-96.0", "" + f_iAN + s_dM); + test("nullnull", "" + f_iAN + s_oNtS); + test("null\u045176", "" + f_iAN + f_strU); + test("null92", "" + f_iAN + sf_strU2); + test("null51", "" + f_iAN + sf_strU1); + test("nullnull", "" + f_iAN + s_iAN); + test("null-54", "" + f_iAN + f_bM); + test("null-87.0", "" + f_iAN + f_fM); + test("nullnull", "" + f_iAN + s_oAN); + test("null19", "" + f_iAN + f_str); + test("null-41", "" + f_iAN + sf_bM); + test("nullnull", "" + f_iAN + sf_IN); + test("nullT", "" + f_iAN + s_c); + test("null-42.0", "" + f_iAN + sf_fM); + test("null25", "" + f_iAN + s_b); + test("nullnull", "" + f_iAN + f_oN); + test("null-1410065408", "" + f_iAN + s_lM); + test("null8.0", "" + f_iAN + s_d); + test("null55.0", "" + f_iAN + s_f); + test("null97000000", "" + f_iAN + s_i); + test("null-9900", "" + f_iAN + f_sM); + test("null935228928", "" + f_iAN + s_l); + test("null-8400", "" + f_iAN + sf_sM); + test("nullC(82)", "" + f_iAN + s_o); + test("nullnull", "" + f_iAN + sf_oNtS); + test("nulltrue", "" + f_iAN + s_bl); + test("null3900", "" + f_iAN + s_s); + test("nullnull", "" + f_iAN + sf_oN); + test("null94000000", "" + f_iAN + f_I); + test("nullnull", "" + f_iAN + f_IN); + test("nulltrue", "" + f_iAN + sf_bl); + test("null5500", "" + f_iAN + sf_s); + test("null-2900", "" + f_iAN + s_sM); + test("null-194313216", "" + f_iAN + sf_l); + test("null12", "" + f_iAN + s_strU1); + test("nullC(87)", "" + f_iAN + sf_o); + test("null91", "" + f_iAN + s_strU2); + test("null21", "" + f_iAN + f_strU1); + test("null18", "" + f_iAN + f_strU2); + test("nullnull", "" + f_iAN + f_iAN); + test("nullnull", "" + f_iAN + s_oN); + test("null\u045180", "" + f_iAN + s_strU); + test("nullC", "" + f_iAN + sf_c); + test("null75", "" + f_iAN + sf_str); + test("null-43", "" + f_iAN + s_bM); + test("null80", "" + f_iAN + sf_b); + test("nullnull", "" + f_iAN + s_IN); + test("null-52.0", "" + f_iAN + s_fM); + test("null75000000", "" + f_iAN + sf_i); + test("null44", "" + f_iAN + f_b); + test("null-1705032704", "" + f_iAN + sf_lM); + test("nullnull", "" + f_iAN + f_oAN); + test("null83.0", "" + f_iAN + f_d); + test("nullI", "" + f_iAN + f_c); + test("null94.0", "" + f_iAN + f_f); + test("null12.0", "" + f_iAN + sf_d); + test("null-99.0", "" + f_iAN + f_dM); + test("null17.0", "" + f_iAN + sf_f); + test("null-84.0", "" + f_iAN + sf_dM); + test("null58000000", "" + f_iAN + f_i); + test("null-55000000", "" + f_iAN + f_iM); + test("null1460392448", "" + f_iAN + f_l); + test("nullC(70)", "" + f_iAN + f_o); + test("null\u04511", "" + f_iAN + sf_strU); + test("null8000", "" + f_iAN + f_s); + test("null18", "" + f_iAN + s_str); + test("null-1000000", "" + f_iAN + s_iM); + test("null1000000", "" + f_iAN + sf_I); + test("nullnull", "" + f_iAN + f_oNtS); + test("nullfalse", "" + f_iAN + f_bl); + test("nullnull", "" + f_iAN + sf_iAN); + test("null-2000000", "" + f_iAN + sf_iM); + test("null-820130816", "" + f_iAN + f_lM); + test("nullnull", "" + f_iAN + sf_oAN); + test("null25000000", "" + f_iAN + s_I); + test("null-96.0", "" + s_oN + s_dM); + test("nullnull", "" + s_oN + s_oNtS); + test("null\u045176", "" + s_oN + f_strU); + test("null92", "" + s_oN + sf_strU2); + test("null51", "" + s_oN + sf_strU1); + test("nullnull", "" + s_oN + s_iAN); + test("null-54", "" + s_oN + f_bM); + test("null-87.0", "" + s_oN + f_fM); + test("nullnull", "" + s_oN + s_oAN); + test("null19", "" + s_oN + f_str); + test("null-41", "" + s_oN + sf_bM); + test("nullnull", "" + s_oN + sf_IN); + test("nullT", "" + s_oN + s_c); + test("null-42.0", "" + s_oN + sf_fM); + test("null25", "" + s_oN + s_b); + test("nullnull", "" + s_oN + f_oN); + test("null-1410065408", "" + s_oN + s_lM); + test("null8.0", "" + s_oN + s_d); + test("null55.0", "" + s_oN + s_f); + test("null97000000", "" + s_oN + s_i); + test("null-9900", "" + s_oN + f_sM); + test("null935228928", "" + s_oN + s_l); + test("null-8400", "" + s_oN + sf_sM); + test("nullC(82)", "" + s_oN + s_o); + test("nullnull", "" + s_oN + sf_oNtS); + test("nulltrue", "" + s_oN + s_bl); + test("null3900", "" + s_oN + s_s); + test("nullnull", "" + s_oN + sf_oN); + test("null94000000", "" + s_oN + f_I); + test("nullnull", "" + s_oN + f_IN); + test("nulltrue", "" + s_oN + sf_bl); + test("null5500", "" + s_oN + sf_s); + test("null-2900", "" + s_oN + s_sM); + test("null-194313216", "" + s_oN + sf_l); + test("null12", "" + s_oN + s_strU1); + test("nullC(87)", "" + s_oN + sf_o); + test("null91", "" + s_oN + s_strU2); + test("null21", "" + s_oN + f_strU1); + test("null18", "" + s_oN + f_strU2); + test("nullnull", "" + s_oN + f_iAN); + test("nullnull", "" + s_oN + s_oN); + test("null\u045180", "" + s_oN + s_strU); + test("nullC", "" + s_oN + sf_c); + test("null75", "" + s_oN + sf_str); + test("null-43", "" + s_oN + s_bM); + test("null80", "" + s_oN + sf_b); + test("nullnull", "" + s_oN + s_IN); + test("null-52.0", "" + s_oN + s_fM); + test("null75000000", "" + s_oN + sf_i); + test("null44", "" + s_oN + f_b); + test("null-1705032704", "" + s_oN + sf_lM); + test("nullnull", "" + s_oN + f_oAN); + test("null83.0", "" + s_oN + f_d); + test("nullI", "" + s_oN + f_c); + test("null94.0", "" + s_oN + f_f); + test("null12.0", "" + s_oN + sf_d); + test("null-99.0", "" + s_oN + f_dM); + test("null17.0", "" + s_oN + sf_f); + test("null-84.0", "" + s_oN + sf_dM); + test("null58000000", "" + s_oN + f_i); + test("null-55000000", "" + s_oN + f_iM); + test("null1460392448", "" + s_oN + f_l); + test("nullC(70)", "" + s_oN + f_o); + test("null\u04511", "" + s_oN + sf_strU); + test("null8000", "" + s_oN + f_s); + test("null18", "" + s_oN + s_str); + test("null-1000000", "" + s_oN + s_iM); + test("null1000000", "" + s_oN + sf_I); + test("nullnull", "" + s_oN + f_oNtS); + test("nullfalse", "" + s_oN + f_bl); + test("nullnull", "" + s_oN + sf_iAN); + test("null-2000000", "" + s_oN + sf_iM); + test("null-820130816", "" + s_oN + f_lM); + test("nullnull", "" + s_oN + sf_oAN); + test("null25000000", "" + s_oN + s_I); + test("\u045180-96.0", "" + s_strU + s_dM); + test("\u045180null", "" + s_strU + s_oNtS); + test("\u045180\u045176", "" + s_strU + f_strU); + test("\u04518092", "" + s_strU + sf_strU2); + test("\u04518051", "" + s_strU + sf_strU1); + test("\u045180null", "" + s_strU + s_iAN); + test("\u045180-54", "" + s_strU + f_bM); + test("\u045180-87.0", "" + s_strU + f_fM); + test("\u045180null", "" + s_strU + s_oAN); + test("\u04518019", "" + s_strU + f_str); + test("\u045180-41", "" + s_strU + sf_bM); + test("\u045180null", "" + s_strU + sf_IN); + test("\u045180T", "" + s_strU + s_c); + test("\u045180-42.0", "" + s_strU + sf_fM); + test("\u04518025", "" + s_strU + s_b); + test("\u045180null", "" + s_strU + f_oN); + test("\u045180-1410065408", "" + s_strU + s_lM); + test("\u0451808.0", "" + s_strU + s_d); + test("\u04518055.0", "" + s_strU + s_f); + test("\u04518097000000", "" + s_strU + s_i); + test("\u045180-9900", "" + s_strU + f_sM); + test("\u045180935228928", "" + s_strU + s_l); + test("\u045180-8400", "" + s_strU + sf_sM); + test("\u045180C(82)", "" + s_strU + s_o); + test("\u045180null", "" + s_strU + sf_oNtS); + test("\u045180true", "" + s_strU + s_bl); + test("\u0451803900", "" + s_strU + s_s); + test("\u045180null", "" + s_strU + sf_oN); + test("\u04518094000000", "" + s_strU + f_I); + test("\u045180null", "" + s_strU + f_IN); + test("\u045180true", "" + s_strU + sf_bl); + test("\u0451805500", "" + s_strU + sf_s); + test("\u045180-2900", "" + s_strU + s_sM); + test("\u045180-194313216", "" + s_strU + sf_l); + test("\u04518012", "" + s_strU + s_strU1); + test("\u045180C(87)", "" + s_strU + sf_o); + test("\u04518091", "" + s_strU + s_strU2); + test("\u04518021", "" + s_strU + f_strU1); + test("\u04518018", "" + s_strU + f_strU2); + test("\u045180null", "" + s_strU + f_iAN); + test("\u045180null", "" + s_strU + s_oN); + test("\u045180\u045180", "" + s_strU + s_strU); + test("\u045180C", "" + s_strU + sf_c); + test("\u04518075", "" + s_strU + sf_str); + test("\u045180-43", "" + s_strU + s_bM); + test("\u04518080", "" + s_strU + sf_b); + test("\u045180null", "" + s_strU + s_IN); + test("\u045180-52.0", "" + s_strU + s_fM); + test("\u04518075000000", "" + s_strU + sf_i); + test("\u04518044", "" + s_strU + f_b); + test("\u045180-1705032704", "" + s_strU + sf_lM); + test("\u045180null", "" + s_strU + f_oAN); + test("\u04518083.0", "" + s_strU + f_d); + test("\u045180I", "" + s_strU + f_c); + test("\u04518094.0", "" + s_strU + f_f); + test("\u04518012.0", "" + s_strU + sf_d); + test("\u045180-99.0", "" + s_strU + f_dM); + test("\u04518017.0", "" + s_strU + sf_f); + test("\u045180-84.0", "" + s_strU + sf_dM); + test("\u04518058000000", "" + s_strU + f_i); + test("\u045180-55000000", "" + s_strU + f_iM); + test("\u0451801460392448", "" + s_strU + f_l); + test("\u045180C(70)", "" + s_strU + f_o); + test("\u045180\u04511", "" + s_strU + sf_strU); + test("\u0451808000", "" + s_strU + f_s); + test("\u04518018", "" + s_strU + s_str); + test("\u045180-1000000", "" + s_strU + s_iM); + test("\u0451801000000", "" + s_strU + sf_I); + test("\u045180null", "" + s_strU + f_oNtS); + test("\u045180false", "" + s_strU + f_bl); + test("\u045180null", "" + s_strU + sf_iAN); + test("\u045180-2000000", "" + s_strU + sf_iM); + test("\u045180-820130816", "" + s_strU + f_lM); + test("\u045180null", "" + s_strU + sf_oAN); + test("\u04518025000000", "" + s_strU + s_I); + test("C-96.0", "" + sf_c + s_dM); + test("Cnull", "" + sf_c + s_oNtS); + test("C\u045176", "" + sf_c + f_strU); + test("C92", "" + sf_c + sf_strU2); + test("C51", "" + sf_c + sf_strU1); + test("Cnull", "" + sf_c + s_iAN); + test("C-54", "" + sf_c + f_bM); + test("C-87.0", "" + sf_c + f_fM); + test("Cnull", "" + sf_c + s_oAN); + test("C19", "" + sf_c + f_str); + test("C-41", "" + sf_c + sf_bM); + test("Cnull", "" + sf_c + sf_IN); + test("CT", "" + sf_c + s_c); + test("C-42.0", "" + sf_c + sf_fM); + test("C25", "" + sf_c + s_b); + test("Cnull", "" + sf_c + f_oN); + test("C-1410065408", "" + sf_c + s_lM); + test("C8.0", "" + sf_c + s_d); + test("C55.0", "" + sf_c + s_f); + test("C97000000", "" + sf_c + s_i); + test("C-9900", "" + sf_c + f_sM); + test("C935228928", "" + sf_c + s_l); + test("C-8400", "" + sf_c + sf_sM); + test("CC(82)", "" + sf_c + s_o); + test("Cnull", "" + sf_c + sf_oNtS); + test("Ctrue", "" + sf_c + s_bl); + test("C3900", "" + sf_c + s_s); + test("Cnull", "" + sf_c + sf_oN); + test("C94000000", "" + sf_c + f_I); + test("Cnull", "" + sf_c + f_IN); + test("Ctrue", "" + sf_c + sf_bl); + test("C5500", "" + sf_c + sf_s); + test("C-2900", "" + sf_c + s_sM); + test("C-194313216", "" + sf_c + sf_l); + test("C12", "" + sf_c + s_strU1); + test("CC(87)", "" + sf_c + sf_o); + test("C91", "" + sf_c + s_strU2); + test("C21", "" + sf_c + f_strU1); + test("C18", "" + sf_c + f_strU2); + test("Cnull", "" + sf_c + f_iAN); + test("Cnull", "" + sf_c + s_oN); + test("C\u045180", "" + sf_c + s_strU); + test("CC", "" + sf_c + sf_c); + test("C75", "" + sf_c + sf_str); + test("C-43", "" + sf_c + s_bM); + test("C80", "" + sf_c + sf_b); + test("Cnull", "" + sf_c + s_IN); + test("C-52.0", "" + sf_c + s_fM); + test("C75000000", "" + sf_c + sf_i); + test("C44", "" + sf_c + f_b); + test("C-1705032704", "" + sf_c + sf_lM); + test("Cnull", "" + sf_c + f_oAN); + test("C83.0", "" + sf_c + f_d); + test("CI", "" + sf_c + f_c); + test("C94.0", "" + sf_c + f_f); + test("C12.0", "" + sf_c + sf_d); + test("C-99.0", "" + sf_c + f_dM); + test("C17.0", "" + sf_c + sf_f); + test("C-84.0", "" + sf_c + sf_dM); + test("C58000000", "" + sf_c + f_i); + test("C-55000000", "" + sf_c + f_iM); + test("C1460392448", "" + sf_c + f_l); + test("CC(70)", "" + sf_c + f_o); + test("C\u04511", "" + sf_c + sf_strU); + test("C8000", "" + sf_c + f_s); + test("C18", "" + sf_c + s_str); + test("C-1000000", "" + sf_c + s_iM); + test("C1000000", "" + sf_c + sf_I); + test("Cnull", "" + sf_c + f_oNtS); + test("Cfalse", "" + sf_c + f_bl); + test("Cnull", "" + sf_c + sf_iAN); + test("C-2000000", "" + sf_c + sf_iM); + test("C-820130816", "" + sf_c + f_lM); + test("Cnull", "" + sf_c + sf_oAN); + test("C25000000", "" + sf_c + s_I); + test("75-96.0", "" + sf_str + s_dM); + test("75null", "" + sf_str + s_oNtS); + test("75\u045176", "" + sf_str + f_strU); + test("7592", "" + sf_str + sf_strU2); + test("7551", "" + sf_str + sf_strU1); + test("75null", "" + sf_str + s_iAN); + test("75-54", "" + sf_str + f_bM); + test("75-87.0", "" + sf_str + f_fM); + test("75null", "" + sf_str + s_oAN); + test("7519", "" + sf_str + f_str); + test("75-41", "" + sf_str + sf_bM); + test("75null", "" + sf_str + sf_IN); + test("75T", "" + sf_str + s_c); + test("75-42.0", "" + sf_str + sf_fM); + test("7525", "" + sf_str + s_b); + test("75null", "" + sf_str + f_oN); + test("75-1410065408", "" + sf_str + s_lM); + test("758.0", "" + sf_str + s_d); + test("7555.0", "" + sf_str + s_f); + test("7597000000", "" + sf_str + s_i); + test("75-9900", "" + sf_str + f_sM); + test("75935228928", "" + sf_str + s_l); + test("75-8400", "" + sf_str + sf_sM); + test("75C(82)", "" + sf_str + s_o); + test("75null", "" + sf_str + sf_oNtS); + test("75true", "" + sf_str + s_bl); + test("753900", "" + sf_str + s_s); + test("75null", "" + sf_str + sf_oN); + test("7594000000", "" + sf_str + f_I); + test("75null", "" + sf_str + f_IN); + test("75true", "" + sf_str + sf_bl); + test("755500", "" + sf_str + sf_s); + test("75-2900", "" + sf_str + s_sM); + test("75-194313216", "" + sf_str + sf_l); + test("7512", "" + sf_str + s_strU1); + test("75C(87)", "" + sf_str + sf_o); + test("7591", "" + sf_str + s_strU2); + test("7521", "" + sf_str + f_strU1); + test("7518", "" + sf_str + f_strU2); + test("75null", "" + sf_str + f_iAN); + test("75null", "" + sf_str + s_oN); + test("75\u045180", "" + sf_str + s_strU); + test("75C", "" + sf_str + sf_c); + test("7575", "" + sf_str + sf_str); + test("75-43", "" + sf_str + s_bM); + test("7580", "" + sf_str + sf_b); + test("75null", "" + sf_str + s_IN); + test("75-52.0", "" + sf_str + s_fM); + test("7575000000", "" + sf_str + sf_i); + test("7544", "" + sf_str + f_b); + test("75-1705032704", "" + sf_str + sf_lM); + test("75null", "" + sf_str + f_oAN); + test("7583.0", "" + sf_str + f_d); + test("75I", "" + sf_str + f_c); + test("7594.0", "" + sf_str + f_f); + test("7512.0", "" + sf_str + sf_d); + test("75-99.0", "" + sf_str + f_dM); + test("7517.0", "" + sf_str + sf_f); + test("75-84.0", "" + sf_str + sf_dM); + test("7558000000", "" + sf_str + f_i); + test("75-55000000", "" + sf_str + f_iM); + test("751460392448", "" + sf_str + f_l); + test("75C(70)", "" + sf_str + f_o); + test("75\u04511", "" + sf_str + sf_strU); + test("758000", "" + sf_str + f_s); + test("7518", "" + sf_str + s_str); + test("75-1000000", "" + sf_str + s_iM); + test("751000000", "" + sf_str + sf_I); + test("75null", "" + sf_str + f_oNtS); + test("75false", "" + sf_str + f_bl); + test("75null", "" + sf_str + sf_iAN); + test("75-2000000", "" + sf_str + sf_iM); + test("75-820130816", "" + sf_str + f_lM); + test("75null", "" + sf_str + sf_oAN); + test("7525000000", "" + sf_str + s_I); + test("-43-96.0", "" + s_bM + s_dM); + test("-43null", "" + s_bM + s_oNtS); + test("-43\u045176", "" + s_bM + f_strU); + test("-4392", "" + s_bM + sf_strU2); + test("-4351", "" + s_bM + sf_strU1); + test("-43null", "" + s_bM + s_iAN); + test("-43-54", "" + s_bM + f_bM); + test("-43-87.0", "" + s_bM + f_fM); + test("-43null", "" + s_bM + s_oAN); + test("-4319", "" + s_bM + f_str); + test("-43-41", "" + s_bM + sf_bM); + test("-43null", "" + s_bM + sf_IN); + test("-43T", "" + s_bM + s_c); + test("-43-42.0", "" + s_bM + sf_fM); + test("-4325", "" + s_bM + s_b); + test("-43null", "" + s_bM + f_oN); + test("-43-1410065408", "" + s_bM + s_lM); + test("-438.0", "" + s_bM + s_d); + test("-4355.0", "" + s_bM + s_f); + test("-4397000000", "" + s_bM + s_i); + test("-43-9900", "" + s_bM + f_sM); + test("-43935228928", "" + s_bM + s_l); + test("-43-8400", "" + s_bM + sf_sM); + test("-43C(82)", "" + s_bM + s_o); + test("-43null", "" + s_bM + sf_oNtS); + test("-43true", "" + s_bM + s_bl); + test("-433900", "" + s_bM + s_s); + test("-43null", "" + s_bM + sf_oN); + test("-4394000000", "" + s_bM + f_I); + test("-43null", "" + s_bM + f_IN); + test("-43true", "" + s_bM + sf_bl); + test("-435500", "" + s_bM + sf_s); + test("-43-2900", "" + s_bM + s_sM); + test("-43-194313216", "" + s_bM + sf_l); + test("-4312", "" + s_bM + s_strU1); + test("-43C(87)", "" + s_bM + sf_o); + test("-4391", "" + s_bM + s_strU2); + test("-4321", "" + s_bM + f_strU1); + test("-4318", "" + s_bM + f_strU2); + test("-43null", "" + s_bM + f_iAN); + test("-43null", "" + s_bM + s_oN); + test("-43\u045180", "" + s_bM + s_strU); + test("-43C", "" + s_bM + sf_c); + test("-4375", "" + s_bM + sf_str); + test("-43-43", "" + s_bM + s_bM); + test("-4380", "" + s_bM + sf_b); + test("-43null", "" + s_bM + s_IN); + test("-43-52.0", "" + s_bM + s_fM); + test("-4375000000", "" + s_bM + sf_i); + test("-4344", "" + s_bM + f_b); + test("-43-1705032704", "" + s_bM + sf_lM); + test("-43null", "" + s_bM + f_oAN); + test("-4383.0", "" + s_bM + f_d); + test("-43I", "" + s_bM + f_c); + test("-4394.0", "" + s_bM + f_f); + test("-4312.0", "" + s_bM + sf_d); + test("-43-99.0", "" + s_bM + f_dM); + test("-4317.0", "" + s_bM + sf_f); + test("-43-84.0", "" + s_bM + sf_dM); + test("-4358000000", "" + s_bM + f_i); + test("-43-55000000", "" + s_bM + f_iM); + test("-431460392448", "" + s_bM + f_l); + test("-43C(70)", "" + s_bM + f_o); + test("-43\u04511", "" + s_bM + sf_strU); + test("-438000", "" + s_bM + f_s); + test("-4318", "" + s_bM + s_str); + test("-43-1000000", "" + s_bM + s_iM); + test("-431000000", "" + s_bM + sf_I); + test("-43null", "" + s_bM + f_oNtS); + test("-43false", "" + s_bM + f_bl); + test("-43null", "" + s_bM + sf_iAN); + test("-43-2000000", "" + s_bM + sf_iM); + test("-43-820130816", "" + s_bM + f_lM); + test("-43null", "" + s_bM + sf_oAN); + test("-4325000000", "" + s_bM + s_I); + test("80-96.0", "" + sf_b + s_dM); + test("80null", "" + sf_b + s_oNtS); + test("80\u045176", "" + sf_b + f_strU); + test("8092", "" + sf_b + sf_strU2); + test("8051", "" + sf_b + sf_strU1); + test("80null", "" + sf_b + s_iAN); + test("80-54", "" + sf_b + f_bM); + test("80-87.0", "" + sf_b + f_fM); + test("80null", "" + sf_b + s_oAN); + test("8019", "" + sf_b + f_str); + test("80-41", "" + sf_b + sf_bM); + test("80null", "" + sf_b + sf_IN); + test("80T", "" + sf_b + s_c); + test("80-42.0", "" + sf_b + sf_fM); + test("8025", "" + sf_b + s_b); + test("80null", "" + sf_b + f_oN); + test("80-1410065408", "" + sf_b + s_lM); + test("808.0", "" + sf_b + s_d); + test("8055.0", "" + sf_b + s_f); + test("8097000000", "" + sf_b + s_i); + test("80-9900", "" + sf_b + f_sM); + test("80935228928", "" + sf_b + s_l); + test("80-8400", "" + sf_b + sf_sM); + test("80C(82)", "" + sf_b + s_o); + test("80null", "" + sf_b + sf_oNtS); + test("80true", "" + sf_b + s_bl); + test("803900", "" + sf_b + s_s); + test("80null", "" + sf_b + sf_oN); + test("8094000000", "" + sf_b + f_I); + test("80null", "" + sf_b + f_IN); + test("80true", "" + sf_b + sf_bl); + test("805500", "" + sf_b + sf_s); + test("80-2900", "" + sf_b + s_sM); + test("80-194313216", "" + sf_b + sf_l); + test("8012", "" + sf_b + s_strU1); + test("80C(87)", "" + sf_b + sf_o); + test("8091", "" + sf_b + s_strU2); + test("8021", "" + sf_b + f_strU1); + test("8018", "" + sf_b + f_strU2); + test("80null", "" + sf_b + f_iAN); + test("80null", "" + sf_b + s_oN); + test("80\u045180", "" + sf_b + s_strU); + test("80C", "" + sf_b + sf_c); + test("8075", "" + sf_b + sf_str); + test("80-43", "" + sf_b + s_bM); + test("8080", "" + sf_b + sf_b); + test("80null", "" + sf_b + s_IN); + test("80-52.0", "" + sf_b + s_fM); + test("8075000000", "" + sf_b + sf_i); + test("8044", "" + sf_b + f_b); + test("80-1705032704", "" + sf_b + sf_lM); + test("80null", "" + sf_b + f_oAN); + test("8083.0", "" + sf_b + f_d); + test("80I", "" + sf_b + f_c); + test("8094.0", "" + sf_b + f_f); + test("8012.0", "" + sf_b + sf_d); + test("80-99.0", "" + sf_b + f_dM); + test("8017.0", "" + sf_b + sf_f); + test("80-84.0", "" + sf_b + sf_dM); + test("8058000000", "" + sf_b + f_i); + test("80-55000000", "" + sf_b + f_iM); + test("801460392448", "" + sf_b + f_l); + test("80C(70)", "" + sf_b + f_o); + test("80\u04511", "" + sf_b + sf_strU); + test("808000", "" + sf_b + f_s); + test("8018", "" + sf_b + s_str); + test("80-1000000", "" + sf_b + s_iM); + test("801000000", "" + sf_b + sf_I); + test("80null", "" + sf_b + f_oNtS); + test("80false", "" + sf_b + f_bl); + test("80null", "" + sf_b + sf_iAN); + test("80-2000000", "" + sf_b + sf_iM); + test("80-820130816", "" + sf_b + f_lM); + test("80null", "" + sf_b + sf_oAN); + test("8025000000", "" + sf_b + s_I); + test("null-96.0", "" + s_IN + s_dM); + test("nullnull", "" + s_IN + s_oNtS); + test("null\u045176", "" + s_IN + f_strU); + test("null92", "" + s_IN + sf_strU2); + test("null51", "" + s_IN + sf_strU1); + test("nullnull", "" + s_IN + s_iAN); + test("null-54", "" + s_IN + f_bM); + test("null-87.0", "" + s_IN + f_fM); + test("nullnull", "" + s_IN + s_oAN); + test("null19", "" + s_IN + f_str); + test("null-41", "" + s_IN + sf_bM); + test("nullnull", "" + s_IN + sf_IN); + test("nullT", "" + s_IN + s_c); + test("null-42.0", "" + s_IN + sf_fM); + test("null25", "" + s_IN + s_b); + test("nullnull", "" + s_IN + f_oN); + test("null-1410065408", "" + s_IN + s_lM); + test("null8.0", "" + s_IN + s_d); + test("null55.0", "" + s_IN + s_f); + test("null97000000", "" + s_IN + s_i); + test("null-9900", "" + s_IN + f_sM); + test("null935228928", "" + s_IN + s_l); + test("null-8400", "" + s_IN + sf_sM); + test("nullC(82)", "" + s_IN + s_o); + test("nullnull", "" + s_IN + sf_oNtS); + test("nulltrue", "" + s_IN + s_bl); + test("null3900", "" + s_IN + s_s); + test("nullnull", "" + s_IN + sf_oN); + test("null94000000", "" + s_IN + f_I); + test("nullnull", "" + s_IN + f_IN); + test("nulltrue", "" + s_IN + sf_bl); + test("null5500", "" + s_IN + sf_s); + test("null-2900", "" + s_IN + s_sM); + test("null-194313216", "" + s_IN + sf_l); + test("null12", "" + s_IN + s_strU1); + test("nullC(87)", "" + s_IN + sf_o); + test("null91", "" + s_IN + s_strU2); + test("null21", "" + s_IN + f_strU1); + test("null18", "" + s_IN + f_strU2); + test("nullnull", "" + s_IN + f_iAN); + test("nullnull", "" + s_IN + s_oN); + test("null\u045180", "" + s_IN + s_strU); + test("nullC", "" + s_IN + sf_c); + test("null75", "" + s_IN + sf_str); + test("null-43", "" + s_IN + s_bM); + test("null80", "" + s_IN + sf_b); + test("nullnull", "" + s_IN + s_IN); + test("null-52.0", "" + s_IN + s_fM); + test("null75000000", "" + s_IN + sf_i); + test("null44", "" + s_IN + f_b); + test("null-1705032704", "" + s_IN + sf_lM); + test("nullnull", "" + s_IN + f_oAN); + test("null83.0", "" + s_IN + f_d); + test("nullI", "" + s_IN + f_c); + test("null94.0", "" + s_IN + f_f); + test("null12.0", "" + s_IN + sf_d); + test("null-99.0", "" + s_IN + f_dM); + test("null17.0", "" + s_IN + sf_f); + test("null-84.0", "" + s_IN + sf_dM); + test("null58000000", "" + s_IN + f_i); + test("null-55000000", "" + s_IN + f_iM); + test("null1460392448", "" + s_IN + f_l); + test("nullC(70)", "" + s_IN + f_o); + test("null\u04511", "" + s_IN + sf_strU); + test("null8000", "" + s_IN + f_s); + test("null18", "" + s_IN + s_str); + test("null-1000000", "" + s_IN + s_iM); + test("null1000000", "" + s_IN + sf_I); + test("nullnull", "" + s_IN + f_oNtS); + test("nullfalse", "" + s_IN + f_bl); + test("nullnull", "" + s_IN + sf_iAN); + test("null-2000000", "" + s_IN + sf_iM); + test("null-820130816", "" + s_IN + f_lM); + test("nullnull", "" + s_IN + sf_oAN); + test("null25000000", "" + s_IN + s_I); + test("-52.0-96.0", "" + s_fM + s_dM); + test("-52.0null", "" + s_fM + s_oNtS); + test("-52.0\u045176", "" + s_fM + f_strU); + test("-52.092", "" + s_fM + sf_strU2); + test("-52.051", "" + s_fM + sf_strU1); + test("-52.0null", "" + s_fM + s_iAN); + test("-52.0-54", "" + s_fM + f_bM); + test("-52.0-87.0", "" + s_fM + f_fM); + test("-52.0null", "" + s_fM + s_oAN); + test("-52.019", "" + s_fM + f_str); + test("-52.0-41", "" + s_fM + sf_bM); + test("-52.0null", "" + s_fM + sf_IN); + test("-52.0T", "" + s_fM + s_c); + test("-52.0-42.0", "" + s_fM + sf_fM); + test("-52.025", "" + s_fM + s_b); + test("-52.0null", "" + s_fM + f_oN); + test("-52.0-1410065408", "" + s_fM + s_lM); + test("-52.08.0", "" + s_fM + s_d); + test("-52.055.0", "" + s_fM + s_f); + test("-52.097000000", "" + s_fM + s_i); + test("-52.0-9900", "" + s_fM + f_sM); + test("-52.0935228928", "" + s_fM + s_l); + test("-52.0-8400", "" + s_fM + sf_sM); + test("-52.0C(82)", "" + s_fM + s_o); + test("-52.0null", "" + s_fM + sf_oNtS); + test("-52.0true", "" + s_fM + s_bl); + test("-52.03900", "" + s_fM + s_s); + test("-52.0null", "" + s_fM + sf_oN); + test("-52.094000000", "" + s_fM + f_I); + test("-52.0null", "" + s_fM + f_IN); + test("-52.0true", "" + s_fM + sf_bl); + test("-52.05500", "" + s_fM + sf_s); + test("-52.0-2900", "" + s_fM + s_sM); + test("-52.0-194313216", "" + s_fM + sf_l); + test("-52.012", "" + s_fM + s_strU1); + test("-52.0C(87)", "" + s_fM + sf_o); + test("-52.091", "" + s_fM + s_strU2); + test("-52.021", "" + s_fM + f_strU1); + test("-52.018", "" + s_fM + f_strU2); + test("-52.0null", "" + s_fM + f_iAN); + test("-52.0null", "" + s_fM + s_oN); + test("-52.0\u045180", "" + s_fM + s_strU); + test("-52.0C", "" + s_fM + sf_c); + test("-52.075", "" + s_fM + sf_str); + test("-52.0-43", "" + s_fM + s_bM); + test("-52.080", "" + s_fM + sf_b); + test("-52.0null", "" + s_fM + s_IN); + test("-52.0-52.0", "" + s_fM + s_fM); + test("-52.075000000", "" + s_fM + sf_i); + test("-52.044", "" + s_fM + f_b); + test("-52.0-1705032704", "" + s_fM + sf_lM); + test("-52.0null", "" + s_fM + f_oAN); + test("-52.083.0", "" + s_fM + f_d); + test("-52.0I", "" + s_fM + f_c); + test("-52.094.0", "" + s_fM + f_f); + test("-52.012.0", "" + s_fM + sf_d); + test("-52.0-99.0", "" + s_fM + f_dM); + test("-52.017.0", "" + s_fM + sf_f); + test("-52.0-84.0", "" + s_fM + sf_dM); + test("-52.058000000", "" + s_fM + f_i); + test("-52.0-55000000", "" + s_fM + f_iM); + test("-52.01460392448", "" + s_fM + f_l); + test("-52.0C(70)", "" + s_fM + f_o); + test("-52.0\u04511", "" + s_fM + sf_strU); + test("-52.08000", "" + s_fM + f_s); + test("-52.018", "" + s_fM + s_str); + test("-52.0-1000000", "" + s_fM + s_iM); + test("-52.01000000", "" + s_fM + sf_I); + test("-52.0null", "" + s_fM + f_oNtS); + test("-52.0false", "" + s_fM + f_bl); + test("-52.0null", "" + s_fM + sf_iAN); + test("-52.0-2000000", "" + s_fM + sf_iM); + test("-52.0-820130816", "" + s_fM + f_lM); + test("-52.0null", "" + s_fM + sf_oAN); + test("-52.025000000", "" + s_fM + s_I); + test("75000000-96.0", "" + sf_i + s_dM); + test("75000000null", "" + sf_i + s_oNtS); + test("75000000\u045176", "" + sf_i + f_strU); + test("7500000092", "" + sf_i + sf_strU2); + test("7500000051", "" + sf_i + sf_strU1); + test("75000000null", "" + sf_i + s_iAN); + test("75000000-54", "" + sf_i + f_bM); + test("75000000-87.0", "" + sf_i + f_fM); + test("75000000null", "" + sf_i + s_oAN); + test("7500000019", "" + sf_i + f_str); + test("75000000-41", "" + sf_i + sf_bM); + test("75000000null", "" + sf_i + sf_IN); + test("75000000T", "" + sf_i + s_c); + test("75000000-42.0", "" + sf_i + sf_fM); + test("7500000025", "" + sf_i + s_b); + test("75000000null", "" + sf_i + f_oN); + test("75000000-1410065408", "" + sf_i + s_lM); + test("750000008.0", "" + sf_i + s_d); + test("7500000055.0", "" + sf_i + s_f); + test("7500000097000000", "" + sf_i + s_i); + test("75000000-9900", "" + sf_i + f_sM); + test("75000000935228928", "" + sf_i + s_l); + test("75000000-8400", "" + sf_i + sf_sM); + test("75000000C(82)", "" + sf_i + s_o); + test("75000000null", "" + sf_i + sf_oNtS); + test("75000000true", "" + sf_i + s_bl); + test("750000003900", "" + sf_i + s_s); + test("75000000null", "" + sf_i + sf_oN); + test("7500000094000000", "" + sf_i + f_I); + test("75000000null", "" + sf_i + f_IN); + test("75000000true", "" + sf_i + sf_bl); + test("750000005500", "" + sf_i + sf_s); + test("75000000-2900", "" + sf_i + s_sM); + test("75000000-194313216", "" + sf_i + sf_l); + test("7500000012", "" + sf_i + s_strU1); + test("75000000C(87)", "" + sf_i + sf_o); + test("7500000091", "" + sf_i + s_strU2); + test("7500000021", "" + sf_i + f_strU1); + test("7500000018", "" + sf_i + f_strU2); + test("75000000null", "" + sf_i + f_iAN); + test("75000000null", "" + sf_i + s_oN); + test("75000000\u045180", "" + sf_i + s_strU); + test("75000000C", "" + sf_i + sf_c); + test("7500000075", "" + sf_i + sf_str); + test("75000000-43", "" + sf_i + s_bM); + test("7500000080", "" + sf_i + sf_b); + test("75000000null", "" + sf_i + s_IN); + test("75000000-52.0", "" + sf_i + s_fM); + test("7500000075000000", "" + sf_i + sf_i); + test("7500000044", "" + sf_i + f_b); + test("75000000-1705032704", "" + sf_i + sf_lM); + test("75000000null", "" + sf_i + f_oAN); + test("7500000083.0", "" + sf_i + f_d); + test("75000000I", "" + sf_i + f_c); + test("7500000094.0", "" + sf_i + f_f); + test("7500000012.0", "" + sf_i + sf_d); + test("75000000-99.0", "" + sf_i + f_dM); + test("7500000017.0", "" + sf_i + sf_f); + test("75000000-84.0", "" + sf_i + sf_dM); + test("7500000058000000", "" + sf_i + f_i); + test("75000000-55000000", "" + sf_i + f_iM); + test("750000001460392448", "" + sf_i + f_l); + test("75000000C(70)", "" + sf_i + f_o); + test("75000000\u04511", "" + sf_i + sf_strU); + test("750000008000", "" + sf_i + f_s); + test("7500000018", "" + sf_i + s_str); + test("75000000-1000000", "" + sf_i + s_iM); + test("750000001000000", "" + sf_i + sf_I); + test("75000000null", "" + sf_i + f_oNtS); + test("75000000false", "" + sf_i + f_bl); + test("75000000null", "" + sf_i + sf_iAN); + test("75000000-2000000", "" + sf_i + sf_iM); + test("75000000-820130816", "" + sf_i + f_lM); + test("75000000null", "" + sf_i + sf_oAN); + test("7500000025000000", "" + sf_i + s_I); + test("44-96.0", "" + f_b + s_dM); + test("44null", "" + f_b + s_oNtS); + test("44\u045176", "" + f_b + f_strU); + test("4492", "" + f_b + sf_strU2); + test("4451", "" + f_b + sf_strU1); + test("44null", "" + f_b + s_iAN); + test("44-54", "" + f_b + f_bM); + test("44-87.0", "" + f_b + f_fM); + test("44null", "" + f_b + s_oAN); + test("4419", "" + f_b + f_str); + test("44-41", "" + f_b + sf_bM); + test("44null", "" + f_b + sf_IN); + test("44T", "" + f_b + s_c); + test("44-42.0", "" + f_b + sf_fM); + test("4425", "" + f_b + s_b); + test("44null", "" + f_b + f_oN); + test("44-1410065408", "" + f_b + s_lM); + test("448.0", "" + f_b + s_d); + test("4455.0", "" + f_b + s_f); + test("4497000000", "" + f_b + s_i); + test("44-9900", "" + f_b + f_sM); + test("44935228928", "" + f_b + s_l); + test("44-8400", "" + f_b + sf_sM); + test("44C(82)", "" + f_b + s_o); + test("44null", "" + f_b + sf_oNtS); + test("44true", "" + f_b + s_bl); + test("443900", "" + f_b + s_s); + test("44null", "" + f_b + sf_oN); + test("4494000000", "" + f_b + f_I); + test("44null", "" + f_b + f_IN); + test("44true", "" + f_b + sf_bl); + test("445500", "" + f_b + sf_s); + test("44-2900", "" + f_b + s_sM); + test("44-194313216", "" + f_b + sf_l); + test("4412", "" + f_b + s_strU1); + test("44C(87)", "" + f_b + sf_o); + test("4491", "" + f_b + s_strU2); + test("4421", "" + f_b + f_strU1); + test("4418", "" + f_b + f_strU2); + test("44null", "" + f_b + f_iAN); + test("44null", "" + f_b + s_oN); + test("44\u045180", "" + f_b + s_strU); + test("44C", "" + f_b + sf_c); + test("4475", "" + f_b + sf_str); + test("44-43", "" + f_b + s_bM); + test("4480", "" + f_b + sf_b); + test("44null", "" + f_b + s_IN); + test("44-52.0", "" + f_b + s_fM); + test("4475000000", "" + f_b + sf_i); + test("4444", "" + f_b + f_b); + test("44-1705032704", "" + f_b + sf_lM); + test("44null", "" + f_b + f_oAN); + test("4483.0", "" + f_b + f_d); + test("44I", "" + f_b + f_c); + test("4494.0", "" + f_b + f_f); + test("4412.0", "" + f_b + sf_d); + test("44-99.0", "" + f_b + f_dM); + test("4417.0", "" + f_b + sf_f); + test("44-84.0", "" + f_b + sf_dM); + test("4458000000", "" + f_b + f_i); + test("44-55000000", "" + f_b + f_iM); + test("441460392448", "" + f_b + f_l); + test("44C(70)", "" + f_b + f_o); + test("44\u04511", "" + f_b + sf_strU); + test("448000", "" + f_b + f_s); + test("4418", "" + f_b + s_str); + test("44-1000000", "" + f_b + s_iM); + test("441000000", "" + f_b + sf_I); + test("44null", "" + f_b + f_oNtS); + test("44false", "" + f_b + f_bl); + test("44null", "" + f_b + sf_iAN); + test("44-2000000", "" + f_b + sf_iM); + test("44-820130816", "" + f_b + f_lM); + test("44null", "" + f_b + sf_oAN); + test("4425000000", "" + f_b + s_I); + test("-1705032704-96.0", "" + sf_lM + s_dM); + test("-1705032704null", "" + sf_lM + s_oNtS); + test("-1705032704\u045176", "" + sf_lM + f_strU); + test("-170503270492", "" + sf_lM + sf_strU2); + test("-170503270451", "" + sf_lM + sf_strU1); + test("-1705032704null", "" + sf_lM + s_iAN); + test("-1705032704-54", "" + sf_lM + f_bM); + test("-1705032704-87.0", "" + sf_lM + f_fM); + test("-1705032704null", "" + sf_lM + s_oAN); + test("-170503270419", "" + sf_lM + f_str); + test("-1705032704-41", "" + sf_lM + sf_bM); + test("-1705032704null", "" + sf_lM + sf_IN); + test("-1705032704T", "" + sf_lM + s_c); + test("-1705032704-42.0", "" + sf_lM + sf_fM); + test("-170503270425", "" + sf_lM + s_b); + test("-1705032704null", "" + sf_lM + f_oN); + test("-1705032704-1410065408", "" + sf_lM + s_lM); + test("-17050327048.0", "" + sf_lM + s_d); + test("-170503270455.0", "" + sf_lM + s_f); + test("-170503270497000000", "" + sf_lM + s_i); + test("-1705032704-9900", "" + sf_lM + f_sM); + test("-1705032704935228928", "" + sf_lM + s_l); + test("-1705032704-8400", "" + sf_lM + sf_sM); + test("-1705032704C(82)", "" + sf_lM + s_o); + test("-1705032704null", "" + sf_lM + sf_oNtS); + test("-1705032704true", "" + sf_lM + s_bl); + test("-17050327043900", "" + sf_lM + s_s); + test("-1705032704null", "" + sf_lM + sf_oN); + test("-170503270494000000", "" + sf_lM + f_I); + test("-1705032704null", "" + sf_lM + f_IN); + test("-1705032704true", "" + sf_lM + sf_bl); + test("-17050327045500", "" + sf_lM + sf_s); + test("-1705032704-2900", "" + sf_lM + s_sM); + test("-1705032704-194313216", "" + sf_lM + sf_l); + test("-170503270412", "" + sf_lM + s_strU1); + test("-1705032704C(87)", "" + sf_lM + sf_o); + test("-170503270491", "" + sf_lM + s_strU2); + test("-170503270421", "" + sf_lM + f_strU1); + test("-170503270418", "" + sf_lM + f_strU2); + test("-1705032704null", "" + sf_lM + f_iAN); + test("-1705032704null", "" + sf_lM + s_oN); + test("-1705032704\u045180", "" + sf_lM + s_strU); + test("-1705032704C", "" + sf_lM + sf_c); + test("-170503270475", "" + sf_lM + sf_str); + test("-1705032704-43", "" + sf_lM + s_bM); + test("-170503270480", "" + sf_lM + sf_b); + test("-1705032704null", "" + sf_lM + s_IN); + test("-1705032704-52.0", "" + sf_lM + s_fM); + test("-170503270475000000", "" + sf_lM + sf_i); + test("-170503270444", "" + sf_lM + f_b); + test("-1705032704-1705032704", "" + sf_lM + sf_lM); + test("-1705032704null", "" + sf_lM + f_oAN); + test("-170503270483.0", "" + sf_lM + f_d); + test("-1705032704I", "" + sf_lM + f_c); + test("-170503270494.0", "" + sf_lM + f_f); + test("-170503270412.0", "" + sf_lM + sf_d); + test("-1705032704-99.0", "" + sf_lM + f_dM); + test("-170503270417.0", "" + sf_lM + sf_f); + test("-1705032704-84.0", "" + sf_lM + sf_dM); + test("-170503270458000000", "" + sf_lM + f_i); + test("-1705032704-55000000", "" + sf_lM + f_iM); + test("-17050327041460392448", "" + sf_lM + f_l); + test("-1705032704C(70)", "" + sf_lM + f_o); + test("-1705032704\u04511", "" + sf_lM + sf_strU); + test("-17050327048000", "" + sf_lM + f_s); + test("-170503270418", "" + sf_lM + s_str); + test("-1705032704-1000000", "" + sf_lM + s_iM); + test("-17050327041000000", "" + sf_lM + sf_I); + test("-1705032704null", "" + sf_lM + f_oNtS); + test("-1705032704false", "" + sf_lM + f_bl); + test("-1705032704null", "" + sf_lM + sf_iAN); + test("-1705032704-2000000", "" + sf_lM + sf_iM); + test("-1705032704-820130816", "" + sf_lM + f_lM); + test("-1705032704null", "" + sf_lM + sf_oAN); + test("-170503270425000000", "" + sf_lM + s_I); + test("null-96.0", "" + f_oAN + s_dM); + test("nullnull", "" + f_oAN + s_oNtS); + test("null\u045176", "" + f_oAN + f_strU); + test("null92", "" + f_oAN + sf_strU2); + test("null51", "" + f_oAN + sf_strU1); + test("nullnull", "" + f_oAN + s_iAN); + test("null-54", "" + f_oAN + f_bM); + test("null-87.0", "" + f_oAN + f_fM); + test("nullnull", "" + f_oAN + s_oAN); + test("null19", "" + f_oAN + f_str); + test("null-41", "" + f_oAN + sf_bM); + test("nullnull", "" + f_oAN + sf_IN); + test("nullT", "" + f_oAN + s_c); + test("null-42.0", "" + f_oAN + sf_fM); + test("null25", "" + f_oAN + s_b); + test("nullnull", "" + f_oAN + f_oN); + test("null-1410065408", "" + f_oAN + s_lM); + test("null8.0", "" + f_oAN + s_d); + test("null55.0", "" + f_oAN + s_f); + test("null97000000", "" + f_oAN + s_i); + test("null-9900", "" + f_oAN + f_sM); + test("null935228928", "" + f_oAN + s_l); + test("null-8400", "" + f_oAN + sf_sM); + test("nullC(82)", "" + f_oAN + s_o); + test("nullnull", "" + f_oAN + sf_oNtS); + test("nulltrue", "" + f_oAN + s_bl); + test("null3900", "" + f_oAN + s_s); + test("nullnull", "" + f_oAN + sf_oN); + test("null94000000", "" + f_oAN + f_I); + test("nullnull", "" + f_oAN + f_IN); + test("nulltrue", "" + f_oAN + sf_bl); + test("null5500", "" + f_oAN + sf_s); + test("null-2900", "" + f_oAN + s_sM); + test("null-194313216", "" + f_oAN + sf_l); + test("null12", "" + f_oAN + s_strU1); + test("nullC(87)", "" + f_oAN + sf_o); + test("null91", "" + f_oAN + s_strU2); + test("null21", "" + f_oAN + f_strU1); + test("null18", "" + f_oAN + f_strU2); + test("nullnull", "" + f_oAN + f_iAN); + test("nullnull", "" + f_oAN + s_oN); + test("null\u045180", "" + f_oAN + s_strU); + test("nullC", "" + f_oAN + sf_c); + test("null75", "" + f_oAN + sf_str); + test("null-43", "" + f_oAN + s_bM); + test("null80", "" + f_oAN + sf_b); + test("nullnull", "" + f_oAN + s_IN); + test("null-52.0", "" + f_oAN + s_fM); + test("null75000000", "" + f_oAN + sf_i); + test("null44", "" + f_oAN + f_b); + test("null-1705032704", "" + f_oAN + sf_lM); + test("nullnull", "" + f_oAN + f_oAN); + test("null83.0", "" + f_oAN + f_d); + test("nullI", "" + f_oAN + f_c); + test("null94.0", "" + f_oAN + f_f); + test("null12.0", "" + f_oAN + sf_d); + test("null-99.0", "" + f_oAN + f_dM); + test("null17.0", "" + f_oAN + sf_f); + test("null-84.0", "" + f_oAN + sf_dM); + test("null58000000", "" + f_oAN + f_i); + test("null-55000000", "" + f_oAN + f_iM); + test("null1460392448", "" + f_oAN + f_l); + test("nullC(70)", "" + f_oAN + f_o); + test("null\u04511", "" + f_oAN + sf_strU); + test("null8000", "" + f_oAN + f_s); + test("null18", "" + f_oAN + s_str); + test("null-1000000", "" + f_oAN + s_iM); + test("null1000000", "" + f_oAN + sf_I); + test("nullnull", "" + f_oAN + f_oNtS); + test("nullfalse", "" + f_oAN + f_bl); + test("nullnull", "" + f_oAN + sf_iAN); + test("null-2000000", "" + f_oAN + sf_iM); + test("null-820130816", "" + f_oAN + f_lM); + test("nullnull", "" + f_oAN + sf_oAN); + test("null25000000", "" + f_oAN + s_I); + test("83.0-96.0", "" + f_d + s_dM); + test("83.0null", "" + f_d + s_oNtS); + test("83.0\u045176", "" + f_d + f_strU); + test("83.092", "" + f_d + sf_strU2); + test("83.051", "" + f_d + sf_strU1); + test("83.0null", "" + f_d + s_iAN); + test("83.0-54", "" + f_d + f_bM); + test("83.0-87.0", "" + f_d + f_fM); + test("83.0null", "" + f_d + s_oAN); + test("83.019", "" + f_d + f_str); + test("83.0-41", "" + f_d + sf_bM); + test("83.0null", "" + f_d + sf_IN); + test("83.0T", "" + f_d + s_c); + test("83.0-42.0", "" + f_d + sf_fM); + test("83.025", "" + f_d + s_b); + test("83.0null", "" + f_d + f_oN); + test("83.0-1410065408", "" + f_d + s_lM); + test("83.08.0", "" + f_d + s_d); + test("83.055.0", "" + f_d + s_f); + test("83.097000000", "" + f_d + s_i); + test("83.0-9900", "" + f_d + f_sM); + test("83.0935228928", "" + f_d + s_l); + test("83.0-8400", "" + f_d + sf_sM); + test("83.0C(82)", "" + f_d + s_o); + test("83.0null", "" + f_d + sf_oNtS); + } + + public void run4() { + test("83.0true", "" + f_d + s_bl); + test("83.03900", "" + f_d + s_s); + test("83.0null", "" + f_d + sf_oN); + test("83.094000000", "" + f_d + f_I); + test("83.0null", "" + f_d + f_IN); + test("83.0true", "" + f_d + sf_bl); + test("83.05500", "" + f_d + sf_s); + test("83.0-2900", "" + f_d + s_sM); + test("83.0-194313216", "" + f_d + sf_l); + test("83.012", "" + f_d + s_strU1); + test("83.0C(87)", "" + f_d + sf_o); + test("83.091", "" + f_d + s_strU2); + test("83.021", "" + f_d + f_strU1); + test("83.018", "" + f_d + f_strU2); + test("83.0null", "" + f_d + f_iAN); + test("83.0null", "" + f_d + s_oN); + test("83.0\u045180", "" + f_d + s_strU); + test("83.0C", "" + f_d + sf_c); + test("83.075", "" + f_d + sf_str); + test("83.0-43", "" + f_d + s_bM); + test("83.080", "" + f_d + sf_b); + test("83.0null", "" + f_d + s_IN); + test("83.0-52.0", "" + f_d + s_fM); + test("83.075000000", "" + f_d + sf_i); + test("83.044", "" + f_d + f_b); + test("83.0-1705032704", "" + f_d + sf_lM); + test("83.0null", "" + f_d + f_oAN); + test("83.083.0", "" + f_d + f_d); + test("83.0I", "" + f_d + f_c); + test("83.094.0", "" + f_d + f_f); + test("83.012.0", "" + f_d + sf_d); + test("83.0-99.0", "" + f_d + f_dM); + test("83.017.0", "" + f_d + sf_f); + test("83.0-84.0", "" + f_d + sf_dM); + test("83.058000000", "" + f_d + f_i); + test("83.0-55000000", "" + f_d + f_iM); + test("83.01460392448", "" + f_d + f_l); + test("83.0C(70)", "" + f_d + f_o); + test("83.0\u04511", "" + f_d + sf_strU); + test("83.08000", "" + f_d + f_s); + test("83.018", "" + f_d + s_str); + test("83.0-1000000", "" + f_d + s_iM); + test("83.01000000", "" + f_d + sf_I); + test("83.0null", "" + f_d + f_oNtS); + test("83.0false", "" + f_d + f_bl); + test("83.0null", "" + f_d + sf_iAN); + test("83.0-2000000", "" + f_d + sf_iM); + test("83.0-820130816", "" + f_d + f_lM); + test("83.0null", "" + f_d + sf_oAN); + test("83.025000000", "" + f_d + s_I); + test("I-96.0", "" + f_c + s_dM); + test("Inull", "" + f_c + s_oNtS); + test("I\u045176", "" + f_c + f_strU); + test("I92", "" + f_c + sf_strU2); + test("I51", "" + f_c + sf_strU1); + test("Inull", "" + f_c + s_iAN); + test("I-54", "" + f_c + f_bM); + test("I-87.0", "" + f_c + f_fM); + test("Inull", "" + f_c + s_oAN); + test("I19", "" + f_c + f_str); + test("I-41", "" + f_c + sf_bM); + test("Inull", "" + f_c + sf_IN); + test("IT", "" + f_c + s_c); + test("I-42.0", "" + f_c + sf_fM); + test("I25", "" + f_c + s_b); + test("Inull", "" + f_c + f_oN); + test("I-1410065408", "" + f_c + s_lM); + test("I8.0", "" + f_c + s_d); + test("I55.0", "" + f_c + s_f); + test("I97000000", "" + f_c + s_i); + test("I-9900", "" + f_c + f_sM); + test("I935228928", "" + f_c + s_l); + test("I-8400", "" + f_c + sf_sM); + test("IC(82)", "" + f_c + s_o); + test("Inull", "" + f_c + sf_oNtS); + test("Itrue", "" + f_c + s_bl); + test("I3900", "" + f_c + s_s); + test("Inull", "" + f_c + sf_oN); + test("I94000000", "" + f_c + f_I); + test("Inull", "" + f_c + f_IN); + test("Itrue", "" + f_c + sf_bl); + test("I5500", "" + f_c + sf_s); + test("I-2900", "" + f_c + s_sM); + test("I-194313216", "" + f_c + sf_l); + test("I12", "" + f_c + s_strU1); + test("IC(87)", "" + f_c + sf_o); + test("I91", "" + f_c + s_strU2); + test("I21", "" + f_c + f_strU1); + test("I18", "" + f_c + f_strU2); + test("Inull", "" + f_c + f_iAN); + test("Inull", "" + f_c + s_oN); + test("I\u045180", "" + f_c + s_strU); + test("IC", "" + f_c + sf_c); + test("I75", "" + f_c + sf_str); + test("I-43", "" + f_c + s_bM); + test("I80", "" + f_c + sf_b); + test("Inull", "" + f_c + s_IN); + test("I-52.0", "" + f_c + s_fM); + test("I75000000", "" + f_c + sf_i); + test("I44", "" + f_c + f_b); + test("I-1705032704", "" + f_c + sf_lM); + test("Inull", "" + f_c + f_oAN); + test("I83.0", "" + f_c + f_d); + test("II", "" + f_c + f_c); + test("I94.0", "" + f_c + f_f); + test("I12.0", "" + f_c + sf_d); + test("I-99.0", "" + f_c + f_dM); + test("I17.0", "" + f_c + sf_f); + test("I-84.0", "" + f_c + sf_dM); + test("I58000000", "" + f_c + f_i); + test("I-55000000", "" + f_c + f_iM); + test("I1460392448", "" + f_c + f_l); + test("IC(70)", "" + f_c + f_o); + test("I\u04511", "" + f_c + sf_strU); + test("I8000", "" + f_c + f_s); + test("I18", "" + f_c + s_str); + test("I-1000000", "" + f_c + s_iM); + test("I1000000", "" + f_c + sf_I); + test("Inull", "" + f_c + f_oNtS); + test("Ifalse", "" + f_c + f_bl); + test("Inull", "" + f_c + sf_iAN); + test("I-2000000", "" + f_c + sf_iM); + test("I-820130816", "" + f_c + f_lM); + test("Inull", "" + f_c + sf_oAN); + test("I25000000", "" + f_c + s_I); + test("94.0-96.0", "" + f_f + s_dM); + test("94.0null", "" + f_f + s_oNtS); + test("94.0\u045176", "" + f_f + f_strU); + test("94.092", "" + f_f + sf_strU2); + test("94.051", "" + f_f + sf_strU1); + test("94.0null", "" + f_f + s_iAN); + test("94.0-54", "" + f_f + f_bM); + test("94.0-87.0", "" + f_f + f_fM); + test("94.0null", "" + f_f + s_oAN); + test("94.019", "" + f_f + f_str); + test("94.0-41", "" + f_f + sf_bM); + test("94.0null", "" + f_f + sf_IN); + test("94.0T", "" + f_f + s_c); + test("94.0-42.0", "" + f_f + sf_fM); + test("94.025", "" + f_f + s_b); + test("94.0null", "" + f_f + f_oN); + test("94.0-1410065408", "" + f_f + s_lM); + test("94.08.0", "" + f_f + s_d); + test("94.055.0", "" + f_f + s_f); + test("94.097000000", "" + f_f + s_i); + test("94.0-9900", "" + f_f + f_sM); + test("94.0935228928", "" + f_f + s_l); + test("94.0-8400", "" + f_f + sf_sM); + test("94.0C(82)", "" + f_f + s_o); + test("94.0null", "" + f_f + sf_oNtS); + test("94.0true", "" + f_f + s_bl); + test("94.03900", "" + f_f + s_s); + test("94.0null", "" + f_f + sf_oN); + test("94.094000000", "" + f_f + f_I); + test("94.0null", "" + f_f + f_IN); + test("94.0true", "" + f_f + sf_bl); + test("94.05500", "" + f_f + sf_s); + test("94.0-2900", "" + f_f + s_sM); + test("94.0-194313216", "" + f_f + sf_l); + test("94.012", "" + f_f + s_strU1); + test("94.0C(87)", "" + f_f + sf_o); + test("94.091", "" + f_f + s_strU2); + test("94.021", "" + f_f + f_strU1); + test("94.018", "" + f_f + f_strU2); + test("94.0null", "" + f_f + f_iAN); + test("94.0null", "" + f_f + s_oN); + test("94.0\u045180", "" + f_f + s_strU); + test("94.0C", "" + f_f + sf_c); + test("94.075", "" + f_f + sf_str); + test("94.0-43", "" + f_f + s_bM); + test("94.080", "" + f_f + sf_b); + test("94.0null", "" + f_f + s_IN); + test("94.0-52.0", "" + f_f + s_fM); + test("94.075000000", "" + f_f + sf_i); + test("94.044", "" + f_f + f_b); + test("94.0-1705032704", "" + f_f + sf_lM); + test("94.0null", "" + f_f + f_oAN); + test("94.083.0", "" + f_f + f_d); + test("94.0I", "" + f_f + f_c); + test("94.094.0", "" + f_f + f_f); + test("94.012.0", "" + f_f + sf_d); + test("94.0-99.0", "" + f_f + f_dM); + test("94.017.0", "" + f_f + sf_f); + test("94.0-84.0", "" + f_f + sf_dM); + test("94.058000000", "" + f_f + f_i); + test("94.0-55000000", "" + f_f + f_iM); + test("94.01460392448", "" + f_f + f_l); + test("94.0C(70)", "" + f_f + f_o); + test("94.0\u04511", "" + f_f + sf_strU); + test("94.08000", "" + f_f + f_s); + test("94.018", "" + f_f + s_str); + test("94.0-1000000", "" + f_f + s_iM); + test("94.01000000", "" + f_f + sf_I); + test("94.0null", "" + f_f + f_oNtS); + test("94.0false", "" + f_f + f_bl); + test("94.0null", "" + f_f + sf_iAN); + test("94.0-2000000", "" + f_f + sf_iM); + test("94.0-820130816", "" + f_f + f_lM); + test("94.0null", "" + f_f + sf_oAN); + test("94.025000000", "" + f_f + s_I); + test("12.0-96.0", "" + sf_d + s_dM); + test("12.0null", "" + sf_d + s_oNtS); + test("12.0\u045176", "" + sf_d + f_strU); + test("12.092", "" + sf_d + sf_strU2); + test("12.051", "" + sf_d + sf_strU1); + test("12.0null", "" + sf_d + s_iAN); + test("12.0-54", "" + sf_d + f_bM); + test("12.0-87.0", "" + sf_d + f_fM); + test("12.0null", "" + sf_d + s_oAN); + test("12.019", "" + sf_d + f_str); + test("12.0-41", "" + sf_d + sf_bM); + test("12.0null", "" + sf_d + sf_IN); + test("12.0T", "" + sf_d + s_c); + test("12.0-42.0", "" + sf_d + sf_fM); + test("12.025", "" + sf_d + s_b); + test("12.0null", "" + sf_d + f_oN); + test("12.0-1410065408", "" + sf_d + s_lM); + test("12.08.0", "" + sf_d + s_d); + test("12.055.0", "" + sf_d + s_f); + test("12.097000000", "" + sf_d + s_i); + test("12.0-9900", "" + sf_d + f_sM); + test("12.0935228928", "" + sf_d + s_l); + test("12.0-8400", "" + sf_d + sf_sM); + test("12.0C(82)", "" + sf_d + s_o); + test("12.0null", "" + sf_d + sf_oNtS); + test("12.0true", "" + sf_d + s_bl); + test("12.03900", "" + sf_d + s_s); + test("12.0null", "" + sf_d + sf_oN); + test("12.094000000", "" + sf_d + f_I); + test("12.0null", "" + sf_d + f_IN); + test("12.0true", "" + sf_d + sf_bl); + test("12.05500", "" + sf_d + sf_s); + test("12.0-2900", "" + sf_d + s_sM); + test("12.0-194313216", "" + sf_d + sf_l); + test("12.012", "" + sf_d + s_strU1); + test("12.0C(87)", "" + sf_d + sf_o); + test("12.091", "" + sf_d + s_strU2); + test("12.021", "" + sf_d + f_strU1); + test("12.018", "" + sf_d + f_strU2); + test("12.0null", "" + sf_d + f_iAN); + test("12.0null", "" + sf_d + s_oN); + test("12.0\u045180", "" + sf_d + s_strU); + test("12.0C", "" + sf_d + sf_c); + test("12.075", "" + sf_d + sf_str); + test("12.0-43", "" + sf_d + s_bM); + test("12.080", "" + sf_d + sf_b); + test("12.0null", "" + sf_d + s_IN); + test("12.0-52.0", "" + sf_d + s_fM); + test("12.075000000", "" + sf_d + sf_i); + test("12.044", "" + sf_d + f_b); + test("12.0-1705032704", "" + sf_d + sf_lM); + test("12.0null", "" + sf_d + f_oAN); + test("12.083.0", "" + sf_d + f_d); + test("12.0I", "" + sf_d + f_c); + test("12.094.0", "" + sf_d + f_f); + test("12.012.0", "" + sf_d + sf_d); + test("12.0-99.0", "" + sf_d + f_dM); + test("12.017.0", "" + sf_d + sf_f); + test("12.0-84.0", "" + sf_d + sf_dM); + test("12.058000000", "" + sf_d + f_i); + test("12.0-55000000", "" + sf_d + f_iM); + test("12.01460392448", "" + sf_d + f_l); + test("12.0C(70)", "" + sf_d + f_o); + test("12.0\u04511", "" + sf_d + sf_strU); + test("12.08000", "" + sf_d + f_s); + test("12.018", "" + sf_d + s_str); + test("12.0-1000000", "" + sf_d + s_iM); + test("12.01000000", "" + sf_d + sf_I); + test("12.0null", "" + sf_d + f_oNtS); + test("12.0false", "" + sf_d + f_bl); + test("12.0null", "" + sf_d + sf_iAN); + test("12.0-2000000", "" + sf_d + sf_iM); + test("12.0-820130816", "" + sf_d + f_lM); + test("12.0null", "" + sf_d + sf_oAN); + test("12.025000000", "" + sf_d + s_I); + test("-99.0-96.0", "" + f_dM + s_dM); + test("-99.0null", "" + f_dM + s_oNtS); + test("-99.0\u045176", "" + f_dM + f_strU); + test("-99.092", "" + f_dM + sf_strU2); + test("-99.051", "" + f_dM + sf_strU1); + test("-99.0null", "" + f_dM + s_iAN); + test("-99.0-54", "" + f_dM + f_bM); + test("-99.0-87.0", "" + f_dM + f_fM); + test("-99.0null", "" + f_dM + s_oAN); + test("-99.019", "" + f_dM + f_str); + test("-99.0-41", "" + f_dM + sf_bM); + test("-99.0null", "" + f_dM + sf_IN); + test("-99.0T", "" + f_dM + s_c); + test("-99.0-42.0", "" + f_dM + sf_fM); + test("-99.025", "" + f_dM + s_b); + test("-99.0null", "" + f_dM + f_oN); + test("-99.0-1410065408", "" + f_dM + s_lM); + test("-99.08.0", "" + f_dM + s_d); + test("-99.055.0", "" + f_dM + s_f); + test("-99.097000000", "" + f_dM + s_i); + test("-99.0-9900", "" + f_dM + f_sM); + test("-99.0935228928", "" + f_dM + s_l); + test("-99.0-8400", "" + f_dM + sf_sM); + test("-99.0C(82)", "" + f_dM + s_o); + test("-99.0null", "" + f_dM + sf_oNtS); + test("-99.0true", "" + f_dM + s_bl); + test("-99.03900", "" + f_dM + s_s); + test("-99.0null", "" + f_dM + sf_oN); + test("-99.094000000", "" + f_dM + f_I); + test("-99.0null", "" + f_dM + f_IN); + test("-99.0true", "" + f_dM + sf_bl); + test("-99.05500", "" + f_dM + sf_s); + test("-99.0-2900", "" + f_dM + s_sM); + test("-99.0-194313216", "" + f_dM + sf_l); + test("-99.012", "" + f_dM + s_strU1); + test("-99.0C(87)", "" + f_dM + sf_o); + test("-99.091", "" + f_dM + s_strU2); + test("-99.021", "" + f_dM + f_strU1); + test("-99.018", "" + f_dM + f_strU2); + test("-99.0null", "" + f_dM + f_iAN); + test("-99.0null", "" + f_dM + s_oN); + test("-99.0\u045180", "" + f_dM + s_strU); + test("-99.0C", "" + f_dM + sf_c); + test("-99.075", "" + f_dM + sf_str); + test("-99.0-43", "" + f_dM + s_bM); + test("-99.080", "" + f_dM + sf_b); + test("-99.0null", "" + f_dM + s_IN); + test("-99.0-52.0", "" + f_dM + s_fM); + test("-99.075000000", "" + f_dM + sf_i); + test("-99.044", "" + f_dM + f_b); + test("-99.0-1705032704", "" + f_dM + sf_lM); + test("-99.0null", "" + f_dM + f_oAN); + test("-99.083.0", "" + f_dM + f_d); + test("-99.0I", "" + f_dM + f_c); + test("-99.094.0", "" + f_dM + f_f); + test("-99.012.0", "" + f_dM + sf_d); + test("-99.0-99.0", "" + f_dM + f_dM); + test("-99.017.0", "" + f_dM + sf_f); + test("-99.0-84.0", "" + f_dM + sf_dM); + test("-99.058000000", "" + f_dM + f_i); + test("-99.0-55000000", "" + f_dM + f_iM); + test("-99.01460392448", "" + f_dM + f_l); + test("-99.0C(70)", "" + f_dM + f_o); + test("-99.0\u04511", "" + f_dM + sf_strU); + test("-99.08000", "" + f_dM + f_s); + test("-99.018", "" + f_dM + s_str); + test("-99.0-1000000", "" + f_dM + s_iM); + test("-99.01000000", "" + f_dM + sf_I); + test("-99.0null", "" + f_dM + f_oNtS); + test("-99.0false", "" + f_dM + f_bl); + test("-99.0null", "" + f_dM + sf_iAN); + test("-99.0-2000000", "" + f_dM + sf_iM); + test("-99.0-820130816", "" + f_dM + f_lM); + test("-99.0null", "" + f_dM + sf_oAN); + test("-99.025000000", "" + f_dM + s_I); + test("17.0-96.0", "" + sf_f + s_dM); + test("17.0null", "" + sf_f + s_oNtS); + test("17.0\u045176", "" + sf_f + f_strU); + test("17.092", "" + sf_f + sf_strU2); + test("17.051", "" + sf_f + sf_strU1); + test("17.0null", "" + sf_f + s_iAN); + test("17.0-54", "" + sf_f + f_bM); + test("17.0-87.0", "" + sf_f + f_fM); + test("17.0null", "" + sf_f + s_oAN); + test("17.019", "" + sf_f + f_str); + test("17.0-41", "" + sf_f + sf_bM); + test("17.0null", "" + sf_f + sf_IN); + test("17.0T", "" + sf_f + s_c); + test("17.0-42.0", "" + sf_f + sf_fM); + test("17.025", "" + sf_f + s_b); + test("17.0null", "" + sf_f + f_oN); + test("17.0-1410065408", "" + sf_f + s_lM); + test("17.08.0", "" + sf_f + s_d); + test("17.055.0", "" + sf_f + s_f); + test("17.097000000", "" + sf_f + s_i); + test("17.0-9900", "" + sf_f + f_sM); + test("17.0935228928", "" + sf_f + s_l); + test("17.0-8400", "" + sf_f + sf_sM); + test("17.0C(82)", "" + sf_f + s_o); + test("17.0null", "" + sf_f + sf_oNtS); + test("17.0true", "" + sf_f + s_bl); + test("17.03900", "" + sf_f + s_s); + test("17.0null", "" + sf_f + sf_oN); + test("17.094000000", "" + sf_f + f_I); + test("17.0null", "" + sf_f + f_IN); + test("17.0true", "" + sf_f + sf_bl); + test("17.05500", "" + sf_f + sf_s); + test("17.0-2900", "" + sf_f + s_sM); + test("17.0-194313216", "" + sf_f + sf_l); + test("17.012", "" + sf_f + s_strU1); + test("17.0C(87)", "" + sf_f + sf_o); + test("17.091", "" + sf_f + s_strU2); + test("17.021", "" + sf_f + f_strU1); + test("17.018", "" + sf_f + f_strU2); + test("17.0null", "" + sf_f + f_iAN); + test("17.0null", "" + sf_f + s_oN); + test("17.0\u045180", "" + sf_f + s_strU); + test("17.0C", "" + sf_f + sf_c); + test("17.075", "" + sf_f + sf_str); + test("17.0-43", "" + sf_f + s_bM); + test("17.080", "" + sf_f + sf_b); + test("17.0null", "" + sf_f + s_IN); + test("17.0-52.0", "" + sf_f + s_fM); + test("17.075000000", "" + sf_f + sf_i); + test("17.044", "" + sf_f + f_b); + test("17.0-1705032704", "" + sf_f + sf_lM); + test("17.0null", "" + sf_f + f_oAN); + test("17.083.0", "" + sf_f + f_d); + test("17.0I", "" + sf_f + f_c); + test("17.094.0", "" + sf_f + f_f); + test("17.012.0", "" + sf_f + sf_d); + test("17.0-99.0", "" + sf_f + f_dM); + test("17.017.0", "" + sf_f + sf_f); + test("17.0-84.0", "" + sf_f + sf_dM); + test("17.058000000", "" + sf_f + f_i); + test("17.0-55000000", "" + sf_f + f_iM); + test("17.01460392448", "" + sf_f + f_l); + test("17.0C(70)", "" + sf_f + f_o); + test("17.0\u04511", "" + sf_f + sf_strU); + test("17.08000", "" + sf_f + f_s); + test("17.018", "" + sf_f + s_str); + test("17.0-1000000", "" + sf_f + s_iM); + test("17.01000000", "" + sf_f + sf_I); + test("17.0null", "" + sf_f + f_oNtS); + test("17.0false", "" + sf_f + f_bl); + test("17.0null", "" + sf_f + sf_iAN); + test("17.0-2000000", "" + sf_f + sf_iM); + test("17.0-820130816", "" + sf_f + f_lM); + test("17.0null", "" + sf_f + sf_oAN); + test("17.025000000", "" + sf_f + s_I); + test("-84.0-96.0", "" + sf_dM + s_dM); + test("-84.0null", "" + sf_dM + s_oNtS); + test("-84.0\u045176", "" + sf_dM + f_strU); + test("-84.092", "" + sf_dM + sf_strU2); + test("-84.051", "" + sf_dM + sf_strU1); + test("-84.0null", "" + sf_dM + s_iAN); + test("-84.0-54", "" + sf_dM + f_bM); + test("-84.0-87.0", "" + sf_dM + f_fM); + test("-84.0null", "" + sf_dM + s_oAN); + test("-84.019", "" + sf_dM + f_str); + test("-84.0-41", "" + sf_dM + sf_bM); + test("-84.0null", "" + sf_dM + sf_IN); + test("-84.0T", "" + sf_dM + s_c); + test("-84.0-42.0", "" + sf_dM + sf_fM); + test("-84.025", "" + sf_dM + s_b); + test("-84.0null", "" + sf_dM + f_oN); + test("-84.0-1410065408", "" + sf_dM + s_lM); + test("-84.08.0", "" + sf_dM + s_d); + test("-84.055.0", "" + sf_dM + s_f); + test("-84.097000000", "" + sf_dM + s_i); + test("-84.0-9900", "" + sf_dM + f_sM); + test("-84.0935228928", "" + sf_dM + s_l); + test("-84.0-8400", "" + sf_dM + sf_sM); + test("-84.0C(82)", "" + sf_dM + s_o); + test("-84.0null", "" + sf_dM + sf_oNtS); + test("-84.0true", "" + sf_dM + s_bl); + test("-84.03900", "" + sf_dM + s_s); + test("-84.0null", "" + sf_dM + sf_oN); + test("-84.094000000", "" + sf_dM + f_I); + test("-84.0null", "" + sf_dM + f_IN); + test("-84.0true", "" + sf_dM + sf_bl); + test("-84.05500", "" + sf_dM + sf_s); + test("-84.0-2900", "" + sf_dM + s_sM); + test("-84.0-194313216", "" + sf_dM + sf_l); + test("-84.012", "" + sf_dM + s_strU1); + test("-84.0C(87)", "" + sf_dM + sf_o); + test("-84.091", "" + sf_dM + s_strU2); + test("-84.021", "" + sf_dM + f_strU1); + test("-84.018", "" + sf_dM + f_strU2); + test("-84.0null", "" + sf_dM + f_iAN); + test("-84.0null", "" + sf_dM + s_oN); + test("-84.0\u045180", "" + sf_dM + s_strU); + test("-84.0C", "" + sf_dM + sf_c); + test("-84.075", "" + sf_dM + sf_str); + test("-84.0-43", "" + sf_dM + s_bM); + test("-84.080", "" + sf_dM + sf_b); + test("-84.0null", "" + sf_dM + s_IN); + test("-84.0-52.0", "" + sf_dM + s_fM); + test("-84.075000000", "" + sf_dM + sf_i); + test("-84.044", "" + sf_dM + f_b); + test("-84.0-1705032704", "" + sf_dM + sf_lM); + test("-84.0null", "" + sf_dM + f_oAN); + test("-84.083.0", "" + sf_dM + f_d); + test("-84.0I", "" + sf_dM + f_c); + test("-84.094.0", "" + sf_dM + f_f); + test("-84.012.0", "" + sf_dM + sf_d); + test("-84.0-99.0", "" + sf_dM + f_dM); + test("-84.017.0", "" + sf_dM + sf_f); + test("-84.0-84.0", "" + sf_dM + sf_dM); + test("-84.058000000", "" + sf_dM + f_i); + test("-84.0-55000000", "" + sf_dM + f_iM); + test("-84.01460392448", "" + sf_dM + f_l); + test("-84.0C(70)", "" + sf_dM + f_o); + test("-84.0\u04511", "" + sf_dM + sf_strU); + test("-84.08000", "" + sf_dM + f_s); + test("-84.018", "" + sf_dM + s_str); + test("-84.0-1000000", "" + sf_dM + s_iM); + test("-84.01000000", "" + sf_dM + sf_I); + test("-84.0null", "" + sf_dM + f_oNtS); + test("-84.0false", "" + sf_dM + f_bl); + test("-84.0null", "" + sf_dM + sf_iAN); + test("-84.0-2000000", "" + sf_dM + sf_iM); + test("-84.0-820130816", "" + sf_dM + f_lM); + test("-84.0null", "" + sf_dM + sf_oAN); + test("-84.025000000", "" + sf_dM + s_I); + test("58000000-96.0", "" + f_i + s_dM); + test("58000000null", "" + f_i + s_oNtS); + test("58000000\u045176", "" + f_i + f_strU); + test("5800000092", "" + f_i + sf_strU2); + test("5800000051", "" + f_i + sf_strU1); + test("58000000null", "" + f_i + s_iAN); + test("58000000-54", "" + f_i + f_bM); + test("58000000-87.0", "" + f_i + f_fM); + test("58000000null", "" + f_i + s_oAN); + test("5800000019", "" + f_i + f_str); + test("58000000-41", "" + f_i + sf_bM); + test("58000000null", "" + f_i + sf_IN); + test("58000000T", "" + f_i + s_c); + test("58000000-42.0", "" + f_i + sf_fM); + test("5800000025", "" + f_i + s_b); + test("58000000null", "" + f_i + f_oN); + test("58000000-1410065408", "" + f_i + s_lM); + test("580000008.0", "" + f_i + s_d); + test("5800000055.0", "" + f_i + s_f); + test("5800000097000000", "" + f_i + s_i); + test("58000000-9900", "" + f_i + f_sM); + test("58000000935228928", "" + f_i + s_l); + test("58000000-8400", "" + f_i + sf_sM); + test("58000000C(82)", "" + f_i + s_o); + test("58000000null", "" + f_i + sf_oNtS); + test("58000000true", "" + f_i + s_bl); + test("580000003900", "" + f_i + s_s); + test("58000000null", "" + f_i + sf_oN); + test("5800000094000000", "" + f_i + f_I); + test("58000000null", "" + f_i + f_IN); + test("58000000true", "" + f_i + sf_bl); + test("580000005500", "" + f_i + sf_s); + test("58000000-2900", "" + f_i + s_sM); + test("58000000-194313216", "" + f_i + sf_l); + test("5800000012", "" + f_i + s_strU1); + test("58000000C(87)", "" + f_i + sf_o); + test("5800000091", "" + f_i + s_strU2); + test("5800000021", "" + f_i + f_strU1); + test("5800000018", "" + f_i + f_strU2); + test("58000000null", "" + f_i + f_iAN); + test("58000000null", "" + f_i + s_oN); + test("58000000\u045180", "" + f_i + s_strU); + test("58000000C", "" + f_i + sf_c); + test("5800000075", "" + f_i + sf_str); + test("58000000-43", "" + f_i + s_bM); + test("5800000080", "" + f_i + sf_b); + test("58000000null", "" + f_i + s_IN); + test("58000000-52.0", "" + f_i + s_fM); + test("5800000075000000", "" + f_i + sf_i); + test("5800000044", "" + f_i + f_b); + test("58000000-1705032704", "" + f_i + sf_lM); + test("58000000null", "" + f_i + f_oAN); + test("5800000083.0", "" + f_i + f_d); + test("58000000I", "" + f_i + f_c); + test("5800000094.0", "" + f_i + f_f); + test("5800000012.0", "" + f_i + sf_d); + test("58000000-99.0", "" + f_i + f_dM); + test("5800000017.0", "" + f_i + sf_f); + test("58000000-84.0", "" + f_i + sf_dM); + test("5800000058000000", "" + f_i + f_i); + test("58000000-55000000", "" + f_i + f_iM); + test("580000001460392448", "" + f_i + f_l); + test("58000000C(70)", "" + f_i + f_o); + test("58000000\u04511", "" + f_i + sf_strU); + test("580000008000", "" + f_i + f_s); + test("5800000018", "" + f_i + s_str); + test("58000000-1000000", "" + f_i + s_iM); + test("580000001000000", "" + f_i + sf_I); + test("58000000null", "" + f_i + f_oNtS); + test("58000000false", "" + f_i + f_bl); + test("58000000null", "" + f_i + sf_iAN); + test("58000000-2000000", "" + f_i + sf_iM); + test("58000000-820130816", "" + f_i + f_lM); + test("58000000null", "" + f_i + sf_oAN); + test("5800000025000000", "" + f_i + s_I); + test("-55000000-96.0", "" + f_iM + s_dM); + test("-55000000null", "" + f_iM + s_oNtS); + test("-55000000\u045176", "" + f_iM + f_strU); + test("-5500000092", "" + f_iM + sf_strU2); + test("-5500000051", "" + f_iM + sf_strU1); + test("-55000000null", "" + f_iM + s_iAN); + test("-55000000-54", "" + f_iM + f_bM); + test("-55000000-87.0", "" + f_iM + f_fM); + test("-55000000null", "" + f_iM + s_oAN); + test("-5500000019", "" + f_iM + f_str); + test("-55000000-41", "" + f_iM + sf_bM); + test("-55000000null", "" + f_iM + sf_IN); + test("-55000000T", "" + f_iM + s_c); + test("-55000000-42.0", "" + f_iM + sf_fM); + test("-5500000025", "" + f_iM + s_b); + test("-55000000null", "" + f_iM + f_oN); + test("-55000000-1410065408", "" + f_iM + s_lM); + test("-550000008.0", "" + f_iM + s_d); + test("-5500000055.0", "" + f_iM + s_f); + test("-5500000097000000", "" + f_iM + s_i); + test("-55000000-9900", "" + f_iM + f_sM); + test("-55000000935228928", "" + f_iM + s_l); + test("-55000000-8400", "" + f_iM + sf_sM); + test("-55000000C(82)", "" + f_iM + s_o); + test("-55000000null", "" + f_iM + sf_oNtS); + test("-55000000true", "" + f_iM + s_bl); + test("-550000003900", "" + f_iM + s_s); + test("-55000000null", "" + f_iM + sf_oN); + test("-5500000094000000", "" + f_iM + f_I); + test("-55000000null", "" + f_iM + f_IN); + test("-55000000true", "" + f_iM + sf_bl); + test("-550000005500", "" + f_iM + sf_s); + test("-55000000-2900", "" + f_iM + s_sM); + test("-55000000-194313216", "" + f_iM + sf_l); + test("-5500000012", "" + f_iM + s_strU1); + test("-55000000C(87)", "" + f_iM + sf_o); + test("-5500000091", "" + f_iM + s_strU2); + test("-5500000021", "" + f_iM + f_strU1); + test("-5500000018", "" + f_iM + f_strU2); + test("-55000000null", "" + f_iM + f_iAN); + test("-55000000null", "" + f_iM + s_oN); + test("-55000000\u045180", "" + f_iM + s_strU); + test("-55000000C", "" + f_iM + sf_c); + test("-5500000075", "" + f_iM + sf_str); + test("-55000000-43", "" + f_iM + s_bM); + test("-5500000080", "" + f_iM + sf_b); + test("-55000000null", "" + f_iM + s_IN); + test("-55000000-52.0", "" + f_iM + s_fM); + test("-5500000075000000", "" + f_iM + sf_i); + test("-5500000044", "" + f_iM + f_b); + test("-55000000-1705032704", "" + f_iM + sf_lM); + test("-55000000null", "" + f_iM + f_oAN); + test("-5500000083.0", "" + f_iM + f_d); + test("-55000000I", "" + f_iM + f_c); + test("-5500000094.0", "" + f_iM + f_f); + test("-5500000012.0", "" + f_iM + sf_d); + test("-55000000-99.0", "" + f_iM + f_dM); + test("-5500000017.0", "" + f_iM + sf_f); + test("-55000000-84.0", "" + f_iM + sf_dM); + test("-5500000058000000", "" + f_iM + f_i); + test("-55000000-55000000", "" + f_iM + f_iM); + test("-550000001460392448", "" + f_iM + f_l); + test("-55000000C(70)", "" + f_iM + f_o); + test("-55000000\u04511", "" + f_iM + sf_strU); + test("-550000008000", "" + f_iM + f_s); + test("-5500000018", "" + f_iM + s_str); + test("-55000000-1000000", "" + f_iM + s_iM); + test("-550000001000000", "" + f_iM + sf_I); + test("-55000000null", "" + f_iM + f_oNtS); + test("-55000000false", "" + f_iM + f_bl); + test("-55000000null", "" + f_iM + sf_iAN); + test("-55000000-2000000", "" + f_iM + sf_iM); + test("-55000000-820130816", "" + f_iM + f_lM); + test("-55000000null", "" + f_iM + sf_oAN); + test("-5500000025000000", "" + f_iM + s_I); + test("1460392448-96.0", "" + f_l + s_dM); + test("1460392448null", "" + f_l + s_oNtS); + test("1460392448\u045176", "" + f_l + f_strU); + test("146039244892", "" + f_l + sf_strU2); + test("146039244851", "" + f_l + sf_strU1); + test("1460392448null", "" + f_l + s_iAN); + test("1460392448-54", "" + f_l + f_bM); + test("1460392448-87.0", "" + f_l + f_fM); + test("1460392448null", "" + f_l + s_oAN); + test("146039244819", "" + f_l + f_str); + test("1460392448-41", "" + f_l + sf_bM); + test("1460392448null", "" + f_l + sf_IN); + test("1460392448T", "" + f_l + s_c); + test("1460392448-42.0", "" + f_l + sf_fM); + test("146039244825", "" + f_l + s_b); + test("1460392448null", "" + f_l + f_oN); + test("1460392448-1410065408", "" + f_l + s_lM); + test("14603924488.0", "" + f_l + s_d); + test("146039244855.0", "" + f_l + s_f); + test("146039244897000000", "" + f_l + s_i); + test("1460392448-9900", "" + f_l + f_sM); + test("1460392448935228928", "" + f_l + s_l); + test("1460392448-8400", "" + f_l + sf_sM); + test("1460392448C(82)", "" + f_l + s_o); + test("1460392448null", "" + f_l + sf_oNtS); + test("1460392448true", "" + f_l + s_bl); + test("14603924483900", "" + f_l + s_s); + test("1460392448null", "" + f_l + sf_oN); + test("146039244894000000", "" + f_l + f_I); + test("1460392448null", "" + f_l + f_IN); + test("1460392448true", "" + f_l + sf_bl); + test("14603924485500", "" + f_l + sf_s); + test("1460392448-2900", "" + f_l + s_sM); + test("1460392448-194313216", "" + f_l + sf_l); + test("146039244812", "" + f_l + s_strU1); + test("1460392448C(87)", "" + f_l + sf_o); + test("146039244891", "" + f_l + s_strU2); + test("146039244821", "" + f_l + f_strU1); + test("146039244818", "" + f_l + f_strU2); + test("1460392448null", "" + f_l + f_iAN); + test("1460392448null", "" + f_l + s_oN); + test("1460392448\u045180", "" + f_l + s_strU); + test("1460392448C", "" + f_l + sf_c); + test("146039244875", "" + f_l + sf_str); + test("1460392448-43", "" + f_l + s_bM); + test("146039244880", "" + f_l + sf_b); + test("1460392448null", "" + f_l + s_IN); + test("1460392448-52.0", "" + f_l + s_fM); + test("146039244875000000", "" + f_l + sf_i); + test("146039244844", "" + f_l + f_b); + test("1460392448-1705032704", "" + f_l + sf_lM); + test("1460392448null", "" + f_l + f_oAN); + test("146039244883.0", "" + f_l + f_d); + test("1460392448I", "" + f_l + f_c); + test("146039244894.0", "" + f_l + f_f); + test("146039244812.0", "" + f_l + sf_d); + test("1460392448-99.0", "" + f_l + f_dM); + test("146039244817.0", "" + f_l + sf_f); + test("1460392448-84.0", "" + f_l + sf_dM); + test("146039244858000000", "" + f_l + f_i); + test("1460392448-55000000", "" + f_l + f_iM); + test("14603924481460392448", "" + f_l + f_l); + test("1460392448C(70)", "" + f_l + f_o); + test("1460392448\u04511", "" + f_l + sf_strU); + test("14603924488000", "" + f_l + f_s); + test("146039244818", "" + f_l + s_str); + test("1460392448-1000000", "" + f_l + s_iM); + test("14603924481000000", "" + f_l + sf_I); + test("1460392448null", "" + f_l + f_oNtS); + test("1460392448false", "" + f_l + f_bl); + test("1460392448null", "" + f_l + sf_iAN); + test("1460392448-2000000", "" + f_l + sf_iM); + test("1460392448-820130816", "" + f_l + f_lM); + test("1460392448null", "" + f_l + sf_oAN); + test("146039244825000000", "" + f_l + s_I); + test("C(70)-96.0", "" + f_o + s_dM); + test("C(70)null", "" + f_o + s_oNtS); + test("C(70)\u045176", "" + f_o + f_strU); + test("C(70)92", "" + f_o + sf_strU2); + test("C(70)51", "" + f_o + sf_strU1); + test("C(70)null", "" + f_o + s_iAN); + test("C(70)-54", "" + f_o + f_bM); + test("C(70)-87.0", "" + f_o + f_fM); + test("C(70)null", "" + f_o + s_oAN); + test("C(70)19", "" + f_o + f_str); + test("C(70)-41", "" + f_o + sf_bM); + test("C(70)null", "" + f_o + sf_IN); + test("C(70)T", "" + f_o + s_c); + test("C(70)-42.0", "" + f_o + sf_fM); + test("C(70)25", "" + f_o + s_b); + test("C(70)null", "" + f_o + f_oN); + test("C(70)-1410065408", "" + f_o + s_lM); + test("C(70)8.0", "" + f_o + s_d); + test("C(70)55.0", "" + f_o + s_f); + test("C(70)97000000", "" + f_o + s_i); + test("C(70)-9900", "" + f_o + f_sM); + test("C(70)935228928", "" + f_o + s_l); + test("C(70)-8400", "" + f_o + sf_sM); + test("C(70)C(82)", "" + f_o + s_o); + test("C(70)null", "" + f_o + sf_oNtS); + test("C(70)true", "" + f_o + s_bl); + test("C(70)3900", "" + f_o + s_s); + test("C(70)null", "" + f_o + sf_oN); + test("C(70)94000000", "" + f_o + f_I); + test("C(70)null", "" + f_o + f_IN); + test("C(70)true", "" + f_o + sf_bl); + test("C(70)5500", "" + f_o + sf_s); + test("C(70)-2900", "" + f_o + s_sM); + test("C(70)-194313216", "" + f_o + sf_l); + test("C(70)12", "" + f_o + s_strU1); + test("C(70)C(87)", "" + f_o + sf_o); + test("C(70)91", "" + f_o + s_strU2); + test("C(70)21", "" + f_o + f_strU1); + test("C(70)18", "" + f_o + f_strU2); + test("C(70)null", "" + f_o + f_iAN); + test("C(70)null", "" + f_o + s_oN); + test("C(70)\u045180", "" + f_o + s_strU); + test("C(70)C", "" + f_o + sf_c); + test("C(70)75", "" + f_o + sf_str); + test("C(70)-43", "" + f_o + s_bM); + test("C(70)80", "" + f_o + sf_b); + test("C(70)null", "" + f_o + s_IN); + test("C(70)-52.0", "" + f_o + s_fM); + test("C(70)75000000", "" + f_o + sf_i); + test("C(70)44", "" + f_o + f_b); + test("C(70)-1705032704", "" + f_o + sf_lM); + test("C(70)null", "" + f_o + f_oAN); + test("C(70)83.0", "" + f_o + f_d); + test("C(70)I", "" + f_o + f_c); + test("C(70)94.0", "" + f_o + f_f); + test("C(70)12.0", "" + f_o + sf_d); + test("C(70)-99.0", "" + f_o + f_dM); + test("C(70)17.0", "" + f_o + sf_f); + test("C(70)-84.0", "" + f_o + sf_dM); + test("C(70)58000000", "" + f_o + f_i); + test("C(70)-55000000", "" + f_o + f_iM); + test("C(70)1460392448", "" + f_o + f_l); + test("C(70)C(70)", "" + f_o + f_o); + test("C(70)\u04511", "" + f_o + sf_strU); + test("C(70)8000", "" + f_o + f_s); + test("C(70)18", "" + f_o + s_str); + test("C(70)-1000000", "" + f_o + s_iM); + test("C(70)1000000", "" + f_o + sf_I); + test("C(70)null", "" + f_o + f_oNtS); + test("C(70)false", "" + f_o + f_bl); + test("C(70)null", "" + f_o + sf_iAN); + test("C(70)-2000000", "" + f_o + sf_iM); + test("C(70)-820130816", "" + f_o + f_lM); + test("C(70)null", "" + f_o + sf_oAN); + test("C(70)25000000", "" + f_o + s_I); + test("\u04511-96.0", "" + sf_strU + s_dM); + test("\u04511null", "" + sf_strU + s_oNtS); + test("\u04511\u045176", "" + sf_strU + f_strU); + test("\u0451192", "" + sf_strU + sf_strU2); + test("\u0451151", "" + sf_strU + sf_strU1); + test("\u04511null", "" + sf_strU + s_iAN); + test("\u04511-54", "" + sf_strU + f_bM); + test("\u04511-87.0", "" + sf_strU + f_fM); + test("\u04511null", "" + sf_strU + s_oAN); + test("\u0451119", "" + sf_strU + f_str); + test("\u04511-41", "" + sf_strU + sf_bM); + test("\u04511null", "" + sf_strU + sf_IN); + test("\u04511T", "" + sf_strU + s_c); + test("\u04511-42.0", "" + sf_strU + sf_fM); + test("\u0451125", "" + sf_strU + s_b); + test("\u04511null", "" + sf_strU + f_oN); + test("\u04511-1410065408", "" + sf_strU + s_lM); + test("\u045118.0", "" + sf_strU + s_d); + test("\u0451155.0", "" + sf_strU + s_f); + test("\u0451197000000", "" + sf_strU + s_i); + test("\u04511-9900", "" + sf_strU + f_sM); + test("\u04511935228928", "" + sf_strU + s_l); + test("\u04511-8400", "" + sf_strU + sf_sM); + test("\u04511C(82)", "" + sf_strU + s_o); + test("\u04511null", "" + sf_strU + sf_oNtS); + test("\u04511true", "" + sf_strU + s_bl); + test("\u045113900", "" + sf_strU + s_s); + test("\u04511null", "" + sf_strU + sf_oN); + test("\u0451194000000", "" + sf_strU + f_I); + test("\u04511null", "" + sf_strU + f_IN); + test("\u04511true", "" + sf_strU + sf_bl); + test("\u045115500", "" + sf_strU + sf_s); + test("\u04511-2900", "" + sf_strU + s_sM); + test("\u04511-194313216", "" + sf_strU + sf_l); + test("\u0451112", "" + sf_strU + s_strU1); + test("\u04511C(87)", "" + sf_strU + sf_o); + test("\u0451191", "" + sf_strU + s_strU2); + test("\u0451121", "" + sf_strU + f_strU1); + test("\u0451118", "" + sf_strU + f_strU2); + test("\u04511null", "" + sf_strU + f_iAN); + test("\u04511null", "" + sf_strU + s_oN); + test("\u04511\u045180", "" + sf_strU + s_strU); + test("\u04511C", "" + sf_strU + sf_c); + test("\u0451175", "" + sf_strU + sf_str); + test("\u04511-43", "" + sf_strU + s_bM); + test("\u0451180", "" + sf_strU + sf_b); + test("\u04511null", "" + sf_strU + s_IN); + test("\u04511-52.0", "" + sf_strU + s_fM); + test("\u0451175000000", "" + sf_strU + sf_i); + test("\u0451144", "" + sf_strU + f_b); + test("\u04511-1705032704", "" + sf_strU + sf_lM); + test("\u04511null", "" + sf_strU + f_oAN); + test("\u0451183.0", "" + sf_strU + f_d); + test("\u04511I", "" + sf_strU + f_c); + test("\u0451194.0", "" + sf_strU + f_f); + test("\u0451112.0", "" + sf_strU + sf_d); + test("\u04511-99.0", "" + sf_strU + f_dM); + test("\u0451117.0", "" + sf_strU + sf_f); + test("\u04511-84.0", "" + sf_strU + sf_dM); + test("\u0451158000000", "" + sf_strU + f_i); + test("\u04511-55000000", "" + sf_strU + f_iM); + test("\u045111460392448", "" + sf_strU + f_l); + test("\u04511C(70)", "" + sf_strU + f_o); + test("\u04511\u04511", "" + sf_strU + sf_strU); + test("\u045118000", "" + sf_strU + f_s); + test("\u0451118", "" + sf_strU + s_str); + test("\u04511-1000000", "" + sf_strU + s_iM); + test("\u045111000000", "" + sf_strU + sf_I); + test("\u04511null", "" + sf_strU + f_oNtS); + test("\u04511false", "" + sf_strU + f_bl); + test("\u04511null", "" + sf_strU + sf_iAN); + test("\u04511-2000000", "" + sf_strU + sf_iM); + test("\u04511-820130816", "" + sf_strU + f_lM); + test("\u04511null", "" + sf_strU + sf_oAN); + test("\u0451125000000", "" + sf_strU + s_I); + test("8000-96.0", "" + f_s + s_dM); + test("8000null", "" + f_s + s_oNtS); + test("8000\u045176", "" + f_s + f_strU); + test("800092", "" + f_s + sf_strU2); + test("800051", "" + f_s + sf_strU1); + test("8000null", "" + f_s + s_iAN); + test("8000-54", "" + f_s + f_bM); + test("8000-87.0", "" + f_s + f_fM); + test("8000null", "" + f_s + s_oAN); + test("800019", "" + f_s + f_str); + test("8000-41", "" + f_s + sf_bM); + test("8000null", "" + f_s + sf_IN); + test("8000T", "" + f_s + s_c); + test("8000-42.0", "" + f_s + sf_fM); + test("800025", "" + f_s + s_b); + test("8000null", "" + f_s + f_oN); + test("8000-1410065408", "" + f_s + s_lM); + test("80008.0", "" + f_s + s_d); + test("800055.0", "" + f_s + s_f); + test("800097000000", "" + f_s + s_i); + test("8000-9900", "" + f_s + f_sM); + test("8000935228928", "" + f_s + s_l); + test("8000-8400", "" + f_s + sf_sM); + test("8000C(82)", "" + f_s + s_o); + test("8000null", "" + f_s + sf_oNtS); + test("8000true", "" + f_s + s_bl); + test("80003900", "" + f_s + s_s); + test("8000null", "" + f_s + sf_oN); + test("800094000000", "" + f_s + f_I); + test("8000null", "" + f_s + f_IN); + test("8000true", "" + f_s + sf_bl); + test("80005500", "" + f_s + sf_s); + test("8000-2900", "" + f_s + s_sM); + test("8000-194313216", "" + f_s + sf_l); + test("800012", "" + f_s + s_strU1); + test("8000C(87)", "" + f_s + sf_o); + test("800091", "" + f_s + s_strU2); + test("800021", "" + f_s + f_strU1); + test("800018", "" + f_s + f_strU2); + test("8000null", "" + f_s + f_iAN); + test("8000null", "" + f_s + s_oN); + test("8000\u045180", "" + f_s + s_strU); + test("8000C", "" + f_s + sf_c); + test("800075", "" + f_s + sf_str); + test("8000-43", "" + f_s + s_bM); + test("800080", "" + f_s + sf_b); + test("8000null", "" + f_s + s_IN); + test("8000-52.0", "" + f_s + s_fM); + test("800075000000", "" + f_s + sf_i); + test("800044", "" + f_s + f_b); + test("8000-1705032704", "" + f_s + sf_lM); + test("8000null", "" + f_s + f_oAN); + test("800083.0", "" + f_s + f_d); + test("8000I", "" + f_s + f_c); + test("800094.0", "" + f_s + f_f); + test("800012.0", "" + f_s + sf_d); + test("8000-99.0", "" + f_s + f_dM); + test("800017.0", "" + f_s + sf_f); + test("8000-84.0", "" + f_s + sf_dM); + test("800058000000", "" + f_s + f_i); + test("8000-55000000", "" + f_s + f_iM); + test("80001460392448", "" + f_s + f_l); + test("8000C(70)", "" + f_s + f_o); + test("8000\u04511", "" + f_s + sf_strU); + test("80008000", "" + f_s + f_s); + test("800018", "" + f_s + s_str); + test("8000-1000000", "" + f_s + s_iM); + test("80001000000", "" + f_s + sf_I); + test("8000null", "" + f_s + f_oNtS); + test("8000false", "" + f_s + f_bl); + test("8000null", "" + f_s + sf_iAN); + test("8000-2000000", "" + f_s + sf_iM); + test("8000-820130816", "" + f_s + f_lM); + test("8000null", "" + f_s + sf_oAN); + test("800025000000", "" + f_s + s_I); + test("18-96.0", "" + s_str + s_dM); + test("18null", "" + s_str + s_oNtS); + test("18\u045176", "" + s_str + f_strU); + test("1892", "" + s_str + sf_strU2); + test("1851", "" + s_str + sf_strU1); + test("18null", "" + s_str + s_iAN); + test("18-54", "" + s_str + f_bM); + test("18-87.0", "" + s_str + f_fM); + test("18null", "" + s_str + s_oAN); + test("1819", "" + s_str + f_str); + test("18-41", "" + s_str + sf_bM); + test("18null", "" + s_str + sf_IN); + test("18T", "" + s_str + s_c); + test("18-42.0", "" + s_str + sf_fM); + test("1825", "" + s_str + s_b); + test("18null", "" + s_str + f_oN); + test("18-1410065408", "" + s_str + s_lM); + test("188.0", "" + s_str + s_d); + test("1855.0", "" + s_str + s_f); + test("1897000000", "" + s_str + s_i); + test("18-9900", "" + s_str + f_sM); + test("18935228928", "" + s_str + s_l); + test("18-8400", "" + s_str + sf_sM); + test("18C(82)", "" + s_str + s_o); + test("18null", "" + s_str + sf_oNtS); + test("18true", "" + s_str + s_bl); + test("183900", "" + s_str + s_s); + test("18null", "" + s_str + sf_oN); + test("1894000000", "" + s_str + f_I); + test("18null", "" + s_str + f_IN); + test("18true", "" + s_str + sf_bl); + test("185500", "" + s_str + sf_s); + test("18-2900", "" + s_str + s_sM); + test("18-194313216", "" + s_str + sf_l); + test("1812", "" + s_str + s_strU1); + test("18C(87)", "" + s_str + sf_o); + test("1891", "" + s_str + s_strU2); + test("1821", "" + s_str + f_strU1); + test("1818", "" + s_str + f_strU2); + test("18null", "" + s_str + f_iAN); + test("18null", "" + s_str + s_oN); + test("18\u045180", "" + s_str + s_strU); + test("18C", "" + s_str + sf_c); + test("1875", "" + s_str + sf_str); + test("18-43", "" + s_str + s_bM); + test("1880", "" + s_str + sf_b); + test("18null", "" + s_str + s_IN); + test("18-52.0", "" + s_str + s_fM); + test("1875000000", "" + s_str + sf_i); + test("1844", "" + s_str + f_b); + } + + public void run5() { + test("18-1705032704", "" + s_str + sf_lM); + test("18null", "" + s_str + f_oAN); + test("1883.0", "" + s_str + f_d); + test("18I", "" + s_str + f_c); + test("1894.0", "" + s_str + f_f); + test("1812.0", "" + s_str + sf_d); + test("18-99.0", "" + s_str + f_dM); + test("1817.0", "" + s_str + sf_f); + test("18-84.0", "" + s_str + sf_dM); + test("1858000000", "" + s_str + f_i); + test("18-55000000", "" + s_str + f_iM); + test("181460392448", "" + s_str + f_l); + test("18C(70)", "" + s_str + f_o); + test("18\u04511", "" + s_str + sf_strU); + test("188000", "" + s_str + f_s); + test("1818", "" + s_str + s_str); + test("18-1000000", "" + s_str + s_iM); + test("181000000", "" + s_str + sf_I); + test("18null", "" + s_str + f_oNtS); + test("18false", "" + s_str + f_bl); + test("18null", "" + s_str + sf_iAN); + test("18-2000000", "" + s_str + sf_iM); + test("18-820130816", "" + s_str + f_lM); + test("18null", "" + s_str + sf_oAN); + test("1825000000", "" + s_str + s_I); + test("-1000000-96.0", "" + s_iM + s_dM); + test("-1000000null", "" + s_iM + s_oNtS); + test("-1000000\u045176", "" + s_iM + f_strU); + test("-100000092", "" + s_iM + sf_strU2); + test("-100000051", "" + s_iM + sf_strU1); + test("-1000000null", "" + s_iM + s_iAN); + test("-1000000-54", "" + s_iM + f_bM); + test("-1000000-87.0", "" + s_iM + f_fM); + test("-1000000null", "" + s_iM + s_oAN); + test("-100000019", "" + s_iM + f_str); + test("-1000000-41", "" + s_iM + sf_bM); + test("-1000000null", "" + s_iM + sf_IN); + test("-1000000T", "" + s_iM + s_c); + test("-1000000-42.0", "" + s_iM + sf_fM); + test("-100000025", "" + s_iM + s_b); + test("-1000000null", "" + s_iM + f_oN); + test("-1000000-1410065408", "" + s_iM + s_lM); + test("-10000008.0", "" + s_iM + s_d); + test("-100000055.0", "" + s_iM + s_f); + test("-100000097000000", "" + s_iM + s_i); + test("-1000000-9900", "" + s_iM + f_sM); + test("-1000000935228928", "" + s_iM + s_l); + test("-1000000-8400", "" + s_iM + sf_sM); + test("-1000000C(82)", "" + s_iM + s_o); + test("-1000000null", "" + s_iM + sf_oNtS); + test("-1000000true", "" + s_iM + s_bl); + test("-10000003900", "" + s_iM + s_s); + test("-1000000null", "" + s_iM + sf_oN); + test("-100000094000000", "" + s_iM + f_I); + test("-1000000null", "" + s_iM + f_IN); + test("-1000000true", "" + s_iM + sf_bl); + test("-10000005500", "" + s_iM + sf_s); + test("-1000000-2900", "" + s_iM + s_sM); + test("-1000000-194313216", "" + s_iM + sf_l); + test("-100000012", "" + s_iM + s_strU1); + test("-1000000C(87)", "" + s_iM + sf_o); + test("-100000091", "" + s_iM + s_strU2); + test("-100000021", "" + s_iM + f_strU1); + test("-100000018", "" + s_iM + f_strU2); + test("-1000000null", "" + s_iM + f_iAN); + test("-1000000null", "" + s_iM + s_oN); + test("-1000000\u045180", "" + s_iM + s_strU); + test("-1000000C", "" + s_iM + sf_c); + test("-100000075", "" + s_iM + sf_str); + test("-1000000-43", "" + s_iM + s_bM); + test("-100000080", "" + s_iM + sf_b); + test("-1000000null", "" + s_iM + s_IN); + test("-1000000-52.0", "" + s_iM + s_fM); + test("-100000075000000", "" + s_iM + sf_i); + test("-100000044", "" + s_iM + f_b); + test("-1000000-1705032704", "" + s_iM + sf_lM); + test("-1000000null", "" + s_iM + f_oAN); + test("-100000083.0", "" + s_iM + f_d); + test("-1000000I", "" + s_iM + f_c); + test("-100000094.0", "" + s_iM + f_f); + test("-100000012.0", "" + s_iM + sf_d); + test("-1000000-99.0", "" + s_iM + f_dM); + test("-100000017.0", "" + s_iM + sf_f); + test("-1000000-84.0", "" + s_iM + sf_dM); + test("-100000058000000", "" + s_iM + f_i); + test("-1000000-55000000", "" + s_iM + f_iM); + test("-10000001460392448", "" + s_iM + f_l); + test("-1000000C(70)", "" + s_iM + f_o); + test("-1000000\u04511", "" + s_iM + sf_strU); + test("-10000008000", "" + s_iM + f_s); + test("-100000018", "" + s_iM + s_str); + test("-1000000-1000000", "" + s_iM + s_iM); + test("-10000001000000", "" + s_iM + sf_I); + test("-1000000null", "" + s_iM + f_oNtS); + test("-1000000false", "" + s_iM + f_bl); + test("-1000000null", "" + s_iM + sf_iAN); + test("-1000000-2000000", "" + s_iM + sf_iM); + test("-1000000-820130816", "" + s_iM + f_lM); + test("-1000000null", "" + s_iM + sf_oAN); + test("-100000025000000", "" + s_iM + s_I); + test("1000000-96.0", "" + sf_I + s_dM); + test("1000000null", "" + sf_I + s_oNtS); + test("1000000\u045176", "" + sf_I + f_strU); + test("100000092", "" + sf_I + sf_strU2); + test("100000051", "" + sf_I + sf_strU1); + test("1000000null", "" + sf_I + s_iAN); + test("1000000-54", "" + sf_I + f_bM); + test("1000000-87.0", "" + sf_I + f_fM); + test("1000000null", "" + sf_I + s_oAN); + test("100000019", "" + sf_I + f_str); + test("1000000-41", "" + sf_I + sf_bM); + test("1000000null", "" + sf_I + sf_IN); + test("1000000T", "" + sf_I + s_c); + test("1000000-42.0", "" + sf_I + sf_fM); + test("100000025", "" + sf_I + s_b); + test("1000000null", "" + sf_I + f_oN); + test("1000000-1410065408", "" + sf_I + s_lM); + test("10000008.0", "" + sf_I + s_d); + test("100000055.0", "" + sf_I + s_f); + test("100000097000000", "" + sf_I + s_i); + test("1000000-9900", "" + sf_I + f_sM); + test("1000000935228928", "" + sf_I + s_l); + test("1000000-8400", "" + sf_I + sf_sM); + test("1000000C(82)", "" + sf_I + s_o); + test("1000000null", "" + sf_I + sf_oNtS); + test("1000000true", "" + sf_I + s_bl); + test("10000003900", "" + sf_I + s_s); + test("1000000null", "" + sf_I + sf_oN); + test("100000094000000", "" + sf_I + f_I); + test("1000000null", "" + sf_I + f_IN); + test("1000000true", "" + sf_I + sf_bl); + test("10000005500", "" + sf_I + sf_s); + test("1000000-2900", "" + sf_I + s_sM); + test("1000000-194313216", "" + sf_I + sf_l); + test("100000012", "" + sf_I + s_strU1); + test("1000000C(87)", "" + sf_I + sf_o); + test("100000091", "" + sf_I + s_strU2); + test("100000021", "" + sf_I + f_strU1); + test("100000018", "" + sf_I + f_strU2); + test("1000000null", "" + sf_I + f_iAN); + test("1000000null", "" + sf_I + s_oN); + test("1000000\u045180", "" + sf_I + s_strU); + test("1000000C", "" + sf_I + sf_c); + test("100000075", "" + sf_I + sf_str); + test("1000000-43", "" + sf_I + s_bM); + test("100000080", "" + sf_I + sf_b); + test("1000000null", "" + sf_I + s_IN); + test("1000000-52.0", "" + sf_I + s_fM); + test("100000075000000", "" + sf_I + sf_i); + test("100000044", "" + sf_I + f_b); + test("1000000-1705032704", "" + sf_I + sf_lM); + test("1000000null", "" + sf_I + f_oAN); + test("100000083.0", "" + sf_I + f_d); + test("1000000I", "" + sf_I + f_c); + test("100000094.0", "" + sf_I + f_f); + test("100000012.0", "" + sf_I + sf_d); + test("1000000-99.0", "" + sf_I + f_dM); + test("100000017.0", "" + sf_I + sf_f); + test("1000000-84.0", "" + sf_I + sf_dM); + test("100000058000000", "" + sf_I + f_i); + test("1000000-55000000", "" + sf_I + f_iM); + test("10000001460392448", "" + sf_I + f_l); + test("1000000C(70)", "" + sf_I + f_o); + test("1000000\u04511", "" + sf_I + sf_strU); + test("10000008000", "" + sf_I + f_s); + test("100000018", "" + sf_I + s_str); + test("1000000-1000000", "" + sf_I + s_iM); + test("10000001000000", "" + sf_I + sf_I); + test("1000000null", "" + sf_I + f_oNtS); + test("1000000false", "" + sf_I + f_bl); + test("1000000null", "" + sf_I + sf_iAN); + test("1000000-2000000", "" + sf_I + sf_iM); + test("1000000-820130816", "" + sf_I + f_lM); + test("1000000null", "" + sf_I + sf_oAN); + test("100000025000000", "" + sf_I + s_I); + test("null-96.0", "" + f_oNtS + s_dM); + test("nullnull", "" + f_oNtS + s_oNtS); + test("null\u045176", "" + f_oNtS + f_strU); + test("null92", "" + f_oNtS + sf_strU2); + test("null51", "" + f_oNtS + sf_strU1); + test("nullnull", "" + f_oNtS + s_iAN); + test("null-54", "" + f_oNtS + f_bM); + test("null-87.0", "" + f_oNtS + f_fM); + test("nullnull", "" + f_oNtS + s_oAN); + test("null19", "" + f_oNtS + f_str); + test("null-41", "" + f_oNtS + sf_bM); + test("nullnull", "" + f_oNtS + sf_IN); + test("nullT", "" + f_oNtS + s_c); + test("null-42.0", "" + f_oNtS + sf_fM); + test("null25", "" + f_oNtS + s_b); + test("nullnull", "" + f_oNtS + f_oN); + test("null-1410065408", "" + f_oNtS + s_lM); + test("null8.0", "" + f_oNtS + s_d); + test("null55.0", "" + f_oNtS + s_f); + test("null97000000", "" + f_oNtS + s_i); + test("null-9900", "" + f_oNtS + f_sM); + test("null935228928", "" + f_oNtS + s_l); + test("null-8400", "" + f_oNtS + sf_sM); + test("nullC(82)", "" + f_oNtS + s_o); + test("nullnull", "" + f_oNtS + sf_oNtS); + test("nulltrue", "" + f_oNtS + s_bl); + test("null3900", "" + f_oNtS + s_s); + test("nullnull", "" + f_oNtS + sf_oN); + test("null94000000", "" + f_oNtS + f_I); + test("nullnull", "" + f_oNtS + f_IN); + test("nulltrue", "" + f_oNtS + sf_bl); + test("null5500", "" + f_oNtS + sf_s); + test("null-2900", "" + f_oNtS + s_sM); + test("null-194313216", "" + f_oNtS + sf_l); + test("null12", "" + f_oNtS + s_strU1); + test("nullC(87)", "" + f_oNtS + sf_o); + test("null91", "" + f_oNtS + s_strU2); + test("null21", "" + f_oNtS + f_strU1); + test("null18", "" + f_oNtS + f_strU2); + test("nullnull", "" + f_oNtS + f_iAN); + test("nullnull", "" + f_oNtS + s_oN); + test("null\u045180", "" + f_oNtS + s_strU); + test("nullC", "" + f_oNtS + sf_c); + test("null75", "" + f_oNtS + sf_str); + test("null-43", "" + f_oNtS + s_bM); + test("null80", "" + f_oNtS + sf_b); + test("nullnull", "" + f_oNtS + s_IN); + test("null-52.0", "" + f_oNtS + s_fM); + test("null75000000", "" + f_oNtS + sf_i); + test("null44", "" + f_oNtS + f_b); + test("null-1705032704", "" + f_oNtS + sf_lM); + test("nullnull", "" + f_oNtS + f_oAN); + test("null83.0", "" + f_oNtS + f_d); + test("nullI", "" + f_oNtS + f_c); + test("null94.0", "" + f_oNtS + f_f); + test("null12.0", "" + f_oNtS + sf_d); + test("null-99.0", "" + f_oNtS + f_dM); + test("null17.0", "" + f_oNtS + sf_f); + test("null-84.0", "" + f_oNtS + sf_dM); + test("null58000000", "" + f_oNtS + f_i); + test("null-55000000", "" + f_oNtS + f_iM); + test("null1460392448", "" + f_oNtS + f_l); + test("nullC(70)", "" + f_oNtS + f_o); + test("null\u04511", "" + f_oNtS + sf_strU); + test("null8000", "" + f_oNtS + f_s); + test("null18", "" + f_oNtS + s_str); + test("null-1000000", "" + f_oNtS + s_iM); + test("null1000000", "" + f_oNtS + sf_I); + test("nullnull", "" + f_oNtS + f_oNtS); + test("nullfalse", "" + f_oNtS + f_bl); + test("nullnull", "" + f_oNtS + sf_iAN); + test("null-2000000", "" + f_oNtS + sf_iM); + test("null-820130816", "" + f_oNtS + f_lM); + test("nullnull", "" + f_oNtS + sf_oAN); + test("null25000000", "" + f_oNtS + s_I); + test("false-96.0", "" + f_bl + s_dM); + test("falsenull", "" + f_bl + s_oNtS); + test("false\u045176", "" + f_bl + f_strU); + test("false92", "" + f_bl + sf_strU2); + test("false51", "" + f_bl + sf_strU1); + test("falsenull", "" + f_bl + s_iAN); + test("false-54", "" + f_bl + f_bM); + test("false-87.0", "" + f_bl + f_fM); + test("falsenull", "" + f_bl + s_oAN); + test("false19", "" + f_bl + f_str); + test("false-41", "" + f_bl + sf_bM); + test("falsenull", "" + f_bl + sf_IN); + test("falseT", "" + f_bl + s_c); + test("false-42.0", "" + f_bl + sf_fM); + test("false25", "" + f_bl + s_b); + test("falsenull", "" + f_bl + f_oN); + test("false-1410065408", "" + f_bl + s_lM); + test("false8.0", "" + f_bl + s_d); + test("false55.0", "" + f_bl + s_f); + test("false97000000", "" + f_bl + s_i); + test("false-9900", "" + f_bl + f_sM); + test("false935228928", "" + f_bl + s_l); + test("false-8400", "" + f_bl + sf_sM); + test("falseC(82)", "" + f_bl + s_o); + test("falsenull", "" + f_bl + sf_oNtS); + test("falsetrue", "" + f_bl + s_bl); + test("false3900", "" + f_bl + s_s); + test("falsenull", "" + f_bl + sf_oN); + test("false94000000", "" + f_bl + f_I); + test("falsenull", "" + f_bl + f_IN); + test("falsetrue", "" + f_bl + sf_bl); + test("false5500", "" + f_bl + sf_s); + test("false-2900", "" + f_bl + s_sM); + test("false-194313216", "" + f_bl + sf_l); + test("false12", "" + f_bl + s_strU1); + test("falseC(87)", "" + f_bl + sf_o); + test("false91", "" + f_bl + s_strU2); + test("false21", "" + f_bl + f_strU1); + test("false18", "" + f_bl + f_strU2); + test("falsenull", "" + f_bl + f_iAN); + test("falsenull", "" + f_bl + s_oN); + test("false\u045180", "" + f_bl + s_strU); + test("falseC", "" + f_bl + sf_c); + test("false75", "" + f_bl + sf_str); + test("false-43", "" + f_bl + s_bM); + test("false80", "" + f_bl + sf_b); + test("falsenull", "" + f_bl + s_IN); + test("false-52.0", "" + f_bl + s_fM); + test("false75000000", "" + f_bl + sf_i); + test("false44", "" + f_bl + f_b); + test("false-1705032704", "" + f_bl + sf_lM); + test("falsenull", "" + f_bl + f_oAN); + test("false83.0", "" + f_bl + f_d); + test("falseI", "" + f_bl + f_c); + test("false94.0", "" + f_bl + f_f); + test("false12.0", "" + f_bl + sf_d); + test("false-99.0", "" + f_bl + f_dM); + test("false17.0", "" + f_bl + sf_f); + test("false-84.0", "" + f_bl + sf_dM); + test("false58000000", "" + f_bl + f_i); + test("false-55000000", "" + f_bl + f_iM); + test("false1460392448", "" + f_bl + f_l); + test("falseC(70)", "" + f_bl + f_o); + test("false\u04511", "" + f_bl + sf_strU); + test("false8000", "" + f_bl + f_s); + test("false18", "" + f_bl + s_str); + test("false-1000000", "" + f_bl + s_iM); + test("false1000000", "" + f_bl + sf_I); + test("falsenull", "" + f_bl + f_oNtS); + test("falsefalse", "" + f_bl + f_bl); + test("falsenull", "" + f_bl + sf_iAN); + test("false-2000000", "" + f_bl + sf_iM); + test("false-820130816", "" + f_bl + f_lM); + test("falsenull", "" + f_bl + sf_oAN); + test("false25000000", "" + f_bl + s_I); + test("null-96.0", "" + sf_iAN + s_dM); + test("nullnull", "" + sf_iAN + s_oNtS); + test("null\u045176", "" + sf_iAN + f_strU); + test("null92", "" + sf_iAN + sf_strU2); + test("null51", "" + sf_iAN + sf_strU1); + test("nullnull", "" + sf_iAN + s_iAN); + test("null-54", "" + sf_iAN + f_bM); + test("null-87.0", "" + sf_iAN + f_fM); + test("nullnull", "" + sf_iAN + s_oAN); + test("null19", "" + sf_iAN + f_str); + test("null-41", "" + sf_iAN + sf_bM); + test("nullnull", "" + sf_iAN + sf_IN); + test("nullT", "" + sf_iAN + s_c); + test("null-42.0", "" + sf_iAN + sf_fM); + test("null25", "" + sf_iAN + s_b); + test("nullnull", "" + sf_iAN + f_oN); + test("null-1410065408", "" + sf_iAN + s_lM); + test("null8.0", "" + sf_iAN + s_d); + test("null55.0", "" + sf_iAN + s_f); + test("null97000000", "" + sf_iAN + s_i); + test("null-9900", "" + sf_iAN + f_sM); + test("null935228928", "" + sf_iAN + s_l); + test("null-8400", "" + sf_iAN + sf_sM); + test("nullC(82)", "" + sf_iAN + s_o); + test("nullnull", "" + sf_iAN + sf_oNtS); + test("nulltrue", "" + sf_iAN + s_bl); + test("null3900", "" + sf_iAN + s_s); + test("nullnull", "" + sf_iAN + sf_oN); + test("null94000000", "" + sf_iAN + f_I); + test("nullnull", "" + sf_iAN + f_IN); + test("nulltrue", "" + sf_iAN + sf_bl); + test("null5500", "" + sf_iAN + sf_s); + test("null-2900", "" + sf_iAN + s_sM); + test("null-194313216", "" + sf_iAN + sf_l); + test("null12", "" + sf_iAN + s_strU1); + test("nullC(87)", "" + sf_iAN + sf_o); + test("null91", "" + sf_iAN + s_strU2); + test("null21", "" + sf_iAN + f_strU1); + test("null18", "" + sf_iAN + f_strU2); + test("nullnull", "" + sf_iAN + f_iAN); + test("nullnull", "" + sf_iAN + s_oN); + test("null\u045180", "" + sf_iAN + s_strU); + test("nullC", "" + sf_iAN + sf_c); + test("null75", "" + sf_iAN + sf_str); + test("null-43", "" + sf_iAN + s_bM); + test("null80", "" + sf_iAN + sf_b); + test("nullnull", "" + sf_iAN + s_IN); + test("null-52.0", "" + sf_iAN + s_fM); + test("null75000000", "" + sf_iAN + sf_i); + test("null44", "" + sf_iAN + f_b); + test("null-1705032704", "" + sf_iAN + sf_lM); + test("nullnull", "" + sf_iAN + f_oAN); + test("null83.0", "" + sf_iAN + f_d); + test("nullI", "" + sf_iAN + f_c); + test("null94.0", "" + sf_iAN + f_f); + test("null12.0", "" + sf_iAN + sf_d); + test("null-99.0", "" + sf_iAN + f_dM); + test("null17.0", "" + sf_iAN + sf_f); + test("null-84.0", "" + sf_iAN + sf_dM); + test("null58000000", "" + sf_iAN + f_i); + test("null-55000000", "" + sf_iAN + f_iM); + test("null1460392448", "" + sf_iAN + f_l); + test("nullC(70)", "" + sf_iAN + f_o); + test("null\u04511", "" + sf_iAN + sf_strU); + test("null8000", "" + sf_iAN + f_s); + test("null18", "" + sf_iAN + s_str); + test("null-1000000", "" + sf_iAN + s_iM); + test("null1000000", "" + sf_iAN + sf_I); + test("nullnull", "" + sf_iAN + f_oNtS); + test("nullfalse", "" + sf_iAN + f_bl); + test("nullnull", "" + sf_iAN + sf_iAN); + test("null-2000000", "" + sf_iAN + sf_iM); + test("null-820130816", "" + sf_iAN + f_lM); + test("nullnull", "" + sf_iAN + sf_oAN); + test("null25000000", "" + sf_iAN + s_I); + test("-2000000-96.0", "" + sf_iM + s_dM); + test("-2000000null", "" + sf_iM + s_oNtS); + test("-2000000\u045176", "" + sf_iM + f_strU); + test("-200000092", "" + sf_iM + sf_strU2); + test("-200000051", "" + sf_iM + sf_strU1); + test("-2000000null", "" + sf_iM + s_iAN); + test("-2000000-54", "" + sf_iM + f_bM); + test("-2000000-87.0", "" + sf_iM + f_fM); + test("-2000000null", "" + sf_iM + s_oAN); + test("-200000019", "" + sf_iM + f_str); + test("-2000000-41", "" + sf_iM + sf_bM); + test("-2000000null", "" + sf_iM + sf_IN); + test("-2000000T", "" + sf_iM + s_c); + test("-2000000-42.0", "" + sf_iM + sf_fM); + test("-200000025", "" + sf_iM + s_b); + test("-2000000null", "" + sf_iM + f_oN); + test("-2000000-1410065408", "" + sf_iM + s_lM); + test("-20000008.0", "" + sf_iM + s_d); + test("-200000055.0", "" + sf_iM + s_f); + test("-200000097000000", "" + sf_iM + s_i); + test("-2000000-9900", "" + sf_iM + f_sM); + test("-2000000935228928", "" + sf_iM + s_l); + test("-2000000-8400", "" + sf_iM + sf_sM); + test("-2000000C(82)", "" + sf_iM + s_o); + test("-2000000null", "" + sf_iM + sf_oNtS); + test("-2000000true", "" + sf_iM + s_bl); + test("-20000003900", "" + sf_iM + s_s); + test("-2000000null", "" + sf_iM + sf_oN); + test("-200000094000000", "" + sf_iM + f_I); + test("-2000000null", "" + sf_iM + f_IN); + test("-2000000true", "" + sf_iM + sf_bl); + test("-20000005500", "" + sf_iM + sf_s); + test("-2000000-2900", "" + sf_iM + s_sM); + test("-2000000-194313216", "" + sf_iM + sf_l); + test("-200000012", "" + sf_iM + s_strU1); + test("-2000000C(87)", "" + sf_iM + sf_o); + test("-200000091", "" + sf_iM + s_strU2); + test("-200000021", "" + sf_iM + f_strU1); + test("-200000018", "" + sf_iM + f_strU2); + test("-2000000null", "" + sf_iM + f_iAN); + test("-2000000null", "" + sf_iM + s_oN); + test("-2000000\u045180", "" + sf_iM + s_strU); + test("-2000000C", "" + sf_iM + sf_c); + test("-200000075", "" + sf_iM + sf_str); + test("-2000000-43", "" + sf_iM + s_bM); + test("-200000080", "" + sf_iM + sf_b); + test("-2000000null", "" + sf_iM + s_IN); + test("-2000000-52.0", "" + sf_iM + s_fM); + test("-200000075000000", "" + sf_iM + sf_i); + test("-200000044", "" + sf_iM + f_b); + test("-2000000-1705032704", "" + sf_iM + sf_lM); + test("-2000000null", "" + sf_iM + f_oAN); + test("-200000083.0", "" + sf_iM + f_d); + test("-2000000I", "" + sf_iM + f_c); + test("-200000094.0", "" + sf_iM + f_f); + test("-200000012.0", "" + sf_iM + sf_d); + test("-2000000-99.0", "" + sf_iM + f_dM); + test("-200000017.0", "" + sf_iM + sf_f); + test("-2000000-84.0", "" + sf_iM + sf_dM); + test("-200000058000000", "" + sf_iM + f_i); + test("-2000000-55000000", "" + sf_iM + f_iM); + test("-20000001460392448", "" + sf_iM + f_l); + test("-2000000C(70)", "" + sf_iM + f_o); + test("-2000000\u04511", "" + sf_iM + sf_strU); + test("-20000008000", "" + sf_iM + f_s); + test("-200000018", "" + sf_iM + s_str); + test("-2000000-1000000", "" + sf_iM + s_iM); + test("-20000001000000", "" + sf_iM + sf_I); + test("-2000000null", "" + sf_iM + f_oNtS); + test("-2000000false", "" + sf_iM + f_bl); + test("-2000000null", "" + sf_iM + sf_iAN); + test("-2000000-2000000", "" + sf_iM + sf_iM); + test("-2000000-820130816", "" + sf_iM + f_lM); + test("-2000000null", "" + sf_iM + sf_oAN); + test("-200000025000000", "" + sf_iM + s_I); + test("-820130816-96.0", "" + f_lM + s_dM); + test("-820130816null", "" + f_lM + s_oNtS); + test("-820130816\u045176", "" + f_lM + f_strU); + test("-82013081692", "" + f_lM + sf_strU2); + test("-82013081651", "" + f_lM + sf_strU1); + test("-820130816null", "" + f_lM + s_iAN); + test("-820130816-54", "" + f_lM + f_bM); + test("-820130816-87.0", "" + f_lM + f_fM); + test("-820130816null", "" + f_lM + s_oAN); + test("-82013081619", "" + f_lM + f_str); + test("-820130816-41", "" + f_lM + sf_bM); + test("-820130816null", "" + f_lM + sf_IN); + test("-820130816T", "" + f_lM + s_c); + test("-820130816-42.0", "" + f_lM + sf_fM); + test("-82013081625", "" + f_lM + s_b); + test("-820130816null", "" + f_lM + f_oN); + test("-820130816-1410065408", "" + f_lM + s_lM); + test("-8201308168.0", "" + f_lM + s_d); + test("-82013081655.0", "" + f_lM + s_f); + test("-82013081697000000", "" + f_lM + s_i); + test("-820130816-9900", "" + f_lM + f_sM); + test("-820130816935228928", "" + f_lM + s_l); + test("-820130816-8400", "" + f_lM + sf_sM); + test("-820130816C(82)", "" + f_lM + s_o); + test("-820130816null", "" + f_lM + sf_oNtS); + test("-820130816true", "" + f_lM + s_bl); + test("-8201308163900", "" + f_lM + s_s); + test("-820130816null", "" + f_lM + sf_oN); + test("-82013081694000000", "" + f_lM + f_I); + test("-820130816null", "" + f_lM + f_IN); + test("-820130816true", "" + f_lM + sf_bl); + test("-8201308165500", "" + f_lM + sf_s); + test("-820130816-2900", "" + f_lM + s_sM); + test("-820130816-194313216", "" + f_lM + sf_l); + test("-82013081612", "" + f_lM + s_strU1); + test("-820130816C(87)", "" + f_lM + sf_o); + test("-82013081691", "" + f_lM + s_strU2); + test("-82013081621", "" + f_lM + f_strU1); + test("-82013081618", "" + f_lM + f_strU2); + test("-820130816null", "" + f_lM + f_iAN); + test("-820130816null", "" + f_lM + s_oN); + test("-820130816\u045180", "" + f_lM + s_strU); + test("-820130816C", "" + f_lM + sf_c); + test("-82013081675", "" + f_lM + sf_str); + test("-820130816-43", "" + f_lM + s_bM); + test("-82013081680", "" + f_lM + sf_b); + test("-820130816null", "" + f_lM + s_IN); + test("-820130816-52.0", "" + f_lM + s_fM); + test("-82013081675000000", "" + f_lM + sf_i); + test("-82013081644", "" + f_lM + f_b); + test("-820130816-1705032704", "" + f_lM + sf_lM); + test("-820130816null", "" + f_lM + f_oAN); + test("-82013081683.0", "" + f_lM + f_d); + test("-820130816I", "" + f_lM + f_c); + test("-82013081694.0", "" + f_lM + f_f); + test("-82013081612.0", "" + f_lM + sf_d); + test("-820130816-99.0", "" + f_lM + f_dM); + test("-82013081617.0", "" + f_lM + sf_f); + test("-820130816-84.0", "" + f_lM + sf_dM); + test("-82013081658000000", "" + f_lM + f_i); + test("-820130816-55000000", "" + f_lM + f_iM); + test("-8201308161460392448", "" + f_lM + f_l); + test("-820130816C(70)", "" + f_lM + f_o); + test("-820130816\u04511", "" + f_lM + sf_strU); + test("-8201308168000", "" + f_lM + f_s); + test("-82013081618", "" + f_lM + s_str); + test("-820130816-1000000", "" + f_lM + s_iM); + test("-8201308161000000", "" + f_lM + sf_I); + test("-820130816null", "" + f_lM + f_oNtS); + test("-820130816false", "" + f_lM + f_bl); + test("-820130816null", "" + f_lM + sf_iAN); + test("-820130816-2000000", "" + f_lM + sf_iM); + test("-820130816-820130816", "" + f_lM + f_lM); + test("-820130816null", "" + f_lM + sf_oAN); + test("-82013081625000000", "" + f_lM + s_I); + test("null-96.0", "" + sf_oAN + s_dM); + test("nullnull", "" + sf_oAN + s_oNtS); + test("null\u045176", "" + sf_oAN + f_strU); + test("null92", "" + sf_oAN + sf_strU2); + test("null51", "" + sf_oAN + sf_strU1); + test("nullnull", "" + sf_oAN + s_iAN); + test("null-54", "" + sf_oAN + f_bM); + test("null-87.0", "" + sf_oAN + f_fM); + test("nullnull", "" + sf_oAN + s_oAN); + test("null19", "" + sf_oAN + f_str); + test("null-41", "" + sf_oAN + sf_bM); + test("nullnull", "" + sf_oAN + sf_IN); + test("nullT", "" + sf_oAN + s_c); + test("null-42.0", "" + sf_oAN + sf_fM); + test("null25", "" + sf_oAN + s_b); + test("nullnull", "" + sf_oAN + f_oN); + test("null-1410065408", "" + sf_oAN + s_lM); + test("null8.0", "" + sf_oAN + s_d); + test("null55.0", "" + sf_oAN + s_f); + test("null97000000", "" + sf_oAN + s_i); + test("null-9900", "" + sf_oAN + f_sM); + test("null935228928", "" + sf_oAN + s_l); + test("null-8400", "" + sf_oAN + sf_sM); + test("nullC(82)", "" + sf_oAN + s_o); + test("nullnull", "" + sf_oAN + sf_oNtS); + test("nulltrue", "" + sf_oAN + s_bl); + test("null3900", "" + sf_oAN + s_s); + test("nullnull", "" + sf_oAN + sf_oN); + test("null94000000", "" + sf_oAN + f_I); + test("nullnull", "" + sf_oAN + f_IN); + test("nulltrue", "" + sf_oAN + sf_bl); + test("null5500", "" + sf_oAN + sf_s); + test("null-2900", "" + sf_oAN + s_sM); + test("null-194313216", "" + sf_oAN + sf_l); + test("null12", "" + sf_oAN + s_strU1); + test("nullC(87)", "" + sf_oAN + sf_o); + test("null91", "" + sf_oAN + s_strU2); + test("null21", "" + sf_oAN + f_strU1); + test("null18", "" + sf_oAN + f_strU2); + test("nullnull", "" + sf_oAN + f_iAN); + test("nullnull", "" + sf_oAN + s_oN); + test("null\u045180", "" + sf_oAN + s_strU); + test("nullC", "" + sf_oAN + sf_c); + test("null75", "" + sf_oAN + sf_str); + test("null-43", "" + sf_oAN + s_bM); + test("null80", "" + sf_oAN + sf_b); + test("nullnull", "" + sf_oAN + s_IN); + test("null-52.0", "" + sf_oAN + s_fM); + test("null75000000", "" + sf_oAN + sf_i); + test("null44", "" + sf_oAN + f_b); + test("null-1705032704", "" + sf_oAN + sf_lM); + test("nullnull", "" + sf_oAN + f_oAN); + test("null83.0", "" + sf_oAN + f_d); + test("nullI", "" + sf_oAN + f_c); + test("null94.0", "" + sf_oAN + f_f); + test("null12.0", "" + sf_oAN + sf_d); + test("null-99.0", "" + sf_oAN + f_dM); + test("null17.0", "" + sf_oAN + sf_f); + test("null-84.0", "" + sf_oAN + sf_dM); + test("null58000000", "" + sf_oAN + f_i); + test("null-55000000", "" + sf_oAN + f_iM); + test("null1460392448", "" + sf_oAN + f_l); + test("nullC(70)", "" + sf_oAN + f_o); + test("null\u04511", "" + sf_oAN + sf_strU); + test("null8000", "" + sf_oAN + f_s); + test("null18", "" + sf_oAN + s_str); + test("null-1000000", "" + sf_oAN + s_iM); + test("null1000000", "" + sf_oAN + sf_I); + test("nullnull", "" + sf_oAN + f_oNtS); + test("nullfalse", "" + sf_oAN + f_bl); + test("nullnull", "" + sf_oAN + sf_iAN); + test("null-2000000", "" + sf_oAN + sf_iM); + test("null-820130816", "" + sf_oAN + f_lM); + test("nullnull", "" + sf_oAN + sf_oAN); + test("null25000000", "" + sf_oAN + s_I); + test("25000000-96.0", "" + s_I + s_dM); + test("25000000null", "" + s_I + s_oNtS); + test("25000000\u045176", "" + s_I + f_strU); + test("2500000092", "" + s_I + sf_strU2); + test("2500000051", "" + s_I + sf_strU1); + test("25000000null", "" + s_I + s_iAN); + test("25000000-54", "" + s_I + f_bM); + test("25000000-87.0", "" + s_I + f_fM); + test("25000000null", "" + s_I + s_oAN); + test("2500000019", "" + s_I + f_str); + test("25000000-41", "" + s_I + sf_bM); + test("25000000null", "" + s_I + sf_IN); + test("25000000T", "" + s_I + s_c); + test("25000000-42.0", "" + s_I + sf_fM); + test("2500000025", "" + s_I + s_b); + test("25000000null", "" + s_I + f_oN); + test("25000000-1410065408", "" + s_I + s_lM); + test("250000008.0", "" + s_I + s_d); + test("2500000055.0", "" + s_I + s_f); + test("2500000097000000", "" + s_I + s_i); + test("25000000-9900", "" + s_I + f_sM); + test("25000000935228928", "" + s_I + s_l); + test("25000000-8400", "" + s_I + sf_sM); + test("25000000C(82)", "" + s_I + s_o); + test("25000000null", "" + s_I + sf_oNtS); + test("25000000true", "" + s_I + s_bl); + test("250000003900", "" + s_I + s_s); + test("25000000null", "" + s_I + sf_oN); + test("2500000094000000", "" + s_I + f_I); + test("25000000null", "" + s_I + f_IN); + test("25000000true", "" + s_I + sf_bl); + test("250000005500", "" + s_I + sf_s); + test("25000000-2900", "" + s_I + s_sM); + test("25000000-194313216", "" + s_I + sf_l); + test("2500000012", "" + s_I + s_strU1); + test("25000000C(87)", "" + s_I + sf_o); + test("2500000091", "" + s_I + s_strU2); + test("2500000021", "" + s_I + f_strU1); + test("2500000018", "" + s_I + f_strU2); + test("25000000null", "" + s_I + f_iAN); + test("25000000null", "" + s_I + s_oN); + test("25000000\u045180", "" + s_I + s_strU); + test("25000000C", "" + s_I + sf_c); + test("2500000075", "" + s_I + sf_str); + test("25000000-43", "" + s_I + s_bM); + test("2500000080", "" + s_I + sf_b); + test("25000000null", "" + s_I + s_IN); + test("25000000-52.0", "" + s_I + s_fM); + test("2500000075000000", "" + s_I + sf_i); + test("2500000044", "" + s_I + f_b); + test("25000000-1705032704", "" + s_I + sf_lM); + test("25000000null", "" + s_I + f_oAN); + test("2500000083.0", "" + s_I + f_d); + test("25000000I", "" + s_I + f_c); + test("2500000094.0", "" + s_I + f_f); + test("2500000012.0", "" + s_I + sf_d); + test("25000000-99.0", "" + s_I + f_dM); + test("2500000017.0", "" + s_I + sf_f); + test("25000000-84.0", "" + s_I + sf_dM); + test("2500000058000000", "" + s_I + f_i); + test("25000000-55000000", "" + s_I + f_iM); + test("250000001460392448", "" + s_I + f_l); + test("25000000C(70)", "" + s_I + f_o); + test("25000000\u04511", "" + s_I + sf_strU); + test("250000008000", "" + s_I + f_s); + test("2500000018", "" + s_I + s_str); + test("25000000-1000000", "" + s_I + s_iM); + test("250000001000000", "" + s_I + sf_I); + test("25000000null", "" + s_I + f_oNtS); + test("25000000false", "" + s_I + f_bl); + test("25000000null", "" + s_I + sf_iAN); + test("25000000-2000000", "" + s_I + sf_iM); + test("25000000-820130816", "" + s_I + f_lM); + test("25000000null", "" + s_I + sf_oAN); + test("2500000025000000", "" + s_I + s_I); + } + +} diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java new file mode 100644 index 00000000000..8b42e4607e4 --- /dev/null +++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; + +public class ImplicitStringConcatShapesTestGen { + public static String escapeToUnicode(String str) { + StringBuilder b = new StringBuilder(); + for (char c : str.toCharArray()) { + if (c < 128) { + b.append(c); + } else { + b.append("\\u").append(String.format("%04X", (int) c)); + } + } + return b.toString(); + } + + public static void main(String... args) throws IOException { + PrintWriter pw = new PrintWriter(System.out); + + String[] types = { + "boolean", + "byte", + "byteMinus", + "char", + "short", + "shortMinus", + "int", + "intMinus", + "integer", + "integerNull", + "float", + "floatMinus", + "long", + "longMinus", + "double", + "doubleMinus", + "object", + "objectNull", + "objectNullToString", + "String", + "StringUTF16", + "StringU1", + "StringU2", + "intArrayNull", + "objectArrayNull", + }; + + for (String t : Files.readAllLines(Paths.get("ImplicitStringConcatShapes-head.template"))) { + pw.println(t); + } + + Map values = new HashMap<>(); + + Random current = new Random(12345); + for (int mode = 0; mode <= 2; mode++) { + for (String type : types) { + int i = current.nextInt(100); + boolean isStatic = (mode | 1) == 1; + boolean isFinal = (mode | 2) == 2; + String fieldName = (isStatic ? "s" : "") + (isFinal ? "f" : "") + "_" + typeSig(type); + String value = initValue(type, i); + String stringValue = stringValue(type, i); + values.put(fieldName, stringValue); + pw.printf(" %s %s %s %s = %s;%n", isStatic ? "static" : "", isFinal ? "final" : "", typeValue(type, i), fieldName, value); + } + } + + pw.println(); + + List lines = new ArrayList<>(); + List l = new ArrayList<>(values.keySet()); + + for (String l1 : l) { + lines.add(String.format("test(\"%s\", \"\" + %s);", + escapeToUnicode(values.get(l1)), + l1 + )); + } + + for (String l1 : l) { + for (String l2 : l) { + lines.add(String.format("test(\"%s\", \"\" + %s + %s);", + escapeToUnicode(values.get(l1) + values.get(l2)), + l1, l2 + )); + } + } + + final int STRIDE = 1000; + int strides = lines.size() / STRIDE + 1; + + pw.println(" public void run() {"); + for (int c = 0; c < strides; c++) { + pw.println(" run" + c + "();"); + } + pw.println(" }"); + pw.println(); + + for (int c = 0; c < strides; c++) { + pw.println(" public void run" + c + "() {"); + for (String line : lines.subList(c * STRIDE, Math.min(lines.size(), (c+1) * STRIDE))) { + pw.println(" " + line); + } + pw.println(" }"); + pw.println(); + } + + pw.println("}"); + + pw.flush(); + pw.close(); + } + + private static String typeSig(String type) { + switch (type) { + case "boolean": return "bl"; + case "byte": return "b"; + case "byteMinus": return "bM"; + case "short": return "s"; + case "shortMinus": return "sM"; + case "char": return "c"; + case "int": return "i"; + case "intMinus": return "iM"; + case "integer": return "I"; + case "integerNull": return "IN"; + case "float": return "f"; + case "floatMinus": return "fM"; + case "long": return "l"; + case "longMinus": return "lM"; + case "double": return "d"; + case "doubleMinus": return "dM"; + case "String": return "str"; + case "StringUTF16": return "strU"; + case "StringU1": return "strU1"; + case "StringU2": return "strU2"; + case "object": return "o"; + case "objectNull": return "oN"; + case "objectNullToString": return "oNtS"; + case "intArrayNull": return "iAN"; + case "objectArrayNull": return "oAN"; + default: + throw new IllegalStateException(); + } + } + + private static String typeValue(String type, int i) { + switch (type) { + case "boolean": + case "byte": + case "byteMinus": + case "char": + case "short": + case "shortMinus": + case "int": + case "intMinus": + case "float": + case "floatMinus": + case "long": + case "longMinus": + case "double": + case "doubleMinus": + return type.replace("Minus", ""); + case "String": + case "StringUTF16": + case "StringU1": + case "StringU2": + return "String"; + case "object": + case "objectNull": + case "objectNullToString": + return "Object"; + case "integer": + case "integerNull": + return "Integer"; + case "intArrayNull": + return "int[]"; + case "objectArrayNull": + return "Object[]"; + default: + throw new IllegalStateException(); + } + } + + private static String initValue(String type, int i) { + switch (type) { + case "boolean": + return String.valueOf((i & 1) == 1); + case "byte": + return String.valueOf(i); + case "byteMinus": + return String.valueOf(-i); + case "short": + return String.valueOf(i*100); + case "shortMinus": + return String.valueOf(-i*100); + case "intMinus": + return String.valueOf(-i*1_000_000); + case "int": + case "integer": + return String.valueOf(i*1_000_000); + case "long": + return String.valueOf(i*1_000_000_000) + "L"; + case "longMinus": + return String.valueOf(-i*1_000_000_000) + "L"; + case "char": + return "'" + (char)(i % 26 + 65) + "'"; + case "double": + return String.valueOf(i) + ".0d"; + case "doubleMinus": + return "-" + String.valueOf(i) + ".0d"; + case "float": + return String.valueOf(i) + ".0f"; + case "floatMinus": + return "-" + String.valueOf(i) + ".0f"; + case "object": + return "new MyClass(" + i + ")"; + case "objectNullToString": + return "new MyClassNullToString()"; + case "integerNull": + case "objectNull": + case "intArrayNull": + case "objectArrayNull": + return "null"; + case "String": + return "\"" + i + "\""; + case "StringUTF16": + return "\"\\u0451" + i + "\""; + case "StringU1": + return "\"\\u0001" + i + "\""; + case "StringU2": + return "\"\\u0002" + i + "\""; + default: + throw new IllegalStateException(); + } + } + + private static String stringValue(String type, int i) { + switch (type) { + case "boolean": + return String.valueOf((i & 1) == 1); + case "byte": + return String.valueOf(i); + case "byteMinus": + return String.valueOf(-i); + case "short": + return String.valueOf(i*100); + case "shortMinus": + return String.valueOf(-i*100); + case "intMinus": + return String.valueOf(-i*1_000_000); + case "int": + case "integer": + return String.valueOf(i*1_000_000); + case "long": + return String.valueOf(i*1_000_000_000); + case "longMinus": + return String.valueOf(-i*1_000_000_000); + case "char": + return String.valueOf((char) (i % 26 + 65)); + case "double": + case "float": + return String.valueOf(i) + ".0"; + case "doubleMinus": + case "floatMinus": + return "-" + String.valueOf(i) + ".0"; + case "object": + return "C(" + i + ")"; + case "integerNull": + case "objectNull": + case "objectNullToString": + case "intArrayNull": + case "objectArrayNull": + return "null"; + case "String": + return "" + i; + case "StringUTF16": + return "\u0451" + i; + case "StringU1": + return "\u0001" + i; + case "StringU2": + return "\u0002" + i; + default: + throw new IllegalStateException(); + } + } + +} diff --git a/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java b/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java new file mode 100644 index 00000000000..e1a3e8085c6 --- /dev/null +++ b/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Serializable; +import java.lang.invoke.*; +import java.util.concurrent.Callable; + +/** + * @test + * @summary Test input invariants for StringConcatFactory + * + * @compile StringConcatFactoryInvariants.java + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT StringConcatFactoryInvariants + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true StringConcatFactoryInvariants + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT -Djava.lang.invoke.stringConcat.debug=true -Djava.lang.invoke.stringConcat.cache=true StringConcatFactoryInvariants + * +*/ +public class StringConcatFactoryInvariants { + + private static final char TAG_ARG = '\u0001'; + private static final char TAG_CONST = '\u0002'; + + public static void main(String[] args) throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + String methodName = "foo"; + MethodType mt = MethodType.methodType(String.class, String.class, int.class); + String recipe = "" + TAG_ARG + TAG_ARG + TAG_CONST; + String[] constants = new String[]{"bar"}; + + final int LIMIT = 200; + + // Simple factory: check for dynamic arguments overflow + Class[] underThreshold = new Class[LIMIT - 1]; + Class[] threshold = new Class[LIMIT]; + Class[] overThreshold = new Class[LIMIT + 1]; + + StringBuilder sbUnderThreshold = new StringBuilder(); + sbUnderThreshold.append(TAG_CONST); + for (int c = 0; c < LIMIT - 1; c++) { + underThreshold[c] = int.class; + threshold[c] = int.class; + overThreshold[c] = int.class; + sbUnderThreshold.append(TAG_ARG); + } + threshold[LIMIT - 1] = int.class; + overThreshold[LIMIT - 1] = int.class; + overThreshold[LIMIT] = int.class; + + String recipeEmpty = ""; + String recipeUnderThreshold = sbUnderThreshold.toString(); + String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString(); + String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString(); + + MethodType mtEmpty = MethodType.methodType(String.class); + MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold); + MethodType mtThreshold = MethodType.methodType(String.class, threshold); + MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold); + + + // Check the basic functionality is working + { + CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt); + test("foo42", (String) cs.getTarget().invokeExact("foo", 42)); + } + + { + CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants); + test("foo42bar", (String) cs.getTarget().invokeExact("foo", 42)); + } + + // Simple factory, check for nulls: + failNPE("Lookup is null", + () -> StringConcatFactory.makeConcat(null, methodName, mt)); + + failNPE("Method name is null", + () -> StringConcatFactory.makeConcat(lookup, null, mt)); + + failNPE("MethodType is null", + () -> StringConcatFactory.makeConcat(lookup, methodName, null)); + + // Advanced factory, check for nulls: + failNPE("Lookup is null", + () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, constants)); + + failNPE("Method name is null", + () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, constants)); + + failNPE("MethodType is null", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, constants)); + + failNPE("Recipe is null", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, constants)); + + failNPE("Constants vararg is null", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, null)); + + // Simple factory, check for return type + fail("Return type: void", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class))); + + fail("Return type: int", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class))); + + fail("Return type: StringBuilder", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class))); + + ok("Return type: Object", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class))); + + ok("Return type: CharSequence", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class))); + + ok("Return type: Serializable", + () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class))); + + // Advanced factory, check for return types + fail("Return type: void", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, constants)); + + fail("Return type: int", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, constants)); + + fail("Return type: StringBuilder", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, constants)); + + ok("Return type: Object", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, constants)); + + ok("Return type: CharSequence", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, constants)); + + ok("Return type: Serializable", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, constants)); + + // Simple factory: check for dynamic arguments overflow + ok("Dynamic arguments is under limit", + () -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold)); + + ok("Dynamic arguments is at the limit", + () -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold)); + + fail("Dynamic arguments is over the limit", + () -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold)); + + // Advanced factory: check for dynamic arguments overflow + ok("Dynamic arguments is under limit", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants)); + + ok("Dynamic arguments is at the limit", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants)); + + fail("Dynamic arguments is over the limit", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants)); + + // Advanced factory: check for mismatched recipe and Constants + ok("Static arguments and recipe match", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar")); + + fail("Static arguments and recipe mismatch", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz")); + + // Advanced factory: check for mismatched recipe and dynamic arguments + fail("Dynamic arguments and recipe mismatch", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants)); + + ok("Dynamic arguments and recipe match", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants)); + + fail("Dynamic arguments and recipe mismatch", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants)); + + // Test passing array as constant + { + String[] arg = {"boo", "bar"}; + + CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg); + test("42boobar", (String) cs1.getTarget().invokeExact(42)); + } + + // Test passing null constant + ok("Can pass regular constants", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo")); + + failNPE("Cannot pass null constants", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new String[]{null})); + + // Simple factory: test empty arguments + ok("Ok to pass empty arguments", + () -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty)); + + // Advanced factory: test empty arguments + ok("Ok to pass empty arguments", + () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty)); + } + + public static void ok(String msg, Callable runnable) { + try { + runnable.call(); + } catch (Throwable e) { + e.printStackTrace(); + throw new IllegalStateException(msg + ", should have passed", e); + } + } + + public static void fail(String msg, Callable runnable) { + boolean expected = false; + try { + runnable.call(); + } catch (StringConcatException e) { + expected = true; + } catch (Throwable e) { + e.printStackTrace(); + } + + if (!expected) { + throw new IllegalStateException(msg + ", should have failed with StringConcatException"); + } + } + + + public static void failNPE(String msg, Callable runnable) { + boolean expected = false; + try { + runnable.call(); + } catch (NullPointerException e) { + expected = true; + } catch (Throwable e) { + e.printStackTrace(); + } + + if (!expected) { + throw new IllegalStateException(msg + ", should have failed with NullPointerException"); + } + } + + public static void test(String expected, String actual) { + // Fingers crossed: String concat should work. + if (!expected.equals(actual)) { + StringBuilder sb = new StringBuilder(); + sb.append("Expected = "); + sb.append(expected); + sb.append(", actual = "); + sb.append(actual); + throw new IllegalStateException(sb.toString()); + } + } + +} diff --git a/jdk/test/java/lang/String/concat/update-tests.sh b/jdk/test/java/lang/String/concat/update-tests.sh new file mode 100644 index 00000000000..533d6e8f0da --- /dev/null +++ b/jdk/test/java/lang/String/concat/update-tests.sh @@ -0,0 +1,26 @@ +# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +#!/bin/bash + +javac ImplicitStringConcatShapesTestGen.java +java ImplicitStringConcatShapesTestGen > ImplicitStringConcatShapes.java +rm ImplicitStringConcatShapesTestGen.class diff --git a/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java b/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java index 7740d5f46ff..6bde4204615 100644 --- a/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java +++ b/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java @@ -28,7 +28,7 @@ * @compile InvokeDynamicPrintArgs.java * @run main/othervm * indify.Indify - * --verify-specifier-count=3 + * --verify-specifier-count=8 * --expand-properties --classpath ${test.classes} * --java test.java.lang.invoke.InvokeDynamicPrintArgs --check-output * @run main/othervm diff --git a/jdk/test/java/lang/invoke/MethodHandleConstants.java b/jdk/test/java/lang/invoke/MethodHandleConstants.java index 57e041ba7bb..5f64e696ef1 100644 --- a/jdk/test/java/lang/invoke/MethodHandleConstants.java +++ b/jdk/test/java/lang/invoke/MethodHandleConstants.java @@ -28,7 +28,7 @@ * @compile MethodHandleConstants.java * @run main/othervm * indify.Indify - * --verify-specifier-count=0 + * --verify-specifier-count=4 * --expand-properties --classpath ${test.classes} * --java test.java.lang.invoke.MethodHandleConstants --check-output * @run main/othervm diff --git a/jdk/test/java/net/SocketPermission/SocketPermissionTest.java b/jdk/test/java/net/SocketPermission/SocketPermissionTest.java index 5cc25ab0c02..1b68415d028 100644 --- a/jdk/test/java/net/SocketPermission/SocketPermissionTest.java +++ b/jdk/test/java/net/SocketPermission/SocketPermissionTest.java @@ -25,8 +25,7 @@ * @test * @bug 8047031 * @summary SocketPermission tests for legacy socket types - * @library ../../../lib/testlibrary - * @run testng/othervm/policy=policy SocketPermissionTest + * @run testng/othervm SocketPermissionTest * @key intermittent */ import java.io.IOException; @@ -39,186 +38,279 @@ import java.net.Socket; import java.net.SocketPermission; import java.security.AccessControlContext; import java.security.AccessController; +import java.security.CodeSource; import java.security.Permission; +import java.security.PermissionCollection; import java.security.Permissions; -import java.security.PrivilegedAction; +import java.security.Policy; +import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; -import java.util.Arrays; -import java.util.function.Function; -import java.util.function.IntConsumer; -import static jdk.testlibrary.Utils.getFreePort; + import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import static org.testng.Assert.*; + +import static java.nio.charset.StandardCharsets.UTF_8; public class SocketPermissionTest { - private int freePort = -1; - - //positive tests - @Test(dataProvider = "positiveProvider") - public void testPositive(Function genAcc, IntConsumer func) { - String addr = "localhost:" + freePort; - AccessControlContext acc = genAcc.apply(addr); - AccessController.doPrivileged((PrivilegedAction) () -> { - func.accept(freePort); - return null; - }, acc); - } - - //negative tests - @Test(dataProvider = "negativeProvider", expectedExceptions = SecurityException.class) - public void testNegative(AccessControlContext acc, IntConsumer func) { - AccessController.doPrivileged((PrivilegedAction) () -> { - func.accept(freePort); - return null; - }, acc); - } @BeforeMethod - public void setFreePort() throws Exception { - freePort = getFreePort(); + public void setupSecurityManager() throws Exception { + // All permissions, a specific ACC will be used to when testing + // with a reduced permission set. + Policy.setPolicy(new Policy() { + final PermissionCollection perms = new Permissions(); + { perms.add(new java.security.AllPermission()); } + public PermissionCollection getPermissions(ProtectionDomain domain) { + return perms; + } + public PermissionCollection getPermissions(CodeSource codesource) { + return perms; + } + public boolean implies(ProtectionDomain domain, Permission perm) { + return perms.implies(perm); + } + } ); + System.setSecurityManager(new SecurityManager()); } - @DataProvider - public Object[][] positiveProvider() { - //test for SocketPermission "host:port","connect,resolve"; - Function generateAcc1 = (addr) -> getAccessControlContext( - new SocketPermission(addr, "listen, connect,resolve")); - IntConsumer func1 = (i) -> connectSocketTest(i); - IntConsumer func2 = (i) -> connectDatagramSocketTest(i); + static final AccessControlContext RESTRICTED_ACC = getAccessControlContext(); - //test for SocketPermission "localhost:1024-","accept"; - Function generateAcc2 = (addr) -> getAccessControlContext( - new SocketPermission(addr, "listen,connect,resolve"), - new SocketPermission("localhost:1024-", "accept")); - IntConsumer func3 = (i) -> acceptServerSocketTest(i); + @Test + public void connectSocketTest() throws Exception { + try (ServerSocket ss = new ServerSocket(0)) { + int port = ss.getLocalPort(); - //test for SocketPermission "229.227.226.221", "connect,accept" - Function generateAcc3 = (addr) -> getAccessControlContext( - new SocketPermission(addr, "listen,resolve"), - new SocketPermission("229.227.226.221", "connect,accept")); - IntConsumer func4 = (i) -> sendDatagramPacketTest(i); - IntConsumer func5 = (i) -> joinGroupMulticastTest(i); - - //test for SocketPermission "host:port", "listen" - Function generateAcc4 = (addr) -> getAccessControlContext( - new SocketPermission(addr, "listen")); - IntConsumer func6 = (i) -> listenDatagramSocketTest(i); - IntConsumer func7 = (i) -> listenMulticastSocketTest(i); - IntConsumer func8 = (i) -> listenServerSocketTest(i); - - return new Object[][]{ - {generateAcc1, func1}, - {generateAcc1, func2}, - {generateAcc2, func3}, - {generateAcc3, func4}, - {generateAcc3, func5}, - {generateAcc4, func6}, - {generateAcc4, func7}, - {generateAcc4, func8} - }; - } - - @DataProvider - public Object[][] negativeProvider() { - IntConsumer[] funcs = {i -> connectSocketTest(i), - i -> connectDatagramSocketTest(i), i -> acceptServerSocketTest(i), - i -> sendDatagramPacketTest(i), i -> joinGroupMulticastTest(i), - i -> listenDatagramSocketTest(i), i -> listenMulticastSocketTest(i), - i -> listenServerSocketTest(i)}; - return Arrays.stream(funcs).map(f -> { - //Construct an AccessControlContext without SocketPermission + String addr = "localhost:" + port; AccessControlContext acc = getAccessControlContext( - new java.io.FilePermission("<>", "read,write,execute,delete"), - new java.net.NetPermission("*"), - new java.util.PropertyPermission("*", "read,write"), - new java.lang.reflect.ReflectPermission("*"), - new java.lang.RuntimePermission("*"), - new java.security.SecurityPermission("*"), - new java.io.SerializablePermission("*")); - return new Object[]{acc, f}; - }).toArray(Object[][]::new); - } + new SocketPermission(addr, "listen,connect,resolve")); - public void connectSocketTest(int port) { - try (ServerSocket server = new ServerSocket(port); - Socket client = new Socket(InetAddress.getLocalHost(), port);) { - } catch (IOException ex) { - throw new RuntimeException(ex); + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (Socket client = new Socket(InetAddress.getLocalHost(), port)) { + } + return null; + }, acc); + + //Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + Socket client = new Socket(InetAddress.getLocalHost(), port); + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } } - public void connectDatagramSocketTest(int port) { - String msg = "Hello"; - try { - InetAddress me = InetAddress.getLocalHost(); - try (DatagramSocket ds = new DatagramSocket(port, me)) { - DatagramPacket dp = new DatagramPacket(msg.getBytes(), - msg.length(), me, port); + @Test + public void connectDatagramSocketTest() throws Exception { + byte[] msg = "Hello".getBytes(UTF_8); + InetAddress lh = InetAddress.getLocalHost(); + + try (DatagramSocket ds = new DatagramSocket(0)) { + int port = ds.getLocalPort(); + + String addr = lh.getHostAddress() + ":" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "connect,resolve")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + DatagramPacket dp = new DatagramPacket(msg, msg.length, lh, port); ds.send(dp); - } - } catch (IOException ex) { - throw new RuntimeException(ex); + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + DatagramPacket dp = new DatagramPacket(msg, msg.length, lh, port); + ds.send(dp); + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } } - public void acceptServerSocketTest(int port) { - try { - InetAddress me = InetAddress.getLocalHost(); - try (ServerSocket server = new ServerSocket(port)) { - Socket client = new Socket(me, port); - server.accept(); - } - } catch (IOException ex) { - throw new RuntimeException(ex); + @Test + public void acceptServerSocketTest() throws Exception { + try (ServerSocket ss = new ServerSocket(0)) { + int port = ss.getLocalPort(); + + String addr = "localhost:" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen,connect,resolve"), + new SocketPermission("localhost:1024-", "accept")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + InetAddress me = InetAddress.getLocalHost(); + try (Socket client = new Socket(me, port)) { + ss.accept(); + } + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + InetAddress me = InetAddress.getLocalHost(); + try (Socket client = new Socket(me, port)) { + ss.accept(); + } + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } } - public static void sendDatagramPacketTest(int port) { - String msg = "Hello"; - try { - InetAddress group = InetAddress.getByName("229.227.226.221"); - try (DatagramSocket s = new DatagramSocket(port)) { - DatagramPacket hi = new DatagramPacket(msg.getBytes(), - msg.length(), group, port); - s.send(hi); - } - } catch (IOException ex) { - throw new RuntimeException(ex); + @Test + public void sendDatagramPacketTest() throws Exception { + byte[] msg = "Hello".getBytes(UTF_8); + InetAddress group = InetAddress.getByName("229.227.226.221"); + + try (DatagramSocket ds = new DatagramSocket(0)) { + int port = ds.getLocalPort(); + + String addr = "localhost:" + port; + //test for SocketPermission "229.227.226.221", "connect,accept" + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen,resolve"), + new SocketPermission("229.227.226.221", "connect,accept")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + DatagramPacket hi = new DatagramPacket(msg, msg.length, group, port); + ds.send(hi); + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + DatagramPacket hi = new DatagramPacket(msg, msg.length, group, port); + ds.send(hi); + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } } - public void joinGroupMulticastTest(int port) { - try { - InetAddress group = InetAddress.getByName("229.227.226.221"); - try (MulticastSocket s = new MulticastSocket(port)) { + @Test + public void joinGroupMulticastTest() throws Exception { + InetAddress group = InetAddress.getByName("229.227.226.221"); + try (MulticastSocket s = new MulticastSocket(0)) { + int port = s.getLocalPort(); + + String addr = "localhost:" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen,resolve"), + new SocketPermission("229.227.226.221", "connect,accept")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { s.joinGroup(group); s.leaveGroup(group); - } - } catch (IOException ex) { - throw new RuntimeException(ex); + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + s.joinGroup(group); + s.leaveGroup(group); + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } + } - public void listenDatagramSocketTest(int port) { - try (DatagramSocket ds = new DatagramSocket(port)) { - } catch (IOException ex) { - throw new RuntimeException(ex); - } + @Test + public void listenDatagramSocketTest() throws Exception { + // the hardcoded port number doesn't really matter since we expect the + // security permission to be checked before the underlying operation. + int port = 8899; + String addr = "localhost:" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (DatagramSocket ds = new DatagramSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (DatagramSocket ds = new DatagramSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } - public void listenMulticastSocketTest(int port) { - try (MulticastSocket ms = new MulticastSocket(port)) { - } catch (IOException ex) { - throw new RuntimeException(ex); - } + @Test + public void listenMulticastSocketTest() throws Exception { + // the hardcoded port number doesn't really matter since we expect the + // security permission to be checked before the underlying operation. + int port = 8899; + String addr = "localhost:" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (MulticastSocket ms = new MulticastSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (MulticastSocket ms = new MulticastSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } } - public void listenServerSocketTest(int port) { - try (ServerSocket ms = new ServerSocket(port)) { - } catch (IOException ex) { - throw new RuntimeException(ex); - } + @Test + public void listenServerSocketTest() throws Exception { + // the hardcoded port number doesn't really matter since we expect the + // security permission to be checked before the underlying operation. + int port = 8899; + String addr = "localhost:" + port; + AccessControlContext acc = getAccessControlContext( + new SocketPermission(addr, "listen")); + + // Positive + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (ServerSocket ss = new ServerSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + return null; + }, acc); + + // Negative + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try (ServerSocket ss = new ServerSocket(port)) { } + catch (IOException intermittentlyExpected) { /* ignore */ } + fail("Expected SecurityException"); + return null; + }, RESTRICTED_ACC); + } catch (SecurityException expected) { } + } private static AccessControlContext getAccessControlContext(Permission... ps) { @@ -234,4 +326,15 @@ public class SocketPermissionTest { return new AccessControlContext(new ProtectionDomain[]{pd}); } + // Standalone entry point for running with, possibly older, JDKs. + public static void main(String[] args) throws Throwable { + SocketPermissionTest test = new SocketPermissionTest(); + test.setupSecurityManager(); + for (java.lang.reflect.Method m : SocketPermissionTest.class.getDeclaredMethods()) { + if (m.getAnnotation(Test.class) != null) { + System.out.println("Invoking " + m.getName()); + m.invoke(test); + } + } + } } diff --git a/jdk/test/java/net/SocketPermission/policy b/jdk/test/java/net/SocketPermission/policy deleted file mode 100644 index 2a71c848da8..00000000000 --- a/jdk/test/java/net/SocketPermission/policy +++ /dev/null @@ -1,3 +0,0 @@ -grant { - permission java.security.AllPermission; -}; diff --git a/jdk/test/java/net/URI/URItoURLTest.java b/jdk/test/java/net/URI/URItoURLTest.java index e1a5577bb74..05fadebf8f9 100644 --- a/jdk/test/java/net/URI/URItoURLTest.java +++ b/jdk/test/java/net/URI/URItoURLTest.java @@ -23,13 +23,16 @@ /** * @test - * @bug 4768755 4677045 - * @summary URL.equal(URL) is inconsistant for opaque URI.toURL() - * and new URL(URI.toString) + * @bug 4768755 4677045 8147462 + * @summary URL.equal(URL) is inconsistent for opaque URI.toURL() + * and new URL(URI.toString) * URI.toURL() does not always work as specified + * Ensure URIs representing invalid/malformed URLs throw similar + * exception with new URL(URI.toString()) and URI.toURL() */ import java.net.*; +import java.util.Objects; public class URItoURLTest { @@ -39,19 +42,43 @@ public class URItoURLTest { URL classUrl = testClass.getClass(). getResource("/java/lang/Object.class"); - String[] uris = { "mailto:xyz@abc.de", + String[] uris = { + "mailto:xyz@abc.de", "file:xyz#ab", "http:abc/xyz/pqr", + "http:abc/xyz/pqr?id=x%0a&ca=true", "file:/C:/v700/dev/unitTesting/tests/apiUtil/uri", "http:///p", + "file:/C:/v700/dev/unitTesting/tests/apiUtil/uri", + "file:/C:/v700/dev%20src/unitTesting/tests/apiUtil/uri", + "file:/C:/v700/dev%20src/./unitTesting/./tests/apiUtil/uri", + "http://localhost:80/abc/./xyz/../pqr?id=x%0a&ca=true", + "file:./test/./x", + "file:./././%20#i=3", + "file:?hmm", + "file:.#hmm", classUrl.toExternalForm(), }; + // Strings that represent valid URIs but invalid URLs that should throw + // MalformedURLException both when calling toURL and new URL(String) + String[] malformedUrls = { + "test:/test", + "fiel:test", + }; + + // Non-absolute URIs should throw IAE when calling toURL but will throw + // MalformedURLException when calling new URL + String[] illegalUris = { + "./test", + "/test", + }; + boolean isTestFailed = false; boolean isURLFailed = false; - for (int i = 0; i < uris.length; i++) { - URI uri = URI.create(uris[i]); + for (String uriString : uris) { + URI uri = URI.create(uriString); URL url1 = new URL(uri.toString()); URL url2 = uri.toURL(); @@ -107,6 +134,42 @@ public class URItoURLTest { System.out.println(); isURLFailed = false; } + for (String malformedUrl : malformedUrls) { + Exception toURLEx = null; + Exception newURLEx = null; + try { + new URI(malformedUrl).toURL(); + } catch (Exception e) { + // expected + toURLEx = e; + } + try { + new URL(new URI(malformedUrl).toString()); + } catch (Exception e) { + // expected + newURLEx = e; + } + if (!(toURLEx instanceof MalformedURLException) || + !(newURLEx instanceof MalformedURLException) || + !toURLEx.getMessage().equals(newURLEx.getMessage())) { + isTestFailed = true; + System.out.println("Expected the same MalformedURLException: " + + newURLEx + " vs " + toURLEx); + } + } + for (String illegalUri : illegalUris) { + try { + new URI(illegalUri).toURL(); + } catch (IllegalArgumentException e) { + // pass + } + + try { + new URL(illegalUri); + } catch (MalformedURLException e) { + // pass + } + } if (isTestFailed) { throw new Exception("URI.toURL() test failed"); } diff --git a/jdk/test/java/net/URL/LocaleDependentURLTest.java b/jdk/test/java/net/URL/LocaleDependentURLTest.java new file mode 100644 index 00000000000..caa967698b2 --- /dev/null +++ b/jdk/test/java/net/URL/LocaleDependentURLTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8147962 + * @summary Verify URLs with upper-case protocols parse correctly in any + * locale + * @run main/othervm LocaleDependentURLTest + */ +import java.net.*; +import java.util.Locale; + +public class LocaleDependentURLTest { + + public static void main(String args[]) throws MalformedURLException { + for (Locale locale : Locale.getAvailableLocales()) { + Locale.setDefault(locale); + new URL("FILE:///TMP/X"); + } + } +} diff --git a/jdk/test/java/nio/file/WatchService/LotsOfCloses.java b/jdk/test/java/nio/file/WatchService/LotsOfCloses.java new file mode 100644 index 00000000000..44ec26180b3 --- /dev/null +++ b/jdk/test/java/nio/file/WatchService/LotsOfCloses.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8148192 + * @summary Invoking close asynchronously can cause register to fail with + * an IOException rather than a ClosedWatchServiceException + * @run main LotsOfCloses + */ + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchService; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class LotsOfCloses { + + private static final Random RAND = new Random(); + + public static void main(String[] args) throws Exception { + Path dir = Files.createTempDirectory("tmp"); + ExecutorService pool = Executors.newCachedThreadPool(); + try { + + // run the test repeatedly + long start = System.currentTimeMillis(); + while ((System.currentTimeMillis() - start) < 5000) { + test(dir, pool); + } + + } finally { + pool.shutdown(); + } + + } + + /** + * Create a WatchService to watch for changes in the given directory + * and then attempt to close the WatchService and change a registration + * at the same time. + */ + static void test(Path dir, ExecutorService pool) throws Exception { + WatchService watcher = FileSystems.getDefault().newWatchService(); + + // initial registration + dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); + + // submit tasks to close the WatchService and update the registration + Future closeResult; + Future registerResult; + + if (RAND.nextBoolean()) { + closeResult = pool.submit(newCloserTask(watcher)); + registerResult = pool.submit(newRegisterTask(watcher, dir)); + } else { + registerResult = pool.submit(newRegisterTask(watcher, dir)); + closeResult = pool.submit(newCloserTask(watcher)); + } + + closeResult.get(); + registerResult.get(); + + } + + /** + * Returns a task that closes the given WatchService. + */ + static Callable newCloserTask(WatchService watcher) { + return () -> { + try { + watcher.close(); + return null; + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }; + } + + /** + * Returns a task that updates the registration of a directory with + * a WatchService. + */ + static Callable newRegisterTask(WatchService watcher, Path dir) { + return () -> { + try { + dir.register(watcher, StandardWatchEventKinds.ENTRY_DELETE); + return true; + } catch (ClosedWatchServiceException e) { + return false; + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }; + } +} diff --git a/jdk/test/java/util/ArrayList/ArrayManagement.java b/jdk/test/java/util/ArrayList/ArrayManagement.java index c3d690871c4..81adc716013 100644 --- a/jdk/test/java/util/ArrayList/ArrayManagement.java +++ b/jdk/test/java/util/ArrayList/ArrayManagement.java @@ -83,6 +83,7 @@ public class ArrayManagement { int oldCapacity = capacity(list); int oldModCount = modCount(list); list.ensureCapacity(capacity); + assertTrue(capacity(list) >= capacity || capacity(list) == 0); assertEquals(modCount(list), (capacity(list) == oldCapacity) ? oldModCount diff --git a/jdk/test/java/util/Map/Defaults.java b/jdk/test/java/util/Map/Defaults.java index 37c2fad21dc..7b2956f8846 100644 --- a/jdk/test/java/util/Map/Defaults.java +++ b/jdk/test/java/util/Map/Defaults.java @@ -48,11 +48,14 @@ import java.util.WeakHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; +import java.util.function.Function; import java.util.function.Supplier; import org.testng.annotations.Test; import org.testng.annotations.DataProvider; +import static java.util.Objects.requireNonNull; import static org.testng.Assert.fail; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -533,6 +536,191 @@ public class Defaults { "Should throw NPE"); } + /** A function that flipflops between running two other functions. */ + static BiFunction twoStep(AtomicBoolean b, + BiFunction first, + BiFunction second) { + return (t, u) -> { + boolean bb = b.get(); + try { + return (b.get() ? first : second).apply(t, u); + } finally { + b.set(!bb); + }}; + } + + /** + * Simulates races by modifying the map within the mapping function. + */ + @Test + public void testConcurrentMap_computeIfAbsent_racy() { + final ConcurrentMap map = new ImplementsConcurrentMap<>(); + final Long two = 2L; + Function f, g; + + // race not detected if function returns null + f = (k) -> { map.put(two, 42L); return null; }; + assertNull(map.computeIfAbsent(two, f)); + assertEquals(42L, (long)map.get(two)); + + map.clear(); + f = (k) -> { map.put(two, 42L); return 86L; }; + assertEquals(42L, (long)map.computeIfAbsent(two, f)); + assertEquals(42L, (long)map.get(two)); + + // mapping function ignored if value already exists + map.put(two, 99L); + assertEquals(99L, (long)map.computeIfAbsent(two, f)); + assertEquals(99L, (long)map.get(two)); + } + + /** + * Simulates races by modifying the map within the remapping function. + */ + @Test + public void testConcurrentMap_computeIfPresent_racy() { + final AtomicBoolean b = new AtomicBoolean(true); + final ConcurrentMap map = new ImplementsConcurrentMap<>(); + final Long two = 2L; + BiFunction f, g; + + for (Long val : new Long[] { null, 86L }) { + map.clear(); + + // Function not invoked if no mapping exists + f = (k, v) -> { map.put(two, 42L); return val; }; + assertNull(map.computeIfPresent(two, f)); + assertNull(map.get(two)); + + map.put(two, 42L); + f = (k, v) -> { map.put(two, 86L); return val; }; + g = (k, v) -> { + assertSame(two, k); + assertEquals(86L, (long)v); + return null; + }; + assertNull(map.computeIfPresent(two, twoStep(b, f, g))); + assertFalse(map.containsKey(two)); + assertTrue(b.get()); + + map.put(two, 42L); + f = (k, v) -> { map.put(two, 86L); return val; }; + g = (k, v) -> { + assertSame(two, k); + assertEquals(86L, (long)v); + return 99L; + }; + assertEquals(99L, (long)map.computeIfPresent(two, twoStep(b, f, g))); + assertTrue(map.containsKey(two)); + assertTrue(b.get()); + } + } + + @Test + public void testConcurrentMap_compute_simple() { + final ConcurrentMap map = new ImplementsConcurrentMap<>(); + BiFunction fun = (k, v) -> ((v == null) ? 0L : k + v); + assertEquals(Long.valueOf(0L), map.compute(3L, fun)); + assertEquals(Long.valueOf(3L), map.compute(3L, fun)); + assertEquals(Long.valueOf(6L), map.compute(3L, fun)); + assertNull(map.compute(3L, (k, v) -> null)); + assertTrue(map.isEmpty()); + + assertEquals(Long.valueOf(0L), map.compute(new Long(3L), fun)); + assertEquals(Long.valueOf(3L), map.compute(new Long(3L), fun)); + assertEquals(Long.valueOf(6L), map.compute(new Long(3L), fun)); + assertNull(map.compute(3L, (k, v) -> null)); + assertTrue(map.isEmpty()); + } + + /** + * Simulates races by modifying the map within the remapping function. + */ + @Test + public void testConcurrentMap_compute_racy() { + final AtomicBoolean b = new AtomicBoolean(true); + final ConcurrentMap map = new ImplementsConcurrentMap<>(); + final Long two = 2L; + BiFunction f, g; + + // null -> null is a no-op; race not detected + f = (k, v) -> { map.put(two, 42L); return null; }; + assertNull(map.compute(two, f)); + assertEquals(42L, (long)map.get(two)); + + for (Long val : new Long[] { null, 86L }) { + map.clear(); + + f = (k, v) -> { map.put(two, 42L); return 86L; }; + g = (k, v) -> { + assertSame(two, k); + assertEquals(42L, (long)v); + return k + v; + }; + assertEquals(44L, (long)map.compute(two, twoStep(b, f, g))); + assertEquals(44L, (long)map.get(two)); + assertTrue(b.get()); + + f = (k, v) -> { map.remove(two); return val; }; + g = (k, v) -> { + assertSame(two, k); + assertNull(v); + return 44L; + }; + assertEquals(44L, (long)map.compute(two, twoStep(b, f, g))); + assertEquals(44L, (long)map.get(two)); + assertTrue(map.containsKey(two)); + assertTrue(b.get()); + + f = (k, v) -> { map.remove(two); return val; }; + g = (k, v) -> { + assertSame(two, k); + assertNull(v); + return null; + }; + assertNull(map.compute(two, twoStep(b, f, g))); + assertNull(map.get(two)); + assertFalse(map.containsKey(two)); + assertTrue(b.get()); + } + } + + /** + * Simulates races by modifying the map within the remapping function. + */ + @Test + public void testConcurrentMap_merge_racy() { + final AtomicBoolean b = new AtomicBoolean(true); + final ConcurrentMap map = new ImplementsConcurrentMap<>(); + final Long two = 2L; + BiFunction f, g; + + for (Long val : new Long[] { null, 86L }) { + map.clear(); + + f = (v, w) -> { throw new AssertionError(); }; + assertEquals(99L, (long)map.merge(two, 99L, f)); + assertEquals(99L, (long)map.get(two)); + + f = (v, w) -> { map.put(two, 42L); return val; }; + g = (v, w) -> { + assertEquals(42L, (long)v); + assertEquals(3L, (long)w); + return v + w; + }; + assertEquals(45L, (long)map.merge(two, 3L, twoStep(b, f, g))); + assertEquals(45L, (long)map.get(two)); + assertTrue(b.get()); + + f = (v, w) -> { map.remove(two); return val; }; + g = (k, v) -> { throw new AssertionError(); }; + assertEquals(55L, (long)map.merge(two, 55L, twoStep(b, f, g))); + assertEquals(55L, (long)map.get(two)); + assertTrue(map.containsKey(two)); + assertFalse(b.get()); b.set(true); + } + } + public enum IntegerEnum { e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, @@ -838,13 +1026,13 @@ public class Defaults { protected ExtendsAbstractMap(M map) { this.map = map; } - public Set> entrySet() { + @Override public Set> entrySet() { return new AbstractSet>() { - public int size() { + @Override public int size() { return map.size(); } - public Iterator> iterator() { + @Override public Iterator> iterator() { final Iterator> source = map.entrySet().iterator(); return new Iterator>() { public boolean hasNext() { return source.hasNext(); } @@ -853,20 +1041,20 @@ public class Defaults { }; } - public boolean add(Map.Entry e) { + @Override public boolean add(Map.Entry e) { return map.entrySet().add(e); } }; } - public V put(K key, V value) { + @Override public V put(K key, V value) { return map.put(key, value); } } /** * A simple mutable concurrent map implementation that provides only default - * implementations of all methods. ie. none of the ConcurrentMap interface + * implementations of all methods, i.e. none of the ConcurrentMap interface * default methods have overridden implementations. * * @param Type of keys @@ -875,14 +1063,26 @@ public class Defaults { public static class ImplementsConcurrentMap extends ExtendsAbstractMap, K, V> implements ConcurrentMap { public ImplementsConcurrentMap() { super(new ConcurrentHashMap()); } - // ConcurrentMap reabstracts these methods + // ConcurrentMap reabstracts these methods. + // + // Unlike ConcurrentHashMap, we have zero tolerance for null values. - public V replace(K k, V v) { return map.replace(k, v); }; + @Override public V replace(K k, V v) { + return map.replace(requireNonNull(k), requireNonNull(v)); + } - public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); }; + @Override public boolean replace(K k, V v, V vv) { + return map.replace(requireNonNull(k), + requireNonNull(v), + requireNonNull(vv)); + } - public boolean remove(Object k, Object v) { return map.remove(k, v); } + @Override public boolean remove(Object k, Object v) { + return map.remove(requireNonNull(k), requireNonNull(v)); + } - public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); } + @Override public V putIfAbsent(K k, V v) { + return map.putIfAbsent(requireNonNull(k), requireNonNull(v)); + } } } diff --git a/jdk/test/java/util/Vector/ArrayManagement.java b/jdk/test/java/util/Vector/ArrayManagement.java new file mode 100644 index 00000000000..ce4e695487c --- /dev/null +++ b/jdk/test/java/util/Vector/ArrayManagement.java @@ -0,0 +1,218 @@ +/* + * Copyright 2016 Google, Inc. 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 8148174 + * @summary brittle white box test of internal array management + * @run testng ArrayManagement + */ + +import java.lang.reflect.Field; +import java.util.AbstractList; +import java.util.Vector; +import java.util.Collections; +import java.util.List; +import java.util.SplittableRandom; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class ArrayManagement { + + /** + * A Vector that exposes all protected elements, and checks class + * invariants. + */ + static class PublicVector extends Vector { + public PublicVector() { super(); } + public PublicVector(int capacity) { super(capacity); } + public PublicVector(int capacity, int capacityIncrement) { + super(capacity, capacityIncrement); + } + public Object[] elementData() { return elementData; } + public int modCount() { return modCount; } + public int capacityIncrement() { return capacityIncrement; } + public int capacity() { return elementData.length; } + + public void ensureCapacity(int minCapacity) { + int oldCapacity = capacity(); + int oldModCount = modCount(); + super.ensureCapacity(minCapacity); + assertTrue(capacity() >= minCapacity); + if (minCapacity <= oldCapacity) + assertEquals(capacity(), oldCapacity); + if (minCapacity > 0) + assertEquals(modCount(), oldModCount + 1); + } + } + + static final int DEFAULT_CAPACITY = 10; + static final SplittableRandom rnd = new SplittableRandom(); + + static int newCapacity(int oldCapacity) { + return 2 * oldCapacity; + } + + static List singletonList() { + return Collections.singletonList(Boolean.TRUE); + } + + /** Opportunistically randomly test various add operations. */ + static void addOneElement(PublicVector list) { + int size = list.size(); + int modCount = list.modCount(); + switch (rnd.nextInt(4)) { + case 0: assertTrue(list.add(Boolean.TRUE)); break; + case 1: list.add(size, Boolean.TRUE); break; + case 2: assertTrue(list.addAll(singletonList())); break; + case 3: assertTrue(list.addAll(size, singletonList())); break; + default: throw new AssertionError(); + } + assertEquals(list.modCount(), modCount + 1); + assertEquals(list.size(), size + 1); + } + + @Test public void defaultCapacity() { + PublicVector list = new PublicVector<>(); + assertEquals(new PublicVector().capacity(), DEFAULT_CAPACITY); + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + addOneElement(list); + assertEquals(list.capacity(), DEFAULT_CAPACITY); + } + addOneElement(list); + assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + } + + @Test public void defaultCapacityEnsureCapacity() { + PublicVector list = new PublicVector<>(); + for (int i = 0; i <= DEFAULT_CAPACITY; i++) { + list.ensureCapacity(i); // no-op! + assertEquals(list.capacity(), DEFAULT_CAPACITY); + } + for (int i = 0; i < DEFAULT_CAPACITY; i++) { + addOneElement(list); + assertEquals(list.capacity(), DEFAULT_CAPACITY); + } + addOneElement(list); + assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + { + int capacity = list.capacity(); + list.ensureCapacity(capacity + 1); + assertEquals(list.capacity(), newCapacity(capacity)); + } + { + int capacity = list.capacity(); + list.ensureCapacity(3 * capacity); + assertEquals(list.capacity(), 3 * capacity); + } + } + + @Test public void ensureCapacityBeyondDefaultCapacity() { + PublicVector list = new PublicVector<>(); + list.ensureCapacity(DEFAULT_CAPACITY + 1); + assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + } + + @Test public void explicitZeroCapacity() { + PublicVector list = new PublicVector<>(0); + assertEquals(list.capacity(), 0); + addOneElement(list); + assertEquals(list.capacity(), 1); + addOneElement(list); + assertEquals(list.capacity(), 2); + addOneElement(list); + assertEquals(list.capacity(), 4); + addOneElement(list); + assertEquals(list.capacity(), 4); + addOneElement(list); + assertEquals(list.capacity(), 8); + addOneElement(list); + assertEquals(list.capacity(), 8); + addOneElement(list); + assertEquals(list.capacity(), 8); + list.clear(); + assertEquals(list.capacity(), 8); + } + + @Test public void explicitZeroCapacityWithCapacityIncrement() { + PublicVector list = new PublicVector<>(0, 2); + assertEquals(list.capacity(), 0); + addOneElement(list); + assertEquals(list.capacity(), 2); + addOneElement(list); + assertEquals(list.capacity(), 2); + addOneElement(list); + assertEquals(list.capacity(), 4); + addOneElement(list); + assertEquals(list.capacity(), 4); + addOneElement(list); + assertEquals(list.capacity(), 6); + addOneElement(list); + assertEquals(list.capacity(), 6); + addOneElement(list); + assertEquals(list.capacity(), 8); + list.clear(); + assertEquals(list.capacity(), 8); + } + + @Test public void explicitLargeCapacity() { + int n = DEFAULT_CAPACITY * 3; + PublicVector list = new PublicVector<>(n); + assertEquals(list.capacity(), n); + list.ensureCapacity(0); + list.ensureCapacity(n); + for (int i = 0; i < n; i++) addOneElement(list); + assertEquals(list.capacity(), n); + + addOneElement(list); + assertEquals(list.capacity(), newCapacity(n)); + } + + @Test public void explicitLargeCapacityWithCapacityIncrement() { + int n = DEFAULT_CAPACITY * 3; + PublicVector list = new PublicVector<>(n, 2); + assertEquals(list.capacity(), n); + list.ensureCapacity(0); + list.ensureCapacity(n); + for (int i = 0; i < n; i++) addOneElement(list); + assertEquals(list.capacity(), n); + + addOneElement(list); + assertEquals(list.capacity(), n + 2); + } + + @Test public void emptyArraysAreNotShared() { + assertNotSame(new PublicVector(0).elementData(), + new PublicVector(0).elementData()); + } + + @Test public void negativeCapacity() { + for (int capacity : new int[] { -1, Integer.MIN_VALUE }) { + try { + new Vector(capacity); + fail("should throw"); + } catch (IllegalArgumentException success) {} + } + } +} diff --git a/jdk/test/java/util/Vector/Bug8148174.java b/jdk/test/java/util/Vector/Bug8148174.java new file mode 100644 index 00000000000..a987cd39155 --- /dev/null +++ b/jdk/test/java/util/Vector/Bug8148174.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Google, Inc. 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 8148174 + * @summary repro for: NegativeArraySizeException in Vector.grow(int) + * @run main/othervm -Xmx17g Bug8148174 + * @ignore This test has huge memory requirements + */ + +public class Bug8148174 { + public static void main(String[] args) { + int size = Integer.MAX_VALUE - 2; + java.util.Vector huge = new java.util.Vector<>(size); + for (int i = 0; i < size; i++) + huge.add(null); + try { + huge.addAll(huge); + throw new Error("expected OutOfMemoryError not thrown"); + } catch (OutOfMemoryError success) {} + } +} diff --git a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java index c236bd231cc..8b0a92d2613 100644 --- a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java +++ b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java @@ -36,17 +36,17 @@ * @author Doug Lea * @bug 8004138 * @summary Check if ForkJoinPool table leaks thrown exceptions. - * @run main/othervm -Xmx2200k FJExceptionTableLeak - * @key intermittent + * @run main/othervm -Xmx8m -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 FJExceptionTableLeak */ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; public class FJExceptionTableLeak { - // This test was observed to fail with jdk7 -Xmx2200k, + // This test was observed to fail with pre-bug-fix jdk7 -Xmx8m, // using STEPS = 220 and TASKS_PER_STEP = 100 - static final int STEPS = 500; + static final int PRE_BUG_FIX_FAILURE_STEPS = 220; + static final int STEPS = 10 * PRE_BUG_FIX_FAILURE_STEPS; static final int TASKS_PER_STEP = 100; static class FailingTaskException extends RuntimeException {} diff --git a/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java b/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java new file mode 100644 index 00000000000..9e140da9f72 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java @@ -0,0 +1,635 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AbstractExecutorServiceTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AbstractExecutorServiceTest.class); + } + + /** + * A no-frills implementation of AbstractExecutorService, designed + * to test the submit methods only. + */ + static class DirectExecutorService extends AbstractExecutorService { + public void execute(Runnable r) { r.run(); } + public void shutdown() { shutdown = true; } + public List shutdownNow() { + shutdown = true; + return Collections.EMPTY_LIST; + } + public boolean isShutdown() { return shutdown; } + public boolean isTerminated() { return isShutdown(); } + public boolean awaitTermination(long timeout, TimeUnit unit) { + return isShutdown(); + } + private volatile boolean shutdown = false; + } + + /** + * execute(runnable) runs it to completion + */ + public void testExecuteRunnable() throws Exception { + ExecutorService e = new DirectExecutorService(); + final AtomicBoolean done = new AtomicBoolean(false); + Future future = e.submit(new CheckedRunnable() { + public void realRun() { + done.set(true); + }}); + assertNull(future.get()); + assertNull(future.get(0, MILLISECONDS)); + assertTrue(done.get()); + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + } + + /** + * Completed submit(callable) returns result + */ + public void testSubmitCallable() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(new StringTask()); + String result = future.get(); + assertSame(TEST_STRING, result); + } + + /** + * Completed submit(runnable) returns successfully + */ + public void testSubmitRunnable() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(new NoOpRunnable()); + future.get(); + assertTrue(future.isDone()); + } + + /** + * Completed submit(runnable, result) returns result + */ + public void testSubmitRunnable2() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + String result = future.get(); + assertSame(TEST_STRING, result); + } + + /** + * A submitted privileged action runs to completion + */ + public void testSubmitPrivilegedAction() throws Exception { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(Executors.callable(new PrivilegedAction() { + public Object run() { + return TEST_STRING; + }})); + + assertSame(TEST_STRING, future.get()); + }}; + + runWithPermissions(r, + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader"), + new RuntimePermission("modifyThread")); + } + + /** + * A submitted privileged exception action runs to completion + */ + public void testSubmitPrivilegedExceptionAction() throws Exception { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() { + public Object run() { + return TEST_STRING; + }})); + + assertSame(TEST_STRING, future.get()); + }}; + + runWithPermissions(r); + } + + /** + * A submitted failed privileged exception action reports exception + */ + public void testSubmitFailedPrivilegedExceptionAction() throws Exception { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new DirectExecutorService(); + Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() { + public Object run() throws Exception { + throw new IndexOutOfBoundsException(); + }})); + + try { + future.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof IndexOutOfBoundsException); + }}}; + + runWithPermissions(r); + } + + /** + * execute(null runnable) throws NPE + */ + public void testExecuteNullRunnable() { + ExecutorService e = new DirectExecutorService(); + try { + e.submit((Runnable) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * submit(null callable) throws NPE + */ + public void testSubmitNullCallable() { + ExecutorService e = new DirectExecutorService(); + try { + e.submit((Callable) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * submit(callable).get() throws InterruptedException if interrupted + */ + public void testInterruptedSubmit() throws InterruptedException { + final CountDownLatch submitted = new CountDownLatch(1); + final CountDownLatch quittingTime = new CountDownLatch(1); + final Callable awaiter = new CheckedCallable() { + public Void realCall() throws InterruptedException { + assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS)); + return null; + }}; + final ExecutorService p + = new ThreadPoolExecutor(1,1,60, TimeUnit.SECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, quittingTime)) { + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws Exception { + Future future = p.submit(awaiter); + submitted.countDown(); + future.get(); + }}); + + await(submitted); + t.interrupt(); + awaitTermination(t); + } + } + + /** + * get of submit(callable) throws ExecutionException if callable + * throws exception + */ + public void testSubmitEE() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + 60, TimeUnit.SECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + Callable c = new Callable() { + public Object call() { throw new ArithmeticException(); }}; + try { + p.submit(c).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof ArithmeticException); + } + } + } + + /** + * invokeAny(null) throws NPE + */ + public void testInvokeAny1() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IAE + */ + public void testInvokeAny2() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NPE if c has null elements + */ + public void testInvokeAny3() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new Callable() { + public Long call() { throw new ArithmeticException(); }}); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(c) throws ExecutionException if no task in c completes + */ + public void testInvokeAny4() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task in c if at least one completes + */ + public void testInvokeAny5() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NPE + */ + public void testInvokeAll1() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NPE if c has null elements + */ + public void testInvokeAll3() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of returned element of invokeAll(c) throws exception on failed task + */ + public void testInvokeAll4() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks in c + */ + public void testInvokeAll5() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NPE + */ + public void testTimedInvokeAny1() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(null time unit) throws NPE + */ + public void testTimedInvokeAnyNullTimeUnit() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IAE + */ + public void testTimedInvokeAny2() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NPE if c has null elements + */ + public void testTimedInvokeAny3() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new Callable() { + public Long call() { throw new ArithmeticException(); }}); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task in c + */ + public void testTimedInvokeAny5() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NPE + */ + public void testTimedInvokeAll1() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(null time unit) throws NPE + */ + public void testTimedInvokeAllNullTimeUnit() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>(), MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NPE if c has null elements + */ + public void testTimedInvokeAll3() throws InterruptedException { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of returned element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks in c + */ + public void testTimedInvokeAll5() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAll cancels tasks not completed by timeout + */ + public void testTimedInvokeAll6() throws Exception { + final ExecutorService e = new DirectExecutorService(); + try (PoolCleaner cleaner = cleaner(e)) { + for (long timeout = timeoutMillis();;) { + List> tasks = new ArrayList<>(); + tasks.add(new StringTask("0")); + tasks.add(Executors.callable(possiblyInterruptedRunnable(timeout), + TEST_STRING)); + tasks.add(new StringTask("2")); + long startTime = System.nanoTime(); + List> futures = + e.invokeAll(tasks, timeout, MILLISECONDS); + assertEquals(tasks.size(), futures.size()); + assertTrue(millisElapsedSince(startTime) >= timeout); + for (Future future : futures) + assertTrue(future.isDone()); + try { + assertEquals("0", futures.get(0).get()); + assertEquals(TEST_STRING, futures.get(1).get()); + } catch (CancellationException retryWithLongerTimeout) { + // unusual delay before starting second task + timeout *= 2; + if (timeout >= LONG_DELAY_MS / 2) + fail("expected exactly one task to be cancelled"); + continue; + } + assertTrue(futures.get(2).isCancelled()); + break; + } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java new file mode 100644 index 00000000000..ddc769d3a45 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java @@ -0,0 +1,205 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.AbstractQueue; +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AbstractQueueTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AbstractQueueTest.class); + } + + static class Succeed extends AbstractQueue { + public boolean offer(Integer x) { + if (x == null) throw new NullPointerException(); + return true; + } + public Integer peek() { return one; } + public Integer poll() { return one; } + public int size() { return 0; } + public Iterator iterator() { return null; } // not needed + } + + static class Fail extends AbstractQueue { + public boolean offer(Integer x) { + if (x == null) throw new NullPointerException(); + return false; + } + public Integer peek() { return null; } + public Integer poll() { return null; } + public int size() { return 0; } + public Iterator iterator() { return null; } // not needed + } + + /** + * add returns true if offer succeeds + */ + public void testAddS() { + Succeed q = new Succeed(); + assertTrue(q.add(two)); + } + + /** + * add throws ISE true if offer fails + */ + public void testAddF() { + Fail q = new Fail(); + try { + q.add(one); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * add throws NPE if offer does + */ + public void testAddNPE() { + Succeed q = new Succeed(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove returns normally if poll succeeds + */ + public void testRemoveS() { + Succeed q = new Succeed(); + q.remove(); + } + + /** + * remove throws NSEE if poll returns null + */ + public void testRemoveF() { + Fail q = new Fail(); + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * element returns normally if peek succeeds + */ + public void testElementS() { + Succeed q = new Succeed(); + q.element(); + } + + /** + * element throws NSEE if peek returns null + */ + public void testElementF() { + Fail q = new Fail(); + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + Succeed q = new Succeed(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + Succeed q = new Succeed(); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + Succeed q = new Succeed(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + Succeed q = new Succeed(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll throws ISE if an add fails + */ + public void testAddAll4() { + Fail q = new Fail(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java new file mode 100644 index 00000000000..88bdd13b4dc --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java @@ -0,0 +1,1280 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.locks.AbstractQueuedLongSynchronizer; +import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AbstractQueuedLongSynchronizerTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AbstractQueuedLongSynchronizerTest.class); + } + + /** + * A simple mutex class, adapted from the class javadoc. Exclusive + * acquire tests exercise this as a sample user extension. + */ + static class Mutex extends AbstractQueuedLongSynchronizer { + /** An eccentric value > 32 bits for locked synchronizer state. */ + static final long LOCKED = (1L << 63) | (1L << 15); + + static final long UNLOCKED = 0; + + public boolean isHeldExclusively() { + long state = getState(); + assertTrue(state == UNLOCKED || state == LOCKED); + return state == LOCKED; + } + + public boolean tryAcquire(long acquires) { + assertEquals(LOCKED, acquires); + return compareAndSetState(UNLOCKED, LOCKED); + } + + public boolean tryRelease(long releases) { + if (getState() != LOCKED) throw new IllegalMonitorStateException(); + setState(UNLOCKED); + return true; + } + + public boolean tryAcquireNanos(long nanos) throws InterruptedException { + return tryAcquireNanos(LOCKED, nanos); + } + + public boolean tryAcquire() { + return tryAcquire(LOCKED); + } + + public boolean tryRelease() { + return tryRelease(LOCKED); + } + + public void acquire() { + acquire(LOCKED); + } + + public void acquireInterruptibly() throws InterruptedException { + acquireInterruptibly(LOCKED); + } + + public void release() { + release(LOCKED); + } + + public ConditionObject newCondition() { + return new ConditionObject(); + } + } + + /** + * A simple latch class, to test shared mode. + */ + static class BooleanLatch extends AbstractQueuedLongSynchronizer { + public boolean isSignalled() { return getState() != 0; } + + public long tryAcquireShared(long ignore) { + return isSignalled() ? 1 : -1; + } + + public boolean tryReleaseShared(long ignore) { + setState(1L << 62); + return true; + } + } + + /** + * A runnable calling acquireInterruptibly that does not expect to + * be interrupted. + */ + class InterruptibleSyncRunnable extends CheckedRunnable { + final Mutex sync; + InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; } + public void realRun() throws InterruptedException { + sync.acquireInterruptibly(); + } + } + + /** + * A runnable calling acquireInterruptibly that expects to be + * interrupted. + */ + class InterruptedSyncRunnable extends CheckedInterruptedRunnable { + final Mutex sync; + InterruptedSyncRunnable(Mutex sync) { this.sync = sync; } + public void realRun() throws InterruptedException { + sync.acquireInterruptibly(); + } + } + + /** A constant to clarify calls to checking methods below. */ + static final Thread[] NO_THREADS = new Thread[0]; + + /** + * Spin-waits until sync.isQueued(t) becomes true. + */ + void waitForQueuedThread(AbstractQueuedLongSynchronizer sync, + Thread t) { + long startTime = System.nanoTime(); + while (!sync.isQueued(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + assertTrue(t.isAlive()); + } + + /** + * Checks that sync has exactly the given queued threads. + */ + void assertHasQueuedThreads(AbstractQueuedLongSynchronizer sync, + Thread... expected) { + Collection actual = sync.getQueuedThreads(); + assertEquals(expected.length > 0, sync.hasQueuedThreads()); + assertEquals(expected.length, sync.getQueueLength()); + assertEquals(expected.length, actual.size()); + assertEquals(expected.length == 0, actual.isEmpty()); + assertEquals(new HashSet(actual), + new HashSet(Arrays.asList(expected))); + } + + /** + * Checks that sync has exactly the given (exclusive) queued threads. + */ + void assertHasExclusiveQueuedThreads(AbstractQueuedLongSynchronizer sync, + Thread... expected) { + assertHasQueuedThreads(sync, expected); + assertEquals(new HashSet(sync.getExclusiveQueuedThreads()), + new HashSet(sync.getQueuedThreads())); + assertEquals(0, sync.getSharedQueuedThreads().size()); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + } + + /** + * Checks that sync has exactly the given (shared) queued threads. + */ + void assertHasSharedQueuedThreads(AbstractQueuedLongSynchronizer sync, + Thread... expected) { + assertHasQueuedThreads(sync, expected); + assertEquals(new HashSet(sync.getSharedQueuedThreads()), + new HashSet(sync.getQueuedThreads())); + assertEquals(0, sync.getExclusiveQueuedThreads().size()); + assertTrue(sync.getExclusiveQueuedThreads().isEmpty()); + } + + /** + * Checks that condition c has exactly the given waiter threads, + * after acquiring mutex. + */ + void assertHasWaitersUnlocked(Mutex sync, ConditionObject c, + Thread... threads) { + sync.acquire(); + assertHasWaitersLocked(sync, c, threads); + sync.release(); + } + + /** + * Checks that condition c has exactly the given waiter threads. + */ + void assertHasWaitersLocked(Mutex sync, ConditionObject c, + Thread... threads) { + assertEquals(threads.length > 0, sync.hasWaiters(c)); + assertEquals(threads.length, sync.getWaitQueueLength(c)); + assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty()); + assertEquals(threads.length, sync.getWaitingThreads(c).size()); + assertEquals(new HashSet(sync.getWaitingThreads(c)), + new HashSet(Arrays.asList(threads))); + } + + enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil } + + /** + * Awaits condition using the specified AwaitMethod. + */ + void await(ConditionObject c, AwaitMethod awaitMethod) + throws InterruptedException { + long timeoutMillis = 2 * LONG_DELAY_MS; + switch (awaitMethod) { + case await: + c.await(); + break; + case awaitTimed: + assertTrue(c.await(timeoutMillis, MILLISECONDS)); + break; + case awaitNanos: + long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(nanosTimeout); + assertTrue(nanosRemaining > 0); + break; + case awaitUntil: + assertTrue(c.awaitUntil(delayedDate(timeoutMillis))); + break; + default: + throw new AssertionError(); + } + } + + /** + * Checks that awaiting the given condition times out (using the + * default timeout duration). + */ + void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) { + long timeoutMillis = timeoutMillis(); + long startTime; + try { + switch (awaitMethod) { + case awaitTimed: + startTime = System.nanoTime(); + assertFalse(c.await(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + break; + case awaitNanos: + startTime = System.nanoTime(); + long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(nanosTimeout); + assertTrue(nanosRemaining <= 0); + assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + break; + case awaitUntil: + // We shouldn't assume that nanoTime and currentTimeMillis + // use the same time source, so don't use nanoTime here. + java.util.Date delayedDate = delayedDate(timeoutMillis()); + assertFalse(c.awaitUntil(delayedDate(timeoutMillis))); + assertTrue(new java.util.Date().getTime() >= delayedDate.getTime()); + break; + default: + throw new UnsupportedOperationException(); + } + } catch (InterruptedException ie) { threadUnexpectedException(ie); } + } + + /** + * isHeldExclusively is false upon construction + */ + public void testIsHeldExclusively() { + Mutex sync = new Mutex(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * acquiring released sync succeeds + */ + public void testAcquire() { + Mutex sync = new Mutex(); + sync.acquire(); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * tryAcquire on a released sync succeeds + */ + public void testTryAcquire() { + Mutex sync = new Mutex(); + assertTrue(sync.tryAcquire()); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { + final Mutex sync = new Mutex(); + assertFalse(sync.hasQueuedThreads()); + sync.acquire(); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.hasQueuedThreads()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.hasQueuedThreads()); + sync.release(); + awaitTermination(t2); + assertFalse(sync.hasQueuedThreads()); + } + + /** + * isQueued(null) throws NullPointerException + */ + public void testIsQueuedNPE() { + final Mutex sync = new Mutex(); + try { + sync.isQueued(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * isQueued reports whether a thread is queued + */ + public void testIsQueued() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertFalse(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + sync.acquire(); + t1.start(); + waitForQueuedThread(sync, t1); + assertTrue(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertTrue(sync.isQueued(t1)); + assertTrue(sync.isQueued(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(sync.isQueued(t1)); + assertTrue(sync.isQueued(t2)); + sync.release(); + awaitTermination(t2); + assertFalse(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + } + + /** + * getFirstQueuedThread returns first waiting thread or null if none + */ + public void testGetFirstQueuedThread() { + final Mutex sync = new Mutex(); + assertNull(sync.getFirstQueuedThread()); + sync.acquire(); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertEquals(t1, sync.getFirstQueuedThread()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertEquals(t1, sync.getFirstQueuedThread()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(t2, sync.getFirstQueuedThread()); + sync.release(); + awaitTermination(t2); + assertNull(sync.getFirstQueuedThread()); + } + + /** + * hasContended reports false if no thread has ever blocked, else true + */ + public void testHasContended() { + final Mutex sync = new Mutex(); + assertFalse(sync.hasContended()); + sync.acquire(); + assertFalse(sync.hasContended()); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.hasContended()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.hasContended()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.hasContended()); + sync.release(); + awaitTermination(t2); + assertTrue(sync.hasContended()); + } + + /** + * getQueuedThreads returns all waiting threads + */ + public void testGetQueuedThreads() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + sync.acquire(); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + t1.start(); + waitForQueuedThread(sync, t1); + assertHasExclusiveQueuedThreads(sync, t1); + assertTrue(sync.getQueuedThreads().contains(t1)); + assertFalse(sync.getQueuedThreads().contains(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertTrue(sync.getQueuedThreads().contains(t1)); + assertTrue(sync.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertHasExclusiveQueuedThreads(sync, t2); + sync.release(); + awaitTermination(t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + } + + /** + * getExclusiveQueuedThreads returns all exclusive waiting threads + */ + public void testGetExclusiveQueuedThreads() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + sync.acquire(); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + t1.start(); + waitForQueuedThread(sync, t1); + assertHasExclusiveQueuedThreads(sync, t1); + assertTrue(sync.getExclusiveQueuedThreads().contains(t1)); + assertFalse(sync.getExclusiveQueuedThreads().contains(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertTrue(sync.getExclusiveQueuedThreads().contains(t1)); + assertTrue(sync.getExclusiveQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertHasExclusiveQueuedThreads(sync, t2); + sync.release(); + awaitTermination(t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + } + + /** + * getSharedQueuedThreads does not include exclusively waiting threads + */ + public void testGetSharedQueuedThreads_Exclusive() { + final Mutex sync = new Mutex(); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + sync.acquire(); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + sync.release(); + awaitTermination(t2); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + } + + /** + * getSharedQueuedThreads returns all shared waiting threads + */ + public void testGetSharedQueuedThreads_Shared() { + final BooleanLatch l = new BooleanLatch(); + assertHasSharedQueuedThreads(l, NO_THREADS); + Thread t1 = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + l.acquireSharedInterruptibly(0); + }}); + waitForQueuedThread(l, t1); + assertHasSharedQueuedThreads(l, t1); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + l.acquireSharedInterruptibly(0); + }}); + waitForQueuedThread(l, t2); + assertHasSharedQueuedThreads(l, t1, t2); + t1.interrupt(); + awaitTermination(t1); + assertHasSharedQueuedThreads(l, t2); + assertTrue(l.releaseShared(0)); + awaitTermination(t2); + assertHasSharedQueuedThreads(l, NO_THREADS); + } + + /** + * tryAcquireNanos is interruptible + */ + public void testTryAcquireNanos_Interruptible() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS)); + }}); + + waitForQueuedThread(sync, t); + t.interrupt(); + awaitTermination(t); + } + + /** + * tryAcquire on exclusively held sync fails + */ + public void testTryAcquireWhenSynced() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(sync.tryAcquire()); + }}); + + awaitTermination(t); + sync.release(); + } + + /** + * tryAcquireNanos on an exclusively held sync times out + */ + public void testAcquireNanos_Timeout() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long nanos = MILLISECONDS.toNanos(timeoutMillis()); + assertFalse(sync.tryAcquireNanos(nanos)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t); + sync.release(); + } + + /** + * getState is true when acquired and false when not + */ + public void testGetState() { + final Mutex sync = new Mutex(); + sync.acquire(); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + + final BooleanLatch acquired = new BooleanLatch(); + final BooleanLatch done = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(acquired.releaseShared(0)); + done.acquireShared(0); + sync.release(); + }}); + + acquired.acquireShared(0); + assertTrue(sync.isHeldExclusively()); + assertTrue(done.releaseShared(0)); + awaitTermination(t); + assertFalse(sync.isHeldExclusively()); + } + + /** + * acquireInterruptibly succeeds when released, else is interruptible + */ + public void testAcquireInterruptibly() throws InterruptedException { + final Mutex sync = new Mutex(); + final BooleanLatch threadStarted = new BooleanLatch(); + sync.acquireInterruptibly(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertTrue(threadStarted.releaseShared(0)); + sync.acquireInterruptibly(); + }}); + + threadStarted.acquireShared(0); + waitForQueuedThread(sync, t); + t.interrupt(); + awaitTermination(t); + assertTrue(sync.isHeldExclusively()); + } + + /** + * owns is true for a condition created by sync else false + */ + public void testOwns() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + assertTrue(sync.owns(c)); + assertFalse(sync2.owns(c)); + } + + /** + * Calling await without holding sync throws IllegalMonitorStateException + */ + public void testAwait_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + for (AwaitMethod awaitMethod : AwaitMethod.values()) { + long startTime = System.nanoTime(); + try { + await(c, awaitMethod); + shouldThrow(); + } catch (IllegalMonitorStateException success) { + } catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * Calling signal without holding sync throws IllegalMonitorStateException + */ + public void testSignal_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + c.signal(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * Calling signalAll without holding sync throws IllegalMonitorStateException + */ + public void testSignalAll_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + c.signalAll(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * await/awaitNanos/awaitUntil without a signal times out + */ + public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); } + public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); } + public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); } + public void testAwait_Timeout(AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertAwaitTimesOut(c, awaitMethod); + sync.release(); + } + + /** + * await/awaitNanos/awaitUntil returns when signalled + */ + public void testSignal_await() { testSignal(AwaitMethod.await); } + public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); } + public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); } + public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); } + public void testSignal(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(acquired.releaseShared(0)); + await(c, awaitMethod); + sync.release(); + }}); + + acquired.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + sync.release(); + awaitTermination(t); + } + + /** + * hasWaiters(null) throws NullPointerException + */ + public void testHasWaitersNPE() { + final Mutex sync = new Mutex(); + try { + sync.hasWaiters(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitQueueLength(null) throws NullPointerException + */ + public void testGetWaitQueueLengthNPE() { + final Mutex sync = new Mutex(); + try { + sync.getWaitQueueLength(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitingThreads throws NPE if null + */ + public void testGetWaitingThreadsNPE() { + final Mutex sync = new Mutex(); + try { + sync.getWaitingThreads(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasWaiters throws IllegalArgumentException if not owned + */ + public void testHasWaitersIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.hasWaiters(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * hasWaiters throws IllegalMonitorStateException if not synced + */ + public void testHasWaitersIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.hasWaiters(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength throws IllegalArgumentException if not owned + */ + public void testGetWaitQueueLengthIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength throws IllegalMonitorStateException if not synced + */ + public void testGetWaitQueueLengthIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads throws IllegalArgumentException if not owned + */ + public void testGetWaitingThreadsIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads throws IllegalMonitorStateException if not synced + */ + public void testGetWaitingThreadsIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * hasWaiters returns true when a thread is waiting, else false + */ + public void testHasWaiters() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertFalse(sync.hasWaiters(c)); + assertTrue(acquired.releaseShared(0)); + c.await(); + sync.release(); + }}); + + acquired.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertTrue(sync.hasWaiters(c)); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + assertFalse(sync.hasWaiters(c)); + sync.release(); + + awaitTermination(t); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength returns number of waiting threads + */ + public void testGetWaitQueueLength() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + final Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertEquals(0, sync.getWaitQueueLength(c)); + assertTrue(acquired1.releaseShared(0)); + c.await(); + sync.release(); + }}); + acquired1.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertEquals(1, sync.getWaitQueueLength(c)); + sync.release(); + + final Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertEquals(1, sync.getWaitQueueLength(c)); + assertTrue(acquired2.releaseShared(0)); + c.await(); + sync.release(); + }}); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertEquals(2, sync.getWaitQueueLength(c)); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertEquals(0, sync.getWaitQueueLength(c)); + sync.release(); + + awaitTermination(t1); + awaitTermination(t2); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads returns only and all waiting threads + */ + public void testGetWaitingThreads() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + final Thread t1 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertTrue(acquired1.releaseShared(0)); + c.await(); + sync.release(); + }}); + + final Thread t2 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(1, sync.getWaitingThreads(c).size()); + assertTrue(acquired2.releaseShared(0)); + c.await(); + sync.release(); + }}); + + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertFalse(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertEquals(0, sync.getWaitingThreads(c).size()); + sync.release(); + + t1.start(); + acquired1.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(1, sync.getWaitingThreads(c).size()); + sync.release(); + + t2.start(); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertTrue(sync.getWaitingThreads(c).contains(t2)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(2, sync.getWaitingThreads(c).size()); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertFalse(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertEquals(0, sync.getWaitingThreads(c).size()); + sync.release(); + + awaitTermination(t1); + awaitTermination(t2); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * awaitUninterruptibly is uninterruptible + */ + public void testAwaitUninterruptibly() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch pleaseInterrupt = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + sync.acquire(); + assertTrue(pleaseInterrupt.releaseShared(0)); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + assertHasWaitersLocked(sync, c, NO_THREADS); + sync.release(); + }}); + + pleaseInterrupt.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + sync.release(); + t.interrupt(); + assertHasWaitersUnlocked(sync, c, t); + assertThreadStaysAlive(t); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + sync.release(); + awaitTermination(t); + } + + /** + * await/awaitNanos/awaitUntil is interruptible + */ + public void testInterruptible_await() { testInterruptible(AwaitMethod.await); } + public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); } + public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); } + public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); } + public void testInterruptible(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch pleaseInterrupt = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(pleaseInterrupt.releaseShared(0)); + await(c, awaitMethod); + }}); + + pleaseInterrupt.acquireShared(0); + t.interrupt(); + awaitTermination(t); + } + + /** + * signalAll wakes up all threads + */ + public void testSignalAll_await() { testSignalAll(AwaitMethod.await); } + public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); } + public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); } + public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); } + public void testSignalAll(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + acquired1.releaseShared(0); + await(c, awaitMethod); + sync.release(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + acquired2.releaseShared(0); + await(c, awaitMethod); + sync.release(); + }}); + + acquired1.acquireShared(0); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + sync.release(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * toString indicates current state + */ + public void testToString() { + Mutex sync = new Mutex(); + assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED)); + sync.acquire(); + assertTrue(sync.toString().contains("State = " + Mutex.LOCKED)); + } + + /** + * A serialized AQS deserializes with current state, but no queued threads + */ + public void testSerialization() { + Mutex sync = new Mutex(); + assertFalse(serialClone(sync).isHeldExclusively()); + sync.acquire(); + Thread t = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t); + assertTrue(sync.isHeldExclusively()); + + Mutex clone = serialClone(sync); + assertTrue(clone.isHeldExclusively()); + assertHasExclusiveQueuedThreads(sync, t); + assertHasExclusiveQueuedThreads(clone, NO_THREADS); + t.interrupt(); + awaitTermination(t); + sync.release(); + assertFalse(sync.isHeldExclusively()); + assertTrue(clone.isHeldExclusively()); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertHasExclusiveQueuedThreads(clone, NO_THREADS); + } + + /** + * tryReleaseShared setting state changes getState + */ + public void testGetStateWithReleaseShared() { + final BooleanLatch l = new BooleanLatch(); + assertFalse(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + } + + /** + * releaseShared has no effect when already signalled + */ + public void testReleaseShared() { + final BooleanLatch l = new BooleanLatch(); + assertFalse(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + } + + /** + * acquireSharedInterruptibly returns after release, but not before + */ + public void testAcquireSharedInterruptibly() { + final BooleanLatch l = new BooleanLatch(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + l.acquireSharedInterruptibly(0); + assertTrue(l.isSignalled()); + l.acquireSharedInterruptibly(0); + assertTrue(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + assertThreadStaysAlive(t); + assertHasSharedQueuedThreads(l, t); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + awaitTermination(t); + } + + /** + * tryAcquireSharedNanos returns after release, but not before + */ + public void testTryAcquireSharedNanos() { + final BooleanLatch l = new BooleanLatch(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS); + assertTrue(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(l.isSignalled()); + assertTrue(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + assertThreadStaysAlive(t); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + awaitTermination(t); + } + + /** + * acquireSharedInterruptibly is interruptible + */ + public void testAcquireSharedInterruptibly_Interruptible() { + final BooleanLatch l = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + l.acquireSharedInterruptibly(0); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + t.interrupt(); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * tryAcquireSharedNanos is interruptible + */ + public void testTryAcquireSharedNanos_Interruptible() { + final BooleanLatch l = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS); + l.tryAcquireSharedNanos(0, nanos); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + t.interrupt(); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * tryAcquireSharedNanos times out if not released before timeout + */ + public void testTryAcquireSharedNanos_Timeout() { + final BooleanLatch l = new BooleanLatch(); + final BooleanLatch observedQueued = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + for (long millis = timeoutMillis(); + !observedQueued.isSignalled(); + millis *= 2) { + long nanos = MILLISECONDS.toNanos(millis); + long startTime = System.nanoTime(); + assertFalse(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(millisElapsedSince(startTime) >= millis); + } + assertFalse(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + observedQueued.releaseShared(0); + assertFalse(l.isSignalled()); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * awaitNanos/timed await with 0 wait times out immediately + */ + public void testAwait_Zero() throws InterruptedException { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertTrue(c.awaitNanos(0L) <= 0); + assertFalse(c.await(0L, NANOSECONDS)); + sync.release(); + } + + /** + * awaitNanos/timed await with maximum negative wait times does not underflow + */ + public void testAwait_NegativeInfinity() throws InterruptedException { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0); + assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS)); + sync.release(); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java new file mode 100644 index 00000000000..8341a7e296c --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java @@ -0,0 +1,1283 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; +import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AbstractQueuedSynchronizerTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AbstractQueuedSynchronizerTest.class); + } + + /** + * A simple mutex class, adapted from the class javadoc. Exclusive + * acquire tests exercise this as a sample user extension. Other + * methods/features of AbstractQueuedSynchronizer are tested via + * other test classes, including those for ReentrantLock, + * ReentrantReadWriteLock, and Semaphore. + */ + static class Mutex extends AbstractQueuedSynchronizer { + /** An eccentric value for locked synchronizer state. */ + static final int LOCKED = (1 << 31) | (1 << 15); + + static final int UNLOCKED = 0; + + @Override public boolean isHeldExclusively() { + int state = getState(); + assertTrue(state == UNLOCKED || state == LOCKED); + return state == LOCKED; + } + + @Override public boolean tryAcquire(int acquires) { + assertEquals(LOCKED, acquires); + return compareAndSetState(UNLOCKED, LOCKED); + } + + @Override public boolean tryRelease(int releases) { + if (getState() != LOCKED) throw new IllegalMonitorStateException(); + assertEquals(LOCKED, releases); + setState(UNLOCKED); + return true; + } + + public boolean tryAcquireNanos(long nanos) throws InterruptedException { + return tryAcquireNanos(LOCKED, nanos); + } + + public boolean tryAcquire() { + return tryAcquire(LOCKED); + } + + public boolean tryRelease() { + return tryRelease(LOCKED); + } + + public void acquire() { + acquire(LOCKED); + } + + public void acquireInterruptibly() throws InterruptedException { + acquireInterruptibly(LOCKED); + } + + public void release() { + release(LOCKED); + } + + public ConditionObject newCondition() { + return new ConditionObject(); + } + } + + /** + * A simple latch class, to test shared mode. + */ + static class BooleanLatch extends AbstractQueuedSynchronizer { + public boolean isSignalled() { return getState() != 0; } + + public int tryAcquireShared(int ignore) { + return isSignalled() ? 1 : -1; + } + + public boolean tryReleaseShared(int ignore) { + setState(1); + return true; + } + } + + /** + * A runnable calling acquireInterruptibly that does not expect to + * be interrupted. + */ + class InterruptibleSyncRunnable extends CheckedRunnable { + final Mutex sync; + InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; } + public void realRun() throws InterruptedException { + sync.acquireInterruptibly(); + } + } + + /** + * A runnable calling acquireInterruptibly that expects to be + * interrupted. + */ + class InterruptedSyncRunnable extends CheckedInterruptedRunnable { + final Mutex sync; + InterruptedSyncRunnable(Mutex sync) { this.sync = sync; } + public void realRun() throws InterruptedException { + sync.acquireInterruptibly(); + } + } + + /** A constant to clarify calls to checking methods below. */ + static final Thread[] NO_THREADS = new Thread[0]; + + /** + * Spin-waits until sync.isQueued(t) becomes true. + */ + void waitForQueuedThread(AbstractQueuedSynchronizer sync, Thread t) { + long startTime = System.nanoTime(); + while (!sync.isQueued(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + assertTrue(t.isAlive()); + } + + /** + * Checks that sync has exactly the given queued threads. + */ + void assertHasQueuedThreads(AbstractQueuedSynchronizer sync, + Thread... expected) { + Collection actual = sync.getQueuedThreads(); + assertEquals(expected.length > 0, sync.hasQueuedThreads()); + assertEquals(expected.length, sync.getQueueLength()); + assertEquals(expected.length, actual.size()); + assertEquals(expected.length == 0, actual.isEmpty()); + assertEquals(new HashSet(actual), + new HashSet(Arrays.asList(expected))); + } + + /** + * Checks that sync has exactly the given (exclusive) queued threads. + */ + void assertHasExclusiveQueuedThreads(AbstractQueuedSynchronizer sync, + Thread... expected) { + assertHasQueuedThreads(sync, expected); + assertEquals(new HashSet(sync.getExclusiveQueuedThreads()), + new HashSet(sync.getQueuedThreads())); + assertEquals(0, sync.getSharedQueuedThreads().size()); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + } + + /** + * Checks that sync has exactly the given (shared) queued threads. + */ + void assertHasSharedQueuedThreads(AbstractQueuedSynchronizer sync, + Thread... expected) { + assertHasQueuedThreads(sync, expected); + assertEquals(new HashSet(sync.getSharedQueuedThreads()), + new HashSet(sync.getQueuedThreads())); + assertEquals(0, sync.getExclusiveQueuedThreads().size()); + assertTrue(sync.getExclusiveQueuedThreads().isEmpty()); + } + + /** + * Checks that condition c has exactly the given waiter threads, + * after acquiring mutex. + */ + void assertHasWaitersUnlocked(Mutex sync, ConditionObject c, + Thread... threads) { + sync.acquire(); + assertHasWaitersLocked(sync, c, threads); + sync.release(); + } + + /** + * Checks that condition c has exactly the given waiter threads. + */ + void assertHasWaitersLocked(Mutex sync, ConditionObject c, + Thread... threads) { + assertEquals(threads.length > 0, sync.hasWaiters(c)); + assertEquals(threads.length, sync.getWaitQueueLength(c)); + assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty()); + assertEquals(threads.length, sync.getWaitingThreads(c).size()); + assertEquals(new HashSet(sync.getWaitingThreads(c)), + new HashSet(Arrays.asList(threads))); + } + + enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil } + + /** + * Awaits condition using the specified AwaitMethod. + */ + void await(ConditionObject c, AwaitMethod awaitMethod) + throws InterruptedException { + long timeoutMillis = 2 * LONG_DELAY_MS; + switch (awaitMethod) { + case await: + c.await(); + break; + case awaitTimed: + assertTrue(c.await(timeoutMillis, MILLISECONDS)); + break; + case awaitNanos: + long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(nanosTimeout); + assertTrue(nanosRemaining > 0); + break; + case awaitUntil: + assertTrue(c.awaitUntil(delayedDate(timeoutMillis))); + break; + default: + throw new AssertionError(); + } + } + + /** + * Checks that awaiting the given condition times out (using the + * default timeout duration). + */ + void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) { + long timeoutMillis = timeoutMillis(); + long startTime; + try { + switch (awaitMethod) { + case awaitTimed: + startTime = System.nanoTime(); + assertFalse(c.await(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + break; + case awaitNanos: + startTime = System.nanoTime(); + long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(nanosTimeout); + assertTrue(nanosRemaining <= 0); + assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + break; + case awaitUntil: + // We shouldn't assume that nanoTime and currentTimeMillis + // use the same time source, so don't use nanoTime here. + java.util.Date delayedDate = delayedDate(timeoutMillis()); + assertFalse(c.awaitUntil(delayedDate(timeoutMillis))); + assertTrue(new java.util.Date().getTime() >= delayedDate.getTime()); + break; + default: + throw new UnsupportedOperationException(); + } + } catch (InterruptedException ie) { threadUnexpectedException(ie); } + } + + /** + * isHeldExclusively is false upon construction + */ + public void testIsHeldExclusively() { + Mutex sync = new Mutex(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * acquiring released sync succeeds + */ + public void testAcquire() { + Mutex sync = new Mutex(); + sync.acquire(); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * tryAcquire on a released sync succeeds + */ + public void testTryAcquire() { + Mutex sync = new Mutex(); + assertTrue(sync.tryAcquire()); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { + final Mutex sync = new Mutex(); + assertFalse(sync.hasQueuedThreads()); + sync.acquire(); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.hasQueuedThreads()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.hasQueuedThreads()); + sync.release(); + awaitTermination(t2); + assertFalse(sync.hasQueuedThreads()); + } + + /** + * isQueued(null) throws NullPointerException + */ + public void testIsQueuedNPE() { + final Mutex sync = new Mutex(); + try { + sync.isQueued(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * isQueued reports whether a thread is queued + */ + public void testIsQueued() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertFalse(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + sync.acquire(); + t1.start(); + waitForQueuedThread(sync, t1); + assertTrue(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertTrue(sync.isQueued(t1)); + assertTrue(sync.isQueued(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(sync.isQueued(t1)); + assertTrue(sync.isQueued(t2)); + sync.release(); + awaitTermination(t2); + assertFalse(sync.isQueued(t1)); + assertFalse(sync.isQueued(t2)); + } + + /** + * getFirstQueuedThread returns first waiting thread or null if none + */ + public void testGetFirstQueuedThread() { + final Mutex sync = new Mutex(); + assertNull(sync.getFirstQueuedThread()); + sync.acquire(); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertEquals(t1, sync.getFirstQueuedThread()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertEquals(t1, sync.getFirstQueuedThread()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(t2, sync.getFirstQueuedThread()); + sync.release(); + awaitTermination(t2); + assertNull(sync.getFirstQueuedThread()); + } + + /** + * hasContended reports false if no thread has ever blocked, else true + */ + public void testHasContended() { + final Mutex sync = new Mutex(); + assertFalse(sync.hasContended()); + sync.acquire(); + assertFalse(sync.hasContended()); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.hasContended()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.hasContended()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.hasContended()); + sync.release(); + awaitTermination(t2); + assertTrue(sync.hasContended()); + } + + /** + * getQueuedThreads returns all waiting threads + */ + public void testGetQueuedThreads() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + sync.acquire(); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + t1.start(); + waitForQueuedThread(sync, t1); + assertHasExclusiveQueuedThreads(sync, t1); + assertTrue(sync.getQueuedThreads().contains(t1)); + assertFalse(sync.getQueuedThreads().contains(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertTrue(sync.getQueuedThreads().contains(t1)); + assertTrue(sync.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertHasExclusiveQueuedThreads(sync, t2); + sync.release(); + awaitTermination(t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + } + + /** + * getExclusiveQueuedThreads returns all exclusive waiting threads + */ + public void testGetExclusiveQueuedThreads() { + final Mutex sync = new Mutex(); + Thread t1 = new Thread(new InterruptedSyncRunnable(sync)); + Thread t2 = new Thread(new InterruptibleSyncRunnable(sync)); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + sync.acquire(); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + t1.start(); + waitForQueuedThread(sync, t1); + assertHasExclusiveQueuedThreads(sync, t1); + assertTrue(sync.getExclusiveQueuedThreads().contains(t1)); + assertFalse(sync.getExclusiveQueuedThreads().contains(t2)); + t2.start(); + waitForQueuedThread(sync, t2); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertTrue(sync.getExclusiveQueuedThreads().contains(t1)); + assertTrue(sync.getExclusiveQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertHasExclusiveQueuedThreads(sync, t2); + sync.release(); + awaitTermination(t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + } + + /** + * getSharedQueuedThreads does not include exclusively waiting threads + */ + public void testGetSharedQueuedThreads_Exclusive() { + final Mutex sync = new Mutex(); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + sync.acquire(); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t1); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync)); + waitForQueuedThread(sync, t2); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + sync.release(); + awaitTermination(t2); + assertTrue(sync.getSharedQueuedThreads().isEmpty()); + } + + /** + * getSharedQueuedThreads returns all shared waiting threads + */ + public void testGetSharedQueuedThreads_Shared() { + final BooleanLatch l = new BooleanLatch(); + assertHasSharedQueuedThreads(l, NO_THREADS); + Thread t1 = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + l.acquireSharedInterruptibly(0); + }}); + waitForQueuedThread(l, t1); + assertHasSharedQueuedThreads(l, t1); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + l.acquireSharedInterruptibly(0); + }}); + waitForQueuedThread(l, t2); + assertHasSharedQueuedThreads(l, t1, t2); + t1.interrupt(); + awaitTermination(t1); + assertHasSharedQueuedThreads(l, t2); + assertTrue(l.releaseShared(0)); + awaitTermination(t2); + assertHasSharedQueuedThreads(l, NO_THREADS); + } + + /** + * tryAcquireNanos is interruptible + */ + public void testTryAcquireNanos_Interruptible() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS)); + }}); + + waitForQueuedThread(sync, t); + t.interrupt(); + awaitTermination(t); + } + + /** + * tryAcquire on exclusively held sync fails + */ + public void testTryAcquireWhenSynced() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(sync.tryAcquire()); + }}); + + awaitTermination(t); + sync.release(); + } + + /** + * tryAcquireNanos on an exclusively held sync times out + */ + public void testAcquireNanos_Timeout() { + final Mutex sync = new Mutex(); + sync.acquire(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long nanos = MILLISECONDS.toNanos(timeoutMillis()); + assertFalse(sync.tryAcquireNanos(nanos)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t); + sync.release(); + } + + /** + * getState is true when acquired and false when not + */ + public void testGetState() { + final Mutex sync = new Mutex(); + sync.acquire(); + assertTrue(sync.isHeldExclusively()); + sync.release(); + assertFalse(sync.isHeldExclusively()); + + final BooleanLatch acquired = new BooleanLatch(); + final BooleanLatch done = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(acquired.releaseShared(0)); + done.acquireShared(0); + sync.release(); + }}); + + acquired.acquireShared(0); + assertTrue(sync.isHeldExclusively()); + assertTrue(done.releaseShared(0)); + awaitTermination(t); + assertFalse(sync.isHeldExclusively()); + } + + /** + * acquireInterruptibly succeeds when released, else is interruptible + */ + public void testAcquireInterruptibly() throws InterruptedException { + final Mutex sync = new Mutex(); + final BooleanLatch threadStarted = new BooleanLatch(); + sync.acquireInterruptibly(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertTrue(threadStarted.releaseShared(0)); + sync.acquireInterruptibly(); + }}); + + threadStarted.acquireShared(0); + waitForQueuedThread(sync, t); + t.interrupt(); + awaitTermination(t); + assertTrue(sync.isHeldExclusively()); + } + + /** + * owns is true for a condition created by sync else false + */ + public void testOwns() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + assertTrue(sync.owns(c)); + assertFalse(sync2.owns(c)); + } + + /** + * Calling await without holding sync throws IllegalMonitorStateException + */ + public void testAwait_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + for (AwaitMethod awaitMethod : AwaitMethod.values()) { + long startTime = System.nanoTime(); + try { + await(c, awaitMethod); + shouldThrow(); + } catch (IllegalMonitorStateException success) { + } catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * Calling signal without holding sync throws IllegalMonitorStateException + */ + public void testSignal_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + c.signal(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * Calling signalAll without holding sync throws IllegalMonitorStateException + */ + public void testSignalAll_IMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + c.signalAll(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * await/awaitNanos/awaitUntil without a signal times out + */ + public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); } + public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); } + public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); } + public void testAwait_Timeout(AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertAwaitTimesOut(c, awaitMethod); + sync.release(); + } + + /** + * await/awaitNanos/awaitUntil returns when signalled + */ + public void testSignal_await() { testSignal(AwaitMethod.await); } + public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); } + public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); } + public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); } + public void testSignal(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(acquired.releaseShared(0)); + await(c, awaitMethod); + sync.release(); + }}); + + acquired.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + sync.release(); + awaitTermination(t); + } + + /** + * hasWaiters(null) throws NullPointerException + */ + public void testHasWaitersNPE() { + final Mutex sync = new Mutex(); + try { + sync.hasWaiters(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitQueueLength(null) throws NullPointerException + */ + public void testGetWaitQueueLengthNPE() { + final Mutex sync = new Mutex(); + try { + sync.getWaitQueueLength(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitingThreads(null) throws NullPointerException + */ + public void testGetWaitingThreadsNPE() { + final Mutex sync = new Mutex(); + try { + sync.getWaitingThreads(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasWaiters throws IllegalArgumentException if not owned + */ + public void testHasWaitersIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.hasWaiters(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * hasWaiters throws IllegalMonitorStateException if not synced + */ + public void testHasWaitersIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.hasWaiters(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength throws IllegalArgumentException if not owned + */ + public void testGetWaitQueueLengthIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength throws IllegalMonitorStateException if not synced + */ + public void testGetWaitQueueLengthIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads throws IllegalArgumentException if not owned + */ + public void testGetWaitingThreadsIAE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final Mutex sync2 = new Mutex(); + try { + sync2.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads throws IllegalMonitorStateException if not synced + */ + public void testGetWaitingThreadsIMSE() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + try { + sync.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * hasWaiters returns true when a thread is waiting, else false + */ + public void testHasWaiters() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertFalse(sync.hasWaiters(c)); + assertTrue(acquired.releaseShared(0)); + c.await(); + sync.release(); + }}); + + acquired.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertTrue(sync.hasWaiters(c)); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + assertFalse(sync.hasWaiters(c)); + sync.release(); + + awaitTermination(t); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitQueueLength returns number of waiting threads + */ + public void testGetWaitQueueLength() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + final Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertEquals(0, sync.getWaitQueueLength(c)); + assertTrue(acquired1.releaseShared(0)); + c.await(); + sync.release(); + }}); + acquired1.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertEquals(1, sync.getWaitQueueLength(c)); + sync.release(); + + final Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertEquals(1, sync.getWaitQueueLength(c)); + assertTrue(acquired2.releaseShared(0)); + c.await(); + sync.release(); + }}); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertEquals(2, sync.getWaitQueueLength(c)); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertEquals(0, sync.getWaitQueueLength(c)); + sync.release(); + + awaitTermination(t1); + awaitTermination(t2); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * getWaitingThreads returns only and all waiting threads + */ + public void testGetWaitingThreads() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + final Thread t1 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertTrue(acquired1.releaseShared(0)); + c.await(); + sync.release(); + }}); + + final Thread t2 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(1, sync.getWaitingThreads(c).size()); + assertTrue(acquired2.releaseShared(0)); + c.await(); + sync.release(); + }}); + + sync.acquire(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertFalse(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertEquals(0, sync.getWaitingThreads(c).size()); + sync.release(); + + t1.start(); + acquired1.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(1, sync.getWaitingThreads(c).size()); + sync.release(); + + t2.start(); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertTrue(sync.getWaitingThreads(c).contains(t1)); + assertTrue(sync.getWaitingThreads(c).contains(t2)); + assertFalse(sync.getWaitingThreads(c).isEmpty()); + assertEquals(2, sync.getWaitingThreads(c).size()); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + assertFalse(sync.getWaitingThreads(c).contains(t1)); + assertFalse(sync.getWaitingThreads(c).contains(t2)); + assertTrue(sync.getWaitingThreads(c).isEmpty()); + assertEquals(0, sync.getWaitingThreads(c).size()); + sync.release(); + + awaitTermination(t1); + awaitTermination(t2); + assertHasWaitersUnlocked(sync, c, NO_THREADS); + } + + /** + * awaitUninterruptibly is uninterruptible + */ + public void testAwaitUninterruptibly() { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch pleaseInterrupt = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + sync.acquire(); + assertTrue(pleaseInterrupt.releaseShared(0)); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + assertHasWaitersLocked(sync, c, NO_THREADS); + sync.release(); + }}); + + pleaseInterrupt.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + sync.release(); + t.interrupt(); + assertHasWaitersUnlocked(sync, c, t); + assertThreadStaysAlive(t); + sync.acquire(); + assertHasWaitersLocked(sync, c, t); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signal(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t); + sync.release(); + awaitTermination(t); + } + + /** + * await/awaitNanos/awaitUntil is interruptible + */ + public void testInterruptible_await() { testInterruptible(AwaitMethod.await); } + public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); } + public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); } + public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); } + public void testInterruptible(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch pleaseInterrupt = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + assertTrue(pleaseInterrupt.releaseShared(0)); + await(c, awaitMethod); + }}); + + pleaseInterrupt.acquireShared(0); + t.interrupt(); + awaitTermination(t); + } + + /** + * signalAll wakes up all threads + */ + public void testSignalAll_await() { testSignalAll(AwaitMethod.await); } + public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); } + public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); } + public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); } + public void testSignalAll(final AwaitMethod awaitMethod) { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + final BooleanLatch acquired1 = new BooleanLatch(); + final BooleanLatch acquired2 = new BooleanLatch(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + acquired1.releaseShared(0); + await(c, awaitMethod); + sync.release(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + sync.acquire(); + acquired2.releaseShared(0); + await(c, awaitMethod); + sync.release(); + }}); + + acquired1.acquireShared(0); + acquired2.acquireShared(0); + sync.acquire(); + assertHasWaitersLocked(sync, c, t1, t2); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + c.signalAll(); + assertHasWaitersLocked(sync, c, NO_THREADS); + assertHasExclusiveQueuedThreads(sync, t1, t2); + sync.release(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * toString indicates current state + */ + public void testToString() { + Mutex sync = new Mutex(); + assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED)); + sync.acquire(); + assertTrue(sync.toString().contains("State = " + Mutex.LOCKED)); + } + + /** + * A serialized AQS deserializes with current state, but no queued threads + */ + public void testSerialization() { + Mutex sync = new Mutex(); + assertFalse(serialClone(sync).isHeldExclusively()); + sync.acquire(); + Thread t = newStartedThread(new InterruptedSyncRunnable(sync)); + waitForQueuedThread(sync, t); + assertTrue(sync.isHeldExclusively()); + + Mutex clone = serialClone(sync); + assertTrue(clone.isHeldExclusively()); + assertHasExclusiveQueuedThreads(sync, t); + assertHasExclusiveQueuedThreads(clone, NO_THREADS); + t.interrupt(); + awaitTermination(t); + sync.release(); + assertFalse(sync.isHeldExclusively()); + assertTrue(clone.isHeldExclusively()); + assertHasExclusiveQueuedThreads(sync, NO_THREADS); + assertHasExclusiveQueuedThreads(clone, NO_THREADS); + } + + /** + * tryReleaseShared setting state changes getState + */ + public void testGetStateWithReleaseShared() { + final BooleanLatch l = new BooleanLatch(); + assertFalse(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + } + + /** + * releaseShared has no effect when already signalled + */ + public void testReleaseShared() { + final BooleanLatch l = new BooleanLatch(); + assertFalse(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + } + + /** + * acquireSharedInterruptibly returns after release, but not before + */ + public void testAcquireSharedInterruptibly() { + final BooleanLatch l = new BooleanLatch(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + l.acquireSharedInterruptibly(0); + assertTrue(l.isSignalled()); + l.acquireSharedInterruptibly(0); + assertTrue(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + assertThreadStaysAlive(t); + assertHasSharedQueuedThreads(l, t); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + awaitTermination(t); + } + + /** + * tryAcquireSharedNanos returns after release, but not before + */ + public void testTryAcquireSharedNanos() { + final BooleanLatch l = new BooleanLatch(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS); + assertTrue(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(l.isSignalled()); + assertTrue(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + assertThreadStaysAlive(t); + assertTrue(l.releaseShared(0)); + assertTrue(l.isSignalled()); + awaitTermination(t); + } + + /** + * acquireSharedInterruptibly is interruptible + */ + public void testAcquireSharedInterruptibly_Interruptible() { + final BooleanLatch l = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + l.acquireSharedInterruptibly(0); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + t.interrupt(); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * tryAcquireSharedNanos is interruptible + */ + public void testTryAcquireSharedNanos_Interruptible() { + final BooleanLatch l = new BooleanLatch(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS); + l.tryAcquireSharedNanos(0, nanos); + }}); + + waitForQueuedThread(l, t); + assertFalse(l.isSignalled()); + t.interrupt(); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * tryAcquireSharedNanos times out if not released before timeout + */ + public void testTryAcquireSharedNanos_Timeout() { + final BooleanLatch l = new BooleanLatch(); + final BooleanLatch observedQueued = new BooleanLatch(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(l.isSignalled()); + for (long millis = timeoutMillis(); + !observedQueued.isSignalled(); + millis *= 2) { + long nanos = MILLISECONDS.toNanos(millis); + long startTime = System.nanoTime(); + assertFalse(l.tryAcquireSharedNanos(0, nanos)); + assertTrue(millisElapsedSince(startTime) >= millis); + } + assertFalse(l.isSignalled()); + }}); + + waitForQueuedThread(l, t); + observedQueued.releaseShared(0); + assertFalse(l.isSignalled()); + awaitTermination(t); + assertFalse(l.isSignalled()); + } + + /** + * awaitNanos/timed await with 0 wait times out immediately + */ + public void testAwait_Zero() throws InterruptedException { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertTrue(c.awaitNanos(0L) <= 0); + assertFalse(c.await(0L, NANOSECONDS)); + sync.release(); + } + + /** + * awaitNanos/timed await with maximum negative wait times does not underflow + */ + public void testAwait_NegativeInfinity() throws InterruptedException { + final Mutex sync = new Mutex(); + final ConditionObject c = sync.newCondition(); + sync.acquire(); + assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0); + assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS)); + sync.release(); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java new file mode 100644 index 00000000000..e7fa0316197 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java @@ -0,0 +1,955 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; + +import junit.framework.Test; + +public class ArrayBlockingQueueTest extends JSR166TestCase { + + public static class Fair extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new ArrayBlockingQueue(SIZE, true); + } + } + + public static class NonFair extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new ArrayBlockingQueue(SIZE, false); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(ArrayBlockingQueueTest.class, + new Fair().testSuite(), + new NonFair().testSuite()); + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private ArrayBlockingQueue populatedQueue(int n) { + ArrayBlockingQueue q = new ArrayBlockingQueue(n); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; i++) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertEquals(n, q.size()); + return q; + } + + /** + * A new queue has the indicated capacity + */ + public void testConstructor1() { + assertEquals(SIZE, new ArrayBlockingQueue(SIZE).remainingCapacity()); + } + + /** + * Constructor throws IAE if capacity argument nonpositive + */ + public void testConstructor2() { + try { + new ArrayBlockingQueue(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new ArrayBlockingQueue(1, true, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + Collection elements = Arrays.asList(new Integer[SIZE]); + try { + new ArrayBlockingQueue(SIZE, false, elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + try { + new ArrayBlockingQueue(SIZE, false, elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from too large collection throws IAE + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + try { + new ArrayBlockingQueue(SIZE - 1, false, elements); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor7() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE, true, elements); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * Queue transitions from empty to full when elements added + */ + public void testEmptyFull() { + ArrayBlockingQueue q = new ArrayBlockingQueue(2); + assertTrue(q.isEmpty()); + assertEquals(2, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertFalse(q.offer(three)); + } + + /** + * remainingCapacity decreases on add, increases on remove + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertEquals(i, q.remove()); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertTrue(q.add(i)); + } + } + + /** + * Offer succeeds if not full; fails if full + */ + public void testOffer() { + ArrayBlockingQueue q = new ArrayBlockingQueue(1); + assertTrue(q.offer(zero)); + assertFalse(q.offer(one)); + } + + /** + * add succeeds if not full; throws ISE if full + */ + public void testAdd() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.add(new Integer(i))); + } + assertEquals(0, q.remainingCapacity()); + try { + q.add(new Integer(SIZE)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + ArrayBlockingQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll throws ISE if not enough room + */ + public void testAddAll4() { + ArrayBlockingQueue q = new ArrayBlockingQueue(1); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * Queue contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * all elements successfully put are contained + */ + public void testPut() throws InterruptedException { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.put(x); + assertTrue(q.contains(x)); + } + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly if full + */ + public void testBlockingPut() throws InterruptedException { + final ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) + q.put(i); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + + Thread.currentThread().interrupt(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly waiting for take when full + */ + public void testPutWithTake() throws InterruptedException { + final int capacity = 2; + final ArrayBlockingQueue q = new ArrayBlockingQueue(capacity); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < capacity; i++) + q.put(i); + pleaseTake.countDown(); + q.put(86); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + assertEquals(0, q.take()); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offer times out if full and elements not taken + */ + public void testTimedOffer() throws InterruptedException { + final ArrayBlockingQueue q = new ArrayBlockingQueue(2); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Object()); + q.put(new Object()); + long startTime = System.nanoTime(); + assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take retrieves elements in FIFO order + */ + public void testTake() throws InterruptedException { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + } + + /** + * Take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final ArrayBlockingQueue q = populatedQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + checkEmpty(q); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch aboutToWait = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + } + aboutToWait.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) { + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + }}); + + await(aboutToWait); + waitForThreadToEnterWaitState(t, LONG_DELAY_MS); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + ArrayBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + assertEquals(i, q.poll()); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + ArrayBlockingQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertEquals(SIZE, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + ArrayBlockingQueue q = populatedQueue(SIZE); + ArrayBlockingQueue p = new ArrayBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + ArrayBlockingQueue q = populatedQueue(SIZE); + ArrayBlockingQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + ArrayBlockingQueue q = populatedQueue(SIZE); + ArrayBlockingQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + void checkToArray(ArrayBlockingQueue q) { + int size = q.size(); + Object[] o = q.toArray(); + assertEquals(size, o.length); + Iterator it = q.iterator(); + for (int i = 0; i < size; i++) { + Integer x = (Integer) it.next(); + assertEquals((Integer)o[0] + i, (int) x); + assertSame(o[i], x); + } + } + + /** + * toArray() contains all elements in FIFO order + */ + public void testToArray() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + q.add(i); + } + // Provoke wraparound + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + assertEquals(i, q.poll()); + checkToArray(q); + q.add(SIZE + i); + } + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + assertEquals(SIZE + i, q.poll()); + } + } + + void checkToArray2(ArrayBlockingQueue q) { + int size = q.size(); + Integer[] a1 = (size == 0) ? null : new Integer[size - 1]; + Integer[] a2 = new Integer[size]; + Integer[] a3 = new Integer[size + 2]; + if (size > 0) Arrays.fill(a1, 42); + Arrays.fill(a2, 42); + Arrays.fill(a3, 42); + Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1); + Integer[] b2 = (Integer[]) q.toArray(a2); + Integer[] b3 = (Integer[]) q.toArray(a3); + assertSame(a2, b2); + assertSame(a3, b3); + Iterator it = q.iterator(); + for (int i = 0; i < size; i++) { + Integer x = (Integer) it.next(); + assertSame(b1[i], x); + assertEquals(b1[0] + i, (int) x); + assertSame(b2[i], x); + assertSame(b3[i], x); + } + assertNull(a3[size]); + assertEquals(42, (int) a3[size + 1]); + if (size > 0) { + assertNotSame(a1, b1); + assertEquals(size, b1.length); + for (int i = 0; i < a1.length; i++) { + assertEquals(42, (int) a1[i]); + } + } + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE); + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + q.add(i); + } + // Provoke wraparound + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + assertEquals(i, q.poll()); + checkToArray2(q); + q.add(SIZE + i); + } + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + assertEquals(SIZE + i, q.poll()); + } + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + ArrayBlockingQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() throws InterruptedException { + ArrayBlockingQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + + it = q.iterator(); + for (i = 0; it.hasNext(); i++) + assertEquals(it.next(), q.take()); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new ArrayBlockingQueue(SIZE).iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final ArrayBlockingQueue q = new ArrayBlockingQueue(3); + q.add(two); + q.add(one); + q.add(three); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertSame(it.next(), one); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final ArrayBlockingQueue q = new ArrayBlockingQueue(3); + q.add(one); + q.add(two); + q.add(three); + + assertEquals("queue should be full", 0, q.remainingCapacity()); + + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final ArrayBlockingQueue q = new ArrayBlockingQueue(3); + q.add(one); + q.add(two); + q.add(three); + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + assertEquals(0, q.size()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + ArrayBlockingQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * offer transfers elements across Executor tasks + */ + public void testOfferInExecutor() { + final ArrayBlockingQueue q = new ArrayBlockingQueue(2); + q.add(one); + q.add(two); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(q.offer(three)); + threadsStarted.await(); + assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, q.remainingCapacity()); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + assertEquals(0, q.remainingCapacity()); + assertSame(one, q.take()); + }}); + } + } + + /** + * timed poll retrieves elements across Executor threads + */ + public void testPollInExecutor() { + final ArrayBlockingQueue q = new ArrayBlockingQueue(2); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * A deserialized serialized queue has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * drainTo(c) empties queue into another collection c + */ + public void testDrainTo() { + ArrayBlockingQueue q = populatedQueue(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(SIZE, l.size()); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); + } + + /** + * drainTo empties full queue, unblocking a waiting put. + */ + public void testDrainToWithActivePut() throws InterruptedException { + final ArrayBlockingQueue q = populatedQueue(SIZE); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Integer(SIZE + 1)); + }}); + + t.start(); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + t.join(); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE * 2); + for (int i = 0; i < SIZE + 2; ++i) { + for (int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(k, l.size()); + assertEquals(SIZE - k, q.size()); + for (int j = 0; j < k; ++j) + assertEquals(l.get(j), new Integer(j)); + do {} while (q.poll() != null); + } + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection[] qs = { + new ArrayBlockingQueue(10), + populatedQueue(2), + }; + + for (Collection q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java b/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java new file mode 100644 index 00000000000..4241f59ef3d --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java @@ -0,0 +1,945 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Random; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ArrayDequeTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ArrayDequeTest.class); + } + + /** + * Returns a new deque of given size containing consecutive + * Integers 0 ... n. + */ + private ArrayDeque populatedDeque(int n) { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; ++i) + assertTrue(q.offerLast(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * new deque is empty + */ + public void testConstructor1() { + assertEquals(0, new ArrayDeque().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new ArrayDeque((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new ArrayDeque(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new ArrayDeque(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Deque contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ArrayDeque q = new ArrayDeque(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.removeFirst(); + q.removeFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.removeFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * push(null) throws NPE + */ + public void testPushNull() { + ArrayDeque q = new ArrayDeque(1); + try { + q.push(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * peekFirst() returns element inserted with push + */ + public void testPush() { + ArrayDeque q = populatedDeque(3); + q.pollLast(); + q.push(four); + assertSame(four, q.peekFirst()); + } + + /** + * pop() removes next element, or throws NSEE if empty + */ + public void testPop() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pop()); + } + try { + q.pop(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * offer(null) throws NPE + */ + public void testOfferNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.offer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offerFirst(null) throws NPE + */ + public void testOfferFirstNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.offerFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offerLast(null) throws NPE + */ + public void testOfferLastNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.offerLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offer(x) succeeds + */ + public void testOffer() { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.offer(zero)); + assertTrue(q.offer(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * offerFirst(x) succeeds + */ + public void testOfferFirst() { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.offerFirst(zero)); + assertTrue(q.offerFirst(one)); + assertSame(one, q.peekFirst()); + assertSame(zero, q.peekLast()); + } + + /** + * offerLast(x) succeeds + */ + public void testOfferLast() { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.offerLast(zero)); + assertTrue(q.offerLast(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addFirst(null) throws NPE + */ + public void testAddFirstNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.addFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addLast(null) throws NPE + */ + public void testAddLastNull() { + ArrayDeque q = new ArrayDeque(); + try { + q.addLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * add(x) succeeds + */ + public void testAdd() { + ArrayDeque q = new ArrayDeque(); + assertTrue(q.add(zero)); + assertTrue(q.add(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * addFirst(x) succeeds + */ + public void testAddFirst() { + ArrayDeque q = new ArrayDeque(); + q.addFirst(zero); + q.addFirst(one); + assertSame(one, q.peekFirst()); + assertSame(zero, q.peekLast()); + } + + /** + * addLast(x) succeeds + */ + public void testAddLast() { + ArrayDeque q = new ArrayDeque(); + q.addLast(zero); + q.addLast(one); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + ArrayDeque q = new ArrayDeque(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + ArrayDeque q = new ArrayDeque(); + try { + q.addAll(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + ArrayDeque q = new ArrayDeque(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Deque contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ArrayDeque q = new ArrayDeque(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * pollFirst() succeeds unless empty + */ + public void testPollFirst() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * pollLast() succeeds unless empty + */ + public void testPollLast() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollLast()); + } + + /** + * poll() succeeds unless empty + */ + public void testPoll() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * remove() removes next element, or throws NSEE if empty + */ + public void testRemove() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * peekFirst() returns next element, or null if empty + */ + public void testPeekFirst() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peekFirst()); + assertEquals(i, q.pollFirst()); + assertTrue(q.peekFirst() == null || + !q.peekFirst().equals(i)); + } + assertNull(q.peekFirst()); + } + + /** + * peek() returns next element, or null if empty + */ + public void testPeek() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * peekLast() returns next element, or null if empty + */ + public void testPeekLast() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.peekLast()); + assertEquals(i, q.pollLast()); + assertTrue(q.peekLast() == null || + !q.peekLast().equals(i)); + } + assertNull(q.peekLast()); + } + + /** + * element() returns first element, or throws NSEE if empty + */ + public void testElement() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * getFirst() returns first element, or throws NSEE if empty + */ + public void testFirstElement() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.getFirst()); + assertEquals(i, q.pollFirst()); + } + try { + q.getFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * getLast() returns last element, or throws NSEE if empty + */ + public void testLastElement() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.getLast()); + assertEquals(i, q.pollLast()); + } + try { + q.getLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirst() removes first element, or throws NSEE if empty + */ + public void testRemoveFirst() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.removeFirst()); + } + try { + q.removeFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekFirst()); + } + + /** + * removeLast() removes last element, or throws NSEE if empty + */ + public void testRemoveLast() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.removeLast()); + } + try { + q.removeLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirstOccurrence(x) removes x and returns true if present + */ + public void testRemoveFirstOccurrence() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + assertFalse(q.removeFirstOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * removeLastOccurrence(x) removes x and returns true if present + */ + public void testRemoveLastOccurrence() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + assertFalse(q.removeLastOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + ArrayDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + assertEquals(i, q.pollFirst()); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + ArrayDeque q = populatedDeque(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertTrue(q.add(new Integer(1))); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + ArrayDeque q = populatedDeque(SIZE); + ArrayDeque p = new ArrayDeque(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + assertTrue(p.add(new Integer(i))); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + ArrayDeque q = populatedDeque(SIZE); + ArrayDeque p = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + assertEquals(changed, (i > 0)); + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.removeFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + ArrayDeque q = populatedDeque(SIZE); + ArrayDeque p = populatedDeque(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + assertFalse(q.contains(p.removeFirst())); + } + } + } + + void checkToArray(ArrayDeque q) { + int size = q.size(); + Object[] o = q.toArray(); + assertEquals(size, o.length); + Iterator it = q.iterator(); + for (int i = 0; i < size; i++) { + Integer x = (Integer) it.next(); + assertEquals((Integer)o[0] + i, (int) x); + assertSame(o[i], x); + } + } + + /** + * toArray() contains all elements in FIFO order + */ + public void testToArray() { + ArrayDeque q = new ArrayDeque(); + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + q.addLast(i); + } + // Provoke wraparound + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + assertEquals(i, q.poll()); + q.addLast(SIZE + i); + } + for (int i = 0; i < SIZE; i++) { + checkToArray(q); + assertEquals(SIZE + i, q.poll()); + } + } + + void checkToArray2(ArrayDeque q) { + int size = q.size(); + Integer[] a1 = (size == 0) ? null : new Integer[size - 1]; + Integer[] a2 = new Integer[size]; + Integer[] a3 = new Integer[size + 2]; + if (size > 0) Arrays.fill(a1, 42); + Arrays.fill(a2, 42); + Arrays.fill(a3, 42); + Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1); + Integer[] b2 = (Integer[]) q.toArray(a2); + Integer[] b3 = (Integer[]) q.toArray(a3); + assertSame(a2, b2); + assertSame(a3, b3); + Iterator it = q.iterator(); + for (int i = 0; i < size; i++) { + Integer x = (Integer) it.next(); + assertSame(b1[i], x); + assertEquals(b1[0] + i, (int) x); + assertSame(b2[i], x); + assertSame(b3[i], x); + } + assertNull(a3[size]); + assertEquals(42, (int) a3[size + 1]); + if (size > 0) { + assertNotSame(a1, b1); + assertEquals(size, b1.length); + for (int i = 0; i < a1.length; i++) { + assertEquals(42, (int) a1[i]); + } + } + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + ArrayDeque q = new ArrayDeque(); + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + q.addLast(i); + } + // Provoke wraparound + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + assertEquals(i, q.poll()); + q.addLast(SIZE + i); + } + for (int i = 0; i < SIZE; i++) { + checkToArray2(q); + assertEquals(SIZE + i, q.poll()); + } + } + + /** + * toArray(null) throws NullPointerException + */ + public void testToArray_NullArg() { + ArrayDeque l = new ArrayDeque(); + l.add(new Object()); + try { + l.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + ArrayDeque l = new ArrayDeque(); + l.add(new Integer(5)); + try { + l.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * Iterator iterates through all elements + */ + public void testIterator() { + ArrayDeque q = populatedDeque(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + Deque c = new ArrayDeque(); + assertIteratorExhausted(c.iterator()); + assertIteratorExhausted(c.descendingIterator()); + } + + /** + * Iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final ArrayDeque q = new ArrayDeque(); + q.add(one); + q.add(two); + q.add(three); + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + } + + /** + * iterator.remove() removes current element + */ + public void testIteratorRemove() { + final ArrayDeque q = new ArrayDeque(); + final Random rng = new Random(); + for (int iters = 0; iters < 100; ++iters) { + int max = rng.nextInt(5) + 2; + int split = rng.nextInt(max - 1) + 1; + for (int j = 1; j <= max; ++j) + q.add(new Integer(j)); + Iterator it = q.iterator(); + for (int j = 1; j <= split; ++j) + assertEquals(it.next(), new Integer(j)); + it.remove(); + assertEquals(it.next(), new Integer(split + 1)); + for (int j = 1; j <= split; ++j) + q.remove(new Integer(j)); + it = q.iterator(); + for (int j = split + 1; j <= max; ++j) { + assertEquals(it.next(), new Integer(j)); + it.remove(); + } + assertFalse(it.hasNext()); + assertTrue(q.isEmpty()); + } + } + + /** + * Descending iterator iterates through all elements + */ + public void testDescendingIterator() { + ArrayDeque q = populatedDeque(SIZE); + int i = 0; + Iterator it = q.descendingIterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + assertFalse(it.hasNext()); + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * Descending iterator ordering is reverse FIFO + */ + public void testDescendingIteratorOrdering() { + final ArrayDeque q = new ArrayDeque(); + for (int iters = 0; iters < 100; ++iters) { + q.add(new Integer(3)); + q.add(new Integer(2)); + q.add(new Integer(1)); + int k = 0; + for (Iterator it = q.descendingIterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + q.remove(); + q.remove(); + q.remove(); + } + } + + /** + * descendingIterator.remove() removes current element + */ + public void testDescendingIteratorRemove() { + final ArrayDeque q = new ArrayDeque(); + final Random rng = new Random(); + for (int iters = 0; iters < 100; ++iters) { + int max = rng.nextInt(5) + 2; + int split = rng.nextInt(max - 1) + 1; + for (int j = max; j >= 1; --j) + q.add(new Integer(j)); + Iterator it = q.descendingIterator(); + for (int j = 1; j <= split; ++j) + assertEquals(it.next(), new Integer(j)); + it.remove(); + assertEquals(it.next(), new Integer(split + 1)); + for (int j = 1; j <= split; ++j) + q.remove(new Integer(j)); + it = q.descendingIterator(); + for (int j = split + 1; j <= max; ++j) { + assertEquals(it.next(), new Integer(j)); + it.remove(); + } + assertFalse(it.hasNext()); + assertTrue(q.isEmpty()); + } + } + + /** + * toString() contains toStrings of elements + */ + public void testToString() { + ArrayDeque q = populatedDeque(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized deque has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedDeque(SIZE); + Queue y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Deque[] qs = { + new ArrayDeque(), + populatedDeque(2), + }; + + for (Deque q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + assertFalse(q.removeFirstOccurrence(null)); + assertFalse(q.removeLastOccurrence(null)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/Atomic8Test.java b/jdk/test/java/util/concurrent/tck/Atomic8Test.java new file mode 100644 index 00000000000..1e49299c21a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java @@ -0,0 +1,596 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class Atomic8Test extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(Atomic8Test.class); + } + + /* + * Tests of atomic class methods accepting lambdas + * introduced in JDK8. + */ + + static long addLong17(long x) { return x + 17; } + static int addInt17(int x) { return x + 17; } + static Integer addInteger17(Integer x) { + return new Integer(x.intValue() + 17); + } + static Integer sumInteger(Integer x, Integer y) { + return new Integer(x.intValue() + y.intValue()); + } + + volatile long aLongField; + volatile int anIntField; + volatile Integer anIntegerField; + + AtomicLongFieldUpdater aLongFieldUpdater() { + return AtomicLongFieldUpdater.newUpdater + (Atomic8Test.class, "aLongField"); + } + + AtomicIntegerFieldUpdater anIntFieldUpdater() { + return AtomicIntegerFieldUpdater.newUpdater + (Atomic8Test.class, "anIntField"); + } + + AtomicReferenceFieldUpdater anIntegerFieldUpdater() { + return AtomicReferenceFieldUpdater.newUpdater + (Atomic8Test.class, Integer.class, "anIntegerField"); + } + + /** + * AtomicLong getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testLongGetAndUpdate() { + AtomicLong a = new AtomicLong(1L); + assertEquals(1L, a.getAndUpdate(Atomic8Test::addLong17)); + assertEquals(18L, a.getAndUpdate(Atomic8Test::addLong17)); + assertEquals(35L, a.get()); + } + + /** + * AtomicLong updateAndGet updates with supplied function and + * returns result. + */ + public void testLongUpdateAndGet() { + AtomicLong a = new AtomicLong(1L); + assertEquals(18L, a.updateAndGet(Atomic8Test::addLong17)); + assertEquals(35L, a.updateAndGet(Atomic8Test::addLong17)); + } + + /** + * AtomicLong getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testLongGetAndAccumulate() { + AtomicLong a = new AtomicLong(1L); + assertEquals(1L, a.getAndAccumulate(2L, Long::sum)); + assertEquals(3L, a.getAndAccumulate(3L, Long::sum)); + assertEquals(6L, a.get()); + } + + /** + * AtomicLong accumulateAndGet updates with supplied function and + * returns result. + */ + public void testLongAccumulateAndGet() { + AtomicLong a = new AtomicLong(1L); + assertEquals(7L, a.accumulateAndGet(6L, Long::sum)); + assertEquals(10L, a.accumulateAndGet(3L, Long::sum)); + assertEquals(10L, a.get()); + } + + /** + * AtomicInteger getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testIntGetAndUpdate() { + AtomicInteger a = new AtomicInteger(1); + assertEquals(1, a.getAndUpdate(Atomic8Test::addInt17)); + assertEquals(18, a.getAndUpdate(Atomic8Test::addInt17)); + assertEquals(35, a.get()); + } + + /** + * AtomicInteger updateAndGet updates with supplied function and + * returns result. + */ + public void testIntUpdateAndGet() { + AtomicInteger a = new AtomicInteger(1); + assertEquals(18, a.updateAndGet(Atomic8Test::addInt17)); + assertEquals(35, a.updateAndGet(Atomic8Test::addInt17)); + assertEquals(35, a.get()); + } + + /** + * AtomicInteger getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testIntGetAndAccumulate() { + AtomicInteger a = new AtomicInteger(1); + assertEquals(1, a.getAndAccumulate(2, Integer::sum)); + assertEquals(3, a.getAndAccumulate(3, Integer::sum)); + assertEquals(6, a.get()); + } + + /** + * AtomicInteger accumulateAndGet updates with supplied function and + * returns result. + */ + public void testIntAccumulateAndGet() { + AtomicInteger a = new AtomicInteger(1); + assertEquals(7, a.accumulateAndGet(6, Integer::sum)); + assertEquals(10, a.accumulateAndGet(3, Integer::sum)); + assertEquals(10, a.get()); + } + + /** + * AtomicReference getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testReferenceGetAndUpdate() { + AtomicReference a = new AtomicReference(one); + assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17)); + assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.get()); + } + + /** + * AtomicReference updateAndGet updates with supplied function and + * returns result. + */ + public void testReferenceUpdateAndGet() { + AtomicReference a = new AtomicReference(one); + assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.get()); + } + + /** + * AtomicReference getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testReferenceGetAndAccumulate() { + AtomicReference a = new AtomicReference(one); + assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger)); + assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger)); + assertEquals(new Integer(6), a.get()); + } + + /** + * AtomicReference accumulateAndGet updates with supplied function and + * returns result. + */ + public void testReferenceAccumulateAndGet() { + AtomicReference a = new AtomicReference(one); + assertEquals(new Integer(7), a.accumulateAndGet(6, Atomic8Test::sumInteger)); + assertEquals(new Integer(10), a.accumulateAndGet(3, Atomic8Test::sumInteger)); + assertEquals(new Integer(10), a.get()); + } + + /** + * AtomicLongArray getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testLongArrayGetAndUpdate() { + AtomicLongArray a = new AtomicLongArray(1); + a.set(0, 1); + assertEquals(1L, a.getAndUpdate(0, Atomic8Test::addLong17)); + assertEquals(18L, a.getAndUpdate(0, Atomic8Test::addLong17)); + assertEquals(35L, a.get(0)); + } + + /** + * AtomicLongArray updateAndGet updates with supplied function and + * returns result. + */ + public void testLongArrayUpdateAndGet() { + AtomicLongArray a = new AtomicLongArray(1); + a.set(0, 1); + assertEquals(18L, a.updateAndGet(0, Atomic8Test::addLong17)); + assertEquals(35L, a.updateAndGet(0, Atomic8Test::addLong17)); + assertEquals(35L, a.get(0)); + } + + /** + * AtomicLongArray getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testLongArrayGetAndAccumulate() { + AtomicLongArray a = new AtomicLongArray(1); + a.set(0, 1); + assertEquals(1L, a.getAndAccumulate(0, 2L, Long::sum)); + assertEquals(3L, a.getAndAccumulate(0, 3L, Long::sum)); + assertEquals(6L, a.get(0)); + } + + /** + * AtomicLongArray accumulateAndGet updates with supplied function and + * returns result. + */ + public void testLongArrayAccumulateAndGet() { + AtomicLongArray a = new AtomicLongArray(1); + a.set(0, 1); + assertEquals(7L, a.accumulateAndGet(0, 6L, Long::sum)); + assertEquals(10L, a.accumulateAndGet(0, 3L, Long::sum)); + assertEquals(10L, a.get(0)); + } + + /** + * AtomicIntegerArray getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testIntArrayGetAndUpdate() { + AtomicIntegerArray a = new AtomicIntegerArray(1); + a.set(0, 1); + assertEquals(1, a.getAndUpdate(0, Atomic8Test::addInt17)); + assertEquals(18, a.getAndUpdate(0, Atomic8Test::addInt17)); + assertEquals(35, a.get(0)); + } + + /** + * AtomicIntegerArray updateAndGet updates with supplied function and + * returns result. + */ + public void testIntArrayUpdateAndGet() { + AtomicIntegerArray a = new AtomicIntegerArray(1); + a.set(0, 1); + assertEquals(18, a.updateAndGet(0, Atomic8Test::addInt17)); + assertEquals(35, a.updateAndGet(0, Atomic8Test::addInt17)); + assertEquals(35, a.get(0)); + } + + /** + * AtomicIntegerArray getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testIntArrayGetAndAccumulate() { + AtomicIntegerArray a = new AtomicIntegerArray(1); + a.set(0, 1); + assertEquals(1, a.getAndAccumulate(0, 2, Integer::sum)); + assertEquals(3, a.getAndAccumulate(0, 3, Integer::sum)); + assertEquals(6, a.get(0)); + } + + /** + * AtomicIntegerArray accumulateAndGet updates with supplied function and + * returns result. + */ + public void testIntArrayAccumulateAndGet() { + AtomicIntegerArray a = new AtomicIntegerArray(1); + a.set(0, 1); + assertEquals(7, a.accumulateAndGet(0, 6, Integer::sum)); + assertEquals(10, a.accumulateAndGet(0, 3, Integer::sum)); + } + + /** + * AtomicReferenceArray getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testReferenceArrayGetAndUpdate() { + AtomicReferenceArray a = new AtomicReferenceArray(1); + a.set(0, one); + assertEquals(new Integer(1), a.getAndUpdate(0, Atomic8Test::addInteger17)); + assertEquals(new Integer(18), a.getAndUpdate(0, Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.get(0)); + } + + /** + * AtomicReferenceArray updateAndGet updates with supplied function and + * returns result. + */ + public void testReferenceArrayUpdateAndGet() { + AtomicReferenceArray a = new AtomicReferenceArray(1); + a.set(0, one); + assertEquals(new Integer(18), a.updateAndGet(0, Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.updateAndGet(0, Atomic8Test::addInteger17)); + } + + /** + * AtomicReferenceArray getAndAccumulate returns previous value and updates + * with supplied function. + */ + public void testReferenceArrayGetAndAccumulate() { + AtomicReferenceArray a = new AtomicReferenceArray(1); + a.set(0, one); + assertEquals(new Integer(1), a.getAndAccumulate(0, 2, Atomic8Test::sumInteger)); + assertEquals(new Integer(3), a.getAndAccumulate(0, 3, Atomic8Test::sumInteger)); + assertEquals(new Integer(6), a.get(0)); + } + + /** + * AtomicReferenceArray accumulateAndGet updates with supplied function and + * returns result. + */ + public void testReferenceArrayAccumulateAndGet() { + AtomicReferenceArray a = new AtomicReferenceArray(1); + a.set(0, one); + assertEquals(new Integer(7), a.accumulateAndGet(0, 6, Atomic8Test::sumInteger)); + assertEquals(new Integer(10), a.accumulateAndGet(0, 3, Atomic8Test::sumInteger)); + } + + /** + * AtomicLongFieldUpdater getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testLongFieldUpdaterGetAndUpdate() { + AtomicLongFieldUpdater a = aLongFieldUpdater(); + a.set(this, 1); + assertEquals(1L, a.getAndUpdate(this, Atomic8Test::addLong17)); + assertEquals(18L, a.getAndUpdate(this, Atomic8Test::addLong17)); + assertEquals(35L, a.get(this)); + assertEquals(35L, aLongField); + } + + /** + * AtomicLongFieldUpdater updateAndGet updates with supplied function and + * returns result. + */ + public void testLongFieldUpdaterUpdateAndGet() { + AtomicLongFieldUpdater a = aLongFieldUpdater(); + a.set(this, 1); + assertEquals(18L, a.updateAndGet(this, Atomic8Test::addLong17)); + assertEquals(35L, a.updateAndGet(this, Atomic8Test::addLong17)); + assertEquals(35L, a.get(this)); + assertEquals(35L, aLongField); + } + + /** + * AtomicLongFieldUpdater getAndAccumulate returns previous value + * and updates with supplied function. + */ + public void testLongFieldUpdaterGetAndAccumulate() { + AtomicLongFieldUpdater a = aLongFieldUpdater(); + a.set(this, 1); + assertEquals(1L, a.getAndAccumulate(this, 2L, Long::sum)); + assertEquals(3L, a.getAndAccumulate(this, 3L, Long::sum)); + assertEquals(6L, a.get(this)); + assertEquals(6L, aLongField); + } + + /** + * AtomicLongFieldUpdater accumulateAndGet updates with supplied + * function and returns result. + */ + public void testLongFieldUpdaterAccumulateAndGet() { + AtomicLongFieldUpdater a = aLongFieldUpdater(); + a.set(this, 1); + assertEquals(7L, a.accumulateAndGet(this, 6L, Long::sum)); + assertEquals(10L, a.accumulateAndGet(this, 3L, Long::sum)); + assertEquals(10L, a.get(this)); + assertEquals(10L, aLongField); + } + + /** + * AtomicIntegerFieldUpdater getAndUpdate returns previous value and updates + * result of supplied function + */ + public void testIntegerFieldUpdaterGetAndUpdate() { + AtomicIntegerFieldUpdater a = anIntFieldUpdater(); + a.set(this, 1); + assertEquals(1, a.getAndUpdate(this, Atomic8Test::addInt17)); + assertEquals(18, a.getAndUpdate(this, Atomic8Test::addInt17)); + assertEquals(35, a.get(this)); + assertEquals(35, anIntField); + } + + /** + * AtomicIntegerFieldUpdater updateAndGet updates with supplied function and + * returns result. + */ + public void testIntegerFieldUpdaterUpdateAndGet() { + AtomicIntegerFieldUpdater a = anIntFieldUpdater(); + a.set(this, 1); + assertEquals(18, a.updateAndGet(this, Atomic8Test::addInt17)); + assertEquals(35, a.updateAndGet(this, Atomic8Test::addInt17)); + assertEquals(35, a.get(this)); + assertEquals(35, anIntField); + } + + /** + * AtomicIntegerFieldUpdater getAndAccumulate returns previous value + * and updates with supplied function. + */ + public void testIntegerFieldUpdaterGetAndAccumulate() { + AtomicIntegerFieldUpdater a = anIntFieldUpdater(); + a.set(this, 1); + assertEquals(1, a.getAndAccumulate(this, 2, Integer::sum)); + assertEquals(3, a.getAndAccumulate(this, 3, Integer::sum)); + assertEquals(6, a.get(this)); + assertEquals(6, anIntField); + } + + /** + * AtomicIntegerFieldUpdater accumulateAndGet updates with supplied + * function and returns result. + */ + public void testIntegerFieldUpdaterAccumulateAndGet() { + AtomicIntegerFieldUpdater a = anIntFieldUpdater(); + a.set(this, 1); + assertEquals(7, a.accumulateAndGet(this, 6, Integer::sum)); + assertEquals(10, a.accumulateAndGet(this, 3, Integer::sum)); + assertEquals(10, a.get(this)); + assertEquals(10, anIntField); + } + + /** + * AtomicReferenceFieldUpdater getAndUpdate returns previous value + * and updates result of supplied function + */ + public void testReferenceFieldUpdaterGetAndUpdate() { + AtomicReferenceFieldUpdater a = anIntegerFieldUpdater(); + a.set(this, one); + assertEquals(new Integer(1), a.getAndUpdate(this, Atomic8Test::addInteger17)); + assertEquals(new Integer(18), a.getAndUpdate(this, Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.get(this)); + assertEquals(new Integer(35), anIntegerField); + } + + /** + * AtomicReferenceFieldUpdater updateAndGet updates with supplied + * function and returns result. + */ + public void testReferenceFieldUpdaterUpdateAndGet() { + AtomicReferenceFieldUpdater a = anIntegerFieldUpdater(); + a.set(this, one); + assertEquals(new Integer(18), a.updateAndGet(this, Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.updateAndGet(this, Atomic8Test::addInteger17)); + assertEquals(new Integer(35), a.get(this)); + assertEquals(new Integer(35), anIntegerField); + } + + /** + * AtomicReferenceFieldUpdater returns previous value and updates + * with supplied function. + */ + public void testReferenceFieldUpdaterGetAndAccumulate() { + AtomicReferenceFieldUpdater a = anIntegerFieldUpdater(); + a.set(this, one); + assertEquals(new Integer(1), a.getAndAccumulate(this, 2, Atomic8Test::sumInteger)); + assertEquals(new Integer(3), a.getAndAccumulate(this, 3, Atomic8Test::sumInteger)); + assertEquals(new Integer(6), a.get(this)); + assertEquals(new Integer(6), anIntegerField); + } + + /** + * AtomicReferenceFieldUpdater accumulateAndGet updates with + * supplied function and returns result. + */ + public void testReferenceFieldUpdaterAccumulateAndGet() { + AtomicReferenceFieldUpdater a = anIntegerFieldUpdater(); + a.set(this, one); + assertEquals(new Integer(7), a.accumulateAndGet(this, 6, Atomic8Test::sumInteger)); + assertEquals(new Integer(10), a.accumulateAndGet(this, 3, Atomic8Test::sumInteger)); + assertEquals(new Integer(10), a.get(this)); + assertEquals(new Integer(10), anIntegerField); + } + + /** + * All Atomic getAndUpdate methods throw NullPointerException on + * null function argument + */ + public void testGetAndUpdateNPE() { + Runnable[] throwingActions = { + () -> new AtomicLong().getAndUpdate(null), + () -> new AtomicInteger().getAndUpdate(null), + () -> new AtomicReference().getAndUpdate(null), + () -> new AtomicLongArray(1).getAndUpdate(0, null), + () -> new AtomicIntegerArray(1).getAndUpdate(0, null), + () -> new AtomicReferenceArray(1).getAndUpdate(0, null), + () -> aLongFieldUpdater().getAndUpdate(this, null), + () -> anIntFieldUpdater().getAndUpdate(this, null), + () -> anIntegerFieldUpdater().getAndUpdate(this, null), + ////() -> aLongFieldUpdater().getAndUpdate(null, Atomic8Test::addLong17), + ////() -> anIntFieldUpdater().getAndUpdate(null, Atomic8Test::addInt17), + ////() -> anIntegerFieldUpdater().getAndUpdate(null, Atomic8Test::addInteger17), + }; + assertThrows(NullPointerException.class, throwingActions); + } + + /** + * All Atomic updateAndGet methods throw NullPointerException on null function argument + */ + public void testUpdateAndGetNPE() { + Runnable[] throwingActions = { + () -> new AtomicLong().updateAndGet(null), + () -> new AtomicInteger().updateAndGet(null), + () -> new AtomicReference().updateAndGet(null), + () -> new AtomicLongArray(1).updateAndGet(0, null), + () -> new AtomicIntegerArray(1).updateAndGet(0, null), + () -> new AtomicReferenceArray(1).updateAndGet(0, null), + () -> aLongFieldUpdater().updateAndGet(this, null), + () -> anIntFieldUpdater().updateAndGet(this, null), + () -> anIntegerFieldUpdater().updateAndGet(this, null), + }; + assertThrows(NullPointerException.class, throwingActions); + } + + /** + * All Atomic getAndAccumulate methods throw NullPointerException + * on null function argument + */ + public void testGetAndAccumulateNPE() { + Runnable[] throwingActions = { + () -> new AtomicLong().getAndAccumulate(1L, null), + () -> new AtomicInteger().getAndAccumulate(1, null), + () -> new AtomicReference().getAndAccumulate(one, null), + () -> new AtomicLongArray(1).getAndAccumulate(0, 1L, null), + () -> new AtomicIntegerArray(1).getAndAccumulate(0, 1, null), + () -> new AtomicReferenceArray(1).getAndAccumulate(0, one, null), + () -> aLongFieldUpdater().getAndAccumulate(this, 1L, null), + () -> anIntFieldUpdater().getAndAccumulate(this, 1, null), + () -> anIntegerFieldUpdater().getAndAccumulate(this, one, null), + }; + assertThrows(NullPointerException.class, throwingActions); + } + + /** + * All Atomic accumulateAndGet methods throw NullPointerException + * on null function argument + */ + public void testAccumulateAndGetNPE() { + Runnable[] throwingActions = { + () -> new AtomicLong().accumulateAndGet(1L, null), + () -> new AtomicInteger().accumulateAndGet(1, null), + () -> new AtomicReference().accumulateAndGet(one, null), + () -> new AtomicLongArray(1).accumulateAndGet(0, 1L, null), + () -> new AtomicIntegerArray(1).accumulateAndGet(0, 1, null), + () -> new AtomicReferenceArray(1).accumulateAndGet(0, one, null), + () -> aLongFieldUpdater().accumulateAndGet(this, 1L, null), + () -> anIntFieldUpdater().accumulateAndGet(this, 1, null), + () -> anIntegerFieldUpdater().accumulateAndGet(this, one, null), + }; + assertThrows(NullPointerException.class, throwingActions); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java b/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java new file mode 100644 index 00000000000..91b5cfa8c1f --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java @@ -0,0 +1,169 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicBooleanTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicBooleanTest.class); + } + + /** + * constructor initializes to given value + */ + public void testConstructor() { + assertTrue(new AtomicBoolean(true).get()); + assertFalse(new AtomicBoolean(false).get()); + } + + /** + * default constructed initializes to false + */ + public void testConstructor2() { + AtomicBoolean ai = new AtomicBoolean(); + assertFalse(ai.get()); + } + + /** + * get returns the last value set + */ + public void testGetSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertTrue(ai.get()); + ai.set(false); + assertFalse(ai.get()); + ai.set(true); + assertTrue(ai.get()); + } + + /** + * get returns the last value lazySet in same thread + */ + public void testGetLazySet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertTrue(ai.get()); + ai.lazySet(false); + assertFalse(ai.get()); + ai.lazySet(true); + assertTrue(ai.get()); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertTrue(ai.compareAndSet(true, false)); + assertFalse(ai.get()); + assertTrue(ai.compareAndSet(false, false)); + assertFalse(ai.get()); + assertFalse(ai.compareAndSet(true, false)); + assertFalse(ai.get()); + assertTrue(ai.compareAndSet(false, true)); + assertTrue(ai.get()); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicBoolean ai = new AtomicBoolean(true); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(false, true)) Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(true, false)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSet(true, false)); + assertFalse(ai.get()); + do {} while (!ai.weakCompareAndSet(false, false)); + assertFalse(ai.get()); + do {} while (!ai.weakCompareAndSet(false, true)); + assertTrue(ai.get()); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getAndSet(false)); + assertEquals(false, ai.getAndSet(false)); + assertEquals(false, ai.getAndSet(true)); + assertTrue(ai.get()); + } + + /** + * a deserialized serialized atomic holds same value + */ + public void testSerialization() throws Exception { + AtomicBoolean x = new AtomicBoolean(); + AtomicBoolean y = serialClone(x); + x.set(true); + AtomicBoolean z = serialClone(x); + assertTrue(x.get()); + assertFalse(y.get()); + assertTrue(z.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + AtomicBoolean ai = new AtomicBoolean(); + assertEquals(Boolean.toString(false), ai.toString()); + ai.set(true); + assertEquals(Boolean.toString(true), ai.toString()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java new file mode 100644 index 00000000000..d3d8f14f5c0 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java @@ -0,0 +1,370 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicIntegerArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicIntegerArrayTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicIntegerArrayTest.class); + } + + /** + * constructor creates array of given size with all elements zero + */ + public void testConstructor() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) + assertEquals(0, aa.get(i)); + } + + /** + * constructor with null array throws NPE + */ + public void testConstructor2NPE() { + try { + int[] a = null; + new AtomicIntegerArray(a); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * constructor with array is of same size and has all elements + */ + public void testConstructor2() { + int[] a = { 17, 3, -42, 99, -7 }; + AtomicIntegerArray aa = new AtomicIntegerArray(a); + assertEquals(a.length, aa.length()); + for (int i = 0; i < a.length; i++) + assertEquals(a[i], aa.get(i)); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + try { + aa.get(index); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.set(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.lazySet(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.compareAndSet(index, 1, 2); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.weakCompareAndSet(index, 1, 2); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.getAndAdd(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.addAndGet(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * get returns the last value set at index + */ + public void testGetSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.get(i)); + aa.set(i, 2); + assertEquals(2, aa.get(i)); + aa.set(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value lazySet at index by same thread + */ + public void testGetLazySet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.lazySet(i, 1); + assertEquals(1, aa.get(i)); + aa.lazySet(i, 2); + assertEquals(2, aa.get(i)); + aa.lazySet(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertTrue(aa.compareAndSet(i, 1, 2)); + assertTrue(aa.compareAndSet(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertFalse(aa.compareAndSet(i, -5, 7)); + assertEquals(-4, aa.get(i)); + assertTrue(aa.compareAndSet(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicIntegerArray a = new AtomicIntegerArray(1); + a.set(0, 1); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(0, 2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(0, 1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, a.get(0)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSet(i, 1, 2)); + do {} while (!aa.weakCompareAndSet(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSet(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * getAndSet returns previous value and sets to given value at given index + */ + public void testGetAndSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndSet(i, 0)); + assertEquals(0, aa.getAndSet(i, -10)); + assertEquals(-10, aa.getAndSet(i, 1)); + } + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndAdd(i, 2)); + assertEquals(3, aa.get(i)); + assertEquals(3, aa.getAndAdd(i, -4)); + assertEquals(-1, aa.get(i)); + } + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndDecrement(i)); + assertEquals(0, aa.getAndDecrement(i)); + assertEquals(-1, aa.getAndDecrement(i)); + } + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndIncrement(i)); + assertEquals(2, aa.get(i)); + aa.set(i, -2); + assertEquals(-2, aa.getAndIncrement(i)); + assertEquals(-1, aa.getAndIncrement(i)); + assertEquals(0, aa.getAndIncrement(i)); + assertEquals(1, aa.get(i)); + } + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(3, aa.addAndGet(i, 2)); + assertEquals(3, aa.get(i)); + assertEquals(-1, aa.addAndGet(i, -4)); + assertEquals(-1, aa.get(i)); + } + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(0, aa.decrementAndGet(i)); + assertEquals(-1, aa.decrementAndGet(i)); + assertEquals(-2, aa.decrementAndGet(i)); + assertEquals(-2, aa.get(i)); + } + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(2, aa.incrementAndGet(i)); + assertEquals(2, aa.get(i)); + aa.set(i, -2); + assertEquals(-1, aa.incrementAndGet(i)); + assertEquals(0, aa.incrementAndGet(i)); + assertEquals(1, aa.incrementAndGet(i)); + assertEquals(1, aa.get(i)); + } + } + + class Counter extends CheckedRunnable { + final AtomicIntegerArray aa; + volatile int counts; + Counter(AtomicIntegerArray a) { aa = a; } + public void realRun() { + for (;;) { + boolean done = true; + for (int i = 0; i < aa.length(); i++) { + int v = aa.get(i); + assertTrue(v >= 0); + if (v != 0) { + done = false; + if (aa.compareAndSet(i, v, v - 1)) + ++counts; + } + } + if (done) + break; + } + } + } + + /** + * Multiple threads using same array of counters successfully + * update a number of times equal to total count + */ + public void testCountingInMultipleThreads() throws InterruptedException { + final AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + int countdown = 10000; + for (int i = 0; i < SIZE; i++) + aa.set(i, countdown); + Counter c1 = new Counter(aa); + Counter c2 = new Counter(aa); + Thread t1 = new Thread(c1); + Thread t2 = new Thread(c2); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + assertEquals(c1.counts+c2.counts, SIZE * countdown); + } + + /** + * a deserialized serialized array holds same values + */ + public void testSerialization() throws Exception { + AtomicIntegerArray x = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) + x.set(i, -i); + AtomicIntegerArray y = serialClone(x); + assertNotSame(x, y); + assertEquals(x.length(), y.length()); + for (int i = 0; i < SIZE; i++) { + assertEquals(x.get(i), y.get(i)); + } + } + + /** + * toString returns current value. + */ + public void testToString() { + int[] a = { 17, 3, -42, 99, -7 }; + AtomicIntegerArray aa = new AtomicIntegerArray(a); + assertEquals(Arrays.toString(a), aa.toString()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java new file mode 100644 index 00000000000..5f7612de975 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java @@ -0,0 +1,363 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { + volatile int x = 0; + protected volatile int protectedField; + private volatile int privateField; + int w; + float z; + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicIntegerFieldUpdaterTest.class); + } + + // for testing subclass access + static class AtomicIntegerFieldUpdaterTestSubclass extends AtomicIntegerFieldUpdaterTest { + public void checkPrivateAccess() { + try { + AtomicIntegerFieldUpdater a = + AtomicIntegerFieldUpdater.newUpdater + (AtomicIntegerFieldUpdaterTest.class, "privateField"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + public void checkCompareAndSetProtectedSub() { + AtomicIntegerFieldUpdater a = + AtomicIntegerFieldUpdater.newUpdater + (AtomicIntegerFieldUpdaterTest.class, "protectedField"); + this.protectedField = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + } + + static class UnrelatedClass { + public void checkPackageAccess(AtomicIntegerFieldUpdaterTest obj) { + obj.x = 72; + AtomicIntegerFieldUpdater a = + AtomicIntegerFieldUpdater.newUpdater + (AtomicIntegerFieldUpdaterTest.class, "x"); + assertEquals(72, a.get(obj)); + assertTrue(a.compareAndSet(obj, 72, 73)); + assertEquals(73, a.get(obj)); + } + + public void checkPrivateAccess(AtomicIntegerFieldUpdaterTest obj) { + try { + AtomicIntegerFieldUpdater a = + AtomicIntegerFieldUpdater.newUpdater + (AtomicIntegerFieldUpdaterTest.class, "privateField"); + throw new AssertionError("should throw"); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + } + + AtomicIntegerFieldUpdater updaterFor(String fieldName) { + return AtomicIntegerFieldUpdater.newUpdater + (AtomicIntegerFieldUpdaterTest.class, fieldName); + } + + /** + * Construction with non-existent field throws RuntimeException + */ + public void testConstructor() { + try { + updaterFor("y"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + /** + * construction with field not of given type throws IllegalArgumentException + */ + public void testConstructor2() { + try { + updaterFor("z"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * construction with non-volatile field throws IllegalArgumentException + */ + public void testConstructor3() { + try { + updaterFor("w"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * construction using private field from subclass throws RuntimeException + */ + public void testPrivateFieldInSubclass() { + AtomicIntegerFieldUpdaterTestSubclass s = + new AtomicIntegerFieldUpdaterTestSubclass(); + s.checkPrivateAccess(); + } + + /** + * construction from unrelated class; package access is allowed, + * private access is not + */ + public void testUnrelatedClassAccess() { + new UnrelatedClass().checkPackageAccess(this); + new UnrelatedClass().checkPrivateAccess(this); + } + + /** + * get returns the last value set or assigned + */ + public void testGetSet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.get(this)); + a.set(this, 2); + assertEquals(2, a.get(this)); + a.set(this, -3); + assertEquals(-3, a.get(this)); + } + + /** + * get returns the last value lazySet by same thread + */ + public void testGetLazySet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.get(this)); + a.lazySet(this, 2); + assertEquals(2, a.get(this)); + a.lazySet(this, -3); + assertEquals(-3, a.get(this)); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * compareAndSet succeeds in changing protected field value if + * equal to expected else fails + */ + public void testCompareAndSetProtected() { + AtomicIntegerFieldUpdater a; + a = updaterFor("protectedField"); + protectedField = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * compareAndSet succeeds in changing protected field value if + * equal to expected else fails + */ + public void testCompareAndSetProtectedInSubclass() { + AtomicIntegerFieldUpdaterTestSubclass s = + new AtomicIntegerFieldUpdaterTestSubclass(); + s.checkCompareAndSetProtectedSub(); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + x = 1; + final AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(AtomicIntegerFieldUpdaterTest.this, 2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(this, 1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, a.get(this)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + do {} while (!a.weakCompareAndSet(this, 1, 2)); + do {} while (!a.weakCompareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + do {} while (!a.weakCompareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndSet(this, 0)); + assertEquals(0, a.getAndSet(this, -10)); + assertEquals(-10, a.getAndSet(this, 1)); + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndAdd(this, 2)); + assertEquals(3, a.get(this)); + assertEquals(3, a.getAndAdd(this, -4)); + assertEquals(-1, a.get(this)); + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndDecrement(this)); + assertEquals(0, a.getAndDecrement(this)); + assertEquals(-1, a.getAndDecrement(this)); + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndIncrement(this)); + assertEquals(2, a.get(this)); + a.set(this, -2); + assertEquals(-2, a.getAndIncrement(this)); + assertEquals(-1, a.getAndIncrement(this)); + assertEquals(0, a.getAndIncrement(this)); + assertEquals(1, a.get(this)); + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(3, a.addAndGet(this, 2)); + assertEquals(3, a.get(this)); + assertEquals(-1, a.addAndGet(this, -4)); + assertEquals(-1, a.get(this)); + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(0, a.decrementAndGet(this)); + assertEquals(-1, a.decrementAndGet(this)); + assertEquals(-2, a.decrementAndGet(this)); + assertEquals(-2, a.get(this)); + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicIntegerFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(2, a.incrementAndGet(this)); + assertEquals(2, a.get(this)); + a.set(this, -2); + assertEquals(-1, a.incrementAndGet(this)); + assertEquals(0, a.incrementAndGet(this)); + assertEquals(1, a.incrementAndGet(this)); + assertEquals(1, a.get(this)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java new file mode 100644 index 00000000000..d148788d792 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java @@ -0,0 +1,294 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicIntegerTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicIntegerTest.class); + } + + final int[] VALUES = { + Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE, + }; + + /** + * constructor initializes to given value + */ + public void testConstructor() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor2() { + AtomicInteger ai = new AtomicInteger(); + assertEquals(0, ai.get()); + } + + /** + * get returns the last value set + */ + public void testGetSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.set(2); + assertEquals(2, ai.get()); + ai.set(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value lazySet in same thread + */ + public void testGetLazySet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.lazySet(2); + assertEquals(2, ai.get()); + ai.lazySet(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicInteger ai = new AtomicInteger(1); + assertTrue(ai.compareAndSet(1, 2)); + assertTrue(ai.compareAndSet(2, -4)); + assertEquals(-4, ai.get()); + assertFalse(ai.compareAndSet(-5, 7)); + assertEquals(-4, ai.get()); + assertTrue(ai.compareAndSet(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicInteger ai = new AtomicInteger(1); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, ai.get()); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSet(1, 2)); + do {} while (!ai.weakCompareAndSet(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSet(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAndSet(0)); + assertEquals(0, ai.getAndSet(-10)); + assertEquals(-10, ai.getAndSet(1)); + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAndAdd(2)); + assertEquals(3, ai.get()); + assertEquals(3, ai.getAndAdd(-4)); + assertEquals(-1, ai.get()); + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAndDecrement()); + assertEquals(0, ai.getAndDecrement()); + assertEquals(-1, ai.getAndDecrement()); + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAndIncrement()); + assertEquals(2, ai.get()); + ai.set(-2); + assertEquals(-2, ai.getAndIncrement()); + assertEquals(-1, ai.getAndIncrement()); + assertEquals(0, ai.getAndIncrement()); + assertEquals(1, ai.get()); + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(3, ai.addAndGet(2)); + assertEquals(3, ai.get()); + assertEquals(-1, ai.addAndGet(-4)); + assertEquals(-1, ai.get()); + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(0, ai.decrementAndGet()); + assertEquals(-1, ai.decrementAndGet()); + assertEquals(-2, ai.decrementAndGet()); + assertEquals(-2, ai.get()); + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(2, ai.incrementAndGet()); + assertEquals(2, ai.get()); + ai.set(-2); + assertEquals(-1, ai.incrementAndGet()); + assertEquals(0, ai.incrementAndGet()); + assertEquals(1, ai.incrementAndGet()); + assertEquals(1, ai.get()); + } + + /** + * a deserialized serialized atomic holds same value + */ + public void testSerialization() throws Exception { + AtomicInteger x = new AtomicInteger(); + AtomicInteger y = serialClone(x); + assertNotSame(x, y); + x.set(22); + AtomicInteger z = serialClone(x); + assertEquals(22, x.get()); + assertEquals(0, y.get()); + assertEquals(22, z.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + AtomicInteger ai = new AtomicInteger(); + assertEquals("0", ai.toString()); + for (int x : VALUES) { + ai.set(x); + assertEquals(Integer.toString(x), ai.toString()); + } + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + AtomicInteger ai = new AtomicInteger(); + assertEquals(0, ai.intValue()); + for (int x : VALUES) { + ai.set(x); + assertEquals(x, ai.intValue()); + } + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + AtomicInteger ai = new AtomicInteger(); + assertEquals(0L, ai.longValue()); + for (int x : VALUES) { + ai.set(x); + assertEquals((long)x, ai.longValue()); + } + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + AtomicInteger ai = new AtomicInteger(); + assertEquals(0.0f, ai.floatValue()); + for (int x : VALUES) { + ai.set(x); + assertEquals((float)x, ai.floatValue()); + } + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + AtomicInteger ai = new AtomicInteger(); + assertEquals(0.0d, ai.doubleValue()); + for (int x : VALUES) { + ai.set(x); + assertEquals((double)x, ai.doubleValue()); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java new file mode 100644 index 00000000000..bd74addbf7f --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java @@ -0,0 +1,369 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLongArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLongArrayTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLongArrayTest.class); + } + + /** + * constructor creates array of given size with all elements zero + */ + public void testConstructor() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) + assertEquals(0, aa.get(i)); + } + + /** + * constructor with null array throws NPE + */ + public void testConstructor2NPE() { + try { + long[] a = null; + new AtomicLongArray(a); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * constructor with array is of same size and has all elements + */ + public void testConstructor2() { + long[] a = { 17L, 3L, -42L, 99L, -7L }; + AtomicLongArray aa = new AtomicLongArray(a); + assertEquals(a.length, aa.length()); + for (int i = 0; i < a.length; i++) + assertEquals(a[i], aa.get(i)); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + try { + aa.get(index); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.set(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.lazySet(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.compareAndSet(index, 1, 2); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.weakCompareAndSet(index, 1, 2); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.getAndAdd(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.addAndGet(index, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * get returns the last value set at index + */ + public void testGetSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.get(i)); + aa.set(i, 2); + assertEquals(2, aa.get(i)); + aa.set(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value lazySet at index by same thread + */ + public void testGetLazySet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.lazySet(i, 1); + assertEquals(1, aa.get(i)); + aa.lazySet(i, 2); + assertEquals(2, aa.get(i)); + aa.lazySet(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertTrue(aa.compareAndSet(i, 1, 2)); + assertTrue(aa.compareAndSet(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertFalse(aa.compareAndSet(i, -5, 7)); + assertEquals(-4, aa.get(i)); + assertTrue(aa.compareAndSet(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws InterruptedException { + final AtomicLongArray a = new AtomicLongArray(1); + a.set(0, 1); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(0, 2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(0, 1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, a.get(0)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSet(i, 1, 2)); + do {} while (!aa.weakCompareAndSet(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSet(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * getAndSet returns previous value and sets to given value at given index + */ + public void testGetAndSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndSet(i, 0)); + assertEquals(0, aa.getAndSet(i, -10)); + assertEquals(-10, aa.getAndSet(i, 1)); + } + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndAdd(i, 2)); + assertEquals(3, aa.get(i)); + assertEquals(3, aa.getAndAdd(i, -4)); + assertEquals(-1, aa.get(i)); + } + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndDecrement(i)); + assertEquals(0, aa.getAndDecrement(i)); + assertEquals(-1, aa.getAndDecrement(i)); + } + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAndIncrement(i)); + assertEquals(2, aa.get(i)); + aa.set(i, -2); + assertEquals(-2, aa.getAndIncrement(i)); + assertEquals(-1, aa.getAndIncrement(i)); + assertEquals(0, aa.getAndIncrement(i)); + assertEquals(1, aa.get(i)); + } + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(3, aa.addAndGet(i, 2)); + assertEquals(3, aa.get(i)); + assertEquals(-1, aa.addAndGet(i, -4)); + assertEquals(-1, aa.get(i)); + } + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(0, aa.decrementAndGet(i)); + assertEquals(-1, aa.decrementAndGet(i)); + assertEquals(-2, aa.decrementAndGet(i)); + assertEquals(-2, aa.get(i)); + } + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(2, aa.incrementAndGet(i)); + assertEquals(2, aa.get(i)); + aa.set(i, -2); + assertEquals(-1, aa.incrementAndGet(i)); + assertEquals(0, aa.incrementAndGet(i)); + assertEquals(1, aa.incrementAndGet(i)); + assertEquals(1, aa.get(i)); + } + } + + class Counter extends CheckedRunnable { + final AtomicLongArray aa; + volatile long counts; + Counter(AtomicLongArray a) { aa = a; } + public void realRun() { + for (;;) { + boolean done = true; + for (int i = 0; i < aa.length(); i++) { + long v = aa.get(i); + assertTrue(v >= 0); + if (v != 0) { + done = false; + if (aa.compareAndSet(i, v, v - 1)) + ++counts; + } + } + if (done) + break; + } + } + } + + /** + * Multiple threads using same array of counters successfully + * update a number of times equal to total count + */ + public void testCountingInMultipleThreads() throws InterruptedException { + final AtomicLongArray aa = new AtomicLongArray(SIZE); + long countdown = 10000; + for (int i = 0; i < SIZE; i++) + aa.set(i, countdown); + Counter c1 = new Counter(aa); + Counter c2 = new Counter(aa); + Thread t1 = new Thread(c1); + Thread t2 = new Thread(c2); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + assertEquals(c1.counts+c2.counts, SIZE * countdown); + } + + /** + * a deserialized serialized array holds same values + */ + public void testSerialization() throws Exception { + AtomicLongArray x = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) + x.set(i, -i); + AtomicLongArray y = serialClone(x); + assertNotSame(x, y); + assertEquals(x.length(), y.length()); + for (int i = 0; i < SIZE; i++) { + assertEquals(x.get(i), y.get(i)); + } + } + + /** + * toString returns current value. + */ + public void testToString() { + long[] a = { 17, 3, -42, 99, -7 }; + AtomicLongArray aa = new AtomicLongArray(a); + assertEquals(Arrays.toString(a), aa.toString()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java new file mode 100644 index 00000000000..b9dc1017017 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java @@ -0,0 +1,363 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLongFieldUpdaterTest extends JSR166TestCase { + volatile long x = 0; + protected volatile long protectedField; + private volatile long privateField; + long w; + float z; + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLongFieldUpdaterTest.class); + } + + // for testing subclass access + static class AtomicLongFieldUpdaterTestSubclass extends AtomicLongFieldUpdaterTest { + public void checkPrivateAccess() { + try { + AtomicLongFieldUpdater a = + AtomicLongFieldUpdater.newUpdater + (AtomicLongFieldUpdaterTest.class, "privateField"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + public void checkCompareAndSetProtectedSub() { + AtomicLongFieldUpdater a = + AtomicLongFieldUpdater.newUpdater + (AtomicLongFieldUpdaterTest.class, "protectedField"); + this.protectedField = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + } + + static class UnrelatedClass { + public void checkPackageAccess(AtomicLongFieldUpdaterTest obj) { + obj.x = 72L; + AtomicLongFieldUpdater a = + AtomicLongFieldUpdater.newUpdater + (AtomicLongFieldUpdaterTest.class, "x"); + assertEquals(72L, a.get(obj)); + assertTrue(a.compareAndSet(obj, 72L, 73L)); + assertEquals(73L, a.get(obj)); + } + + public void checkPrivateAccess(AtomicLongFieldUpdaterTest obj) { + try { + AtomicLongFieldUpdater a = + AtomicLongFieldUpdater.newUpdater + (AtomicLongFieldUpdaterTest.class, "privateField"); + throw new AssertionError("should throw"); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + } + + AtomicLongFieldUpdater updaterFor(String fieldName) { + return AtomicLongFieldUpdater.newUpdater + (AtomicLongFieldUpdaterTest.class, fieldName); + } + + /** + * Construction with non-existent field throws RuntimeException + */ + public void testConstructor() { + try { + updaterFor("y"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + /** + * construction with field not of given type throws IllegalArgumentException + */ + public void testConstructor2() { + try { + updaterFor("z"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * construction with non-volatile field throws IllegalArgumentException + */ + public void testConstructor3() { + try { + updaterFor("w"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * construction using private field from subclass throws RuntimeException + */ + public void testPrivateFieldInSubclass() { + AtomicLongFieldUpdaterTestSubclass s = + new AtomicLongFieldUpdaterTestSubclass(); + s.checkPrivateAccess(); + } + + /** + * construction from unrelated class; package access is allowed, + * private access is not + */ + public void testUnrelatedClassAccess() { + new UnrelatedClass().checkPackageAccess(this); + new UnrelatedClass().checkPrivateAccess(this); + } + + /** + * get returns the last value set or assigned + */ + public void testGetSet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.get(this)); + a.set(this, 2); + assertEquals(2, a.get(this)); + a.set(this, -3); + assertEquals(-3, a.get(this)); + } + + /** + * get returns the last value lazySet by same thread + */ + public void testGetLazySet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.get(this)); + a.lazySet(this, 2); + assertEquals(2, a.get(this)); + a.lazySet(this, -3); + assertEquals(-3, a.get(this)); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * compareAndSet succeeds in changing protected field value if + * equal to expected else fails + */ + public void testCompareAndSetProtected() { + AtomicLongFieldUpdater a; + a = updaterFor("protectedField"); + protectedField = 1; + assertTrue(a.compareAndSet(this, 1, 2)); + assertTrue(a.compareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + assertFalse(a.compareAndSet(this, -5, 7)); + assertEquals(-4, a.get(this)); + assertTrue(a.compareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * compareAndSet succeeds in changing protected field value if + * equal to expected else fails + */ + public void testCompareAndSetProtectedInSubclass() { + AtomicLongFieldUpdaterTestSubclass s = + new AtomicLongFieldUpdaterTestSubclass(); + s.checkCompareAndSetProtectedSub(); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + x = 1; + final AtomicLongFieldUpdater a; + a = updaterFor("x"); + + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(AtomicLongFieldUpdaterTest.this, 2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(this, 1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, a.get(this)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + do {} while (!a.weakCompareAndSet(this, 1, 2)); + do {} while (!a.weakCompareAndSet(this, 2, -4)); + assertEquals(-4, a.get(this)); + do {} while (!a.weakCompareAndSet(this, -4, 7)); + assertEquals(7, a.get(this)); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndSet(this, 0)); + assertEquals(0, a.getAndSet(this, -10)); + assertEquals(-10, a.getAndSet(this, 1)); + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndAdd(this, 2)); + assertEquals(3, a.get(this)); + assertEquals(3, a.getAndAdd(this, -4)); + assertEquals(-1, a.get(this)); + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndDecrement(this)); + assertEquals(0, a.getAndDecrement(this)); + assertEquals(-1, a.getAndDecrement(this)); + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(1, a.getAndIncrement(this)); + assertEquals(2, a.get(this)); + a.set(this, -2); + assertEquals(-2, a.getAndIncrement(this)); + assertEquals(-1, a.getAndIncrement(this)); + assertEquals(0, a.getAndIncrement(this)); + assertEquals(1, a.get(this)); + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(3, a.addAndGet(this, 2)); + assertEquals(3, a.get(this)); + assertEquals(-1, a.addAndGet(this, -4)); + assertEquals(-1, a.get(this)); + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(0, a.decrementAndGet(this)); + assertEquals(-1, a.decrementAndGet(this)); + assertEquals(-2, a.decrementAndGet(this)); + assertEquals(-2, a.get(this)); + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicLongFieldUpdater a; + a = updaterFor("x"); + x = 1; + assertEquals(2, a.incrementAndGet(this)); + assertEquals(2, a.get(this)); + a.set(this, -2); + assertEquals(-1, a.incrementAndGet(this)); + assertEquals(0, a.incrementAndGet(this)); + assertEquals(1, a.incrementAndGet(this)); + assertEquals(1, a.get(this)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongTest.java new file mode 100644 index 00000000000..f5191af99e6 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLongTest.java @@ -0,0 +1,297 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLongTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLongTest.class); + } + + final long[] VALUES = { + Long.MIN_VALUE, + Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE, + Long.MAX_VALUE, + }; + + /** + * constructor initializes to given value + */ + public void testConstructor() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor2() { + AtomicLong ai = new AtomicLong(); + assertEquals(0, ai.get()); + } + + /** + * get returns the last value set + */ + public void testGetSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.set(2); + assertEquals(2, ai.get()); + ai.set(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value lazySet in same thread + */ + public void testGetLazySet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.lazySet(2); + assertEquals(2, ai.get()); + ai.lazySet(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicLong ai = new AtomicLong(1); + assertTrue(ai.compareAndSet(1, 2)); + assertTrue(ai.compareAndSet(2, -4)); + assertEquals(-4, ai.get()); + assertFalse(ai.compareAndSet(-5, 7)); + assertEquals(-4, ai.get()); + assertTrue(ai.compareAndSet(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicLong ai = new AtomicLong(1); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(2, 3)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(1, 2)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertEquals(3, ai.get()); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSet(1, 2)); + do {} while (!ai.weakCompareAndSet(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSet(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAndSet(0)); + assertEquals(0, ai.getAndSet(-10)); + assertEquals(-10, ai.getAndSet(1)); + } + + /** + * getAndAdd returns previous value and adds given value + */ + public void testGetAndAdd() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAndAdd(2)); + assertEquals(3, ai.get()); + assertEquals(3, ai.getAndAdd(-4)); + assertEquals(-1, ai.get()); + } + + /** + * getAndDecrement returns previous value and decrements + */ + public void testGetAndDecrement() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAndDecrement()); + assertEquals(0, ai.getAndDecrement()); + assertEquals(-1, ai.getAndDecrement()); + } + + /** + * getAndIncrement returns previous value and increments + */ + public void testGetAndIncrement() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAndIncrement()); + assertEquals(2, ai.get()); + ai.set(-2); + assertEquals(-2, ai.getAndIncrement()); + assertEquals(-1, ai.getAndIncrement()); + assertEquals(0, ai.getAndIncrement()); + assertEquals(1, ai.get()); + } + + /** + * addAndGet adds given value to current, and returns current value + */ + public void testAddAndGet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(3, ai.addAndGet(2)); + assertEquals(3, ai.get()); + assertEquals(-1, ai.addAndGet(-4)); + assertEquals(-1, ai.get()); + } + + /** + * decrementAndGet decrements and returns current value + */ + public void testDecrementAndGet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(0, ai.decrementAndGet()); + assertEquals(-1, ai.decrementAndGet()); + assertEquals(-2, ai.decrementAndGet()); + assertEquals(-2, ai.get()); + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndGet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(2, ai.incrementAndGet()); + assertEquals(2, ai.get()); + ai.set(-2); + assertEquals(-1, ai.incrementAndGet()); + assertEquals(0, ai.incrementAndGet()); + assertEquals(1, ai.incrementAndGet()); + assertEquals(1, ai.get()); + } + + /** + * a deserialized serialized atomic holds same value + */ + public void testSerialization() throws Exception { + AtomicLong x = new AtomicLong(); + AtomicLong y = serialClone(x); + assertNotSame(x, y); + x.set(-22); + AtomicLong z = serialClone(x); + assertNotSame(y, z); + assertEquals(-22, x.get()); + assertEquals(0, y.get()); + assertEquals(-22, z.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + AtomicLong ai = new AtomicLong(); + assertEquals("0", ai.toString()); + for (long x : VALUES) { + ai.set(x); + assertEquals(Long.toString(x), ai.toString()); + } + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + AtomicLong ai = new AtomicLong(); + assertEquals(0, ai.intValue()); + for (long x : VALUES) { + ai.set(x); + assertEquals((int)x, ai.intValue()); + } + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + AtomicLong ai = new AtomicLong(); + assertEquals(0L, ai.longValue()); + for (long x : VALUES) { + ai.set(x); + assertEquals(x, ai.longValue()); + } + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + AtomicLong ai = new AtomicLong(); + assertEquals(0.0f, ai.floatValue()); + for (long x : VALUES) { + ai.set(x); + assertEquals((float)x, ai.floatValue()); + } + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + AtomicLong ai = new AtomicLong(); + assertEquals(0.0d, ai.doubleValue()); + for (long x : VALUES) { + ai.set(x); + assertEquals((double)x, ai.doubleValue()); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java new file mode 100644 index 00000000000..b1c77c468d2 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java @@ -0,0 +1,180 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicMarkableReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicMarkableReferenceTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicMarkableReferenceTest.class); + } + + /** + * constructor initializes to given reference and mark + */ + public void testConstructor() { + AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + assertSame(one, ai.getReference()); + assertFalse(ai.isMarked()); + AtomicMarkableReference a2 = new AtomicMarkableReference(null, true); + assertNull(a2.getReference()); + assertTrue(a2.isMarked()); + } + + /** + * get returns the last values of reference and mark set + */ + public void testGetSet() { + boolean[] mark = new boolean[1]; + AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + assertSame(one, ai.getReference()); + assertFalse(ai.isMarked()); + assertSame(one, ai.get(mark)); + assertFalse(mark[0]); + ai.set(two, false); + assertSame(two, ai.getReference()); + assertFalse(ai.isMarked()); + assertSame(two, ai.get(mark)); + assertFalse(mark[0]); + ai.set(one, true); + assertSame(one, ai.getReference()); + assertTrue(ai.isMarked()); + assertSame(one, ai.get(mark)); + assertTrue(mark[0]); + } + + /** + * attemptMark succeeds in single thread + */ + public void testAttemptMark() { + boolean[] mark = new boolean[1]; + AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + assertFalse(ai.isMarked()); + assertTrue(ai.attemptMark(one, true)); + assertTrue(ai.isMarked()); + assertSame(one, ai.get(mark)); + assertTrue(mark[0]); + } + + /** + * compareAndSet succeeds in changing values if equal to expected reference + * and mark else fails + */ + public void testCompareAndSet() { + boolean[] mark = new boolean[1]; + AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + assertSame(one, ai.get(mark)); + assertFalse(ai.isMarked()); + assertFalse(mark[0]); + + assertTrue(ai.compareAndSet(one, two, false, false)); + assertSame(two, ai.get(mark)); + assertFalse(mark[0]); + + assertTrue(ai.compareAndSet(two, m3, false, true)); + assertSame(m3, ai.get(mark)); + assertTrue(mark[0]); + + assertFalse(ai.compareAndSet(two, m3, true, true)); + assertSame(m3, ai.get(mark)); + assertTrue(mark[0]); + } + + /** + * compareAndSet in one thread enables another waiting for reference value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(two, three, false, false)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(one, two, false, false)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(three, ai.getReference()); + assertFalse(ai.isMarked()); + } + + /** + * compareAndSet in one thread enables another waiting for mark value + * to succeed + */ + public void testCompareAndSetInMultipleThreads2() throws Exception { + final AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(one, one, true, false)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(one, one, false, true)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(one, ai.getReference()); + assertFalse(ai.isMarked()); + } + + /** + * repeated weakCompareAndSet succeeds in changing values when equal + * to expected + */ + public void testWeakCompareAndSet() { + boolean[] mark = new boolean[1]; + AtomicMarkableReference ai = new AtomicMarkableReference(one, false); + assertSame(one, ai.get(mark)); + assertFalse(ai.isMarked()); + assertFalse(mark[0]); + + do {} while (!ai.weakCompareAndSet(one, two, false, false)); + assertSame(two, ai.get(mark)); + assertFalse(mark[0]); + + do {} while (!ai.weakCompareAndSet(two, m3, false, true)); + assertSame(m3, ai.get(mark)); + assertTrue(mark[0]); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java new file mode 100644 index 00000000000..2457d40b426 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java @@ -0,0 +1,246 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReferenceArrayTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReferenceArrayTest.class); + } + + /** + * constructor creates array of given size with all elements null + */ + public void testConstructor() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + assertNull(aa.get(i)); + } + } + + /** + * constructor with null array throws NPE + */ + public void testConstructor2NPE() { + try { + Integer[] a = null; + new AtomicReferenceArray(a); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * constructor with array is of same size and has all elements + */ + public void testConstructor2() { + Integer[] a = { two, one, three, four, seven }; + AtomicReferenceArray aa = new AtomicReferenceArray(a); + assertEquals(a.length, aa.length()); + for (int i = 0; i < a.length; i++) + assertEquals(a[i], aa.get(i)); + } + + /** + * Initialize AtomicReferenceArray with SubClass[] + */ + public void testConstructorSubClassArray() { + Integer[] a = { two, one, three, four, seven }; + AtomicReferenceArray aa = new AtomicReferenceArray(a); + assertEquals(a.length, aa.length()); + for (int i = 0; i < a.length; i++) { + assertSame(a[i], aa.get(i)); + Long x = Long.valueOf(i); + aa.set(i, x); + assertSame(x, aa.get(i)); + } + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + try { + aa.get(index); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.set(index, null); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.lazySet(index, null); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.compareAndSet(index, null, null); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + try { + aa.weakCompareAndSet(index, null, null); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * get returns the last value set at index + */ + public void testGetSet() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertSame(one, aa.get(i)); + aa.set(i, two); + assertSame(two, aa.get(i)); + aa.set(i, m3); + assertSame(m3, aa.get(i)); + } + } + + /** + * get returns the last value lazySet at index by same thread + */ + public void testGetLazySet() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.lazySet(i, one); + assertSame(one, aa.get(i)); + aa.lazySet(i, two); + assertSame(two, aa.get(i)); + aa.lazySet(i, m3); + assertSame(m3, aa.get(i)); + } + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertTrue(aa.compareAndSet(i, one, two)); + assertTrue(aa.compareAndSet(i, two, m4)); + assertSame(m4, aa.get(i)); + assertFalse(aa.compareAndSet(i, m5, seven)); + assertSame(m4, aa.get(i)); + assertTrue(aa.compareAndSet(i, m4, seven)); + assertSame(seven, aa.get(i)); + } + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws InterruptedException { + final AtomicReferenceArray a = new AtomicReferenceArray(1); + a.set(0, one); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(0, two, three)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(0, one, two)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(three, a.get(0)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSet(i, one, two)); + do {} while (!aa.weakCompareAndSet(i, two, m4)); + assertSame(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSet(i, m4, seven)); + assertSame(seven, aa.get(i)); + } + } + + /** + * getAndSet returns previous value and sets to given value at given index + */ + public void testGetAndSet() { + AtomicReferenceArray aa = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertSame(one, aa.getAndSet(i, zero)); + assertSame(zero, aa.getAndSet(i, m10)); + assertSame(m10, aa.getAndSet(i, one)); + } + } + + /** + * a deserialized serialized array holds same values + */ + public void testSerialization() throws Exception { + AtomicReferenceArray x = new AtomicReferenceArray(SIZE); + for (int i = 0; i < SIZE; i++) { + x.set(i, new Integer(-i)); + } + AtomicReferenceArray y = serialClone(x); + assertNotSame(x, y); + assertEquals(x.length(), y.length()); + for (int i = 0; i < SIZE; i++) { + assertEquals(x.get(i), y.get(i)); + } + } + + /** + * toString returns current value. + */ + public void testToString() { + Integer[] a = { two, one, three, four, seven }; + AtomicReferenceArray aa = new AtomicReferenceArray(a); + assertEquals(Arrays.toString(a), aa.toString()); + } +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java new file mode 100644 index 00000000000..fb50658ea91 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java @@ -0,0 +1,265 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase { + volatile Integer x = null; + protected volatile Integer protectedField; + private volatile Integer privateField; + Object z; + Integer w; + volatile int i; + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReferenceFieldUpdaterTest.class); + } + + // for testing subclass access + static class AtomicReferenceFieldUpdaterTestSubclass extends AtomicReferenceFieldUpdaterTest { + public void checkPrivateAccess() { + try { + AtomicReferenceFieldUpdater a = + AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + public void checkCompareAndSetProtectedSub() { + AtomicReferenceFieldUpdater a = + AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterTest.class, Integer.class, "protectedField"); + this.protectedField = one; + assertTrue(a.compareAndSet(this, one, two)); + assertTrue(a.compareAndSet(this, two, m4)); + assertSame(m4, a.get(this)); + assertFalse(a.compareAndSet(this, m5, seven)); + assertFalse(seven == a.get(this)); + assertTrue(a.compareAndSet(this, m4, seven)); + assertSame(seven, a.get(this)); + } + } + + static class UnrelatedClass { + public void checkPackageAccess(AtomicReferenceFieldUpdaterTest obj) { + obj.x = one; + AtomicReferenceFieldUpdater a = + AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterTest.class, Integer.class, "x"); + assertSame(one, a.get(obj)); + assertTrue(a.compareAndSet(obj, one, two)); + assertSame(two, a.get(obj)); + } + + public void checkPrivateAccess(AtomicReferenceFieldUpdaterTest obj) { + try { + AtomicReferenceFieldUpdater a = + AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField"); + throw new AssertionError("should throw"); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + } + + static AtomicReferenceFieldUpdater updaterFor(String fieldName) { + return AtomicReferenceFieldUpdater.newUpdater + (AtomicReferenceFieldUpdaterTest.class, Integer.class, fieldName); + } + + /** + * Construction with non-existent field throws RuntimeException + */ + public void testConstructor() { + try { + updaterFor("y"); + shouldThrow(); + } catch (RuntimeException success) { + assertNotNull(success.getCause()); + } + } + + /** + * construction with field not of given type throws ClassCastException + */ + public void testConstructor2() { + try { + updaterFor("z"); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * Constructor with non-volatile field throws IllegalArgumentException + */ + public void testConstructor3() { + try { + updaterFor("w"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor with non-reference field throws ClassCastException + */ + public void testConstructor4() { + try { + updaterFor("i"); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * construction using private field from subclass throws RuntimeException + */ + public void testPrivateFieldInSubclass() { + AtomicReferenceFieldUpdaterTestSubclass s = + new AtomicReferenceFieldUpdaterTestSubclass(); + s.checkPrivateAccess(); + } + + /** + * construction from unrelated class; package access is allowed, + * private access is not + */ + public void testUnrelatedClassAccess() { + new UnrelatedClass().checkPackageAccess(this); + new UnrelatedClass().checkPrivateAccess(this); + } + + /** + * get returns the last value set or assigned + */ + public void testGetSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = one; + assertSame(one, a.get(this)); + a.set(this, two); + assertSame(two, a.get(this)); + a.set(this, m3); + assertSame(m3, a.get(this)); + } + + /** + * get returns the last value lazySet by same thread + */ + public void testGetLazySet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = one; + assertSame(one, a.get(this)); + a.lazySet(this, two); + assertSame(two, a.get(this)); + a.lazySet(this, m3); + assertSame(m3, a.get(this)); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = one; + assertTrue(a.compareAndSet(this, one, two)); + assertTrue(a.compareAndSet(this, two, m4)); + assertSame(m4, a.get(this)); + assertFalse(a.compareAndSet(this, m5, seven)); + assertFalse(seven == a.get(this)); + assertTrue(a.compareAndSet(this, m4, seven)); + assertSame(seven, a.get(this)); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + x = one; + final AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three)) + Thread.yield(); + }}); + + t.start(); + assertTrue(a.compareAndSet(this, one, two)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(three, a.get(this)); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = one; + do {} while (!a.weakCompareAndSet(this, one, two)); + do {} while (!a.weakCompareAndSet(this, two, m4)); + assertSame(m4, a.get(this)); + do {} while (!a.weakCompareAndSet(this, m4, seven)); + assertSame(seven, a.get(this)); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicReferenceFieldUpdater a; + a = updaterFor("x"); + x = one; + assertSame(one, a.getAndSet(this, zero)); + assertSame(zero, a.getAndSet(this, m10)); + assertSame(m10, a.getAndSet(this, 1)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java new file mode 100644 index 00000000000..bae2a43906a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java @@ -0,0 +1,170 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReferenceTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReferenceTest.class); + } + + /** + * constructor initializes to given value + */ + public void testConstructor() { + AtomicReference ai = new AtomicReference(one); + assertSame(one, ai.get()); + } + + /** + * default constructed initializes to null + */ + public void testConstructor2() { + AtomicReference ai = new AtomicReference(); + assertNull(ai.get()); + } + + /** + * get returns the last value set + */ + public void testGetSet() { + AtomicReference ai = new AtomicReference(one); + assertSame(one, ai.get()); + ai.set(two); + assertSame(two, ai.get()); + ai.set(m3); + assertSame(m3, ai.get()); + } + + /** + * get returns the last value lazySet in same thread + */ + public void testGetLazySet() { + AtomicReference ai = new AtomicReference(one); + assertSame(one, ai.get()); + ai.lazySet(two); + assertSame(two, ai.get()); + ai.lazySet(m3); + assertSame(m3, ai.get()); + } + + /** + * compareAndSet succeeds in changing value if equal to expected else fails + */ + public void testCompareAndSet() { + AtomicReference ai = new AtomicReference(one); + assertTrue(ai.compareAndSet(one, two)); + assertTrue(ai.compareAndSet(two, m4)); + assertSame(m4, ai.get()); + assertFalse(ai.compareAndSet(m5, seven)); + assertSame(m4, ai.get()); + assertTrue(ai.compareAndSet(m4, seven)); + assertSame(seven, ai.get()); + } + + /** + * compareAndSet in one thread enables another waiting for value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicReference ai = new AtomicReference(one); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(two, three)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(one, two)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(three, ai.get()); + } + + /** + * repeated weakCompareAndSet succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSet() { + AtomicReference ai = new AtomicReference(one); + do {} while (!ai.weakCompareAndSet(one, two)); + do {} while (!ai.weakCompareAndSet(two, m4)); + assertSame(m4, ai.get()); + do {} while (!ai.weakCompareAndSet(m4, seven)); + assertSame(seven, ai.get()); + } + + /** + * getAndSet returns previous value and sets to given value + */ + public void testGetAndSet() { + AtomicReference ai = new AtomicReference(one); + assertSame(one, ai.getAndSet(zero)); + assertSame(zero, ai.getAndSet(m10)); + assertSame(m10, ai.getAndSet(one)); + } + + /** + * a deserialized serialized atomic holds same value + */ + public void testSerialization() throws Exception { + AtomicReference x = new AtomicReference(); + AtomicReference y = serialClone(x); + assertNotSame(x, y); + x.set(one); + AtomicReference z = serialClone(x); + assertNotSame(y, z); + assertEquals(one, x.get()); + assertEquals(null, y.get()); + assertEquals(one, z.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + AtomicReference ai = new AtomicReference(one); + assertEquals(one.toString(), ai.toString()); + ai.set(two); + assertEquals(two.toString(), ai.toString()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java new file mode 100644 index 00000000000..1a352130d3e --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java @@ -0,0 +1,180 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.concurrent.atomic.AtomicStampedReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicStampedReferenceTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicStampedReferenceTest.class); + } + + /** + * constructor initializes to given reference and stamp + */ + public void testConstructor() { + AtomicStampedReference ai = new AtomicStampedReference(one, 0); + assertSame(one, ai.getReference()); + assertEquals(0, ai.getStamp()); + AtomicStampedReference a2 = new AtomicStampedReference(null, 1); + assertNull(a2.getReference()); + assertEquals(1, a2.getStamp()); + } + + /** + * get returns the last values of reference and stamp set + */ + public void testGetSet() { + int[] mark = new int[1]; + AtomicStampedReference ai = new AtomicStampedReference(one, 0); + assertSame(one, ai.getReference()); + assertEquals(0, ai.getStamp()); + assertSame(one, ai.get(mark)); + assertEquals(0, mark[0]); + ai.set(two, 0); + assertSame(two, ai.getReference()); + assertEquals(0, ai.getStamp()); + assertSame(two, ai.get(mark)); + assertEquals(0, mark[0]); + ai.set(one, 1); + assertSame(one, ai.getReference()); + assertEquals(1, ai.getStamp()); + assertSame(one, ai.get(mark)); + assertEquals(1, mark[0]); + } + + /** + * attemptStamp succeeds in single thread + */ + public void testAttemptStamp() { + int[] mark = new int[1]; + AtomicStampedReference ai = new AtomicStampedReference(one, 0); + assertEquals(0, ai.getStamp()); + assertTrue(ai.attemptStamp(one, 1)); + assertEquals(1, ai.getStamp()); + assertSame(one, ai.get(mark)); + assertEquals(1, mark[0]); + } + + /** + * compareAndSet succeeds in changing values if equal to expected reference + * and stamp else fails + */ + public void testCompareAndSet() { + int[] mark = new int[1]; + AtomicStampedReference ai = new AtomicStampedReference(one, 0); + assertSame(one, ai.get(mark)); + assertEquals(0, ai.getStamp()); + assertEquals(0, mark[0]); + + assertTrue(ai.compareAndSet(one, two, 0, 0)); + assertSame(two, ai.get(mark)); + assertEquals(0, mark[0]); + + assertTrue(ai.compareAndSet(two, m3, 0, 1)); + assertSame(m3, ai.get(mark)); + assertEquals(1, mark[0]); + + assertFalse(ai.compareAndSet(two, m3, 1, 1)); + assertSame(m3, ai.get(mark)); + assertEquals(1, mark[0]); + } + + /** + * compareAndSet in one thread enables another waiting for reference value + * to succeed + */ + public void testCompareAndSetInMultipleThreads() throws Exception { + final AtomicStampedReference ai = new AtomicStampedReference(one, 0); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(two, three, 0, 0)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(one, two, 0, 0)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(three, ai.getReference()); + assertEquals(0, ai.getStamp()); + } + + /** + * compareAndSet in one thread enables another waiting for stamp value + * to succeed + */ + public void testCompareAndSetInMultipleThreads2() throws Exception { + final AtomicStampedReference ai = new AtomicStampedReference(one, 0); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + while (!ai.compareAndSet(one, one, 1, 2)) + Thread.yield(); + }}); + + t.start(); + assertTrue(ai.compareAndSet(one, one, 0, 1)); + t.join(LONG_DELAY_MS); + assertFalse(t.isAlive()); + assertSame(one, ai.getReference()); + assertEquals(2, ai.getStamp()); + } + + /** + * repeated weakCompareAndSet succeeds in changing values when equal + * to expected + */ + public void testWeakCompareAndSet() { + int[] mark = new int[1]; + AtomicStampedReference ai = new AtomicStampedReference(one, 0); + assertSame(one, ai.get(mark)); + assertEquals(0, ai.getStamp()); + assertEquals(0, mark[0]); + + do {} while (!ai.weakCompareAndSet(one, two, 0, 0)); + assertSame(two, ai.get(mark)); + assertEquals(0, mark[0]); + + do {} while (!ai.weakCompareAndSet(two, m3, 0, 1)); + assertSame(m3, ai.get(mark)); + assertEquals(1, mark[0]); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java new file mode 100644 index 00000000000..aa38b3bd735 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java @@ -0,0 +1,403 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from members + * of JCP JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + * + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Contains "contract" tests applicable to all BlockingQueue implementations. + */ +public abstract class BlockingQueueTest extends JSR166TestCase { + /* + * This is the start of an attempt to refactor the tests for the + * various related implementations of related interfaces without + * too much duplicated code. junit does not really support such + * testing. Here subclasses of TestCase not only contain tests, + * but also configuration information that describes the + * implementation class, most importantly how to instantiate + * instances. + */ + + /** Like suite(), but non-static */ + public Test testSuite() { + // TODO: filter the returned tests using the configuration + // information provided by the subclass via protected methods. + return new TestSuite(this.getClass()); + } + + //---------------------------------------------------------------- + // Configuration methods + //---------------------------------------------------------------- + + /** Returns an empty instance of the implementation class. */ + protected abstract BlockingQueue emptyCollection(); + + /** + * Returns an element suitable for insertion in the collection. + * Override for collections with unusual element types. + */ + protected Object makeElement(int i) { + return Integer.valueOf(i); + } + + //---------------------------------------------------------------- + // Tests + //---------------------------------------------------------------- + + /** + * offer(null) throws NullPointerException + */ + public void testOfferNull() { + final Queue q = emptyCollection(); + try { + q.offer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * add(null) throws NullPointerException + */ + public void testAddNull() { + final Collection q = emptyCollection(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * timed offer(null) throws NullPointerException + */ + public void testTimedOfferNull() throws InterruptedException { + final BlockingQueue q = emptyCollection(); + long startTime = System.nanoTime(); + try { + q.offer(null, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + + /** + * put(null) throws NullPointerException + */ + public void testPutNull() throws InterruptedException { + final BlockingQueue q = emptyCollection(); + try { + q.put(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null) throws NullPointerException + */ + public void testAddAllNull() throws InterruptedException { + final Collection q = emptyCollection(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NullPointerException + */ + public void testAddAllNullElements() { + final Collection q = emptyCollection(); + final Collection elements = Arrays.asList(new Integer[SIZE]); + try { + q.addAll(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * toArray(null) throws NullPointerException + */ + public void testToArray_NullArray() { + final Collection q = emptyCollection(); + try { + q.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * drainTo(null) throws NullPointerException + */ + public void testDrainToNull() { + final BlockingQueue q = emptyCollection(); + try { + q.drainTo(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * drainTo(this) throws IllegalArgumentException + */ + public void testDrainToSelf() { + final BlockingQueue q = emptyCollection(); + try { + q.drainTo(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * drainTo(null, n) throws NullPointerException + */ + public void testDrainToNullN() { + final BlockingQueue q = emptyCollection(); + try { + q.drainTo(null, 0); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * drainTo(this, n) throws IllegalArgumentException + */ + public void testDrainToSelfN() { + final BlockingQueue q = emptyCollection(); + try { + q.drainTo(q, 0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * drainTo(c, n) returns 0 and does nothing when n <= 0 + */ + public void testDrainToNonPositiveMaxElements() { + final BlockingQueue q = emptyCollection(); + final int[] ns = { 0, -1, -42, Integer.MIN_VALUE }; + for (int n : ns) + assertEquals(0, q.drainTo(new ArrayList(), n)); + if (q.remainingCapacity() > 0) { + // Not SynchronousQueue, that is + Object one = makeElement(1); + q.add(one); + ArrayList c = new ArrayList(); + for (int n : ns) + assertEquals(0, q.drainTo(new ArrayList(), n)); + assertEquals(1, q.size()); + assertSame(one, q.poll()); + assertTrue(c.isEmpty()); + } + } + + /** + * timed poll before a delayed offer times out; after offer succeeds; + * on interruption throws + */ + public void testTimedPollWithOffer() throws InterruptedException { + final BlockingQueue q = emptyCollection(); + final CheckedBarrier barrier = new CheckedBarrier(2); + final Object zero = makeElement(0); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + + barrier.await(); + + assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS)); + + Thread.currentThread().interrupt(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + barrier.await(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + barrier.await(); + long startTime = System.nanoTime(); + assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + + barrier.await(); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take() blocks interruptibly when empty + */ + public void testTakeFromEmptyBlocksInterruptibly() { + final BlockingQueue q = emptyCollection(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(threadStarted); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take() throws InterruptedException immediately if interrupted + * before waiting + */ + public void testTakeFromEmptyAfterInterrupt() { + final BlockingQueue q = emptyCollection(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + awaitTermination(t); + } + + /** + * timed poll() blocks interruptibly when empty + */ + public void testTimedPollFromEmptyBlocksInterruptibly() { + final BlockingQueue q = emptyCollection(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + try { + q.poll(2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(threadStarted); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed poll() throws InterruptedException immediately if + * interrupted before waiting + */ + public void testTimedPollFromEmptyAfterInterrupt() { + final BlockingQueue q = emptyCollection(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + try { + q.poll(2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + awaitTermination(t); + } + + /** + * remove(x) removes x and returns true if present + * TODO: move to superclass CollectionTest.java + */ + public void testRemoveElement() { + final BlockingQueue q = emptyCollection(); + final int size = Math.min(q.remainingCapacity(), SIZE); + final Object[] elts = new Object[size]; + assertFalse(q.contains(makeElement(99))); + assertFalse(q.remove(makeElement(99))); + checkEmpty(q); + for (int i = 0; i < size; i++) + q.add(elts[i] = makeElement(i)); + for (int i = 1; i < size; i += 2) { + for (int pass = 0; pass < 2; pass++) { + assertEquals((pass == 0), q.contains(elts[i])); + assertEquals((pass == 0), q.remove(elts[i])); + assertFalse(q.contains(elts[i])); + assertTrue(q.contains(elts[i - 1])); + if (i < size - 1) + assertTrue(q.contains(elts[i + 1])); + } + } + if (size > 0) + assertTrue(q.contains(elts[0])); + for (int i = size - 2; i >= 0; i -= 2) { + assertTrue(q.contains(elts[i])); + assertFalse(q.contains(elts[i + 1])); + assertTrue(q.remove(elts[i])); + assertFalse(q.contains(elts[i])); + assertFalse(q.remove(elts[i + 1])); + assertFalse(q.contains(elts[i + 1])); + } + checkEmpty(q); + } + + /** For debugging. */ + public void XXXXtestFails() { + fail(emptyCollection().getClass().toString()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/Collection8Test.java b/jdk/test/java/util/concurrent/tck/Collection8Test.java new file mode 100644 index 00000000000..1a10b280228 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/Collection8Test.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +import junit.framework.Test; + +/** + * Contains tests applicable to all jdk8+ Collection implementations. + * An extension of CollectionTest. + */ +public class Collection8Test extends JSR166TestCase { + final CollectionImplementation impl; + + /** Tests are parameterized by a Collection implementation. */ + Collection8Test(CollectionImplementation impl, String methodName) { + super(methodName); + this.impl = impl; + } + + public static Test testSuite(CollectionImplementation impl) { + return parameterizedTestSuite(Collection8Test.class, + CollectionImplementation.class, + impl); + } + + /** + * stream().forEach returns elements in the collection + */ + public void testForEach() throws Throwable { + final Collection c = impl.emptyCollection(); + final AtomicLong count = new AtomicLong(0L); + final Object x = impl.makeElement(1); + final Object y = impl.makeElement(2); + final ArrayList found = new ArrayList(); + Consumer spy = (o) -> { found.add(o); }; + c.stream().forEach(spy); + assertTrue(found.isEmpty()); + + assertTrue(c.add(x)); + c.stream().forEach(spy); + assertEquals(Collections.singletonList(x), found); + found.clear(); + + assertTrue(c.add(y)); + c.stream().forEach(spy); + assertEquals(2, found.size()); + assertTrue(found.contains(x)); + assertTrue(found.contains(y)); + found.clear(); + + c.clear(); + c.stream().forEach(spy); + assertTrue(found.isEmpty()); + } + + public void testForEachConcurrentStressTest() throws Throwable { + if (!impl.isConcurrent()) return; + final Collection c = impl.emptyCollection(); + final long testDurationMillis = SHORT_DELAY_MS; + final AtomicBoolean done = new AtomicBoolean(false); + final Object elt = impl.makeElement(1); + ExecutorService pool = Executors.newCachedThreadPool(); + Runnable checkElt = () -> { + while (!done.get()) + c.stream().forEach((x) -> { assertSame(x, elt); }); }; + Runnable addRemove = () -> { + while (!done.get()) { + assertTrue(c.add(elt)); + assertTrue(c.remove(elt)); + }}; + Future f1 = pool.submit(checkElt); + Future f2 = pool.submit(addRemove); + Thread.sleep(testDurationMillis); + done.set(true); + pool.shutdown(); + assertTrue(pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertNull(f1.get(LONG_DELAY_MS, MILLISECONDS)); + assertNull(f2.get(LONG_DELAY_MS, MILLISECONDS)); + } + + // public void testCollection8DebugFail() { fail(); } +} diff --git a/jdk/test/java/util/concurrent/tck/CollectionImplementation.java b/jdk/test/java/util/concurrent/tck/CollectionImplementation.java new file mode 100644 index 00000000000..54ffb7c0921 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CollectionImplementation.java @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Collection; + +/** Allows tests to work with different Collection implementations. */ +public interface CollectionImplementation { + /** Returns the Collection class. */ + public Class klazz(); + /** Returns an empty collection. */ + public Collection emptyCollection(); + public Object makeElement(int i); + public boolean isConcurrent(); + public boolean permitsNulls(); +} diff --git a/jdk/test/java/util/concurrent/tck/CollectionTest.java b/jdk/test/java/util/concurrent/tck/CollectionTest.java new file mode 100644 index 00000000000..7b532af8019 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CollectionTest.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Collection; + +import junit.framework.Test; + +/** + * Contains tests applicable to all Collection implementations. + */ +public class CollectionTest extends JSR166TestCase { + final CollectionImplementation impl; + + /** Tests are parameterized by a Collection implementation. */ + CollectionTest(CollectionImplementation impl, String methodName) { + super(methodName); + this.impl = impl; + } + + public static Test testSuite(CollectionImplementation impl) { + return newTestSuite + (parameterizedTestSuite(CollectionTest.class, + CollectionImplementation.class, + impl), + jdk8ParameterizedTestSuite(CollectionTest.class, + CollectionImplementation.class, + impl)); + } + + /** A test of the CollectionImplementation implementation ! */ + public void testEmptyMeansEmpty() { + assertTrue(impl.emptyCollection().isEmpty()); + assertEquals(0, impl.emptyCollection().size()); + } + + // public void testCollectionDebugFail() { fail(); } +} diff --git a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java new file mode 100644 index 00000000000..a74a0403f2f --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java @@ -0,0 +1,3980 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static java.util.concurrent.CompletableFuture.failedFuture; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CompletableFutureTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(CompletableFutureTest.class); + } + + static class CFException extends RuntimeException {} + + void checkIncomplete(CompletableFuture f) { + assertFalse(f.isDone()); + assertFalse(f.isCancelled()); + assertTrue(f.toString().contains("Not completed")); + try { + assertNull(f.getNow(null)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + f.get(0L, SECONDS); + shouldThrow(); + } + catch (TimeoutException success) {} + catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(CompletableFuture f, T value) { + checkTimedGet(f, value); + + try { + assertEquals(value, f.join()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertEquals(value, f.getNow(null)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertEquals(value, f.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertFalse(f.isCompletedExceptionally()); + assertTrue(f.toString().contains("[Completed normally]")); + } + + /** + * Returns the "raw" internal exceptional completion of f, + * without any additional wrapping with CompletionException. + */ + Throwable exceptionalCompletion(CompletableFuture f) { + // handle (and whenComplete) can distinguish between "direct" + // and "wrapped" exceptional completion + return f.handle((U u, Throwable t) -> t).join(); + } + + void checkCompletedExceptionally(CompletableFuture f, + boolean wrapped, + Consumer checker) { + Throwable cause = exceptionalCompletion(f); + if (wrapped) { + assertTrue(cause instanceof CompletionException); + cause = cause.getCause(); + } + checker.accept(cause); + + long startTime = System.nanoTime(); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(cause, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + + try { + f.join(); + shouldThrow(); + } catch (CompletionException success) { + assertSame(cause, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + f.getNow(null); + shouldThrow(); + } catch (CompletionException success) { + assertSame(cause, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(cause, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + assertFalse(f.isCancelled()); + assertTrue(f.isDone()); + assertTrue(f.isCompletedExceptionally()); + assertTrue(f.toString().contains("[Completed exceptionally]")); + } + + void checkCompletedWithWrappedCFException(CompletableFuture f) { + checkCompletedExceptionally(f, true, + (t) -> assertTrue(t instanceof CFException)); + } + + void checkCompletedWithWrappedCancellationException(CompletableFuture f) { + checkCompletedExceptionally(f, true, + (t) -> assertTrue(t instanceof CancellationException)); + } + + void checkCompletedWithTimeoutException(CompletableFuture f) { + checkCompletedExceptionally(f, false, + (t) -> assertTrue(t instanceof TimeoutException)); + } + + void checkCompletedWithWrappedException(CompletableFuture f, + Throwable ex) { + checkCompletedExceptionally(f, true, (t) -> assertSame(t, ex)); + } + + void checkCompletedExceptionally(CompletableFuture f, Throwable ex) { + checkCompletedExceptionally(f, false, (t) -> assertSame(t, ex)); + } + + void checkCancelled(CompletableFuture f) { + long startTime = System.nanoTime(); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) {} + try { + f.getNow(null); + shouldThrow(); + } catch (CancellationException success) {} + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + assertTrue(exceptionalCompletion(f) instanceof CancellationException); + + assertTrue(f.isDone()); + assertTrue(f.isCompletedExceptionally()); + assertTrue(f.isCancelled()); + assertTrue(f.toString().contains("[Completed exceptionally]")); + } + + /** + * A newly constructed CompletableFuture is incomplete, as indicated + * by methods isDone, isCancelled, and getNow + */ + public void testConstructor() { + CompletableFuture f = new CompletableFuture<>(); + checkIncomplete(f); + } + + /** + * complete completes normally, as indicated by methods isDone, + * isCancelled, join, get, and getNow + */ + public void testComplete() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + checkIncomplete(f); + assertTrue(f.complete(v1)); + assertFalse(f.complete(v1)); + checkCompletedNormally(f, v1); + }} + + /** + * completeExceptionally completes exceptionally, as indicated by + * methods isDone, isCancelled, join, get, and getNow + */ + public void testCompleteExceptionally() { + CompletableFuture f = new CompletableFuture<>(); + CFException ex = new CFException(); + checkIncomplete(f); + f.completeExceptionally(ex); + checkCompletedExceptionally(f, ex); + } + + /** + * cancel completes exceptionally and reports cancelled, as indicated by + * methods isDone, isCancelled, join, get, and getNow + */ + public void testCancel() { + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + { + CompletableFuture f = new CompletableFuture<>(); + checkIncomplete(f); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.cancel(!mayInterruptIfRunning)); + checkCancelled(f); + }} + + /** + * obtrudeValue forces completion with given value + */ + public void testObtrudeValue() { + CompletableFuture f = new CompletableFuture<>(); + checkIncomplete(f); + assertTrue(f.complete(one)); + checkCompletedNormally(f, one); + f.obtrudeValue(three); + checkCompletedNormally(f, three); + f.obtrudeValue(two); + checkCompletedNormally(f, two); + f = new CompletableFuture<>(); + f.obtrudeValue(three); + checkCompletedNormally(f, three); + f.obtrudeValue(null); + checkCompletedNormally(f, null); + f = new CompletableFuture<>(); + f.completeExceptionally(new CFException()); + f.obtrudeValue(four); + checkCompletedNormally(f, four); + } + + /** + * obtrudeException forces completion with given exception + */ + public void testObtrudeException() { + for (Integer v1 : new Integer[] { 1, null }) + { + CFException ex; + CompletableFuture f; + + f = new CompletableFuture<>(); + assertTrue(f.complete(v1)); + for (int i = 0; i < 2; i++) { + f.obtrudeException(ex = new CFException()); + checkCompletedExceptionally(f, ex); + } + + f = new CompletableFuture<>(); + for (int i = 0; i < 2; i++) { + f.obtrudeException(ex = new CFException()); + checkCompletedExceptionally(f, ex); + } + + f = new CompletableFuture<>(); + f.completeExceptionally(ex = new CFException()); + f.obtrudeValue(v1); + checkCompletedNormally(f, v1); + f.obtrudeException(ex = new CFException()); + checkCompletedExceptionally(f, ex); + f.completeExceptionally(new CFException()); + checkCompletedExceptionally(f, ex); + assertFalse(f.complete(v1)); + checkCompletedExceptionally(f, ex); + }} + + /** + * getNumberOfDependents returns number of dependent tasks + */ + public void testGetNumberOfDependents() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + assertEquals(0, f.getNumberOfDependents()); + final CompletableFuture g = m.thenRun(f, new Noop(m)); + assertEquals(1, f.getNumberOfDependents()); + assertEquals(0, g.getNumberOfDependents()); + final CompletableFuture h = m.thenRun(f, new Noop(m)); + assertEquals(2, f.getNumberOfDependents()); + assertEquals(0, h.getNumberOfDependents()); + assertTrue(f.complete(v1)); + checkCompletedNormally(g, null); + checkCompletedNormally(h, null); + assertEquals(0, f.getNumberOfDependents()); + assertEquals(0, g.getNumberOfDependents()); + assertEquals(0, h.getNumberOfDependents()); + }} + + /** + * toString indicates current completion state + */ + public void testToString() { + CompletableFuture f; + + f = new CompletableFuture(); + assertTrue(f.toString().contains("[Not completed]")); + + assertTrue(f.complete("foo")); + assertTrue(f.toString().contains("[Completed normally]")); + + f = new CompletableFuture(); + assertTrue(f.completeExceptionally(new IndexOutOfBoundsException())); + assertTrue(f.toString().contains("[Completed exceptionally]")); + + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { + f = new CompletableFuture(); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.toString().contains("[Completed exceptionally]")); + } + } + + /** + * completedFuture returns a completed CompletableFuture with given value + */ + public void testCompletedFuture() { + CompletableFuture f = CompletableFuture.completedFuture("test"); + checkCompletedNormally(f, "test"); + } + + abstract class CheckedAction { + int invocationCount = 0; + final ExecutionMode m; + CheckedAction(ExecutionMode m) { this.m = m; } + void invoked() { + m.checkExecutionMode(); + assertEquals(0, invocationCount++); + } + void assertNotInvoked() { assertEquals(0, invocationCount); } + void assertInvoked() { assertEquals(1, invocationCount); } + } + + abstract class CheckedIntegerAction extends CheckedAction { + Integer value; + CheckedIntegerAction(ExecutionMode m) { super(m); } + void assertValue(Integer expected) { + assertInvoked(); + assertEquals(expected, value); + } + } + + class IntegerSupplier extends CheckedAction + implements Supplier + { + final Integer value; + IntegerSupplier(ExecutionMode m, Integer value) { + super(m); + this.value = value; + } + public Integer get() { + invoked(); + return value; + } + } + + // A function that handles and produces null values as well. + static Integer inc(Integer x) { + return (x == null) ? null : x + 1; + } + + class NoopConsumer extends CheckedIntegerAction + implements Consumer + { + NoopConsumer(ExecutionMode m) { super(m); } + public void accept(Integer x) { + invoked(); + value = x; + } + } + + class IncFunction extends CheckedIntegerAction + implements Function + { + IncFunction(ExecutionMode m) { super(m); } + public Integer apply(Integer x) { + invoked(); + return value = inc(x); + } + } + + // Choose non-commutative actions for better coverage + // A non-commutative function that handles and produces null values as well. + static Integer subtract(Integer x, Integer y) { + return (x == null && y == null) ? null : + ((x == null) ? 42 : x.intValue()) + - ((y == null) ? 99 : y.intValue()); + } + + class SubtractAction extends CheckedIntegerAction + implements BiConsumer + { + SubtractAction(ExecutionMode m) { super(m); } + public void accept(Integer x, Integer y) { + invoked(); + value = subtract(x, y); + } + } + + class SubtractFunction extends CheckedIntegerAction + implements BiFunction + { + SubtractFunction(ExecutionMode m) { super(m); } + public Integer apply(Integer x, Integer y) { + invoked(); + return value = subtract(x, y); + } + } + + class Noop extends CheckedAction implements Runnable { + Noop(ExecutionMode m) { super(m); } + public void run() { + invoked(); + } + } + + class FailingSupplier extends CheckedAction + implements Supplier + { + FailingSupplier(ExecutionMode m) { super(m); } + public Integer get() { + invoked(); + throw new CFException(); + } + } + + class FailingConsumer extends CheckedIntegerAction + implements Consumer + { + FailingConsumer(ExecutionMode m) { super(m); } + public void accept(Integer x) { + invoked(); + value = x; + throw new CFException(); + } + } + + class FailingBiConsumer extends CheckedIntegerAction + implements BiConsumer + { + FailingBiConsumer(ExecutionMode m) { super(m); } + public void accept(Integer x, Integer y) { + invoked(); + value = subtract(x, y); + throw new CFException(); + } + } + + class FailingFunction extends CheckedIntegerAction + implements Function + { + FailingFunction(ExecutionMode m) { super(m); } + public Integer apply(Integer x) { + invoked(); + value = x; + throw new CFException(); + } + } + + class FailingBiFunction extends CheckedIntegerAction + implements BiFunction + { + FailingBiFunction(ExecutionMode m) { super(m); } + public Integer apply(Integer x, Integer y) { + invoked(); + value = subtract(x, y); + throw new CFException(); + } + } + + class FailingRunnable extends CheckedAction implements Runnable { + FailingRunnable(ExecutionMode m) { super(m); } + public void run() { + invoked(); + throw new CFException(); + } + } + + class CompletableFutureInc extends CheckedIntegerAction + implements Function> + { + CompletableFutureInc(ExecutionMode m) { super(m); } + public CompletableFuture apply(Integer x) { + invoked(); + value = x; + CompletableFuture f = new CompletableFuture<>(); + assertTrue(f.complete(inc(x))); + return f; + } + } + + class FailingCompletableFutureFunction extends CheckedIntegerAction + implements Function> + { + FailingCompletableFutureFunction(ExecutionMode m) { super(m); } + public CompletableFuture apply(Integer x) { + invoked(); + value = x; + throw new CFException(); + } + } + + // Used for explicit executor tests + static final class ThreadExecutor implements Executor { + final AtomicInteger count = new AtomicInteger(0); + static final ThreadGroup tg = new ThreadGroup("ThreadExecutor"); + static boolean startedCurrentThread() { + return Thread.currentThread().getThreadGroup() == tg; + } + + public void execute(Runnable r) { + count.getAndIncrement(); + new Thread(tg, r).start(); + } + } + + static final boolean defaultExecutorIsCommonPool + = ForkJoinPool.getCommonPoolParallelism() > 1; + + /** + * Permits the testing of parallel code for the 3 different + * execution modes without copy/pasting all the test methods. + */ + enum ExecutionMode { + SYNC { + public void checkExecutionMode() { + assertFalse(ThreadExecutor.startedCurrentThread()); + assertNull(ForkJoinTask.getPool()); + } + public CompletableFuture runAsync(Runnable a) { + throw new UnsupportedOperationException(); + } + public CompletableFuture supplyAsync(Supplier a) { + throw new UnsupportedOperationException(); + } + public CompletableFuture thenRun + (CompletableFuture f, Runnable a) { + return f.thenRun(a); + } + public CompletableFuture thenAccept + (CompletableFuture f, Consumer a) { + return f.thenAccept(a); + } + public CompletableFuture thenApply + (CompletableFuture f, Function a) { + return f.thenApply(a); + } + public CompletableFuture thenCompose + (CompletableFuture f, + Function> a) { + return f.thenCompose(a); + } + public CompletableFuture handle + (CompletableFuture f, + BiFunction a) { + return f.handle(a); + } + public CompletableFuture whenComplete + (CompletableFuture f, + BiConsumer a) { + return f.whenComplete(a); + } + public CompletableFuture runAfterBoth + (CompletableFuture f, CompletableFuture g, Runnable a) { + return f.runAfterBoth(g, a); + } + public CompletableFuture thenAcceptBoth + (CompletableFuture f, + CompletionStage g, + BiConsumer a) { + return f.thenAcceptBoth(g, a); + } + public CompletableFuture thenCombine + (CompletableFuture f, + CompletionStage g, + BiFunction a) { + return f.thenCombine(g, a); + } + public CompletableFuture runAfterEither + (CompletableFuture f, + CompletionStage g, + java.lang.Runnable a) { + return f.runAfterEither(g, a); + } + public CompletableFuture acceptEither + (CompletableFuture f, + CompletionStage g, + Consumer a) { + return f.acceptEither(g, a); + } + public CompletableFuture applyToEither + (CompletableFuture f, + CompletionStage g, + Function a) { + return f.applyToEither(g, a); + } + }, + + ASYNC { + public void checkExecutionMode() { + assertEquals(defaultExecutorIsCommonPool, + (ForkJoinPool.commonPool() == ForkJoinTask.getPool())); + } + public CompletableFuture runAsync(Runnable a) { + return CompletableFuture.runAsync(a); + } + public CompletableFuture supplyAsync(Supplier a) { + return CompletableFuture.supplyAsync(a); + } + public CompletableFuture thenRun + (CompletableFuture f, Runnable a) { + return f.thenRunAsync(a); + } + public CompletableFuture thenAccept + (CompletableFuture f, Consumer a) { + return f.thenAcceptAsync(a); + } + public CompletableFuture thenApply + (CompletableFuture f, Function a) { + return f.thenApplyAsync(a); + } + public CompletableFuture thenCompose + (CompletableFuture f, + Function> a) { + return f.thenComposeAsync(a); + } + public CompletableFuture handle + (CompletableFuture f, + BiFunction a) { + return f.handleAsync(a); + } + public CompletableFuture whenComplete + (CompletableFuture f, + BiConsumer a) { + return f.whenCompleteAsync(a); + } + public CompletableFuture runAfterBoth + (CompletableFuture f, CompletableFuture g, Runnable a) { + return f.runAfterBothAsync(g, a); + } + public CompletableFuture thenAcceptBoth + (CompletableFuture f, + CompletionStage g, + BiConsumer a) { + return f.thenAcceptBothAsync(g, a); + } + public CompletableFuture thenCombine + (CompletableFuture f, + CompletionStage g, + BiFunction a) { + return f.thenCombineAsync(g, a); + } + public CompletableFuture runAfterEither + (CompletableFuture f, + CompletionStage g, + java.lang.Runnable a) { + return f.runAfterEitherAsync(g, a); + } + public CompletableFuture acceptEither + (CompletableFuture f, + CompletionStage g, + Consumer a) { + return f.acceptEitherAsync(g, a); + } + public CompletableFuture applyToEither + (CompletableFuture f, + CompletionStage g, + Function a) { + return f.applyToEitherAsync(g, a); + } + }, + + EXECUTOR { + public void checkExecutionMode() { + assertTrue(ThreadExecutor.startedCurrentThread()); + } + public CompletableFuture runAsync(Runnable a) { + return CompletableFuture.runAsync(a, new ThreadExecutor()); + } + public CompletableFuture supplyAsync(Supplier a) { + return CompletableFuture.supplyAsync(a, new ThreadExecutor()); + } + public CompletableFuture thenRun + (CompletableFuture f, Runnable a) { + return f.thenRunAsync(a, new ThreadExecutor()); + } + public CompletableFuture thenAccept + (CompletableFuture f, Consumer a) { + return f.thenAcceptAsync(a, new ThreadExecutor()); + } + public CompletableFuture thenApply + (CompletableFuture f, Function a) { + return f.thenApplyAsync(a, new ThreadExecutor()); + } + public CompletableFuture thenCompose + (CompletableFuture f, + Function> a) { + return f.thenComposeAsync(a, new ThreadExecutor()); + } + public CompletableFuture handle + (CompletableFuture f, + BiFunction a) { + return f.handleAsync(a, new ThreadExecutor()); + } + public CompletableFuture whenComplete + (CompletableFuture f, + BiConsumer a) { + return f.whenCompleteAsync(a, new ThreadExecutor()); + } + public CompletableFuture runAfterBoth + (CompletableFuture f, CompletableFuture g, Runnable a) { + return f.runAfterBothAsync(g, a, new ThreadExecutor()); + } + public CompletableFuture thenAcceptBoth + (CompletableFuture f, + CompletionStage g, + BiConsumer a) { + return f.thenAcceptBothAsync(g, a, new ThreadExecutor()); + } + public CompletableFuture thenCombine + (CompletableFuture f, + CompletionStage g, + BiFunction a) { + return f.thenCombineAsync(g, a, new ThreadExecutor()); + } + public CompletableFuture runAfterEither + (CompletableFuture f, + CompletionStage g, + java.lang.Runnable a) { + return f.runAfterEitherAsync(g, a, new ThreadExecutor()); + } + public CompletableFuture acceptEither + (CompletableFuture f, + CompletionStage g, + Consumer a) { + return f.acceptEitherAsync(g, a, new ThreadExecutor()); + } + public CompletableFuture applyToEither + (CompletableFuture f, + CompletionStage g, + Function a) { + return f.applyToEitherAsync(g, a, new ThreadExecutor()); + } + }; + + public abstract void checkExecutionMode(); + public abstract CompletableFuture runAsync(Runnable a); + public abstract CompletableFuture supplyAsync(Supplier a); + public abstract CompletableFuture thenRun + (CompletableFuture f, Runnable a); + public abstract CompletableFuture thenAccept + (CompletableFuture f, Consumer a); + public abstract CompletableFuture thenApply + (CompletableFuture f, Function a); + public abstract CompletableFuture thenCompose + (CompletableFuture f, + Function> a); + public abstract CompletableFuture handle + (CompletableFuture f, + BiFunction a); + public abstract CompletableFuture whenComplete + (CompletableFuture f, + BiConsumer a); + public abstract CompletableFuture runAfterBoth + (CompletableFuture f, CompletableFuture g, Runnable a); + public abstract CompletableFuture thenAcceptBoth + (CompletableFuture f, + CompletionStage g, + BiConsumer a); + public abstract CompletableFuture thenCombine + (CompletableFuture f, + CompletionStage g, + BiFunction a); + public abstract CompletableFuture runAfterEither + (CompletableFuture f, + CompletionStage g, + java.lang.Runnable a); + public abstract CompletableFuture acceptEither + (CompletableFuture f, + CompletionStage g, + Consumer a); + public abstract CompletableFuture applyToEither + (CompletableFuture f, + CompletionStage g, + Function a); + } + + /** + * exceptionally action is not invoked when source completes + * normally, and source result is propagated + */ + public void testExceptionally_normalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final AtomicInteger a = new AtomicInteger(0); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = f.exceptionally + ((Throwable t) -> { + a.getAndIncrement(); + threadFail("should not be called"); + return null; // unreached + }); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedNormally(g, v1); + checkCompletedNormally(f, v1); + assertEquals(0, a.get()); + }} + + /** + * exceptionally action completes with function value on source + * exception + */ + public void testExceptionally_exceptionalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) f.completeExceptionally(ex); + final CompletableFuture g = f.exceptionally + ((Throwable t) -> { + ExecutionMode.SYNC.checkExecutionMode(); + threadAssertSame(t, ex); + a.getAndIncrement(); + return v1; + }); + if (createIncomplete) f.completeExceptionally(ex); + + checkCompletedNormally(g, v1); + assertEquals(1, a.get()); + }} + + /** + * If an "exceptionally action" throws an exception, it completes + * exceptionally with that exception + */ + public void testExceptionally_exceptionalCompletionActionFailed() { + for (boolean createIncomplete : new boolean[] { true, false }) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex1 = new CFException(); + final CFException ex2 = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) f.completeExceptionally(ex1); + final CompletableFuture g = f.exceptionally + ((Throwable t) -> { + ExecutionMode.SYNC.checkExecutionMode(); + threadAssertSame(t, ex1); + a.getAndIncrement(); + throw ex2; + }); + if (createIncomplete) f.completeExceptionally(ex1); + + checkCompletedWithWrappedException(g, ex2); + checkCompletedExceptionally(f, ex1); + assertEquals(1, a.get()); + }} + + /** + * whenComplete action executes on normal completion, propagating + * source result. + */ + public void testWhenComplete_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final AtomicInteger a = new AtomicInteger(0); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.whenComplete + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertSame(result, v1); + threadAssertNull(t); + a.getAndIncrement(); + }); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedNormally(g, v1); + checkCompletedNormally(f, v1); + assertEquals(1, a.get()); + }} + + /** + * whenComplete action executes on exceptional completion, propagating + * source result. + */ + public void testWhenComplete_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) f.completeExceptionally(ex); + final CompletableFuture g = m.whenComplete + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertNull(result); + threadAssertSame(t, ex); + a.getAndIncrement(); + }); + if (createIncomplete) f.completeExceptionally(ex); + + checkCompletedWithWrappedException(g, ex); + checkCompletedExceptionally(f, ex); + assertEquals(1, a.get()); + }} + + /** + * whenComplete action executes on cancelled source, propagating + * CancellationException. + */ + public void testWhenComplete_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean createIncomplete : new boolean[] { true, false }) + { + final AtomicInteger a = new AtomicInteger(0); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture g = m.whenComplete + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertNull(result); + threadAssertTrue(t instanceof CancellationException); + a.getAndIncrement(); + }); + if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); + + checkCompletedWithWrappedCancellationException(g); + checkCancelled(f); + assertEquals(1, a.get()); + }} + + /** + * If a whenComplete action throws an exception when triggered by + * a normal completion, it completes exceptionally + */ + public void testWhenComplete_sourceCompletedNormallyActionFailed() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.whenComplete + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertSame(result, v1); + threadAssertNull(t); + a.getAndIncrement(); + throw ex; + }); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedWithWrappedException(g, ex); + checkCompletedNormally(f, v1); + assertEquals(1, a.get()); + }} + + /** + * If a whenComplete action throws an exception when triggered by + * a source completion that also throws an exception, the source + * exception takes precedence (unlike handle) + */ + public void testWhenComplete_sourceFailedActionFailed() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (ExecutionMode m : ExecutionMode.values()) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex1 = new CFException(); + final CFException ex2 = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + + if (!createIncomplete) f.completeExceptionally(ex1); + final CompletableFuture g = m.whenComplete + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertSame(t, ex1); + threadAssertNull(result); + a.getAndIncrement(); + throw ex2; + }); + if (createIncomplete) f.completeExceptionally(ex1); + + checkCompletedWithWrappedException(g, ex1); + checkCompletedExceptionally(f, ex1); + // oops... temporarily disabled +// if (testImplementationDetails) { +// assertEquals(1, ex1.getSuppressed().length); +// assertSame(ex2, ex1.getSuppressed()[0]); +// } + assertEquals(1, a.get()); + }} + + /** + * handle action completes normally with function value on normal + * completion of source + */ + public void testHandle_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final AtomicInteger a = new AtomicInteger(0); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.handle + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertSame(result, v1); + threadAssertNull(t); + a.getAndIncrement(); + return inc(v1); + }); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedNormally(g, inc(v1)); + checkCompletedNormally(f, v1); + assertEquals(1, a.get()); + }} + + /** + * handle action completes normally with function value on + * exceptional completion of source + */ + public void testHandle_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final AtomicInteger a = new AtomicInteger(0); + final CFException ex = new CFException(); + if (!createIncomplete) f.completeExceptionally(ex); + final CompletableFuture g = m.handle + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertNull(result); + threadAssertSame(t, ex); + a.getAndIncrement(); + return v1; + }); + if (createIncomplete) f.completeExceptionally(ex); + + checkCompletedNormally(g, v1); + checkCompletedExceptionally(f, ex); + assertEquals(1, a.get()); + }} + + /** + * handle action completes normally with function value on + * cancelled source + */ + public void testHandle_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final AtomicInteger a = new AtomicInteger(0); + if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture g = m.handle + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertNull(result); + threadAssertTrue(t instanceof CancellationException); + a.getAndIncrement(); + return v1; + }); + if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); + + checkCompletedNormally(g, v1); + checkCancelled(f); + assertEquals(1, a.get()); + }} + + /** + * If a "handle action" throws an exception when triggered by + * a normal completion, it completes exceptionally + */ + public void testHandle_sourceCompletedNormallyActionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final AtomicInteger a = new AtomicInteger(0); + final CFException ex = new CFException(); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.handle + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertSame(result, v1); + threadAssertNull(t); + a.getAndIncrement(); + throw ex; + }); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedWithWrappedException(g, ex); + checkCompletedNormally(f, v1); + assertEquals(1, a.get()); + }} + + /** + * If a "handle action" throws an exception when triggered by + * a source completion that also throws an exception, the action + * exception takes precedence (unlike whenComplete) + */ + public void testHandle_sourceFailedActionFailed() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (ExecutionMode m : ExecutionMode.values()) + { + final AtomicInteger a = new AtomicInteger(0); + final CFException ex1 = new CFException(); + final CFException ex2 = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + + if (!createIncomplete) f.completeExceptionally(ex1); + final CompletableFuture g = m.handle + (f, + (Integer result, Throwable t) -> { + m.checkExecutionMode(); + threadAssertNull(result); + threadAssertSame(ex1, t); + a.getAndIncrement(); + throw ex2; + }); + if (createIncomplete) f.completeExceptionally(ex1); + + checkCompletedWithWrappedException(g, ex2); + checkCompletedExceptionally(f, ex1); + assertEquals(1, a.get()); + }} + + /** + * runAsync completes after running Runnable + */ + public void testRunAsync_normalCompletion() { + ExecutionMode[] executionModes = { + ExecutionMode.ASYNC, + ExecutionMode.EXECUTOR, + }; + for (ExecutionMode m : executionModes) + { + final Noop r = new Noop(m); + final CompletableFuture f = m.runAsync(r); + assertNull(f.join()); + checkCompletedNormally(f, null); + r.assertInvoked(); + }} + + /** + * failing runAsync completes exceptionally after running Runnable + */ + public void testRunAsync_exceptionalCompletion() { + ExecutionMode[] executionModes = { + ExecutionMode.ASYNC, + ExecutionMode.EXECUTOR, + }; + for (ExecutionMode m : executionModes) + { + final FailingRunnable r = new FailingRunnable(m); + final CompletableFuture f = m.runAsync(r); + checkCompletedWithWrappedCFException(f); + r.assertInvoked(); + }} + + /** + * supplyAsync completes with result of supplier + */ + public void testSupplyAsync_normalCompletion() { + ExecutionMode[] executionModes = { + ExecutionMode.ASYNC, + ExecutionMode.EXECUTOR, + }; + for (ExecutionMode m : executionModes) + for (Integer v1 : new Integer[] { 1, null }) + { + final IntegerSupplier r = new IntegerSupplier(m, v1); + final CompletableFuture f = m.supplyAsync(r); + assertSame(v1, f.join()); + checkCompletedNormally(f, v1); + r.assertInvoked(); + }} + + /** + * Failing supplyAsync completes exceptionally + */ + public void testSupplyAsync_exceptionalCompletion() { + ExecutionMode[] executionModes = { + ExecutionMode.ASYNC, + ExecutionMode.EXECUTOR, + }; + for (ExecutionMode m : executionModes) + { + FailingSupplier r = new FailingSupplier(m); + CompletableFuture f = m.supplyAsync(r); + checkCompletedWithWrappedCFException(f); + r.assertInvoked(); + }} + + // seq completion methods + + /** + * thenRun result completes normally after normal completion of source + */ + public void testThenRun_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.thenRun(f, rs[0]); + final CompletableFuture h1 = m.runAfterBoth(f, f, rs[1]); + final CompletableFuture h2 = m.runAfterEither(f, f, rs[2]); + checkIncomplete(h0); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(f.complete(v1)); + final CompletableFuture h3 = m.thenRun(f, rs[3]); + final CompletableFuture h4 = m.runAfterBoth(f, f, rs[4]); + final CompletableFuture h5 = m.runAfterEither(f, f, rs[5]); + + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + checkCompletedNormally(h4, null); + checkCompletedNormally(h5, null); + checkCompletedNormally(f, v1); + for (Noop r : rs) r.assertInvoked(); + }} + + /** + * thenRun result completes exceptionally after exceptional + * completion of source + */ + public void testThenRun_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + { + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.thenRun(f, rs[0]); + final CompletableFuture h1 = m.runAfterBoth(f, f, rs[1]); + final CompletableFuture h2 = m.runAfterEither(f, f, rs[2]); + checkIncomplete(h0); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(f.completeExceptionally(ex)); + final CompletableFuture h3 = m.thenRun(f, rs[3]); + final CompletableFuture h4 = m.runAfterBoth(f, f, rs[4]); + final CompletableFuture h5 = m.runAfterEither(f, f, rs[5]); + + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedWithWrappedException(h4, ex); + checkCompletedWithWrappedException(h5, ex); + checkCompletedExceptionally(f, ex); + for (Noop r : rs) r.assertNotInvoked(); + }} + + /** + * thenRun result completes exceptionally if source cancelled + */ + public void testThenRun_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + { + final CompletableFuture f = new CompletableFuture<>(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.thenRun(f, rs[0]); + final CompletableFuture h1 = m.runAfterBoth(f, f, rs[1]); + final CompletableFuture h2 = m.runAfterEither(f, f, rs[2]); + checkIncomplete(h0); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture h3 = m.thenRun(f, rs[3]); + final CompletableFuture h4 = m.runAfterBoth(f, f, rs[4]); + final CompletableFuture h5 = m.runAfterEither(f, f, rs[5]); + + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + checkCompletedWithWrappedCancellationException(h4); + checkCompletedWithWrappedCancellationException(h5); + checkCancelled(f); + for (Noop r : rs) r.assertNotInvoked(); + }} + + /** + * thenRun result completes exceptionally if action does + */ + public void testThenRun_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final FailingRunnable[] rs = new FailingRunnable[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m); + + final CompletableFuture h0 = m.thenRun(f, rs[0]); + final CompletableFuture h1 = m.runAfterBoth(f, f, rs[1]); + final CompletableFuture h2 = m.runAfterEither(f, f, rs[2]); + assertTrue(f.complete(v1)); + final CompletableFuture h3 = m.thenRun(f, rs[3]); + final CompletableFuture h4 = m.runAfterBoth(f, f, rs[4]); + final CompletableFuture h5 = m.runAfterEither(f, f, rs[5]); + + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedCFException(h4); + checkCompletedWithWrappedCFException(h5); + checkCompletedNormally(f, v1); + }} + + /** + * thenApply result completes normally after normal completion of source + */ + public void testThenApply_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.thenApply(f, rs[0]); + final CompletableFuture h1 = m.applyToEither(f, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + assertTrue(f.complete(v1)); + final CompletableFuture h2 = m.thenApply(f, rs[2]); + final CompletableFuture h3 = m.applyToEither(f, f, rs[3]); + + checkCompletedNormally(h0, inc(v1)); + checkCompletedNormally(h1, inc(v1)); + checkCompletedNormally(h2, inc(v1)); + checkCompletedNormally(h3, inc(v1)); + checkCompletedNormally(f, v1); + for (IncFunction r : rs) r.assertValue(inc(v1)); + }} + + /** + * thenApply result completes exceptionally after exceptional + * completion of source + */ + public void testThenApply_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + { + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.thenApply(f, rs[0]); + final CompletableFuture h1 = m.applyToEither(f, f, rs[1]); + assertTrue(f.completeExceptionally(ex)); + final CompletableFuture h2 = m.thenApply(f, rs[2]); + final CompletableFuture h3 = m.applyToEither(f, f, rs[3]); + + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedExceptionally(f, ex); + for (IncFunction r : rs) r.assertNotInvoked(); + }} + + /** + * thenApply result completes exceptionally if source cancelled + */ + public void testThenApply_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + { + final CompletableFuture f = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.thenApply(f, rs[0]); + final CompletableFuture h1 = m.applyToEither(f, f, rs[1]); + assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture h2 = m.thenApply(f, rs[2]); + final CompletableFuture h3 = m.applyToEither(f, f, rs[3]); + + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + checkCancelled(f); + for (IncFunction r : rs) r.assertNotInvoked(); + }} + + /** + * thenApply result completes exceptionally if action does + */ + public void testThenApply_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final FailingFunction[] rs = new FailingFunction[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m); + + final CompletableFuture h0 = m.thenApply(f, rs[0]); + final CompletableFuture h1 = m.applyToEither(f, f, rs[1]); + assertTrue(f.complete(v1)); + final CompletableFuture h2 = m.thenApply(f, rs[2]); + final CompletableFuture h3 = m.applyToEither(f, f, rs[3]); + + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + checkCompletedNormally(f, v1); + }} + + /** + * thenAccept result completes normally after normal completion of source + */ + public void testThenAccept_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final NoopConsumer[] rs = new NoopConsumer[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.thenAccept(f, rs[0]); + final CompletableFuture h1 = m.acceptEither(f, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + assertTrue(f.complete(v1)); + final CompletableFuture h2 = m.thenAccept(f, rs[2]); + final CompletableFuture h3 = m.acceptEither(f, f, rs[3]); + + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + checkCompletedNormally(f, v1); + for (NoopConsumer r : rs) r.assertValue(v1); + }} + + /** + * thenAccept result completes exceptionally after exceptional + * completion of source + */ + public void testThenAccept_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + { + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + final NoopConsumer[] rs = new NoopConsumer[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.thenAccept(f, rs[0]); + final CompletableFuture h1 = m.acceptEither(f, f, rs[1]); + assertTrue(f.completeExceptionally(ex)); + final CompletableFuture h2 = m.thenAccept(f, rs[2]); + final CompletableFuture h3 = m.acceptEither(f, f, rs[3]); + + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedExceptionally(f, ex); + for (NoopConsumer r : rs) r.assertNotInvoked(); + }} + + /** + * thenAccept result completes exceptionally if source cancelled + */ + public void testThenAccept_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + { + final CompletableFuture f = new CompletableFuture<>(); + final NoopConsumer[] rs = new NoopConsumer[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.thenAccept(f, rs[0]); + final CompletableFuture h1 = m.acceptEither(f, f, rs[1]); + assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture h2 = m.thenAccept(f, rs[2]); + final CompletableFuture h3 = m.acceptEither(f, f, rs[3]); + + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + checkCancelled(f); + for (NoopConsumer r : rs) r.assertNotInvoked(); + }} + + /** + * thenAccept result completes exceptionally if action does + */ + public void testThenAccept_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final FailingConsumer[] rs = new FailingConsumer[4]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m); + + final CompletableFuture h0 = m.thenAccept(f, rs[0]); + final CompletableFuture h1 = m.acceptEither(f, f, rs[1]); + assertTrue(f.complete(v1)); + final CompletableFuture h2 = m.thenAccept(f, rs[2]); + final CompletableFuture h3 = m.acceptEither(f, f, rs[3]); + + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + checkCompletedNormally(f, v1); + }} + + /** + * thenCombine result completes normally after normal completion + * of sources + */ + public void testThenCombine_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final SubtractFunction[] rs = new SubtractFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new SubtractFunction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h0 = m.thenCombine(f, g, rs[0]); + final CompletableFuture h1 = m.thenCombine(fst, fst, rs[1]); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.thenCombine(f, g, rs[2]); + final CompletableFuture h3 = m.thenCombine(fst, fst, rs[3]); + checkIncomplete(h0); rs[0].assertNotInvoked(); + checkIncomplete(h2); rs[2].assertNotInvoked(); + checkCompletedNormally(h1, subtract(w1, w1)); + checkCompletedNormally(h3, subtract(w1, w1)); + rs[1].assertValue(subtract(w1, w1)); + rs[3].assertValue(subtract(w1, w1)); + assertTrue(snd.complete(w2)); + final CompletableFuture h4 = m.thenCombine(f, g, rs[4]); + + checkCompletedNormally(h0, subtract(v1, v2)); + checkCompletedNormally(h2, subtract(v1, v2)); + checkCompletedNormally(h4, subtract(v1, v2)); + rs[0].assertValue(subtract(v1, v2)); + rs[2].assertValue(subtract(v1, v2)); + rs[4].assertValue(subtract(v1, v2)); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * thenCombine result completes exceptionally after exceptional + * completion of either source + */ + public void testThenCombine_exceptionalCompletion() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final SubtractFunction r1 = new SubtractFunction(m); + final SubtractFunction r2 = new SubtractFunction(m); + final SubtractFunction r3 = new SubtractFunction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.completeExceptionally(ex) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.completeExceptionally(ex); + + final CompletableFuture h1 = m.thenCombine(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.thenCombine(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.thenCombine(f, g, r3); + + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCompletedExceptionally(failFirst ? fst : snd, ex); + }} + + /** + * thenCombine result completes exceptionally if either source cancelled + */ + public void testThenCombine_sourceCancelled() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final SubtractFunction r1 = new SubtractFunction(m); + final SubtractFunction r2 = new SubtractFunction(m); + final SubtractFunction r3 = new SubtractFunction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.cancel(mayInterruptIfRunning) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.cancel(mayInterruptIfRunning); + + final CompletableFuture h1 = m.thenCombine(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.thenCombine(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.thenCombine(f, g, r3); + + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCancelled(failFirst ? fst : snd); + }} + + /** + * thenCombine result completes exceptionally if action does + */ + public void testThenCombine_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingBiFunction r1 = new FailingBiFunction(m); + final FailingBiFunction r2 = new FailingBiFunction(m); + final FailingBiFunction r3 = new FailingBiFunction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h1 = m.thenCombine(f, g, r1); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.thenCombine(f, g, r2); + assertTrue(snd.complete(w2)); + final CompletableFuture h3 = m.thenCombine(f, g, r3); + + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + r1.assertInvoked(); + r2.assertInvoked(); + r3.assertInvoked(); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * thenAcceptBoth result completes normally after normal + * completion of sources + */ + public void testThenAcceptBoth_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final SubtractAction r1 = new SubtractAction(m); + final SubtractAction r2 = new SubtractAction(m); + final SubtractAction r3 = new SubtractAction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h1 = m.thenAcceptBoth(f, g, r1); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.thenAcceptBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + assertTrue(snd.complete(w2)); + final CompletableFuture h3 = m.thenAcceptBoth(f, g, r3); + + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + r1.assertValue(subtract(v1, v2)); + r2.assertValue(subtract(v1, v2)); + r3.assertValue(subtract(v1, v2)); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * thenAcceptBoth result completes exceptionally after exceptional + * completion of either source + */ + public void testThenAcceptBoth_exceptionalCompletion() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final SubtractAction r1 = new SubtractAction(m); + final SubtractAction r2 = new SubtractAction(m); + final SubtractAction r3 = new SubtractAction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.completeExceptionally(ex) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.completeExceptionally(ex); + + final CompletableFuture h1 = m.thenAcceptBoth(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.thenAcceptBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.thenAcceptBoth(f, g, r3); + + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCompletedExceptionally(failFirst ? fst : snd, ex); + }} + + /** + * thenAcceptBoth result completes exceptionally if either source cancelled + */ + public void testThenAcceptBoth_sourceCancelled() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final SubtractAction r1 = new SubtractAction(m); + final SubtractAction r2 = new SubtractAction(m); + final SubtractAction r3 = new SubtractAction(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.cancel(mayInterruptIfRunning) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.cancel(mayInterruptIfRunning); + + final CompletableFuture h1 = m.thenAcceptBoth(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.thenAcceptBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.thenAcceptBoth(f, g, r3); + + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCancelled(failFirst ? fst : snd); + }} + + /** + * thenAcceptBoth result completes exceptionally if action does + */ + public void testThenAcceptBoth_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingBiConsumer r1 = new FailingBiConsumer(m); + final FailingBiConsumer r2 = new FailingBiConsumer(m); + final FailingBiConsumer r3 = new FailingBiConsumer(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h1 = m.thenAcceptBoth(f, g, r1); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.thenAcceptBoth(f, g, r2); + assertTrue(snd.complete(w2)); + final CompletableFuture h3 = m.thenAcceptBoth(f, g, r3); + + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + r1.assertInvoked(); + r2.assertInvoked(); + r3.assertInvoked(); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * runAfterBoth result completes normally after normal + * completion of sources + */ + public void testRunAfterBoth_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final Noop r1 = new Noop(m); + final Noop r2 = new Noop(m); + final Noop r3 = new Noop(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h1 = m.runAfterBoth(f, g, r1); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.runAfterBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + assertTrue(snd.complete(w2)); + final CompletableFuture h3 = m.runAfterBoth(f, g, r3); + + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + r1.assertInvoked(); + r2.assertInvoked(); + r3.assertInvoked(); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * runAfterBoth result completes exceptionally after exceptional + * completion of either source + */ + public void testRunAfterBoth_exceptionalCompletion() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final Noop r1 = new Noop(m); + final Noop r2 = new Noop(m); + final Noop r3 = new Noop(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.completeExceptionally(ex) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.completeExceptionally(ex); + + final CompletableFuture h1 = m.runAfterBoth(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.runAfterBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.runAfterBoth(f, g, r3); + + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCompletedExceptionally(failFirst ? fst : snd, ex); + }} + + /** + * runAfterBoth result completes exceptionally if either source cancelled + */ + public void testRunAfterBoth_sourceCancelled() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean fFirst : new boolean[] { true, false }) + for (boolean failFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final Noop r1 = new Noop(m); + final Noop r2 = new Noop(m); + final Noop r3 = new Noop(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Callable complete1 = failFirst ? + () -> fst.cancel(mayInterruptIfRunning) : + () -> fst.complete(v1); + final Callable complete2 = failFirst ? + () -> snd.complete(v1) : + () -> snd.cancel(mayInterruptIfRunning); + + final CompletableFuture h1 = m.runAfterBoth(f, g, r1); + assertTrue(complete1.call()); + final CompletableFuture h2 = m.runAfterBoth(f, g, r2); + checkIncomplete(h1); + checkIncomplete(h2); + assertTrue(complete2.call()); + final CompletableFuture h3 = m.runAfterBoth(f, g, r3); + + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + r1.assertNotInvoked(); + r2.assertNotInvoked(); + r3.assertNotInvoked(); + checkCompletedNormally(failFirst ? snd : fst, v1); + checkCancelled(failFirst ? fst : snd); + }} + + /** + * runAfterBoth result completes exceptionally if action does + */ + public void testRunAfterBoth_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingRunnable r1 = new FailingRunnable(m); + final FailingRunnable r2 = new FailingRunnable(m); + final FailingRunnable r3 = new FailingRunnable(m); + + final CompletableFuture fst = fFirst ? f : g; + final CompletableFuture snd = !fFirst ? f : g; + final Integer w1 = fFirst ? v1 : v2; + final Integer w2 = !fFirst ? v1 : v2; + + final CompletableFuture h1 = m.runAfterBoth(f, g, r1); + assertTrue(fst.complete(w1)); + final CompletableFuture h2 = m.runAfterBoth(f, g, r2); + assertTrue(snd.complete(w2)); + final CompletableFuture h3 = m.runAfterBoth(f, g, r3); + + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + r1.assertInvoked(); + r2.assertInvoked(); + r3.assertInvoked(); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * applyToEither result completes normally after normal completion + * of either source + */ + public void testApplyToEither_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.complete(v1); + checkCompletedNormally(h0, inc(v1)); + checkCompletedNormally(h1, inc(v1)); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + checkCompletedNormally(h2, inc(v1)); + checkCompletedNormally(h3, inc(v1)); + g.complete(v2); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.applyToEither(f, g, rs[4]); + final CompletableFuture h5 = m.applyToEither(g, f, rs[5]); + rs[4].assertValue(h4.join()); + rs[5].assertValue(h5.join()); + assertTrue(Objects.equals(inc(v1), h4.join()) || + Objects.equals(inc(v2), h4.join())); + assertTrue(Objects.equals(inc(v1), h5.join()) || + Objects.equals(inc(v2), h5.join())); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + checkCompletedNormally(h0, inc(v1)); + checkCompletedNormally(h1, inc(v1)); + checkCompletedNormally(h2, inc(v1)); + checkCompletedNormally(h3, inc(v1)); + for (int i = 0; i < 4; i++) rs[i].assertValue(inc(v1)); + }} + + /** + * applyToEither result completes exceptionally after exceptional + * completion of either source + */ + public void testApplyToEither_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final IncFunction[] rs = new IncFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.completeExceptionally(ex); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + g.complete(v1); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.applyToEither(f, g, rs[4]); + final CompletableFuture h5 = m.applyToEither(g, f, rs[5]); + try { + assertEquals(inc(v1), h4.join()); + rs[4].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h4, ex); + rs[4].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h5.join()); + rs[5].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h5, ex); + rs[5].assertNotInvoked(); + } + + checkCompletedExceptionally(f, ex); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedWithWrappedException(h4, ex); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + public void testApplyToEither_exceptionalCompletion2() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final IncFunction[] rs = new IncFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + + // unspecified behavior - both source completions available + try { + assertEquals(inc(v1), h0.join()); + rs[0].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h0, ex); + rs[0].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h1.join()); + rs[1].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h1, ex); + rs[1].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h2.join()); + rs[2].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h2, ex); + rs[2].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h3.join()); + rs[3].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h3, ex); + rs[3].assertNotInvoked(); + } + + checkCompletedNormally(f, v1); + checkCompletedExceptionally(g, ex); + }} + + /** + * applyToEither result completes exceptionally if either source cancelled + */ + public void testApplyToEither_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.cancel(mayInterruptIfRunning); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + g.complete(v1); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.applyToEither(f, g, rs[4]); + final CompletableFuture h5 = m.applyToEither(g, f, rs[5]); + try { + assertEquals(inc(v1), h4.join()); + rs[4].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h4); + rs[4].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h5.join()); + rs[5].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h5); + rs[5].assertNotInvoked(); + } + + checkCancelled(f); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + public void testApplyToEither_sourceCancelled2() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final IncFunction[] rs = new IncFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + assertTrue(fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning)); + assertTrue(!fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning)); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + + // unspecified behavior - both source completions available + try { + assertEquals(inc(v1), h0.join()); + rs[0].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h0); + rs[0].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h1.join()); + rs[1].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h1); + rs[1].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h2.join()); + rs[2].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h2); + rs[2].assertNotInvoked(); + } + try { + assertEquals(inc(v1), h3.join()); + rs[3].assertValue(inc(v1)); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h3); + rs[3].assertNotInvoked(); + } + + checkCompletedNormally(f, v1); + checkCancelled(g); + }} + + /** + * applyToEither result completes exceptionally if action does + */ + public void testApplyToEither_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingFunction[] rs = new FailingFunction[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m); + + final CompletableFuture h0 = m.applyToEither(f, g, rs[0]); + final CompletableFuture h1 = m.applyToEither(g, f, rs[1]); + f.complete(v1); + final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); + final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + for (int i = 0; i < 4; i++) rs[i].assertValue(v1); + + g.complete(v2); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.applyToEither(f, g, rs[4]); + final CompletableFuture h5 = m.applyToEither(g, f, rs[5]); + + checkCompletedWithWrappedCFException(h4); + assertTrue(Objects.equals(v1, rs[4].value) || + Objects.equals(v2, rs[4].value)); + checkCompletedWithWrappedCFException(h5); + assertTrue(Objects.equals(v1, rs[5].value) || + Objects.equals(v2, rs[5].value)); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * acceptEither result completes normally after normal completion + * of either source + */ + public void testAcceptEither_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final NoopConsumer[] rs = new NoopConsumer[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.acceptEither(f, g, rs[0]); + final CompletableFuture h1 = m.acceptEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.complete(v1); + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + rs[0].assertValue(v1); + rs[1].assertValue(v1); + final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); + final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + rs[2].assertValue(v1); + rs[3].assertValue(v1); + g.complete(v2); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.acceptEither(f, g, rs[4]); + final CompletableFuture h5 = m.acceptEither(g, f, rs[5]); + checkCompletedNormally(h4, null); + checkCompletedNormally(h5, null); + assertTrue(Objects.equals(v1, rs[4].value) || + Objects.equals(v2, rs[4].value)); + assertTrue(Objects.equals(v1, rs[5].value) || + Objects.equals(v2, rs[5].value)); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + for (int i = 0; i < 4; i++) rs[i].assertValue(v1); + }} + + /** + * acceptEither result completes exceptionally after exceptional + * completion of either source + */ + public void testAcceptEither_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final NoopConsumer[] rs = new NoopConsumer[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.acceptEither(f, g, rs[0]); + final CompletableFuture h1 = m.acceptEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.completeExceptionally(ex); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); + final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + + g.complete(v1); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.acceptEither(f, g, rs[4]); + final CompletableFuture h5 = m.acceptEither(g, f, rs[5]); + try { + assertNull(h4.join()); + rs[4].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h4, ex); + rs[4].assertNotInvoked(); + } + try { + assertNull(h5.join()); + rs[5].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h5, ex); + rs[5].assertNotInvoked(); + } + + checkCompletedExceptionally(f, ex); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedWithWrappedException(h4, ex); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + public void testAcceptEither_exceptionalCompletion2() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final NoopConsumer[] rs = new NoopConsumer[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.acceptEither(f, g, rs[0]); + final CompletableFuture h1 = m.acceptEither(g, f, rs[1]); + assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); + final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); + + // unspecified behavior - both source completions available + try { + assertEquals(null, h0.join()); + rs[0].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h0, ex); + rs[0].assertNotInvoked(); + } + try { + assertEquals(null, h1.join()); + rs[1].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h1, ex); + rs[1].assertNotInvoked(); + } + try { + assertEquals(null, h2.join()); + rs[2].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h2, ex); + rs[2].assertNotInvoked(); + } + try { + assertEquals(null, h3.join()); + rs[3].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h3, ex); + rs[3].assertNotInvoked(); + } + + checkCompletedNormally(f, v1); + checkCompletedExceptionally(g, ex); + }} + + /** + * acceptEither result completes exceptionally if either source cancelled + */ + public void testAcceptEither_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final NoopConsumer[] rs = new NoopConsumer[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); + + final CompletableFuture h0 = m.acceptEither(f, g, rs[0]); + final CompletableFuture h1 = m.acceptEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.cancel(mayInterruptIfRunning); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); + final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + + g.complete(v1); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.acceptEither(f, g, rs[4]); + final CompletableFuture h5 = m.acceptEither(g, f, rs[5]); + try { + assertNull(h4.join()); + rs[4].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h4); + rs[4].assertNotInvoked(); + } + try { + assertNull(h5.join()); + rs[5].assertValue(v1); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h5); + rs[5].assertNotInvoked(); + } + + checkCancelled(f); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + /** + * acceptEither result completes exceptionally if action does + */ + public void testAcceptEither_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingConsumer[] rs = new FailingConsumer[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m); + + final CompletableFuture h0 = m.acceptEither(f, g, rs[0]); + final CompletableFuture h1 = m.acceptEither(g, f, rs[1]); + f.complete(v1); + final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); + final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + for (int i = 0; i < 4; i++) rs[i].assertValue(v1); + + g.complete(v2); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.acceptEither(f, g, rs[4]); + final CompletableFuture h5 = m.acceptEither(g, f, rs[5]); + + checkCompletedWithWrappedCFException(h4); + assertTrue(Objects.equals(v1, rs[4].value) || + Objects.equals(v2, rs[4].value)); + checkCompletedWithWrappedCFException(h5); + assertTrue(Objects.equals(v1, rs[5].value) || + Objects.equals(v2, rs[5].value)); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + }} + + /** + * runAfterEither result completes normally after normal completion + * of either source + */ + public void testRunAfterEither_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.runAfterEither(f, g, rs[0]); + final CompletableFuture h1 = m.runAfterEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.complete(v1); + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + rs[0].assertInvoked(); + rs[1].assertInvoked(); + final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); + final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + rs[2].assertInvoked(); + rs[3].assertInvoked(); + + g.complete(v2); + + final CompletableFuture h4 = m.runAfterEither(f, g, rs[4]); + final CompletableFuture h5 = m.runAfterEither(g, f, rs[5]); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + checkCompletedNormally(h0, null); + checkCompletedNormally(h1, null); + checkCompletedNormally(h2, null); + checkCompletedNormally(h3, null); + checkCompletedNormally(h4, null); + checkCompletedNormally(h5, null); + for (int i = 0; i < 6; i++) rs[i].assertInvoked(); + }} + + /** + * runAfterEither result completes exceptionally after exceptional + * completion of either source + */ + public void testRunAfterEither_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.runAfterEither(f, g, rs[0]); + final CompletableFuture h1 = m.runAfterEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + assertTrue(f.completeExceptionally(ex)); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); + final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + + assertTrue(g.complete(v1)); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.runAfterEither(f, g, rs[4]); + final CompletableFuture h5 = m.runAfterEither(g, f, rs[5]); + try { + assertNull(h4.join()); + rs[4].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h4, ex); + rs[4].assertNotInvoked(); + } + try { + assertNull(h5.join()); + rs[5].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h5, ex); + rs[5].assertNotInvoked(); + } + + checkCompletedExceptionally(f, ex); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedException(h0, ex); + checkCompletedWithWrappedException(h1, ex); + checkCompletedWithWrappedException(h2, ex); + checkCompletedWithWrappedException(h3, ex); + checkCompletedWithWrappedException(h4, ex); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + public void testRunAfterEither_exceptionalCompletion2() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean fFirst : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CFException ex = new CFException(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.runAfterEither(f, g, rs[0]); + final CompletableFuture h1 = m.runAfterEither(g, f, rs[1]); + assertTrue( fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); + final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); + final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); + + // unspecified behavior - both source completions available + try { + assertEquals(null, h0.join()); + rs[0].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h0, ex); + rs[0].assertNotInvoked(); + } + try { + assertEquals(null, h1.join()); + rs[1].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h1, ex); + rs[1].assertNotInvoked(); + } + try { + assertEquals(null, h2.join()); + rs[2].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h2, ex); + rs[2].assertNotInvoked(); + } + try { + assertEquals(null, h3.join()); + rs[3].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedException(h3, ex); + rs[3].assertNotInvoked(); + } + + checkCompletedNormally(f, v1); + checkCompletedExceptionally(g, ex); + }} + + /** + * runAfterEither result completes exceptionally if either source cancelled + */ + public void testRunAfterEither_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final Noop[] rs = new Noop[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); + + final CompletableFuture h0 = m.runAfterEither(f, g, rs[0]); + final CompletableFuture h1 = m.runAfterEither(g, f, rs[1]); + checkIncomplete(h0); + checkIncomplete(h1); + rs[0].assertNotInvoked(); + rs[1].assertNotInvoked(); + f.cancel(mayInterruptIfRunning); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); + final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + + assertTrue(g.complete(v1)); + + // unspecified behavior - both source completions available + final CompletableFuture h4 = m.runAfterEither(f, g, rs[4]); + final CompletableFuture h5 = m.runAfterEither(g, f, rs[5]); + try { + assertNull(h4.join()); + rs[4].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h4); + rs[4].assertNotInvoked(); + } + try { + assertNull(h5.join()); + rs[5].assertInvoked(); + } catch (CompletionException ok) { + checkCompletedWithWrappedCancellationException(h5); + rs[5].assertNotInvoked(); + } + + checkCancelled(f); + checkCompletedNormally(g, v1); + checkCompletedWithWrappedCancellationException(h0); + checkCompletedWithWrappedCancellationException(h1); + checkCompletedWithWrappedCancellationException(h2); + checkCompletedWithWrappedCancellationException(h3); + for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); + }} + + /** + * runAfterEither result completes exceptionally if action does + */ + public void testRunAfterEither_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (Integer v1 : new Integer[] { 1, null }) + for (Integer v2 : new Integer[] { 2, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final FailingRunnable[] rs = new FailingRunnable[6]; + for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m); + + final CompletableFuture h0 = m.runAfterEither(f, g, rs[0]); + final CompletableFuture h1 = m.runAfterEither(g, f, rs[1]); + assertTrue(f.complete(v1)); + final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); + final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); + checkCompletedWithWrappedCFException(h0); + checkCompletedWithWrappedCFException(h1); + checkCompletedWithWrappedCFException(h2); + checkCompletedWithWrappedCFException(h3); + for (int i = 0; i < 4; i++) rs[i].assertInvoked(); + assertTrue(g.complete(v2)); + final CompletableFuture h4 = m.runAfterEither(f, g, rs[4]); + final CompletableFuture h5 = m.runAfterEither(g, f, rs[5]); + checkCompletedWithWrappedCFException(h4); + checkCompletedWithWrappedCFException(h5); + + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v2); + for (int i = 0; i < 6; i++) rs[i].assertInvoked(); + }} + + /** + * thenCompose result completes normally after normal completion of source + */ + public void testThenCompose_normalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFutureInc r = new CompletableFutureInc(m); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.thenCompose(f, r); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedNormally(g, inc(v1)); + checkCompletedNormally(f, v1); + r.assertValue(v1); + }} + + /** + * thenCompose result completes exceptionally after exceptional + * completion of source + */ + public void testThenCompose_exceptionalCompletion() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + { + final CFException ex = new CFException(); + final CompletableFutureInc r = new CompletableFutureInc(m); + final CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) f.completeExceptionally(ex); + final CompletableFuture g = m.thenCompose(f, r); + if (createIncomplete) f.completeExceptionally(ex); + + checkCompletedWithWrappedException(g, ex); + checkCompletedExceptionally(f, ex); + r.assertNotInvoked(); + }} + + /** + * thenCompose result completes exceptionally if action does + */ + public void testThenCompose_actionFailed() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + final CompletableFuture f = new CompletableFuture<>(); + final FailingCompletableFutureFunction r + = new FailingCompletableFutureFunction(m); + if (!createIncomplete) assertTrue(f.complete(v1)); + final CompletableFuture g = m.thenCompose(f, r); + if (createIncomplete) assertTrue(f.complete(v1)); + + checkCompletedWithWrappedCFException(g); + checkCompletedNormally(f, v1); + }} + + /** + * thenCompose result completes exceptionally if source cancelled + */ + public void testThenCompose_sourceCancelled() { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean createIncomplete : new boolean[] { true, false }) + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + { + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFutureInc r = new CompletableFutureInc(m); + if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); + final CompletableFuture g = m.thenCompose(f, r); + if (createIncomplete) { + checkIncomplete(g); + assertTrue(f.cancel(mayInterruptIfRunning)); + } + + checkCompletedWithWrappedCancellationException(g); + checkCancelled(f); + }} + + /** + * thenCompose result completes exceptionally if the result of the action does + */ + public void testThenCompose_actionReturnsFailingFuture() { + for (ExecutionMode m : ExecutionMode.values()) + for (int order = 0; order < 6; order++) + for (Integer v1 : new Integer[] { 1, null }) + { + final CFException ex = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + final CompletableFuture g = new CompletableFuture<>(); + final CompletableFuture h; + // Test all permutations of orders + switch (order) { + case 0: + assertTrue(f.complete(v1)); + assertTrue(g.completeExceptionally(ex)); + h = m.thenCompose(f, (x -> g)); + break; + case 1: + assertTrue(f.complete(v1)); + h = m.thenCompose(f, (x -> g)); + assertTrue(g.completeExceptionally(ex)); + break; + case 2: + assertTrue(g.completeExceptionally(ex)); + assertTrue(f.complete(v1)); + h = m.thenCompose(f, (x -> g)); + break; + case 3: + assertTrue(g.completeExceptionally(ex)); + h = m.thenCompose(f, (x -> g)); + assertTrue(f.complete(v1)); + break; + case 4: + h = m.thenCompose(f, (x -> g)); + assertTrue(f.complete(v1)); + assertTrue(g.completeExceptionally(ex)); + break; + case 5: + h = m.thenCompose(f, (x -> g)); + assertTrue(f.complete(v1)); + assertTrue(g.completeExceptionally(ex)); + break; + default: throw new AssertionError(); + } + + checkCompletedExceptionally(g, ex); + checkCompletedWithWrappedException(h, ex); + checkCompletedNormally(f, v1); + }} + + // other static methods + + /** + * allOf(no component futures) returns a future completed normally + * with the value null + */ + public void testAllOf_empty() throws Exception { + CompletableFuture f = CompletableFuture.allOf(); + checkCompletedNormally(f, null); + } + + /** + * allOf returns a future completed normally with the value null + * when all components complete normally + */ + public void testAllOf_normal() throws Exception { + for (int k = 1; k < 10; k++) { + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[k]; + for (int i = 0; i < k; i++) + fs[i] = new CompletableFuture<>(); + CompletableFuture f = CompletableFuture.allOf(fs); + for (int i = 0; i < k; i++) { + checkIncomplete(f); + checkIncomplete(CompletableFuture.allOf(fs)); + fs[i].complete(one); + } + checkCompletedNormally(f, null); + checkCompletedNormally(CompletableFuture.allOf(fs), null); + } + } + + public void testAllOf_backwards() throws Exception { + for (int k = 1; k < 10; k++) { + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[k]; + for (int i = 0; i < k; i++) + fs[i] = new CompletableFuture<>(); + CompletableFuture f = CompletableFuture.allOf(fs); + for (int i = k - 1; i >= 0; i--) { + checkIncomplete(f); + checkIncomplete(CompletableFuture.allOf(fs)); + fs[i].complete(one); + } + checkCompletedNormally(f, null); + checkCompletedNormally(CompletableFuture.allOf(fs), null); + } + } + + public void testAllOf_exceptional() throws Exception { + for (int k = 1; k < 10; k++) { + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[k]; + CFException ex = new CFException(); + for (int i = 0; i < k; i++) + fs[i] = new CompletableFuture<>(); + CompletableFuture f = CompletableFuture.allOf(fs); + for (int i = 0; i < k; i++) { + checkIncomplete(f); + checkIncomplete(CompletableFuture.allOf(fs)); + if (i != k / 2) { + fs[i].complete(i); + checkCompletedNormally(fs[i], i); + } else { + fs[i].completeExceptionally(ex); + checkCompletedExceptionally(fs[i], ex); + } + } + checkCompletedWithWrappedException(f, ex); + checkCompletedWithWrappedException(CompletableFuture.allOf(fs), ex); + } + } + + /** + * anyOf(no component futures) returns an incomplete future + */ + public void testAnyOf_empty() throws Exception { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = CompletableFuture.anyOf(); + checkIncomplete(f); + + f.complete(v1); + checkCompletedNormally(f, v1); + }} + + /** + * anyOf returns a future completed normally with a value when + * a component future does + */ + public void testAnyOf_normal() throws Exception { + for (int k = 0; k < 10; k++) { + CompletableFuture[] fs = new CompletableFuture[k]; + for (int i = 0; i < k; i++) + fs[i] = new CompletableFuture<>(); + CompletableFuture f = CompletableFuture.anyOf(fs); + checkIncomplete(f); + for (int i = 0; i < k; i++) { + fs[i].complete(i); + checkCompletedNormally(f, 0); + int x = (int) CompletableFuture.anyOf(fs).join(); + assertTrue(0 <= x && x <= i); + } + } + } + public void testAnyOf_normal_backwards() throws Exception { + for (int k = 0; k < 10; k++) { + CompletableFuture[] fs = new CompletableFuture[k]; + for (int i = 0; i < k; i++) + fs[i] = new CompletableFuture<>(); + CompletableFuture f = CompletableFuture.anyOf(fs); + checkIncomplete(f); + for (int i = k - 1; i >= 0; i--) { + fs[i].complete(i); + checkCompletedNormally(f, k - 1); + int x = (int) CompletableFuture.anyOf(fs).join(); + assertTrue(i <= x && x <= k - 1); + } + } + } + + /** + * anyOf result completes exceptionally when any component does. + */ + public void testAnyOf_exceptional() throws Exception { + for (int k = 0; k < 10; k++) { + CompletableFuture[] fs = new CompletableFuture[k]; + CFException[] exs = new CFException[k]; + for (int i = 0; i < k; i++) { + fs[i] = new CompletableFuture<>(); + exs[i] = new CFException(); + } + CompletableFuture f = CompletableFuture.anyOf(fs); + checkIncomplete(f); + for (int i = 0; i < k; i++) { + fs[i].completeExceptionally(exs[i]); + checkCompletedWithWrappedException(f, exs[0]); + checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs)); + } + } + } + + public void testAnyOf_exceptional_backwards() throws Exception { + for (int k = 0; k < 10; k++) { + CompletableFuture[] fs = new CompletableFuture[k]; + CFException[] exs = new CFException[k]; + for (int i = 0; i < k; i++) { + fs[i] = new CompletableFuture<>(); + exs[i] = new CFException(); + } + CompletableFuture f = CompletableFuture.anyOf(fs); + checkIncomplete(f); + for (int i = k - 1; i >= 0; i--) { + fs[i].completeExceptionally(exs[i]); + checkCompletedWithWrappedException(f, exs[k - 1]); + checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs)); + } + } + } + + /** + * Completion methods throw NullPointerException with null arguments + */ + public void testNPE() { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = new CompletableFuture<>(); + CompletableFuture nullFuture = (CompletableFuture)null; + ThreadExecutor exec = new ThreadExecutor(); + + Runnable[] throwingActions = { + () -> CompletableFuture.supplyAsync(null), + () -> CompletableFuture.supplyAsync(null, exec), + () -> CompletableFuture.supplyAsync(new IntegerSupplier(ExecutionMode.SYNC, 42), null), + + () -> CompletableFuture.runAsync(null), + () -> CompletableFuture.runAsync(null, exec), + () -> CompletableFuture.runAsync(() -> {}, null), + + () -> f.completeExceptionally(null), + + () -> f.thenApply(null), + () -> f.thenApplyAsync(null), + () -> f.thenApplyAsync((x) -> x, null), + () -> f.thenApplyAsync(null, exec), + + () -> f.thenAccept(null), + () -> f.thenAcceptAsync(null), + () -> f.thenAcceptAsync((x) -> {} , null), + () -> f.thenAcceptAsync(null, exec), + + () -> f.thenRun(null), + () -> f.thenRunAsync(null), + () -> f.thenRunAsync(() -> {} , null), + () -> f.thenRunAsync(null, exec), + + () -> f.thenCombine(g, null), + () -> f.thenCombineAsync(g, null), + () -> f.thenCombineAsync(g, null, exec), + () -> f.thenCombine(nullFuture, (x, y) -> x), + () -> f.thenCombineAsync(nullFuture, (x, y) -> x), + () -> f.thenCombineAsync(nullFuture, (x, y) -> x, exec), + () -> f.thenCombineAsync(g, (x, y) -> x, null), + + () -> f.thenAcceptBoth(g, null), + () -> f.thenAcceptBothAsync(g, null), + () -> f.thenAcceptBothAsync(g, null, exec), + () -> f.thenAcceptBoth(nullFuture, (x, y) -> {}), + () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}), + () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}, exec), + () -> f.thenAcceptBothAsync(g, (x, y) -> {}, null), + + () -> f.runAfterBoth(g, null), + () -> f.runAfterBothAsync(g, null), + () -> f.runAfterBothAsync(g, null, exec), + () -> f.runAfterBoth(nullFuture, () -> {}), + () -> f.runAfterBothAsync(nullFuture, () -> {}), + () -> f.runAfterBothAsync(nullFuture, () -> {}, exec), + () -> f.runAfterBothAsync(g, () -> {}, null), + + () -> f.applyToEither(g, null), + () -> f.applyToEitherAsync(g, null), + () -> f.applyToEitherAsync(g, null, exec), + () -> f.applyToEither(nullFuture, (x) -> x), + () -> f.applyToEitherAsync(nullFuture, (x) -> x), + () -> f.applyToEitherAsync(nullFuture, (x) -> x, exec), + () -> f.applyToEitherAsync(g, (x) -> x, null), + + () -> f.acceptEither(g, null), + () -> f.acceptEitherAsync(g, null), + () -> f.acceptEitherAsync(g, null, exec), + () -> f.acceptEither(nullFuture, (x) -> {}), + () -> f.acceptEitherAsync(nullFuture, (x) -> {}), + () -> f.acceptEitherAsync(nullFuture, (x) -> {}, exec), + () -> f.acceptEitherAsync(g, (x) -> {}, null), + + () -> f.runAfterEither(g, null), + () -> f.runAfterEitherAsync(g, null), + () -> f.runAfterEitherAsync(g, null, exec), + () -> f.runAfterEither(nullFuture, () -> {}), + () -> f.runAfterEitherAsync(nullFuture, () -> {}), + () -> f.runAfterEitherAsync(nullFuture, () -> {}, exec), + () -> f.runAfterEitherAsync(g, () -> {}, null), + + () -> f.thenCompose(null), + () -> f.thenComposeAsync(null), + () -> f.thenComposeAsync(new CompletableFutureInc(ExecutionMode.EXECUTOR), null), + () -> f.thenComposeAsync(null, exec), + + () -> f.exceptionally(null), + + () -> f.handle(null), + + () -> CompletableFuture.allOf((CompletableFuture)null), + () -> CompletableFuture.allOf((CompletableFuture[])null), + () -> CompletableFuture.allOf(f, null), + () -> CompletableFuture.allOf(null, f), + + () -> CompletableFuture.anyOf((CompletableFuture)null), + () -> CompletableFuture.anyOf((CompletableFuture[])null), + () -> CompletableFuture.anyOf(f, null), + () -> CompletableFuture.anyOf(null, f), + + () -> f.obtrudeException(null), + + () -> CompletableFuture.delayedExecutor(1L, SECONDS, null), + () -> CompletableFuture.delayedExecutor(1L, null, new ThreadExecutor()), + () -> CompletableFuture.delayedExecutor(1L, null), + + () -> f.orTimeout(1L, null), + () -> f.completeOnTimeout(42, 1L, null), + + () -> CompletableFuture.failedFuture(null), + () -> CompletableFuture.failedStage(null), + }; + + assertThrows(NullPointerException.class, throwingActions); + assertEquals(0, exec.count.get()); + } + + /** + * toCompletableFuture returns this CompletableFuture. + */ + public void testToCompletableFuture() { + CompletableFuture f = new CompletableFuture<>(); + assertSame(f, f.toCompletableFuture()); + } + + // jdk9 + + /** + * newIncompleteFuture returns an incomplete CompletableFuture + */ + public void testNewIncompleteFuture() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = f.newIncompleteFuture(); + checkIncomplete(f); + checkIncomplete(g); + f.complete(v1); + checkCompletedNormally(f, v1); + checkIncomplete(g); + g.complete(v1); + checkCompletedNormally(g, v1); + assertSame(g.getClass(), CompletableFuture.class); + }} + + /** + * completedStage returns a completed CompletionStage + */ + public void testCompletedStage() { + AtomicInteger x = new AtomicInteger(0); + AtomicReference r = new AtomicReference(); + CompletionStage f = CompletableFuture.completedStage(1); + f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); + assertEquals(x.get(), 1); + assertNull(r.get()); + } + + /** + * defaultExecutor by default returns the commonPool if + * it supports more than one thread. + */ + public void testDefaultExecutor() { + CompletableFuture f = new CompletableFuture<>(); + Executor e = f.defaultExecutor(); + Executor c = ForkJoinPool.commonPool(); + if (ForkJoinPool.getCommonPoolParallelism() > 1) + assertSame(e, c); + else + assertNotSame(e, c); + } + + /** + * failedFuture returns a CompletableFuture completed + * exceptionally with the given Exception + */ + public void testFailedFuture() { + CFException ex = new CFException(); + CompletableFuture f = CompletableFuture.failedFuture(ex); + checkCompletedExceptionally(f, ex); + } + + /** + * failedFuture(null) throws NPE + */ + public void testFailedFuture_null() { + try { + CompletableFuture f = CompletableFuture.failedFuture(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * copy returns a CompletableFuture that is completed normally, + * with the same value, when source is. + */ + public void testCopy() { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = f.copy(); + checkIncomplete(f); + checkIncomplete(g); + f.complete(1); + checkCompletedNormally(f, 1); + checkCompletedNormally(g, 1); + } + + /** + * copy returns a CompletableFuture that is completed exceptionally + * when source is. + */ + public void testCopy2() { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = f.copy(); + checkIncomplete(f); + checkIncomplete(g); + CFException ex = new CFException(); + f.completeExceptionally(ex); + checkCompletedExceptionally(f, ex); + checkCompletedWithWrappedException(g, ex); + } + + /** + * minimalCompletionStage returns a CompletableFuture that is + * completed normally, with the same value, when source is. + */ + public void testMinimalCompletionStage() { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage g = f.minimalCompletionStage(); + AtomicInteger x = new AtomicInteger(0); + AtomicReference r = new AtomicReference(); + checkIncomplete(f); + g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); + f.complete(1); + checkCompletedNormally(f, 1); + assertEquals(x.get(), 1); + assertNull(r.get()); + } + + /** + * minimalCompletionStage returns a CompletableFuture that is + * completed exceptionally when source is. + */ + public void testMinimalCompletionStage2() { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage g = f.minimalCompletionStage(); + AtomicInteger x = new AtomicInteger(0); + AtomicReference r = new AtomicReference(); + g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); + checkIncomplete(f); + CFException ex = new CFException(); + f.completeExceptionally(ex); + checkCompletedExceptionally(f, ex); + assertEquals(x.get(), 0); + assertEquals(r.get().getCause(), ex); + } + + /** + * failedStage returns a CompletionStage completed + * exceptionally with the given Exception + */ + public void testFailedStage() { + CFException ex = new CFException(); + CompletionStage f = CompletableFuture.failedStage(ex); + AtomicInteger x = new AtomicInteger(0); + AtomicReference r = new AtomicReference(); + f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); + assertEquals(x.get(), 0); + assertEquals(r.get(), ex); + } + + /** + * completeAsync completes with value of given supplier + */ + public void testCompleteAsync() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + f.completeAsync(() -> v1); + f.join(); + checkCompletedNormally(f, v1); + }} + + /** + * completeAsync completes exceptionally if given supplier throws + */ + public void testCompleteAsync2() { + CompletableFuture f = new CompletableFuture<>(); + CFException ex = new CFException(); + f.completeAsync(() -> {if (true) throw ex; return 1;}); + try { + f.join(); + shouldThrow(); + } catch (CompletionException success) {} + checkCompletedWithWrappedException(f, ex); + } + + /** + * completeAsync with given executor completes with value of given supplier + */ + public void testCompleteAsync3() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + ThreadExecutor executor = new ThreadExecutor(); + f.completeAsync(() -> v1, executor); + assertSame(v1, f.join()); + checkCompletedNormally(f, v1); + assertEquals(1, executor.count.get()); + }} + + /** + * completeAsync with given executor completes exceptionally if + * given supplier throws + */ + public void testCompleteAsync4() { + CompletableFuture f = new CompletableFuture<>(); + CFException ex = new CFException(); + ThreadExecutor executor = new ThreadExecutor(); + f.completeAsync(() -> {if (true) throw ex; return 1;}, executor); + try { + f.join(); + shouldThrow(); + } catch (CompletionException success) {} + checkCompletedWithWrappedException(f, ex); + assertEquals(1, executor.count.get()); + } + + /** + * orTimeout completes with TimeoutException if not complete + */ + public void testOrTimeout_timesOut() { + long timeoutMillis = timeoutMillis(); + CompletableFuture f = new CompletableFuture<>(); + long startTime = System.nanoTime(); + f.orTimeout(timeoutMillis, MILLISECONDS); + checkCompletedWithTimeoutException(f); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + } + + /** + * orTimeout completes normally if completed before timeout + */ + public void testOrTimeout_completed() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = new CompletableFuture<>(); + long startTime = System.nanoTime(); + f.complete(v1); + f.orTimeout(LONG_DELAY_MS, MILLISECONDS); + g.orTimeout(LONG_DELAY_MS, MILLISECONDS); + g.complete(v1); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v1); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + }} + + /** + * completeOnTimeout completes with given value if not complete + */ + public void testCompleteOnTimeout_timesOut() { + testInParallel(() -> testCompleteOnTimeout_timesOut(42), + () -> testCompleteOnTimeout_timesOut(null)); + } + + public void testCompleteOnTimeout_timesOut(Integer v) { + long timeoutMillis = timeoutMillis(); + CompletableFuture f = new CompletableFuture<>(); + long startTime = System.nanoTime(); + f.completeOnTimeout(v, timeoutMillis, MILLISECONDS); + assertSame(v, f.join()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + f.complete(99); // should have no effect + checkCompletedNormally(f, v); + } + + /** + * completeOnTimeout has no effect if completed within timeout + */ + public void testCompleteOnTimeout_completed() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture g = new CompletableFuture<>(); + long startTime = System.nanoTime(); + f.complete(v1); + f.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS); + g.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS); + g.complete(v1); + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v1); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + }} + + /** + * delayedExecutor returns an executor that delays submission + */ + public void testDelayedExecutor() { + testInParallel(() -> testDelayedExecutor(null, null), + () -> testDelayedExecutor(null, 1), + () -> testDelayedExecutor(new ThreadExecutor(), 1), + () -> testDelayedExecutor(new ThreadExecutor(), 1)); + } + + public void testDelayedExecutor(Executor executor, Integer v) throws Exception { + long timeoutMillis = timeoutMillis(); + // Use an "unreasonably long" long timeout to catch lingering threads + long longTimeoutMillis = 1000 * 60 * 60 * 24; + final Executor delayer, longDelayer; + if (executor == null) { + delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS); + longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS); + } else { + delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS, executor); + longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS, executor); + } + long startTime = System.nanoTime(); + CompletableFuture f = + CompletableFuture.supplyAsync(() -> v, delayer); + CompletableFuture g = + CompletableFuture.supplyAsync(() -> v, longDelayer); + + assertNull(g.getNow(null)); + + assertSame(v, f.get(LONG_DELAY_MS, MILLISECONDS)); + long millisElapsed = millisElapsedSince(startTime); + assertTrue(millisElapsed >= timeoutMillis); + assertTrue(millisElapsed < LONG_DELAY_MS / 2); + + checkCompletedNormally(f, v); + + checkIncomplete(g); + assertTrue(g.cancel(true)); + } + + //--- tests of implementation details; not part of official tck --- + + Object resultOf(CompletableFuture f) { + try { + java.lang.reflect.Field resultField + = CompletableFuture.class.getDeclaredField("result"); + resultField.setAccessible(true); + return resultField.get(f); + } catch (Throwable t) { throw new AssertionError(t); } + } + + public void testExceptionPropagationReusesResultObject() { + if (!testImplementationDetails) return; + for (ExecutionMode m : ExecutionMode.values()) + { + final CFException ex = new CFException(); + final CompletableFuture v42 = CompletableFuture.completedFuture(42); + final CompletableFuture incomplete = new CompletableFuture<>(); + + List, CompletableFuture>> funs + = new ArrayList<>(); + + funs.add((y) -> m.thenRun(y, new Noop(m))); + funs.add((y) -> m.thenAccept(y, new NoopConsumer(m))); + funs.add((y) -> m.thenApply(y, new IncFunction(m))); + + funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m))); + funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m))); + funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m))); + + funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m))); + funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m))); + funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m))); + + funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {})); + + funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m))); + + funs.add((y) -> CompletableFuture.allOf(new CompletableFuture[] {y, v42})); + funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture[] {y, incomplete})); + + for (Function, CompletableFuture> + fun : funs) { + CompletableFuture f = new CompletableFuture<>(); + f.completeExceptionally(ex); + CompletableFuture src = m.thenApply(f, new IncFunction(m)); + checkCompletedWithWrappedException(src, ex); + CompletableFuture dep = fun.apply(src); + checkCompletedWithWrappedException(dep, ex); + assertSame(resultOf(src), resultOf(dep)); + } + + for (Function, CompletableFuture> + fun : funs) { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture dep = fun.apply(src); + f.completeExceptionally(ex); + checkCompletedWithWrappedException(src, ex); + checkCompletedWithWrappedException(dep, ex); + assertSame(resultOf(src), resultOf(dep)); + } + + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (Function, CompletableFuture> + fun : funs) { + CompletableFuture f = new CompletableFuture<>(); + f.cancel(mayInterruptIfRunning); + checkCancelled(f); + CompletableFuture src = m.thenApply(f, new IncFunction(m)); + checkCompletedWithWrappedCancellationException(src); + CompletableFuture dep = fun.apply(src); + checkCompletedWithWrappedCancellationException(dep); + assertSame(resultOf(src), resultOf(dep)); + } + + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) + for (Function, CompletableFuture> + fun : funs) { + CompletableFuture f = new CompletableFuture<>(); + CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture dep = fun.apply(src); + f.cancel(mayInterruptIfRunning); + checkCancelled(f); + checkCompletedWithWrappedCancellationException(src); + checkCompletedWithWrappedCancellationException(dep); + assertSame(resultOf(src), resultOf(dep)); + } + }} + + /** + * Minimal completion stages throw UOE for all non-CompletionStage methods + */ + public void testMinimalCompletionStage_minimality() { + if (!testImplementationDetails) return; + Function toSignature = + (method) -> method.getName() + Arrays.toString(method.getParameterTypes()); + Predicate isNotStatic = + (method) -> (method.getModifiers() & Modifier.STATIC) == 0; + List minimalMethods = + Stream.of(Object.class, CompletionStage.class) + .flatMap((klazz) -> Stream.of(klazz.getMethods())) + .filter(isNotStatic) + .collect(Collectors.toList()); + // Methods from CompletableFuture permitted NOT to throw UOE + String[] signatureWhitelist = { + "newIncompleteFuture[]", + "defaultExecutor[]", + "minimalCompletionStage[]", + "copy[]", + }; + Set permittedMethodSignatures = + Stream.concat(minimalMethods.stream().map(toSignature), + Stream.of(signatureWhitelist)) + .collect(Collectors.toSet()); + List allMethods = Stream.of(CompletableFuture.class.getMethods()) + .filter(isNotStatic) + .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) + .collect(Collectors.toList()); + + CompletionStage minimalStage = + new CompletableFuture().minimalCompletionStage(); + + List bugs = new ArrayList<>(); + for (Method method : allMethods) { + Class[] parameterTypes = method.getParameterTypes(); + Object[] args = new Object[parameterTypes.length]; + // Manufacture boxed primitives for primitive params + for (int i = 0; i < args.length; i++) { + Class type = parameterTypes[i]; + if (parameterTypes[i] == boolean.class) + args[i] = false; + else if (parameterTypes[i] == int.class) + args[i] = 0; + else if (parameterTypes[i] == long.class) + args[i] = 0L; + } + try { + method.invoke(minimalStage, args); + bugs.add(method); + } + catch (java.lang.reflect.InvocationTargetException expected) { + if (! (expected.getCause() instanceof UnsupportedOperationException)) { + bugs.add(method); + // expected.getCause().printStackTrace(); + } + } + catch (ReflectiveOperationException bad) { throw new Error(bad); } + } + if (!bugs.isEmpty()) + throw new Error("Methods did not throw UOE: " + bugs.toString()); + } + + static class Monad { + static class ZeroException extends RuntimeException { + public ZeroException() { super("monadic zero"); } + } + // "return", "unit" + static CompletableFuture unit(T value) { + return completedFuture(value); + } + // monadic zero ? + static CompletableFuture zero() { + return failedFuture(new ZeroException()); + } + // >=> + static Function> compose + (Function> f, + Function> g) { + return (x) -> f.apply(x).thenCompose(g); + } + + static void assertZero(CompletableFuture f) { + try { + f.getNow(null); + throw new AssertionFailedError("should throw"); + } catch (CompletionException success) { + assertTrue(success.getCause() instanceof ZeroException); + } + } + + static void assertFutureEquals(CompletableFuture f, + CompletableFuture g) { + T fval = null, gval = null; + Throwable fex = null, gex = null; + + try { fval = f.get(); } + catch (ExecutionException ex) { fex = ex.getCause(); } + catch (Throwable ex) { fex = ex; } + + try { gval = g.get(); } + catch (ExecutionException ex) { gex = ex.getCause(); } + catch (Throwable ex) { gex = ex; } + + if (fex != null || gex != null) + assertSame(fex.getClass(), gex.getClass()); + else + assertEquals(fval, gval); + } + + static class PlusFuture extends CompletableFuture { + AtomicReference firstFailure = new AtomicReference<>(null); + } + + /** Implements "monadic plus". */ + static CompletableFuture plus(CompletableFuture f, + CompletableFuture g) { + PlusFuture plus = new PlusFuture(); + BiConsumer action = (T result, Throwable ex) -> { + try { + if (ex == null) { + if (plus.complete(result)) + if (plus.firstFailure.get() != null) + plus.firstFailure.set(null); + } + else if (plus.firstFailure.compareAndSet(null, ex)) { + if (plus.isDone()) + plus.firstFailure.set(null); + } + else { + // first failure has precedence + Throwable first = plus.firstFailure.getAndSet(null); + + // may fail with "Self-suppression not permitted" + try { first.addSuppressed(ex); } + catch (Exception ignored) {} + + plus.completeExceptionally(first); + } + } catch (Throwable unexpected) { + plus.completeExceptionally(unexpected); + } + }; + f.whenComplete(action); + g.whenComplete(action); + return plus; + } + } + + /** + * CompletableFuture is an additive monad - sort of. + * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads + */ + public void testAdditiveMonad() throws Throwable { + Function> unit = Monad::unit; + CompletableFuture zero = Monad.zero(); + + // Some mutually non-commutative functions + Function> triple + = (x) -> Monad.unit(3 * x); + Function> inc + = (x) -> Monad.unit(x + 1); + + // unit is a right identity: m >>= unit === m + Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit), + inc.apply(5L)); + // unit is a left identity: (unit x) >>= f === f x + Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc), + inc.apply(5L)); + + // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) ) + Monad.assertFutureEquals( + unit.apply(5L).thenCompose(inc).thenCompose(triple), + unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple))); + + // The case for CompletableFuture as an additive monad is weaker... + + // zero is a monadic zero + Monad.assertZero(zero); + + // left zero: zero >>= f === zero + Monad.assertZero(zero.thenCompose(inc)); + // right zero: f >>= (\x -> zero) === zero + Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero)); + + // f plus zero === f + Monad.assertFutureEquals(Monad.unit(5L), + Monad.plus(Monad.unit(5L), zero)); + // zero plus f === f + Monad.assertFutureEquals(Monad.unit(5L), + Monad.plus(zero, Monad.unit(5L))); + // zero plus zero === zero + Monad.assertZero(Monad.plus(zero, zero)); + { + CompletableFuture f = Monad.plus(Monad.unit(5L), + Monad.unit(8L)); + // non-determinism + assertTrue(f.get() == 5L || f.get() == 8L); + } + + CompletableFuture godot = new CompletableFuture<>(); + // f plus godot === f (doesn't wait for godot) + Monad.assertFutureEquals(Monad.unit(5L), + Monad.plus(Monad.unit(5L), godot)); + // godot plus f === f (doesn't wait for godot) + Monad.assertFutureEquals(Monad.unit(5L), + Monad.plus(godot, Monad.unit(5L))); + } + +// static U join(CompletionStage stage) { +// CompletableFuture f = new CompletableFuture<>(); +// stage.whenComplete((v, ex) -> { +// if (ex != null) f.completeExceptionally(ex); else f.complete(v); +// }); +// return f.join(); +// } + +// static boolean isDone(CompletionStage stage) { +// CompletableFuture f = new CompletableFuture<>(); +// stage.whenComplete((v, ex) -> { +// if (ex != null) f.completeExceptionally(ex); else f.complete(v); +// }); +// return f.isDone(); +// } + +// static U join2(CompletionStage stage) { +// return stage.toCompletableFuture().copy().join(); +// } + +// static boolean isDone2(CompletionStage stage) { +// return stage.toCompletableFuture().copy().isDone(); +// } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java b/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java new file mode 100644 index 00000000000..518fab33170 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java @@ -0,0 +1,1118 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.Spliterator.CONCURRENT; +import static java.util.Spliterator.DISTINCT; +import static java.util.Spliterator.NONNULL; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.BiFunction; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentHashMap8Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentHashMap8Test.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static ConcurrentHashMap map5() { + ConcurrentHashMap map = new ConcurrentHashMap(5); + assertTrue(map.isEmpty()); + map.put(one, "A"); + map.put(two, "B"); + map.put(three, "C"); + map.put(four, "D"); + map.put(five, "E"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map; + } + + /** + * getOrDefault returns value if present, else default + */ + public void testGetOrDefault() { + ConcurrentHashMap map = map5(); + assertEquals(map.getOrDefault(one, "Z"), "A"); + assertEquals(map.getOrDefault(six, "Z"), "Z"); + } + + /** + * computeIfAbsent adds when the given key is not present + */ + public void testComputeIfAbsent() { + ConcurrentHashMap map = map5(); + map.computeIfAbsent(six, (x) -> "Z"); + assertTrue(map.containsKey(six)); + } + + /** + * computeIfAbsent does not replace if the key is already present + */ + public void testComputeIfAbsent2() { + ConcurrentHashMap map = map5(); + assertEquals("A", map.computeIfAbsent(one, (x) -> "Z")); + } + + /** + * computeIfAbsent does not add if function returns null + */ + public void testComputeIfAbsent3() { + ConcurrentHashMap map = map5(); + map.computeIfAbsent(six, (x) -> null); + assertFalse(map.containsKey(six)); + } + + /** + * computeIfPresent does not replace if the key is already present + */ + public void testComputeIfPresent() { + ConcurrentHashMap map = map5(); + map.computeIfPresent(six, (x, y) -> "Z"); + assertFalse(map.containsKey(six)); + } + + /** + * computeIfPresent adds when the given key is not present + */ + public void testComputeIfPresent2() { + ConcurrentHashMap map = map5(); + assertEquals("Z", map.computeIfPresent(one, (x, y) -> "Z")); + } + + /** + * compute does not replace if the function returns null + */ + public void testCompute() { + ConcurrentHashMap map = map5(); + map.compute(six, (x, y) -> null); + assertFalse(map.containsKey(six)); + } + + /** + * compute adds when the given key is not present + */ + public void testCompute2() { + ConcurrentHashMap map = map5(); + assertEquals("Z", map.compute(six, (x, y) -> "Z")); + } + + /** + * compute replaces when the given key is present + */ + public void testCompute3() { + ConcurrentHashMap map = map5(); + assertEquals("Z", map.compute(one, (x, y) -> "Z")); + } + + /** + * compute removes when the given key is present and function returns null + */ + public void testCompute4() { + ConcurrentHashMap map = map5(); + map.compute(one, (x, y) -> null); + assertFalse(map.containsKey(one)); + } + + /** + * merge adds when the given key is not present + */ + public void testMerge1() { + ConcurrentHashMap map = map5(); + assertEquals("Y", map.merge(six, "Y", (x, y) -> "Z")); + } + + /** + * merge replaces when the given key is present + */ + public void testMerge2() { + ConcurrentHashMap map = map5(); + assertEquals("Z", map.merge(one, "Y", (x, y) -> "Z")); + } + + /** + * merge removes when the given key is present and function returns null + */ + public void testMerge3() { + ConcurrentHashMap map = map5(); + map.merge(one, "Y", (x, y) -> null); + assertFalse(map.containsKey(one)); + } + + static Set populatedSet(int n) { + Set a = ConcurrentHashMap.newKeySet(); + assertTrue(a.isEmpty()); + for (int i = 0; i < n; i++) + assertTrue(a.add(i)); + assertEquals(n == 0, a.isEmpty()); + assertEquals(n, a.size()); + return a; + } + + static Set populatedSet(Integer[] elements) { + Set a = ConcurrentHashMap.newKeySet(); + assertTrue(a.isEmpty()); + for (int i = 0; i < elements.length; i++) + assertTrue(a.add(elements[i])); + assertFalse(a.isEmpty()); + assertEquals(elements.length, a.size()); + return a; + } + + /** + * replaceAll replaces all matching values. + */ + public void testReplaceAll() { + ConcurrentHashMap map = map5(); + map.replaceAll((x, y) -> { return x > 3 ? "Z" : y; }); + assertEquals("A", map.get(one)); + assertEquals("B", map.get(two)); + assertEquals("C", map.get(three)); + assertEquals("Z", map.get(four)); + assertEquals("Z", map.get(five)); + } + + /** + * Default-constructed set is empty + */ + public void testNewKeySet() { + Set a = ConcurrentHashMap.newKeySet(); + assertTrue(a.isEmpty()); + } + + /** + * keySet.add adds the key with the established value to the map; + * remove removes it. + */ + public void testKeySetAddRemove() { + ConcurrentHashMap map = map5(); + Set set1 = map.keySet(); + Set set2 = map.keySet(true); + set2.add(six); + assertTrue(((ConcurrentHashMap.KeySetView)set2).getMap() == map); + assertTrue(((ConcurrentHashMap.KeySetView)set1).getMap() == map); + assertEquals(set2.size(), map.size()); + assertEquals(set1.size(), map.size()); + assertTrue((Boolean)map.get(six)); + assertTrue(set1.contains(six)); + assertTrue(set2.contains(six)); + set2.remove(six); + assertNull(map.get(six)); + assertFalse(set1.contains(six)); + assertFalse(set2.contains(six)); + } + + /** + * keySet.addAll adds each element from the given collection + */ + public void testAddAll() { + Set full = populatedSet(3); + assertTrue(full.addAll(Arrays.asList(three, four, five))); + assertEquals(6, full.size()); + assertFalse(full.addAll(Arrays.asList(three, four, five))); + assertEquals(6, full.size()); + } + + /** + * keySet.addAll adds each element from the given collection that did not + * already exist in the set + */ + public void testAddAll2() { + Set full = populatedSet(3); + // "one" is duplicate and will not be added + assertTrue(full.addAll(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + assertFalse(full.addAll(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + } + + /** + * keySet.add will not add the element if it already exists in the set + */ + public void testAdd2() { + Set full = populatedSet(3); + assertFalse(full.add(one)); + assertEquals(3, full.size()); + } + + /** + * keySet.add adds the element when it does not exist in the set + */ + public void testAdd3() { + Set full = populatedSet(3); + assertTrue(full.add(three)); + assertTrue(full.contains(three)); + assertFalse(full.add(three)); + assertTrue(full.contains(three)); + } + + /** + * keySet.add throws UnsupportedOperationException if no default + * mapped value + */ + public void testAdd4() { + Set full = map5().keySet(); + try { + full.add(three); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } + + /** + * keySet.add throws NullPointerException if the specified key is + * null + */ + public void testAdd5() { + Set full = populatedSet(3); + try { + full.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * KeySetView.getMappedValue returns the map's mapped value + */ + public void testGetMappedValue() { + ConcurrentHashMap map = map5(); + assertNull(map.keySet().getMappedValue()); + try { + map.keySet(null); + shouldThrow(); + } catch (NullPointerException success) {} + ConcurrentHashMap.KeySetView set = map.keySet(one); + assertFalse(set.add(one)); + assertTrue(set.add(six)); + assertTrue(set.add(seven)); + assertTrue(set.getMappedValue() == one); + assertTrue(map.get(one) != one); + assertTrue(map.get(six) == one); + assertTrue(map.get(seven) == one); + } + + void checkSpliteratorCharacteristics(Spliterator sp, + int requiredCharacteristics) { + assertEquals(requiredCharacteristics, + requiredCharacteristics & sp.characteristics()); + } + + /** + * KeySetView.spliterator returns spliterator over the elements in this set + */ + public void testKeySetSpliterator() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap map = map5(); + Set set = map.keySet(); + Spliterator sp = set.spliterator(); + checkSpliteratorCharacteristics(sp, CONCURRENT | DISTINCT | NONNULL); + assertEquals(sp.estimateSize(), map.size()); + Spliterator sp2 = sp.trySplit(); + sp.forEachRemaining((Integer x) -> adder.add(x.longValue())); + long v = adder.sumThenReset(); + sp2.forEachRemaining((Integer x) -> adder.add(x.longValue())); + long v2 = adder.sum(); + assertEquals(v + v2, 15); + } + + /** + * keyset.clear removes all elements from the set + */ + public void testClear() { + Set full = populatedSet(3); + full.clear(); + assertEquals(0, full.size()); + } + + /** + * keyset.contains returns true for added elements + */ + public void testContains() { + Set full = populatedSet(3); + assertTrue(full.contains(one)); + assertFalse(full.contains(five)); + } + + /** + * KeySets with equal elements are equal + */ + public void testEquals() { + Set a = populatedSet(3); + Set b = populatedSet(3); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertEquals(a.hashCode(), b.hashCode()); + a.add(m1); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + b.add(m1); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertEquals(a.hashCode(), b.hashCode()); + } + + /** + * KeySet.containsAll returns true for collections with subset of elements + */ + public void testContainsAll() { + Collection full = populatedSet(3); + assertTrue(full.containsAll(Arrays.asList())); + assertTrue(full.containsAll(Arrays.asList(one))); + assertTrue(full.containsAll(Arrays.asList(one, two))); + assertFalse(full.containsAll(Arrays.asList(one, two, six))); + assertFalse(full.containsAll(Arrays.asList(six))); + } + + /** + * KeySet.isEmpty is true when empty, else false + */ + public void testIsEmpty() { + assertTrue(populatedSet(0).isEmpty()); + assertFalse(populatedSet(3).isEmpty()); + } + + /** + * KeySet.iterator() returns an iterator containing the elements of the + * set + */ + public void testIterator() { + Collection empty = ConcurrentHashMap.newKeySet(); + int size = 20; + assertFalse(empty.iterator().hasNext()); + try { + empty.iterator().next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + + Integer[] elements = new Integer[size]; + for (int i = 0; i < size; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + Iterator it = full.iterator(); + for (int j = 0; j < size; j++) { + assertTrue(it.hasNext()); + it.next(); + } + assertIteratorExhausted(it); + } + + /** + * iterator of empty collections has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(ConcurrentHashMap.newKeySet().iterator()); + assertIteratorExhausted(new ConcurrentHashMap().entrySet().iterator()); + assertIteratorExhausted(new ConcurrentHashMap().values().iterator()); + assertIteratorExhausted(new ConcurrentHashMap().keySet().iterator()); + } + + /** + * KeySet.iterator.remove removes current element + */ + public void testIteratorRemove() { + Set q = populatedSet(3); + Iterator it = q.iterator(); + Object removed = it.next(); + it.remove(); + + it = q.iterator(); + assertFalse(it.next().equals(removed)); + assertFalse(it.next().equals(removed)); + assertFalse(it.hasNext()); + } + + /** + * KeySet.toString holds toString of elements + */ + public void testToString() { + assertEquals("[]", ConcurrentHashMap.newKeySet().toString()); + Set full = populatedSet(3); + String s = full.toString(); + for (int i = 0; i < 3; ++i) + assertTrue(s.contains(String.valueOf(i))); + } + + /** + * KeySet.removeAll removes all elements from the given collection + */ + public void testRemoveAll() { + Set full = populatedSet(3); + assertTrue(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + assertFalse(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + } + + /** + * KeySet.remove removes an element + */ + public void testRemove() { + Set full = populatedSet(3); + full.remove(one); + assertFalse(full.contains(one)); + assertEquals(2, full.size()); + } + + /** + * keySet.size returns the number of elements + */ + public void testSize() { + Set empty = ConcurrentHashMap.newKeySet(); + Set full = populatedSet(3); + assertEquals(3, full.size()); + assertEquals(0, empty.size()); + } + + /** + * KeySet.toArray() returns an Object array containing all elements from + * the set + */ + public void testToArray() { + Object[] a = ConcurrentHashMap.newKeySet().toArray(); + assertTrue(Arrays.equals(new Object[0], a)); + assertSame(Object[].class, a.getClass()); + int size = 20; + Integer[] elements = new Integer[size]; + for (int i = 0; i < size; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray()))); + assertTrue(full.containsAll(Arrays.asList(full.toArray()))); + assertSame(Object[].class, full.toArray().getClass()); + } + + /** + * toArray(Integer array) returns an Integer array containing all + * elements from the set + */ + public void testToArray2() { + Collection empty = ConcurrentHashMap.newKeySet(); + Integer[] a; + int size = 20; + + a = new Integer[0]; + assertSame(a, empty.toArray(a)); + + a = new Integer[size / 2]; + Arrays.fill(a, 42); + assertSame(a, empty.toArray(a)); + assertNull(a[0]); + for (int i = 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + + Integer[] elements = new Integer[size]; + for (int i = 0; i < size; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + Arrays.fill(a, 42); + assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a)))); + for (int i = 0; i < a.length; i++) + assertEquals(42, (int) a[i]); + assertSame(Integer[].class, full.toArray(a).getClass()); + + a = new Integer[size]; + Arrays.fill(a, 42); + assertSame(a, full.toArray(a)); + assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a)))); + } + + /** + * A deserialized serialized set is equal + */ + public void testSerialization() throws Exception { + int size = 20; + Set x = populatedSet(size); + Set y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + } + + static final int SIZE = 10000; + static ConcurrentHashMap longMap; + + static ConcurrentHashMap longMap() { + if (longMap == null) { + longMap = new ConcurrentHashMap(SIZE); + for (int i = 0; i < SIZE; ++i) + longMap.put(Long.valueOf(i), Long.valueOf(2 *i)); + } + return longMap; + } + + // explicit function class to avoid type inference problems + static class AddKeys implements BiFunction, Map.Entry, Map.Entry> { + public Map.Entry apply(Map.Entry x, Map.Entry y) { + return new AbstractMap.SimpleEntry + (Long.valueOf(x.getKey().longValue() + y.getKey().longValue()), + Long.valueOf(1L)); + } + } + + /** + * forEachKeySequentially traverses all keys + */ + public void testForEachKeySequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachKey(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2); + } + + /** + * forEachValueSequentially traverses all values + */ + public void testForEachValueSequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachValue(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), SIZE * (SIZE - 1)); + } + + /** + * forEachSequentially traverses all mappings + */ + public void testForEachSequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEach(Long.MAX_VALUE, (Long x, Long y) -> adder.add(x.longValue() + y.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * forEachEntrySequentially traverses all entries + */ + public void testForEachEntrySequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachEntry(Long.MAX_VALUE, (Map.Entry e) -> adder.add(e.getKey().longValue() + e.getValue().longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * forEachKeyInParallel traverses all keys + */ + public void testForEachKeyInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachKey(1L, (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2); + } + + /** + * forEachValueInParallel traverses all values + */ + public void testForEachValueInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachValue(1L, (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), SIZE * (SIZE - 1)); + } + + /** + * forEachInParallel traverses all mappings + */ + public void testForEachInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEach(1L, (Long x, Long y) -> adder.add(x.longValue() + y.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * forEachEntryInParallel traverses all entries + */ + public void testForEachEntryInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachEntry(1L, (Map.Entry e) -> adder.add(e.getKey().longValue() + e.getValue().longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachKeySequentially traverses the given + * transformations of all keys + */ + public void testMappedForEachKeySequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachKey(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachValueSequentially traverses the given + * transformations of all values + */ + public void testMappedForEachValueSequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachValue(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1)); + } + + /** + * Mapped forEachSequentially traverses the given + * transformations of all mappings + */ + public void testMappedForEachSequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEach(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachEntrySequentially traverses the given + * transformations of all entries + */ + public void testMappedForEachEntrySequentially() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachEntry(Long.MAX_VALUE, (Map.Entry e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachKeyInParallel traverses the given + * transformations of all keys + */ + public void testMappedForEachKeyInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachKey(1L, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachValueInParallel traverses the given + * transformations of all values + */ + public void testMappedForEachValueInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachValue(1L, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1)); + } + + /** + * Mapped forEachInParallel traverses the given + * transformations of all mappings + */ + public void testMappedForEachInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEach(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped forEachEntryInParallel traverses the given + * transformations of all entries + */ + public void testMappedForEachEntryInParallel() { + LongAdder adder = new LongAdder(); + ConcurrentHashMap m = longMap(); + m.forEachEntry(1L, (Map.Entry e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()), + (Long x) -> adder.add(x.longValue())); + assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysSequentially accumulates across all keys, + */ + public void testReduceKeysSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceValuesSequentially accumulates across all values + */ + public void testReduceValuesSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceEntriesSequentially accumulates across all entries + */ + public void testReduceEntriesSequentially() { + ConcurrentHashMap m = longMap(); + Map.Entry r; + r = m.reduceEntries(Long.MAX_VALUE, new AddKeys()); + assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysInParallel accumulates across all keys + */ + public void testReduceKeysInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.reduceKeys(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceValuesInParallel accumulates across all values + */ + public void testReduceValuesInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.reduceValues(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)SIZE * (SIZE - 1)); + } + + /** + * reduceEntriesInParallel accumulate across all entries + */ + public void testReduceEntriesInParallel() { + ConcurrentHashMap m = longMap(); + Map.Entry r; + r = m.reduceEntries(1L, new AddKeys()); + assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped reduceKeysSequentially accumulates mapped keys + */ + public void testMapReduceKeysSequentially() { + ConcurrentHashMap m = longMap(); + Long r = m.reduceKeys(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped reduceValuesSequentially accumulates mapped values + */ + public void testMapReduceValuesSequentially() { + ConcurrentHashMap m = longMap(); + Long r = m.reduceValues(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)4 * SIZE * (SIZE - 1)); + } + + /** + * reduceSequentially accumulates across all transformed mappings + */ + public void testMappedReduceSequentially() { + ConcurrentHashMap m = longMap(); + Long r = m.reduce(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + + assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped reduceKeysInParallel, accumulates mapped keys + */ + public void testMapReduceKeysInParallel() { + ConcurrentHashMap m = longMap(); + Long r = m.reduceKeys(1L, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2); + } + + /** + * Mapped reduceValuesInParallel accumulates mapped values + */ + public void testMapReduceValuesInParallel() { + ConcurrentHashMap m = longMap(); + Long r = m.reduceValues(1L, (Long x) -> Long.valueOf(4 * x.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)4 * SIZE * (SIZE - 1)); + } + + /** + * reduceInParallel accumulate across all transformed mappings + */ + public void testMappedReduceInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.reduce(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()), + (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue())); + assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysToLongSequentially accumulates mapped keys + */ + public void testReduceKeysToLongSequentially() { + ConcurrentHashMap m = longMap(); + long lr = m.reduceKeysToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum); + assertEquals(lr, (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysToIntSequentially accumulates mapped keys + */ + public void testReduceKeysToIntSequentially() { + ConcurrentHashMap m = longMap(); + int ir = m.reduceKeysToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum); + assertEquals(ir, SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysToDoubleSequentially accumulates mapped keys + */ + public void testReduceKeysToDoubleSequentially() { + ConcurrentHashMap m = longMap(); + double dr = m.reduceKeysToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum); + assertEquals(dr, (double)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceValuesToLongSequentially accumulates mapped values + */ + public void testReduceValuesToLongSequentially() { + ConcurrentHashMap m = longMap(); + long lr = m.reduceValuesToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum); + assertEquals(lr, (long)SIZE * (SIZE - 1)); + } + + /** + * reduceValuesToIntSequentially accumulates mapped values + */ + public void testReduceValuesToIntSequentially() { + ConcurrentHashMap m = longMap(); + int ir = m.reduceValuesToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum); + assertEquals(ir, SIZE * (SIZE - 1)); + } + + /** + * reduceValuesToDoubleSequentially accumulates mapped values + */ + public void testReduceValuesToDoubleSequentially() { + ConcurrentHashMap m = longMap(); + double dr = m.reduceValuesToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum); + assertEquals(dr, (double)SIZE * (SIZE - 1)); + } + + /** + * reduceKeysToLongInParallel accumulates mapped keys + */ + public void testReduceKeysToLongInParallel() { + ConcurrentHashMap m = longMap(); + long lr = m.reduceKeysToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum); + assertEquals(lr, (long)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysToIntInParallel accumulates mapped keys + */ + public void testReduceKeysToIntInParallel() { + ConcurrentHashMap m = longMap(); + int ir = m.reduceKeysToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum); + assertEquals(ir, SIZE * (SIZE - 1) / 2); + } + + /** + * reduceKeysToDoubleInParallel accumulates mapped values + */ + public void testReduceKeysToDoubleInParallel() { + ConcurrentHashMap m = longMap(); + double dr = m.reduceKeysToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum); + assertEquals(dr, (double)SIZE * (SIZE - 1) / 2); + } + + /** + * reduceValuesToLongInParallel accumulates mapped values + */ + public void testReduceValuesToLongInParallel() { + ConcurrentHashMap m = longMap(); + long lr = m.reduceValuesToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum); + assertEquals(lr, (long)SIZE * (SIZE - 1)); + } + + /** + * reduceValuesToIntInParallel accumulates mapped values + */ + public void testReduceValuesToIntInParallel() { + ConcurrentHashMap m = longMap(); + int ir = m.reduceValuesToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum); + assertEquals(ir, SIZE * (SIZE - 1)); + } + + /** + * reduceValuesToDoubleInParallel accumulates mapped values + */ + public void testReduceValuesToDoubleInParallel() { + ConcurrentHashMap m = longMap(); + double dr = m.reduceValuesToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum); + assertEquals(dr, (double)SIZE * (SIZE - 1)); + } + + /** + * searchKeysSequentially returns a non-null result of search + * function, or null if none + */ + public void testSearchKeysSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() < 0L ? x : null); + assertNull(r); + } + + /** + * searchValuesSequentially returns a non-null result of search + * function, or null if none + */ + public void testSearchValuesSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchValues(Long.MAX_VALUE, + (Long x) -> (x.longValue() == (long)(SIZE/2)) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchValues(Long.MAX_VALUE, + (Long x) -> (x.longValue() < 0L) ? x : null); + assertNull(r); + } + + /** + * searchSequentially returns a non-null result of search + * function, or null if none + */ + public void testSearchSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() < 0L ? x : null); + assertNull(r); + } + + /** + * searchEntriesSequentially returns a non-null result of search + * function, or null if none + */ + public void testSearchEntriesSequentially() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchEntries(Long.MAX_VALUE, (Map.Entry e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchEntries(Long.MAX_VALUE, (Map.Entry e) -> e.getKey().longValue() < 0L ? e.getKey() : null); + assertNull(r); + } + + /** + * searchKeysInParallel returns a non-null result of search + * function, or null if none + */ + public void testSearchKeysInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchKeys(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchKeys(1L, (Long x) -> x.longValue() < 0L ? x : null); + assertNull(r); + } + + /** + * searchValuesInParallel returns a non-null result of search + * function, or null if none + */ + public void testSearchValuesInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchValues(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchValues(1L, (Long x) -> x.longValue() < 0L ? x : null); + assertNull(r); + } + + /** + * searchInParallel returns a non-null result of search function, + * or null if none + */ + public void testSearchInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.search(1L, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.search(1L, (Long x, Long y) -> x.longValue() < 0L ? x : null); + assertNull(r); + } + + /** + * searchEntriesInParallel returns a non-null result of search + * function, or null if none + */ + public void testSearchEntriesInParallel() { + ConcurrentHashMap m = longMap(); + Long r; + r = m.searchEntries(1L, (Map.Entry e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null); + assertEquals((long)r, (long)(SIZE/2)); + r = m.searchEntries(1L, (Map.Entry e) -> e.getKey().longValue() < 0L ? e.getKey() : null); + assertNull(r); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java new file mode 100644 index 00000000000..766511902af --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java @@ -0,0 +1,833 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentHashMapTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentHashMapTest.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static ConcurrentHashMap map5() { + ConcurrentHashMap map = new ConcurrentHashMap(5); + assertTrue(map.isEmpty()); + map.put(one, "A"); + map.put(two, "B"); + map.put(three, "C"); + map.put(four, "D"); + map.put(five, "E"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map; + } + + /** Re-implement Integer.compare for old java versions */ + static int compare(int x, int y) { + return (x < y) ? -1 : (x > y) ? 1 : 0; + } + + // classes for testing Comparable fallbacks + static class BI implements Comparable { + private final int value; + BI(int value) { this.value = value; } + public int compareTo(BI other) { + return compare(value, other.value); + } + public boolean equals(Object x) { + return (x instanceof BI) && ((BI)x).value == value; + } + public int hashCode() { return 42; } + } + static class CI extends BI { CI(int value) { super(value); } } + static class DI extends BI { DI(int value) { super(value); } } + + static class BS implements Comparable { + private final String value; + BS(String value) { this.value = value; } + public int compareTo(BS other) { + return value.compareTo(other.value); + } + public boolean equals(Object x) { + return (x instanceof BS) && value.equals(((BS)x).value); + } + public int hashCode() { return 42; } + } + + static class LexicographicList> extends ArrayList + implements Comparable> { + LexicographicList(Collection c) { super(c); } + LexicographicList(E e) { super(Collections.singleton(e)); } + public int compareTo(LexicographicList other) { + int common = Math.min(size(), other.size()); + int r = 0; + for (int i = 0; i < common; i++) { + if ((r = get(i).compareTo(other.get(i))) != 0) + break; + } + if (r == 0) + r = compare(size(), other.size()); + return r; + } + private static final long serialVersionUID = 0; + } + + static class CollidingObject { + final String value; + CollidingObject(final String value) { this.value = value; } + public int hashCode() { return this.value.hashCode() & 1; } + public boolean equals(final Object obj) { + return (obj instanceof CollidingObject) && ((CollidingObject)obj).value.equals(value); + } + } + + static class ComparableCollidingObject extends CollidingObject implements Comparable { + ComparableCollidingObject(final String value) { super(value); } + public int compareTo(final ComparableCollidingObject o) { + return value.compareTo(o.value); + } + } + + /** + * Inserted elements that are subclasses of the same Comparable + * class are found. + */ + public void testComparableFamily() { + int size = 500; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { + assertTrue(m.put(new CI(i), true) == null); + } + for (int i = 0; i < size; i++) { + assertTrue(m.containsKey(new CI(i))); + assertTrue(m.containsKey(new DI(i))); + } + } + + /** + * Elements of classes with erased generic type parameters based + * on Comparable can be inserted and found. + */ + public void testGenericComparable() { + int size = 120; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { + BI bi = new BI(i); + BS bs = new BS(String.valueOf(i)); + LexicographicList bis = new LexicographicList(bi); + LexicographicList bss = new LexicographicList(bs); + assertTrue(m.putIfAbsent(bis, true) == null); + assertTrue(m.containsKey(bis)); + if (m.putIfAbsent(bss, true) == null) + assertTrue(m.containsKey(bss)); + assertTrue(m.containsKey(bis)); + } + for (int i = 0; i < size; i++) { + assertTrue(m.containsKey(Collections.singletonList(new BI(i)))); + } + } + + /** + * Elements of non-comparable classes equal to those of classes + * with erased generic type parameters based on Comparable can be + * inserted and found. + */ + public void testGenericComparable2() { + int size = 500; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { + m.put(Collections.singletonList(new BI(i)), true); + } + + for (int i = 0; i < size; i++) { + LexicographicList bis = new LexicographicList(new BI(i)); + assertTrue(m.containsKey(bis)); + } + } + + /** + * Mixtures of instances of comparable and non-comparable classes + * can be inserted and found. + */ + public void testMixedComparable() { + int size = 1200; // makes measured test run time -> 35ms + ConcurrentHashMap map = + new ConcurrentHashMap(); + Random rng = new Random(); + for (int i = 0; i < size; i++) { + Object x; + switch (rng.nextInt(4)) { + case 0: + x = new Object(); + break; + case 1: + x = new CollidingObject(Integer.toString(i)); + break; + default: + x = new ComparableCollidingObject(Integer.toString(i)); + } + assertNull(map.put(x, x)); + } + int count = 0; + for (Object k : map.keySet()) { + assertEquals(map.get(k), k); + ++count; + } + assertEquals(count, size); + assertEquals(map.size(), size); + for (Object k : map.keySet()) { + assertEquals(map.put(k, k), k); + } + } + + /** + * clear removes all pairs + */ + public void testClear() { + ConcurrentHashMap map = map5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * Maps with same contents are equal + */ + public void testEquals() { + ConcurrentHashMap map1 = map5(); + ConcurrentHashMap map2 = map5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * hashCode() equals sum of each key.hashCode ^ value.hashCode + */ + public void testHashCode() { + ConcurrentHashMap map = map5(); + int sum = 0; + for (Map.Entry e : map.entrySet()) + sum += e.getKey().hashCode() ^ e.getValue().hashCode(); + assertEquals(sum, map.hashCode()); + } + + /** + * contains returns true for contained value + */ + public void testContains() { + ConcurrentHashMap map = map5(); + assertTrue(map.contains("A")); + assertFalse(map.contains("Z")); + } + + /** + * containsKey returns true for contained key + */ + public void testContainsKey() { + ConcurrentHashMap map = map5(); + assertTrue(map.containsKey(one)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testContainsValue() { + ConcurrentHashMap map = map5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * enumeration returns an enumeration containing the correct + * elements + */ + public void testEnumeration() { + ConcurrentHashMap map = map5(); + Enumeration e = map.elements(); + int count = 0; + while (e.hasMoreElements()) { + count++; + e.nextElement(); + } + assertEquals(5, count); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testGet() { + ConcurrentHashMap map = map5(); + assertEquals("A", (String)map.get(one)); + ConcurrentHashMap empty = new ConcurrentHashMap(); + assertNull(map.get("anything")); + assertNull(empty.get("anything")); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testIsEmpty() { + ConcurrentHashMap empty = new ConcurrentHashMap(); + ConcurrentHashMap map = map5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * keys returns an enumeration containing all the keys from the map + */ + public void testKeys() { + ConcurrentHashMap map = map5(); + Enumeration e = map.keys(); + int count = 0; + while (e.hasMoreElements()) { + count++; + e.nextElement(); + } + assertEquals(5, count); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testKeySet() { + ConcurrentHashMap map = map5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(one)); + assertTrue(s.contains(two)); + assertTrue(s.contains(three)); + assertTrue(s.contains(four)); + assertTrue(s.contains(five)); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testValuesToArray() { + ConcurrentHashMap map = map5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet.toArray contains all entries + */ + public void testEntrySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** + * values collection contains all values + */ + public void testValues() { + ConcurrentHashMap map = map5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testEntrySet() { + ConcurrentHashMap map = map5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testPutAll() { + ConcurrentHashMap empty = new ConcurrentHashMap(); + ConcurrentHashMap map = map5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(one)); + assertTrue(empty.containsKey(two)); + assertTrue(empty.containsKey(three)); + assertTrue(empty.containsKey(four)); + assertTrue(empty.containsKey(five)); + } + + /** + * putIfAbsent works when the given key is not present + */ + public void testPutIfAbsent() { + ConcurrentHashMap map = map5(); + map.putIfAbsent(six, "Z"); + assertTrue(map.containsKey(six)); + } + + /** + * putIfAbsent does not add the pair if the key is already present + */ + public void testPutIfAbsent2() { + ConcurrentHashMap map = map5(); + assertEquals("A", map.putIfAbsent(one, "Z")); + } + + /** + * replace fails when the given key is not present + */ + public void testReplace() { + ConcurrentHashMap map = map5(); + assertNull(map.replace(six, "Z")); + assertFalse(map.containsKey(six)); + } + + /** + * replace succeeds if the key is already present + */ + public void testReplace2() { + ConcurrentHashMap map = map5(); + assertNotNull(map.replace(one, "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * replace value fails when the given key not mapped to expected value + */ + public void testReplaceValue() { + ConcurrentHashMap map = map5(); + assertEquals("A", map.get(one)); + assertFalse(map.replace(one, "Z", "Z")); + assertEquals("A", map.get(one)); + } + + /** + * replace value succeeds when the given key mapped to expected value + */ + public void testReplaceValue2() { + ConcurrentHashMap map = map5(); + assertEquals("A", map.get(one)); + assertTrue(map.replace(one, "A", "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testRemove() { + ConcurrentHashMap map = map5(); + map.remove(five); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + } + + /** + * remove(key,value) removes only if pair present + */ + public void testRemove2() { + ConcurrentHashMap map = map5(); + map.remove(five, "E"); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + map.remove(four, "A"); + assertEquals(4, map.size()); + assertTrue(map.containsKey(four)); + } + + /** + * size returns the correct values + */ + public void testSize() { + ConcurrentHashMap map = map5(); + ConcurrentHashMap empty = new ConcurrentHashMap(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testToString() { + ConcurrentHashMap map = map5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception tests + + /** + * Cannot create with only negative capacity + */ + public void testConstructor1() { + try { + new ConcurrentHashMap(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor (initialCapacity, loadFactor) throws + * IllegalArgumentException if either argument is negative + */ + public void testConstructor2() { + try { + new ConcurrentHashMap(-1, .75f); + shouldThrow(); + } catch (IllegalArgumentException success) {} + + try { + new ConcurrentHashMap(16, -1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor (initialCapacity, loadFactor, concurrencyLevel) + * throws IllegalArgumentException if any argument is negative + */ + public void testConstructor3() { + try { + new ConcurrentHashMap(-1, .75f, 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + + try { + new ConcurrentHashMap(16, -1, 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + + try { + new ConcurrentHashMap(16, .75f, -1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * ConcurrentHashMap(map) throws NullPointerException if the given + * map is null + */ + public void testConstructor4() { + try { + new ConcurrentHashMap(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * ConcurrentHashMap(map) creates a new map with the same mappings + * as the given map + */ + public void testConstructor5() { + ConcurrentHashMap map1 = map5(); + ConcurrentHashMap map2 = new ConcurrentHashMap(map5()); + assertTrue(map2.equals(map1)); + map2.put(one, "F"); + assertFalse(map2.equals(map1)); + } + + /** + * get(null) throws NPE + */ + public void testGet_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) throws NPE + */ + public void testContainsKey_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsValue(null) throws NPE + */ + public void testContainsValue_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.containsValue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * contains(null) throws NPE + */ + public void testContains_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.contains(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testPut1_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(x, null) throws NPE + */ + public void testPut2_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.put("whatever", null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * putIfAbsent(null, x) throws NPE + */ + public void testPutIfAbsent1_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.putIfAbsent(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x) throws NPE + */ + public void testReplace_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.replace(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x, y) throws NPE + */ + public void testReplaceValue_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.replace(null, one, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * putIfAbsent(x, null) throws NPE + */ + public void testPutIfAbsent2_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.putIfAbsent("whatever", null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(x, null) throws NPE + */ + public void testReplace2_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.replace("whatever", null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(x, null, y) throws NPE + */ + public void testReplaceValue2_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.replace("whatever", null, "A"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(x, y, null) throws NPE + */ + public void testReplaceValue3_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + try { + c.replace("whatever", one, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE + */ + public void testRemove1_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + c.put("sadsdf", "asdads"); + try { + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null, x) throws NPE + */ + public void testRemove2_NullPointerException() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + c.put("sadsdf", "asdads"); + try { + c.remove(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(x, null) returns false + */ + public void testRemove3() { + ConcurrentHashMap c = new ConcurrentHashMap(5); + c.put("sadsdf", "asdads"); + assertFalse(c.remove("sadsdf", null)); + } + + /** + * A deserialized map equals original + */ + public void testSerialization() throws Exception { + Map x = map5(); + Map y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * SetValue of an EntrySet entry sets value in the map. + */ + public void testSetValueWriteThrough() { + // Adapted from a bug report by Eric Zoerner + ConcurrentHashMap map = new ConcurrentHashMap(2, 5.0f, 1); + assertTrue(map.isEmpty()); + for (int i = 0; i < 20; i++) + map.put(new Integer(i), new Integer(i)); + assertFalse(map.isEmpty()); + Map.Entry entry1 = (Map.Entry)map.entrySet().iterator().next(); + // Unless it happens to be first (in which case remainder of + // test is skipped), remove a possibly-colliding key from map + // which, under some implementations, may cause entry1 to be + // cloned in map + if (!entry1.getKey().equals(new Integer(16))) { + map.remove(new Integer(16)); + entry1.setValue("XYZ"); + assertTrue(map.containsValue("XYZ")); // fails if write-through broken + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java new file mode 100644 index 00000000000..daab5381529 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java @@ -0,0 +1,926 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ConcurrentLinkedDeque; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentLinkedDequeTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ConcurrentLinkedDequeTest.class); + } + + /** + * Returns a new deque of given size containing consecutive + * Integers 0 ... n. + */ + private ConcurrentLinkedDeque populatedDeque(int n) { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; ++i) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * new deque is empty + */ + public void testConstructor1() { + assertTrue(new ConcurrentLinkedDeque().isEmpty()); + assertEquals(0, new ConcurrentLinkedDeque().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new ConcurrentLinkedDeque((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new ConcurrentLinkedDeque(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new ConcurrentLinkedDeque(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Deque contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.isEmpty()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * size() changes when elements added and removed + */ + public void testSize() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.remove(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * push(null) throws NPE + */ + public void testPushNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.push(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * peekFirst() returns element inserted with push + */ + public void testPush() { + ConcurrentLinkedDeque q = populatedDeque(3); + q.pollLast(); + q.push(four); + assertSame(four, q.peekFirst()); + } + + /** + * pop() removes first element, or throws NSEE if empty + */ + public void testPop() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pop()); + } + try { + q.pop(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * offer(null) throws NPE + */ + public void testOfferNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.offer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offerFirst(null) throws NPE + */ + public void testOfferFirstNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.offerFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offerLast(null) throws NPE + */ + public void testOfferLastNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.offerLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offer(x) succeeds + */ + public void testOffer() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.offer(zero)); + assertTrue(q.offer(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * offerFirst(x) succeeds + */ + public void testOfferFirst() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.offerFirst(zero)); + assertTrue(q.offerFirst(one)); + assertSame(one, q.peekFirst()); + assertSame(zero, q.peekLast()); + } + + /** + * offerLast(x) succeeds + */ + public void testOfferLast() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.offerLast(zero)); + assertTrue(q.offerLast(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addFirst(null) throws NPE + */ + public void testAddFirstNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.addFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addLast(null) throws NPE + */ + public void testAddLastNull() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.addLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * add(x) succeeds + */ + public void testAdd() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertTrue(q.add(zero)); + assertTrue(q.add(one)); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * addFirst(x) succeeds + */ + public void testAddFirst() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + q.addFirst(zero); + q.addFirst(one); + assertSame(one, q.peekFirst()); + assertSame(zero, q.peekLast()); + } + + /** + * addLast(x) succeeds + */ + public void testAddLast() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + q.addLast(zero); + q.addLast(one); + assertSame(zero, q.peekFirst()); + assertSame(one, q.peekLast()); + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + try { + q.addAll(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Deque contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * pollFirst() succeeds unless empty + */ + public void testPollFirst() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * pollLast() succeeds unless empty + */ + public void testPollLast() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollLast()); + } + + /** + * poll() succeeds unless empty + */ + public void testPoll() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * peek() returns next element, or null if empty + */ + public void testPeek() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element() returns first element, or throws NSEE if empty + */ + public void testElement() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove() removes next element, or throws NSEE if empty + */ + public void testRemove() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * peekFirst() returns next element, or null if empty + */ + public void testPeekFirst() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peekFirst()); + assertEquals(i, q.pollFirst()); + assertTrue(q.peekFirst() == null || + !q.peekFirst().equals(i)); + } + assertNull(q.peekFirst()); + } + + /** + * peekLast() returns next element, or null if empty + */ + public void testPeekLast() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.peekLast()); + assertEquals(i, q.pollLast()); + assertTrue(q.peekLast() == null || + !q.peekLast().equals(i)); + } + assertNull(q.peekLast()); + } + + /** + * getFirst() returns first element, or throws NSEE if empty + */ + public void testFirstElement() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.getFirst()); + assertEquals(i, q.pollFirst()); + } + try { + q.getFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * getLast() returns last element, or throws NSEE if empty + */ + public void testLastElement() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.getLast()); + assertEquals(i, q.pollLast()); + } + try { + q.getLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirst() removes first element, or throws NSEE if empty + */ + public void testRemoveFirst() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.removeFirst()); + } + try { + q.removeFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekFirst()); + } + + /** + * removeLast() removes last element, or throws NSEE if empty + */ + public void testRemoveLast() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.removeLast()); + } + try { + q.removeLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirstOccurrence(x) removes x and returns true if present + */ + public void testRemoveFirstOccurrence() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + assertFalse(q.removeFirstOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * removeLastOccurrence(x) removes x and returns true if present + */ + public void testRemoveLastOccurrence() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + assertFalse(q.removeLastOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear() removes all elements + */ + public void testClear() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(one); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + ConcurrentLinkedDeque p = new ConcurrentLinkedDeque(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if change + */ + public void testRetainAll() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + ConcurrentLinkedDeque p = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + ConcurrentLinkedDeque p = populatedDeque(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray() contains all elements in FIFO order + */ + public void testToArray() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.poll()); + } + + /** + * toArray(null) throws NullPointerException + */ + public void testToArray_NullArg() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + try { + q.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * Iterator iterates through all elements + */ + public void testIterator() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + Deque c = new ConcurrentLinkedDeque(); + assertIteratorExhausted(c.iterator()); + assertIteratorExhausted(c.descendingIterator()); + } + + /** + * Iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + q.add(one); + q.add(two); + q.add(three); + + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + q.add(one); + q.add(two); + q.add(three); + + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + + assertEquals("deque should be empty again", 0, q.size()); + } + + /** + * iterator.remove() removes current element + */ + public void testIteratorRemove() { + final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + final Random rng = new Random(); + for (int iters = 0; iters < 100; ++iters) { + int max = rng.nextInt(5) + 2; + int split = rng.nextInt(max - 1) + 1; + for (int j = 1; j <= max; ++j) + q.add(new Integer(j)); + Iterator it = q.iterator(); + for (int j = 1; j <= split; ++j) + assertEquals(it.next(), new Integer(j)); + it.remove(); + assertEquals(it.next(), new Integer(split + 1)); + for (int j = 1; j <= split; ++j) + q.remove(new Integer(j)); + it = q.iterator(); + for (int j = split + 1; j <= max; ++j) { + assertEquals(it.next(), new Integer(j)); + it.remove(); + } + assertFalse(it.hasNext()); + assertTrue(q.isEmpty()); + } + } + + /** + * Descending iterator iterates through all elements + */ + public void testDescendingIterator() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + int i = 0; + Iterator it = q.descendingIterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + assertFalse(it.hasNext()); + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * Descending iterator ordering is reverse FIFO + */ + public void testDescendingIteratorOrdering() { + final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + for (int iters = 0; iters < 100; ++iters) { + q.add(new Integer(3)); + q.add(new Integer(2)); + q.add(new Integer(1)); + int k = 0; + for (Iterator it = q.descendingIterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + q.remove(); + q.remove(); + q.remove(); + } + } + + /** + * descendingIterator.remove() removes current element + */ + public void testDescendingIteratorRemove() { + final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); + final Random rng = new Random(); + for (int iters = 0; iters < 100; ++iters) { + int max = rng.nextInt(5) + 2; + int split = rng.nextInt(max - 1) + 1; + for (int j = max; j >= 1; --j) + q.add(new Integer(j)); + Iterator it = q.descendingIterator(); + for (int j = 1; j <= split; ++j) + assertEquals(it.next(), new Integer(j)); + it.remove(); + assertEquals(it.next(), new Integer(split + 1)); + for (int j = 1; j <= split; ++j) + q.remove(new Integer(j)); + it = q.descendingIterator(); + for (int j = split + 1; j <= max; ++j) { + assertEquals(it.next(), new Integer(j)); + it.remove(); + } + assertFalse(it.hasNext()); + assertTrue(q.isEmpty()); + } + } + + /** + * toString() contains toStrings of elements + */ + public void testToString() { + ConcurrentLinkedDeque q = populatedDeque(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized deque has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedDeque(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * contains(null) always return false. + * remove(null) always throws NullPointerException. + */ + public void testNeverContainsNull() { + Deque[] qs = { + new ConcurrentLinkedDeque(), + populatedDeque(2), + }; + + for (Deque q : qs) { + assertFalse(q.contains(null)); + try { + assertFalse(q.remove(null)); + shouldThrow(); + } catch (NullPointerException success) {} + try { + assertFalse(q.removeFirstOccurrence(null)); + shouldThrow(); + } catch (NullPointerException success) {} + try { + assertFalse(q.removeLastOccurrence(null)); + shouldThrow(); + } catch (NullPointerException success) {} + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java new file mode 100644 index 00000000000..bafaa6581f0 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java @@ -0,0 +1,564 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentLinkedQueueTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ConcurrentLinkedQueueTest.class); + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private ConcurrentLinkedQueue populatedQueue(int n) { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; ++i) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * new queue is empty + */ + public void testConstructor1() { + assertEquals(0, new ConcurrentLinkedQueue().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new ConcurrentLinkedQueue((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new ConcurrentLinkedQueue(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new ConcurrentLinkedQueue(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + assertTrue(q.isEmpty()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.remove(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * offer(null) throws NPE + */ + public void testOfferNull() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + try { + q.offer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Offer returns true + */ + public void testOffer() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + assertTrue(q.offer(zero)); + assertTrue(q.offer(one)); + } + + /** + * add returns true + */ + public void testAdd() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + assertTrue(q.add(new Integer(i))); + } + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + try { + q.addAll(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(one); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + ConcurrentLinkedQueue p = new ConcurrentLinkedQueue(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if change + */ + public void testRetainAll() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + ConcurrentLinkedQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + ConcurrentLinkedQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements in FIFO order + */ + public void testToArray() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.poll()); + } + + /** + * toArray(null) throws NullPointerException + */ + public void testToArray_NullArg() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + try { + q.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new ConcurrentLinkedQueue().iterator()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + q.add(one); + q.add(two); + q.add(three); + + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + q.add(one); + q.add(two); + q.add(three); + + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + + assertEquals("queue should be empty again", 0, q.size()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + q.add(one); + q.add(two); + q.add(three); + Iterator it = q.iterator(); + it.next(); + it.remove(); + it = q.iterator(); + assertSame(it.next(), two); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + ConcurrentLinkedQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized queue has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection[] qs = { + new ConcurrentLinkedQueue(), + populatedQueue(2), + }; + + for (Collection q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java new file mode 100644 index 00000000000..515303e3e64 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java @@ -0,0 +1,1306 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListMap; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentSkipListMapTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentSkipListMapTest.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static ConcurrentSkipListMap map5() { + ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + assertTrue(map.isEmpty()); + map.put(one, "A"); + map.put(five, "E"); + map.put(three, "C"); + map.put(two, "B"); + map.put(four, "D"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map; + } + + /** + * clear removes all pairs + */ + public void testClear() { + ConcurrentSkipListMap map = map5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * copy constructor creates map equal to source map + */ + public void testConstructFromSorted() { + ConcurrentSkipListMap map = map5(); + ConcurrentSkipListMap map2 = new ConcurrentSkipListMap(map); + assertEquals(map, map2); + } + + /** + * Maps with same contents are equal + */ + public void testEquals() { + ConcurrentSkipListMap map1 = map5(); + ConcurrentSkipListMap map2 = map5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testContainsKey() { + ConcurrentSkipListMap map = map5(); + assertTrue(map.containsKey(one)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testContainsValue() { + ConcurrentSkipListMap map = map5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testGet() { + ConcurrentSkipListMap map = map5(); + assertEquals("A", (String)map.get(one)); + ConcurrentSkipListMap empty = new ConcurrentSkipListMap(); + assertNull(empty.get(one)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testIsEmpty() { + ConcurrentSkipListMap empty = new ConcurrentSkipListMap(); + ConcurrentSkipListMap map = map5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testFirstKey() { + ConcurrentSkipListMap map = map5(); + assertEquals(one, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testLastKey() { + ConcurrentSkipListMap map = map5(); + assertEquals(five, map.lastKey()); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + ConcurrentSkipListMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * descendingkeySet.toArray returns contains all keys + */ + public void testDescendingKeySetToArray() { + ConcurrentSkipListMap map = map5(); + Set s = map.descendingKeySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + assertTrue(s.containsAll(Arrays.asList(ar))); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testKeySet() { + ConcurrentSkipListMap map = map5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(one)); + assertTrue(s.contains(two)); + assertTrue(s.contains(three)); + assertTrue(s.contains(four)); + assertTrue(s.contains(five)); + } + + /** + * keySet is ordered + */ + public void testKeySetOrder() { + ConcurrentSkipListMap map = map5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descending iterator of key set is inverse ordered + */ + public void testKeySetDescendingIteratorOrder() { + ConcurrentSkipListMap map = map5(); + NavigableSet s = map.navigableKeySet(); + Iterator i = s.descendingIterator(); + Integer last = (Integer)i.next(); + assertEquals(last, five); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descendingKeySet is ordered + */ + public void testDescendingKeySetOrder() { + ConcurrentSkipListMap map = map5(); + Set s = map.descendingKeySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, five); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descending iterator of descendingKeySet is ordered + */ + public void testDescendingKeySetDescendingIteratorOrder() { + ConcurrentSkipListMap map = map5(); + NavigableSet s = map.descendingKeySet(); + Iterator i = s.descendingIterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * Values.toArray contains all values + */ + public void testValuesToArray() { + ConcurrentSkipListMap map = map5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * values collection contains all values + */ + public void testValues() { + ConcurrentSkipListMap map = map5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testEntrySet() { + ConcurrentSkipListMap map = map5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * descendingEntrySet contains all pairs + */ + public void testDescendingEntrySet() { + ConcurrentSkipListMap map = map5(); + Set s = map.descendingMap().entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * entrySet.toArray contains all entries + */ + public void testEntrySetToArray() { + ConcurrentSkipListMap map = map5(); + Set s = map.entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** + * descendingEntrySet.toArray contains all entries + */ + public void testDescendingEntrySetToArray() { + ConcurrentSkipListMap map = map5(); + Set s = map.descendingMap().entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testPutAll() { + ConcurrentSkipListMap empty = new ConcurrentSkipListMap(); + ConcurrentSkipListMap map = map5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(one)); + assertTrue(empty.containsKey(two)); + assertTrue(empty.containsKey(three)); + assertTrue(empty.containsKey(four)); + assertTrue(empty.containsKey(five)); + } + + /** + * putIfAbsent works when the given key is not present + */ + public void testPutIfAbsent() { + ConcurrentSkipListMap map = map5(); + map.putIfAbsent(six, "Z"); + assertTrue(map.containsKey(six)); + } + + /** + * putIfAbsent does not add the pair if the key is already present + */ + public void testPutIfAbsent2() { + ConcurrentSkipListMap map = map5(); + assertEquals("A", map.putIfAbsent(one, "Z")); + } + + /** + * replace fails when the given key is not present + */ + public void testReplace() { + ConcurrentSkipListMap map = map5(); + assertNull(map.replace(six, "Z")); + assertFalse(map.containsKey(six)); + } + + /** + * replace succeeds if the key is already present + */ + public void testReplace2() { + ConcurrentSkipListMap map = map5(); + assertNotNull(map.replace(one, "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * replace value fails when the given key not mapped to expected value + */ + public void testReplaceValue() { + ConcurrentSkipListMap map = map5(); + assertEquals("A", map.get(one)); + assertFalse(map.replace(one, "Z", "Z")); + assertEquals("A", map.get(one)); + } + + /** + * replace value succeeds when the given key mapped to expected value + */ + public void testReplaceValue2() { + ConcurrentSkipListMap map = map5(); + assertEquals("A", map.get(one)); + assertTrue(map.replace(one, "A", "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testRemove() { + ConcurrentSkipListMap map = map5(); + map.remove(five); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + } + + /** + * remove(key,value) removes only if pair present + */ + public void testRemove2() { + ConcurrentSkipListMap map = map5(); + assertTrue(map.containsKey(five)); + assertEquals("E", map.get(five)); + map.remove(five, "E"); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + map.remove(four, "A"); + assertEquals(4, map.size()); + assertTrue(map.containsKey(four)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testLowerEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e1 = map.lowerEntry(three); + assertEquals(two, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(one); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testHigherEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e1 = map.higherEntry(three); + assertEquals(four, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.higherEntry(five); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(six); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testFloorEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e1 = map.floorEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.floorEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.floorEntry(one); + assertEquals(one, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testCeilingEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e1 = map.ceilingEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(five); + assertEquals(five, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(six); + assertNull(e4); + } + + /** + * lowerEntry, higherEntry, ceilingEntry, and floorEntry return + * immutable entries + */ + public void testEntryImmutability() { + ConcurrentSkipListMap map = map5(); + Map.Entry e = map.lowerEntry(three); + assertEquals(two, e.getKey()); + try { + e.setValue("X"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.higherEntry(zero); + assertEquals(one, e.getKey()); + try { + e.setValue("X"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.floorEntry(one); + assertEquals(one, e.getKey()); + try { + e.setValue("X"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.ceilingEntry(five); + assertEquals(five, e.getKey()); + try { + e.setValue("X"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } + + /** + * lowerKey returns preceding element + */ + public void testLowerKey() { + ConcurrentSkipListMap q = map5(); + Object e1 = q.lowerKey(three); + assertEquals(two, e1); + + Object e2 = q.lowerKey(six); + assertEquals(five, e2); + + Object e3 = q.lowerKey(one); + assertNull(e3); + + Object e4 = q.lowerKey(zero); + assertNull(e4); + } + + /** + * higherKey returns next element + */ + public void testHigherKey() { + ConcurrentSkipListMap q = map5(); + Object e1 = q.higherKey(three); + assertEquals(four, e1); + + Object e2 = q.higherKey(zero); + assertEquals(one, e2); + + Object e3 = q.higherKey(five); + assertNull(e3); + + Object e4 = q.higherKey(six); + assertNull(e4); + } + + /** + * floorKey returns preceding element + */ + public void testFloorKey() { + ConcurrentSkipListMap q = map5(); + Object e1 = q.floorKey(three); + assertEquals(three, e1); + + Object e2 = q.floorKey(six); + assertEquals(five, e2); + + Object e3 = q.floorKey(one); + assertEquals(one, e3); + + Object e4 = q.floorKey(zero); + assertNull(e4); + } + + /** + * ceilingKey returns next element + */ + public void testCeilingKey() { + ConcurrentSkipListMap q = map5(); + Object e1 = q.ceilingKey(three); + assertEquals(three, e1); + + Object e2 = q.ceilingKey(zero); + assertEquals(one, e2); + + Object e3 = q.ceilingKey(five); + assertEquals(five, e3); + + Object e4 = q.ceilingKey(six); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testPollFirstEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(two, e.getKey()); + map.put(one, "A"); + e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(three, e.getKey()); + map.remove(four); + e = map.pollFirstEntry(); + assertEquals(five, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testPollLastEntry() { + ConcurrentSkipListMap map = map5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(four, e.getKey()); + map.put(five, "E"); + e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(three, e.getKey()); + map.remove(two); + e = map.pollLastEntry(); + assertEquals(one, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testSize() { + ConcurrentSkipListMap map = map5(); + ConcurrentSkipListMap empty = new ConcurrentSkipListMap(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testToString() { + ConcurrentSkipListMap map = map5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception tests + + /** + * get(null) of nonempty map throws NPE + */ + public void testGet_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) of nonempty map throws NPE + */ + public void testContainsKey_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsValue(null) throws NPE + */ + public void testContainsValue_NullPointerException() { + ConcurrentSkipListMap c = new ConcurrentSkipListMap(); + try { + c.containsValue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testPut1_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * putIfAbsent(null, x) throws NPE + */ + public void testPutIfAbsent1_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.putIfAbsent(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x) throws NPE + */ + public void testReplace_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.replace(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x, y) throws NPE + */ + public void testReplaceValue_NullPointerException() { + ConcurrentSkipListMap c = map5(); + try { + c.replace(null, one, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE + */ + public void testRemove1_NullPointerException() { + ConcurrentSkipListMap c = new ConcurrentSkipListMap(); + c.put("sadsdf", "asdads"); + try { + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null, x) throws NPE + */ + public void testRemove2_NullPointerException() { + ConcurrentSkipListMap c = new ConcurrentSkipListMap(); + c.put("sadsdf", "asdads"); + try { + c.remove(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(x, null) returns false + */ + public void testRemove3() { + ConcurrentSkipListMap c = new ConcurrentSkipListMap(); + c.put("sadsdf", "asdads"); + assertFalse(c.remove("sadsdf", null)); + } + + /** + * A deserialized map equals original + */ + public void testSerialization() throws Exception { + NavigableMap x = map5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testSubMapContents() { + ConcurrentSkipListMap map = map5(); + NavigableMap sm = map.subMap(two, true, four, false); + assertEquals(two, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(three, k); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals("C", sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testSubMapContents2() { + ConcurrentSkipListMap map = map5(); + NavigableMap sm = map.subMap(two, true, three, false); + assertEquals(1, sm.size()); + assertEquals(two, sm.firstKey()); + assertEquals(two, sm.lastKey()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertFalse(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(three), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testHeadMapContents() { + ConcurrentSkipListMap map = map5(); + NavigableMap sm = map.headMap(four, false); + assertTrue(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(four, map.firstKey()); + } + + /** + * tailMap returns map with keys in requested range + */ + public void testTailMapContents() { + ConcurrentSkipListMap map = map5(); + NavigableMap sm = map.tailMap(two, true); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertTrue(sm.containsKey(four)); + assertTrue(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(five, k); + k = (Integer)(r.next()); + assertEquals(four, k); + k = (Integer)(r.next()); + assertEquals(three, k); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(two, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(three, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(four, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + NavigableMap ssm = sm.tailMap(four, true); + assertEquals(four, ssm.firstKey()); + assertEquals(five, ssm.lastKey()); + assertEquals("D", ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + + Random rnd = new Random(666); + BitSet bs; + + /** + * Submaps of submaps subdivide correctly + */ + public void testRecursiveSubMaps() throws Exception { + int mapSize = expensiveTests ? 1000 : 100; + Class cl = ConcurrentSkipListMap.class; + NavigableMap map = newMap(cl); + bs = new BitSet(mapSize); + + populate(map, mapSize); + check(map, 0, mapSize - 1, true); + check(map.descendingMap(), 0, mapSize - 1, false); + + mutateMap(map, 0, mapSize - 1); + check(map, 0, mapSize - 1, true); + check(map.descendingMap(), 0, mapSize - 1, false); + + bashSubMap(map.subMap(0, true, mapSize, false), + 0, mapSize - 1, true); + } + + static NavigableMap newMap(Class cl) throws Exception { + NavigableMap result = + (NavigableMap) cl.newInstance(); + assertEquals(0, result.size()); + assertFalse(result.keySet().iterator().hasNext()); + return result; + } + + void populate(NavigableMap map, int limit) { + for (int i = 0, n = 2 * limit / 3; i < n; i++) { + int key = rnd.nextInt(limit); + put(map, key); + } + } + + void mutateMap(NavigableMap map, int min, int max) { + int size = map.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(map, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (map.size() < size) { + int key = min + rnd.nextInt(rangeSize); + assertTrue(key >= min && key <= max); + put(map, key); + } + } + + void mutateSubMap(NavigableMap map, int min, int max) { + int size = map.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(map, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (map.size() < size) { + int key = min - 5 + rnd.nextInt(rangeSize + 10); + if (key >= min && key <= max) { + put(map, key); + } else { + try { + map.put(key, 2 * key); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + } + + void put(NavigableMap map, int key) { + if (map.put(key, 2 * key) == null) + bs.set(key); + } + + void remove(NavigableMap map, int key) { + if (map.remove(key) != null) + bs.clear(key); + } + + void bashSubMap(NavigableMap map, + int min, int max, boolean ascending) { + check(map, min, max, ascending); + check(map.descendingMap(), min, max, !ascending); + + mutateSubMap(map, min, max); + check(map, min, max, ascending); + check(map.descendingMap(), min, max, !ascending); + + // Recurse + if (max - min < 2) + return; + int midPoint = (min + max) / 2; + + // headMap - pick direction and endpoint inclusion randomly + boolean incl = rnd.nextBoolean(); + NavigableMap hm = map.headMap(midPoint, incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true); + else + bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1), + false); + } else { + if (rnd.nextBoolean()) + bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false); + else + bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max, + true); + } + + // tailMap - pick direction and endpoint inclusion randomly + incl = rnd.nextBoolean(); + NavigableMap tm = map.tailMap(midPoint,incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true); + else + bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max, + false); + } else { + if (rnd.nextBoolean()) { + bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false); + } else { + bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1), + true); + } + } + + // subMap - pick direction and endpoint inclusion randomly + int rangeSize = max - min + 1; + int[] endpoints = new int[2]; + endpoints[0] = min + rnd.nextInt(rangeSize); + endpoints[1] = min + rnd.nextInt(rangeSize); + Arrays.sort(endpoints); + boolean lowIncl = rnd.nextBoolean(); + boolean highIncl = rnd.nextBoolean(); + if (ascending) { + NavigableMap sm = map.subMap( + endpoints[0], lowIncl, endpoints[1], highIncl); + if (rnd.nextBoolean()) + bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + else + bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + } else { + NavigableMap sm = map.subMap( + endpoints[1], highIncl, endpoints[0], lowIncl); + if (rnd.nextBoolean()) + bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + else + bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + } + } + + /** + * min and max are both inclusive. If max < min, interval is empty. + */ + void check(NavigableMap map, + final int min, final int max, final boolean ascending) { + class ReferenceSet { + int lower(int key) { + return ascending ? lowerAscending(key) : higherAscending(key); + } + int floor(int key) { + return ascending ? floorAscending(key) : ceilingAscending(key); + } + int ceiling(int key) { + return ascending ? ceilingAscending(key) : floorAscending(key); + } + int higher(int key) { + return ascending ? higherAscending(key) : lowerAscending(key); + } + int first() { + return ascending ? firstAscending() : lastAscending(); + } + int last() { + return ascending ? lastAscending() : firstAscending(); + } + int lowerAscending(int key) { + return floorAscending(key - 1); + } + int floorAscending(int key) { + if (key < min) + return -1; + else if (key > max) + key = max; + + // BitSet should support this! Test would run much faster + while (key >= min) { + if (bs.get(key)) + return key; + key--; + } + return -1; + } + int ceilingAscending(int key) { + if (key < min) + key = min; + else if (key > max) + return -1; + int result = bs.nextSetBit(key); + return result > max ? -1 : result; + } + int higherAscending(int key) { + return ceilingAscending(key + 1); + } + private int firstAscending() { + int result = ceilingAscending(min); + return result > max ? -1 : result; + } + private int lastAscending() { + int result = floorAscending(max); + return result < min ? -1 : result; + } + } + ReferenceSet rs = new ReferenceSet(); + + // Test contents using containsKey + int size = 0; + for (int i = min; i <= max; i++) { + boolean bsContainsI = bs.get(i); + assertEquals(bsContainsI, map.containsKey(i)); + if (bsContainsI) + size++; + } + assertEquals(size, map.size()); + + // Test contents using contains keySet iterator + int size2 = 0; + int previousKey = -1; + for (int key : map.keySet()) { + assertTrue(bs.get(key)); + size2++; + assertTrue(previousKey < 0 || + (ascending ? key - previousKey > 0 : key - previousKey < 0)); + previousKey = key; + } + assertEquals(size2, size); + + // Test navigation ops + for (int key = min - 1; key <= max + 1; key++) { + assertEq(map.lowerKey(key), rs.lower(key)); + assertEq(map.floorKey(key), rs.floor(key)); + assertEq(map.higherKey(key), rs.higher(key)); + assertEq(map.ceilingKey(key), rs.ceiling(key)); + } + + // Test extrema + if (map.size() != 0) { + assertEq(map.firstKey(), rs.first()); + assertEq(map.lastKey(), rs.last()); + } else { + assertEq(rs.first(), -1); + assertEq(rs.last(), -1); + try { + map.firstKey(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + map.lastKey(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + } + + static void assertEq(Integer i, int j) { + if (i == null) + assertEquals(j, -1); + else + assertEquals((int) i, j); + } + + static boolean eq(Integer i, int j) { + return (i == null) ? j == -1 : i == j; + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java new file mode 100644 index 00000000000..9012417716c --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java @@ -0,0 +1,1007 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; +import java.util.SortedSet; +import java.util.concurrent.ConcurrentSkipListSet; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentSkipListSetTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentSkipListSetTest.class); + } + + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * Returns a new set of given size containing consecutive + * Integers 0 ... n. + */ + private ConcurrentSkipListSet populatedSet(int n) { + ConcurrentSkipListSet q = + new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.add(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.add(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * Returns a new set of first 5 ints. + */ + private ConcurrentSkipListSet set5() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + q.add(one); + q.add(two); + q.add(three); + q.add(four); + q.add(five); + assertEquals(5, q.size()); + return q; + } + + /** + * A new set has unbounded capacity + */ + public void testConstructor1() { + assertEquals(0, new ConcurrentSkipListSet().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new ConcurrentSkipListSet((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new ConcurrentSkipListSet(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new ConcurrentSkipListSet(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * The comparator used in constructor is used + */ + public void testConstructor7() { + MyReverseComparator cmp = new MyReverseComparator(); + ConcurrentSkipListSet q = new ConcurrentSkipListSet(cmp); + assertEquals(cmp, q.comparator()); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + q.addAll(Arrays.asList(ints)); + for (int i = SIZE - 1; i >= 0; --i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.pollFirst(); + q.pollFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + ConcurrentSkipListSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Add of comparable element succeeds + */ + public void testAdd() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.add(zero)); + assertTrue(q.add(one)); + } + + /** + * Add of duplicate element fails + */ + public void testAddDup() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.add(zero)); + assertFalse(q.add(zero)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testAddNonComparable() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(i, q.pollFirst()); + } + + /** + * pollFirst succeeds unless empty + */ + public void testPollFirst() { + ConcurrentSkipListSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * pollLast succeeds unless empty + */ + public void testPollLast() { + ConcurrentSkipListSet q = populatedSet(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + ConcurrentSkipListSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + ConcurrentSkipListSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + ConcurrentSkipListSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + ConcurrentSkipListSet q = populatedSet(SIZE); + ConcurrentSkipListSet p = new ConcurrentSkipListSet(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + ConcurrentSkipListSet q = populatedSet(SIZE); + ConcurrentSkipListSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + ConcurrentSkipListSet q = populatedSet(SIZE); + ConcurrentSkipListSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testLower() { + ConcurrentSkipListSet q = set5(); + Object e1 = q.lower(three); + assertEquals(two, e1); + + Object e2 = q.lower(six); + assertEquals(five, e2); + + Object e3 = q.lower(one); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testHigher() { + ConcurrentSkipListSet q = set5(); + Object e1 = q.higher(three); + assertEquals(four, e1); + + Object e2 = q.higher(zero); + assertEquals(one, e2); + + Object e3 = q.higher(five); + assertNull(e3); + + Object e4 = q.higher(six); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testFloor() { + ConcurrentSkipListSet q = set5(); + Object e1 = q.floor(three); + assertEquals(three, e1); + + Object e2 = q.floor(six); + assertEquals(five, e2); + + Object e3 = q.floor(one); + assertEquals(one, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testCeiling() { + ConcurrentSkipListSet q = set5(); + Object e1 = q.ceiling(three); + assertEquals(three, e1); + + Object e2 = q.ceiling(zero); + assertEquals(one, e2); + + Object e3 = q.ceiling(five); + assertEquals(five, e3); + + Object e4 = q.ceiling(six); + assertNull(e4); + } + + /** + * toArray contains all elements in sorted order + */ + public void testToArray() { + ConcurrentSkipListSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements in sorted order + */ + public void testToArray2() { + ConcurrentSkipListSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + assertSame(ints, q.toArray(ints)); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + ConcurrentSkipListSet q = populatedSet(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty set has no elements + */ + public void testEmptyIterator() { + NavigableSet s = new ConcurrentSkipListSet(); + assertIteratorExhausted(s.iterator()); + assertIteratorExhausted(s.descendingSet().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + ConcurrentSkipListSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testSerialization() throws Exception { + NavigableSet x = populatedSet(SIZE); + NavigableSet y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testSubSetContents() { + ConcurrentSkipListSet set = set5(); + SortedSet sm = set.subSet(two, four); + assertEquals(two, sm.first()); + assertEquals(three, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.first()); + assertEquals(three, sm.last()); + assertTrue(sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testSubSetContents2() { + ConcurrentSkipListSet set = set5(); + SortedSet sm = set.subSet(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.first()); + assertEquals(two, sm.last()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertFalse(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(three)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testHeadSetContents() { + ConcurrentSkipListSet set = set5(); + SortedSet sm = set.headSet(four); + assertTrue(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(four, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testTailSetContents() { + ConcurrentSkipListSet set = set5(); + SortedSet sm = set.tailSet(two); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertTrue(sm.contains(four)); + assertTrue(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(four); + assertEquals(four, ssm.first()); + assertEquals(five, ssm.last()); + assertTrue(ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + + Random rnd = new Random(666); + + /** + * Subsets of subsets subdivide correctly + */ + public void testRecursiveSubSets() throws Exception { + int setSize = expensiveTests ? 1000 : 100; + Class cl = ConcurrentSkipListSet.class; + + NavigableSet set = newSet(cl); + BitSet bs = new BitSet(setSize); + + populate(set, setSize, bs); + check(set, 0, setSize - 1, true, bs); + check(set.descendingSet(), 0, setSize - 1, false, bs); + + mutateSet(set, 0, setSize - 1, bs); + check(set, 0, setSize - 1, true, bs); + check(set.descendingSet(), 0, setSize - 1, false, bs); + + bashSubSet(set.subSet(0, true, setSize, false), + 0, setSize - 1, true, bs); + } + + /** + * addAll is idempotent + */ + public void testAddAll_idempotent() throws Exception { + Set x = populatedSet(SIZE); + Set y = new ConcurrentSkipListSet(x); + y.addAll(x); + assertEquals(x, y); + assertEquals(y, x); + } + + static NavigableSet newSet(Class cl) throws Exception { + NavigableSet result = (NavigableSet) cl.newInstance(); + assertEquals(0, result.size()); + assertFalse(result.iterator().hasNext()); + return result; + } + + void populate(NavigableSet set, int limit, BitSet bs) { + for (int i = 0, n = 2 * limit / 3; i < n; i++) { + int element = rnd.nextInt(limit); + put(set, element, bs); + } + } + + void mutateSet(NavigableSet set, int min, int max, BitSet bs) { + int size = set.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs); + } + + // Remove a bunch of entries with iterator + for (Iterator it = set.iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (set.size() < size) { + int element = min + rnd.nextInt(rangeSize); + assertTrue(element >= min && element <= max); + put(set, element, bs); + } + } + + void mutateSubSet(NavigableSet set, int min, int max, + BitSet bs) { + int size = set.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs); + } + + // Remove a bunch of entries with iterator + for (Iterator it = set.iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (set.size() < size) { + int element = min - 5 + rnd.nextInt(rangeSize + 10); + if (element >= min && element <= max) { + put(set, element, bs); + } else { + try { + set.add(element); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + } + + void put(NavigableSet set, int element, BitSet bs) { + if (set.add(element)) + bs.set(element); + } + + void remove(NavigableSet set, int element, BitSet bs) { + if (set.remove(element)) + bs.clear(element); + } + + void bashSubSet(NavigableSet set, + int min, int max, boolean ascending, + BitSet bs) { + check(set, min, max, ascending, bs); + check(set.descendingSet(), min, max, !ascending, bs); + + mutateSubSet(set, min, max, bs); + check(set, min, max, ascending, bs); + check(set.descendingSet(), min, max, !ascending, bs); + + // Recurse + if (max - min < 2) + return; + int midPoint = (min + max) / 2; + + // headSet - pick direction and endpoint inclusion randomly + boolean incl = rnd.nextBoolean(); + NavigableSet hm = set.headSet(midPoint, incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true, bs); + else + bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1), + false, bs); + } else { + if (rnd.nextBoolean()) + bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false, bs); + else + bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max, + true, bs); + } + + // tailSet - pick direction and endpoint inclusion randomly + incl = rnd.nextBoolean(); + NavigableSet tm = set.tailSet(midPoint,incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true, bs); + else + bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max, + false, bs); + } else { + if (rnd.nextBoolean()) { + bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false, bs); + } else { + bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1), + true, bs); + } + } + + // subSet - pick direction and endpoint inclusion randomly + int rangeSize = max - min + 1; + int[] endpoints = new int[2]; + endpoints[0] = min + rnd.nextInt(rangeSize); + endpoints[1] = min + rnd.nextInt(rangeSize); + Arrays.sort(endpoints); + boolean lowIncl = rnd.nextBoolean(); + boolean highIncl = rnd.nextBoolean(); + if (ascending) { + NavigableSet sm = set.subSet( + endpoints[0], lowIncl, endpoints[1], highIncl); + if (rnd.nextBoolean()) + bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true, bs); + else + bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false, bs); + } else { + NavigableSet sm = set.subSet( + endpoints[1], highIncl, endpoints[0], lowIncl); + if (rnd.nextBoolean()) + bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false, bs); + else + bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true, bs); + } + } + + /** + * min and max are both inclusive. If max < min, interval is empty. + */ + void check(NavigableSet set, + final int min, final int max, final boolean ascending, + final BitSet bs) { + class ReferenceSet { + int lower(int element) { + return ascending ? + lowerAscending(element) : higherAscending(element); + } + int floor(int element) { + return ascending ? + floorAscending(element) : ceilingAscending(element); + } + int ceiling(int element) { + return ascending ? + ceilingAscending(element) : floorAscending(element); + } + int higher(int element) { + return ascending ? + higherAscending(element) : lowerAscending(element); + } + int first() { + return ascending ? firstAscending() : lastAscending(); + } + int last() { + return ascending ? lastAscending() : firstAscending(); + } + int lowerAscending(int element) { + return floorAscending(element - 1); + } + int floorAscending(int element) { + if (element < min) + return -1; + else if (element > max) + element = max; + + // BitSet should support this! Test would run much faster + while (element >= min) { + if (bs.get(element)) + return element; + element--; + } + return -1; + } + int ceilingAscending(int element) { + if (element < min) + element = min; + else if (element > max) + return -1; + int result = bs.nextSetBit(element); + return result > max ? -1 : result; + } + int higherAscending(int element) { + return ceilingAscending(element + 1); + } + private int firstAscending() { + int result = ceilingAscending(min); + return result > max ? -1 : result; + } + private int lastAscending() { + int result = floorAscending(max); + return result < min ? -1 : result; + } + } + ReferenceSet rs = new ReferenceSet(); + + // Test contents using containsElement + int size = 0; + for (int i = min; i <= max; i++) { + boolean bsContainsI = bs.get(i); + assertEquals(bsContainsI, set.contains(i)); + if (bsContainsI) + size++; + } + assertEquals(size, set.size()); + + // Test contents using contains elementSet iterator + int size2 = 0; + int previousElement = -1; + for (int element : set) { + assertTrue(bs.get(element)); + size2++; + assertTrue(previousElement < 0 || (ascending ? + element - previousElement > 0 : element - previousElement < 0)); + previousElement = element; + } + assertEquals(size2, size); + + // Test navigation ops + for (int element = min - 1; element <= max + 1; element++) { + assertEq(set.lower(element), rs.lower(element)); + assertEq(set.floor(element), rs.floor(element)); + assertEq(set.higher(element), rs.higher(element)); + assertEq(set.ceiling(element), rs.ceiling(element)); + } + + // Test extrema + if (set.size() != 0) { + assertEq(set.first(), rs.first()); + assertEq(set.last(), rs.last()); + } else { + assertEq(rs.first(), -1); + assertEq(rs.last(), -1); + try { + set.first(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + set.last(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + } + + static void assertEq(Integer i, int j) { + if (i == null) + assertEquals(j, -1); + else + assertEquals((int) i, j); + } + + static boolean eq(Integer i, int j) { + return (i == null) ? j == -1 : i == j; + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java new file mode 100644 index 00000000000..ddc82f78a56 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java @@ -0,0 +1,1450 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.SortedMap; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentSkipListSubMapTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentSkipListSubMapTest.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static ConcurrentNavigableMap map5() { + ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + assertTrue(map.isEmpty()); + map.put(zero, "Z"); + map.put(one, "A"); + map.put(five, "E"); + map.put(three, "C"); + map.put(two, "B"); + map.put(four, "D"); + map.put(seven, "F"); + assertFalse(map.isEmpty()); + assertEquals(7, map.size()); + return map.subMap(one, true, seven, false); + } + + /** + * Returns a new map from Integers -5 to -1 to Strings "A"-"E". + */ + private static ConcurrentNavigableMap dmap5() { + ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + assertTrue(map.isEmpty()); + map.put(m1, "A"); + map.put(m5, "E"); + map.put(m3, "C"); + map.put(m2, "B"); + map.put(m4, "D"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map.descendingMap(); + } + + private static ConcurrentNavigableMap map0() { + ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + assertTrue(map.isEmpty()); + return map.tailMap(one, true); + } + + private static ConcurrentNavigableMap dmap0() { + ConcurrentSkipListMap map = new ConcurrentSkipListMap(); + assertTrue(map.isEmpty()); + return map; + } + + /** + * clear removes all pairs + */ + public void testClear() { + ConcurrentNavigableMap map = map5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * Maps with same contents are equal + */ + public void testEquals() { + ConcurrentNavigableMap map1 = map5(); + ConcurrentNavigableMap map2 = map5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testContainsKey() { + ConcurrentNavigableMap map = map5(); + assertTrue(map.containsKey(one)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testContainsValue() { + ConcurrentNavigableMap map = map5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testGet() { + ConcurrentNavigableMap map = map5(); + assertEquals("A", (String)map.get(one)); + ConcurrentNavigableMap empty = map0(); + assertNull(empty.get(one)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testIsEmpty() { + ConcurrentNavigableMap empty = map0(); + ConcurrentNavigableMap map = map5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testFirstKey() { + ConcurrentNavigableMap map = map5(); + assertEquals(one, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testLastKey() { + ConcurrentNavigableMap map = map5(); + assertEquals(five, map.lastKey()); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testKeySet() { + ConcurrentNavigableMap map = map5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(one)); + assertTrue(s.contains(two)); + assertTrue(s.contains(three)); + assertTrue(s.contains(four)); + assertTrue(s.contains(five)); + } + + /** + * keySet is ordered + */ + public void testKeySetOrder() { + ConcurrentNavigableMap map = map5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + } + } + + /** + * values collection contains all values + */ + public void testValues() { + ConcurrentNavigableMap map = map5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + ConcurrentNavigableMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * descendingkeySet.toArray returns contains all keys + */ + public void testDescendingKeySetToArray() { + ConcurrentNavigableMap map = map5(); + Set s = map.descendingKeySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + assertTrue(s.containsAll(Arrays.asList(ar))); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testValuesToArray() { + ConcurrentNavigableMap map = map5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testEntrySet() { + ConcurrentNavigableMap map = map5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testPutAll() { + ConcurrentNavigableMap empty = map0(); + ConcurrentNavigableMap map = map5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(one)); + assertTrue(empty.containsKey(two)); + assertTrue(empty.containsKey(three)); + assertTrue(empty.containsKey(four)); + assertTrue(empty.containsKey(five)); + } + + /** + * putIfAbsent works when the given key is not present + */ + public void testPutIfAbsent() { + ConcurrentNavigableMap map = map5(); + map.putIfAbsent(six, "Z"); + assertTrue(map.containsKey(six)); + } + + /** + * putIfAbsent does not add the pair if the key is already present + */ + public void testPutIfAbsent2() { + ConcurrentNavigableMap map = map5(); + assertEquals("A", map.putIfAbsent(one, "Z")); + } + + /** + * replace fails when the given key is not present + */ + public void testReplace() { + ConcurrentNavigableMap map = map5(); + assertNull(map.replace(six, "Z")); + assertFalse(map.containsKey(six)); + } + + /** + * replace succeeds if the key is already present + */ + public void testReplace2() { + ConcurrentNavigableMap map = map5(); + assertNotNull(map.replace(one, "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * replace value fails when the given key not mapped to expected value + */ + public void testReplaceValue() { + ConcurrentNavigableMap map = map5(); + assertEquals("A", map.get(one)); + assertFalse(map.replace(one, "Z", "Z")); + assertEquals("A", map.get(one)); + } + + /** + * replace value succeeds when the given key mapped to expected value + */ + public void testReplaceValue2() { + ConcurrentNavigableMap map = map5(); + assertEquals("A", map.get(one)); + assertTrue(map.replace(one, "A", "Z")); + assertEquals("Z", map.get(one)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testRemove() { + ConcurrentNavigableMap map = map5(); + map.remove(five); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + } + + /** + * remove(key,value) removes only if pair present + */ + public void testRemove2() { + ConcurrentNavigableMap map = map5(); + assertTrue(map.containsKey(five)); + assertEquals("E", map.get(five)); + map.remove(five, "E"); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + map.remove(four, "A"); + assertEquals(4, map.size()); + assertTrue(map.containsKey(four)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testLowerEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e1 = map.lowerEntry(three); + assertEquals(two, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(one); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testHigherEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e1 = map.higherEntry(three); + assertEquals(four, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.higherEntry(five); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(six); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testFloorEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e1 = map.floorEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.floorEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.floorEntry(one); + assertEquals(one, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testCeilingEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e1 = map.ceilingEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(five); + assertEquals(five, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(six); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testPollFirstEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(two, e.getKey()); + map.put(one, "A"); + e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(three, e.getKey()); + map.remove(four); + e = map.pollFirstEntry(); + assertEquals(five, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testPollLastEntry() { + ConcurrentNavigableMap map = map5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(four, e.getKey()); + map.put(five, "E"); + e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(three, e.getKey()); + map.remove(two); + e = map.pollLastEntry(); + assertEquals(one, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testSize() { + ConcurrentNavigableMap map = map5(); + ConcurrentNavigableMap empty = map0(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testToString() { + ConcurrentNavigableMap map = map5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception tests + + /** + * get(null) of nonempty map throws NPE + */ + public void testGet_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) of nonempty map throws NPE + */ + public void testContainsKey_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsValue(null) throws NPE + */ + public void testContainsValue_NullPointerException() { + try { + ConcurrentNavigableMap c = map0(); + c.containsValue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testPut1_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * putIfAbsent(null, x) throws NPE + */ + public void testPutIfAbsent1_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.putIfAbsent(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x) throws NPE + */ + public void testReplace_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.replace(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x, y) throws NPE + */ + public void testReplaceValue_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.replace(null, one, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE + */ + public void testRemove1_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null, x) throws NPE + */ + public void testRemove2_NullPointerException() { + try { + ConcurrentNavigableMap c = map5(); + c.remove(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A deserialized map equals original + */ + public void testSerialization() throws Exception { + NavigableMap x = map5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testSubMapContents() { + ConcurrentNavigableMap map = map5(); + SortedMap sm = map.subMap(two, four); + assertEquals(two, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals("C", sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testSubMapContents2() { + ConcurrentNavigableMap map = map5(); + SortedMap sm = map.subMap(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.firstKey()); + assertEquals(two, sm.lastKey()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertFalse(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(three), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testHeadMapContents() { + ConcurrentNavigableMap map = map5(); + SortedMap sm = map.headMap(four); + assertTrue(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(four, map.firstKey()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testTailMapContents() { + ConcurrentNavigableMap map = map5(); + SortedMap sm = map.tailMap(two); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertTrue(sm.containsKey(four)); + assertTrue(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(two, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(three, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(four, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + SortedMap ssm = sm.tailMap(four); + assertEquals(four, ssm.firstKey()); + assertEquals(five, ssm.lastKey()); + assertEquals("D", ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + + /** + * clear removes all pairs + */ + public void testDescendingClear() { + ConcurrentNavigableMap map = dmap5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * Maps with same contents are equal + */ + public void testDescendingEquals() { + ConcurrentNavigableMap map1 = dmap5(); + ConcurrentNavigableMap map2 = dmap5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testDescendingContainsKey() { + ConcurrentNavigableMap map = dmap5(); + assertTrue(map.containsKey(m1)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testDescendingContainsValue() { + ConcurrentNavigableMap map = dmap5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testDescendingGet() { + ConcurrentNavigableMap map = dmap5(); + assertEquals("A", (String)map.get(m1)); + ConcurrentNavigableMap empty = dmap0(); + assertNull(empty.get(m1)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testDescendingIsEmpty() { + ConcurrentNavigableMap empty = dmap0(); + ConcurrentNavigableMap map = dmap5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testDescendingFirstKey() { + ConcurrentNavigableMap map = dmap5(); + assertEquals(m1, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testDescendingLastKey() { + ConcurrentNavigableMap map = dmap5(); + assertEquals(m5, map.lastKey()); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testDescendingKeySet() { + ConcurrentNavigableMap map = dmap5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(m1)); + assertTrue(s.contains(m2)); + assertTrue(s.contains(m3)); + assertTrue(s.contains(m4)); + assertTrue(s.contains(m5)); + } + + /** + * keySet is ordered + */ + public void testDescendingKeySetOrder() { + ConcurrentNavigableMap map = dmap5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, m1); + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + } + } + + /** + * values collection contains all values + */ + public void testDescendingValues() { + ConcurrentNavigableMap map = dmap5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testDescendingAscendingKeySetToArray() { + ConcurrentNavigableMap map = dmap5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * descendingkeySet.toArray returns contains all keys + */ + public void testDescendingDescendingKeySetToArray() { + ConcurrentNavigableMap map = dmap5(); + Set s = map.descendingKeySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + assertTrue(s.containsAll(Arrays.asList(ar))); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testDescendingValuesToArray() { + ConcurrentNavigableMap map = dmap5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testDescendingEntrySet() { + ConcurrentNavigableMap map = dmap5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(m1) && e.getValue().equals("A")) || + (e.getKey().equals(m2) && e.getValue().equals("B")) || + (e.getKey().equals(m3) && e.getValue().equals("C")) || + (e.getKey().equals(m4) && e.getValue().equals("D")) || + (e.getKey().equals(m5) && e.getValue().equals("E"))); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testDescendingPutAll() { + ConcurrentNavigableMap empty = dmap0(); + ConcurrentNavigableMap map = dmap5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(m1)); + assertTrue(empty.containsKey(m2)); + assertTrue(empty.containsKey(m3)); + assertTrue(empty.containsKey(m4)); + assertTrue(empty.containsKey(m5)); + } + + /** + * putIfAbsent works when the given key is not present + */ + public void testDescendingPutIfAbsent() { + ConcurrentNavigableMap map = dmap5(); + map.putIfAbsent(six, "Z"); + assertTrue(map.containsKey(six)); + } + + /** + * putIfAbsent does not add the pair if the key is already present + */ + public void testDescendingPutIfAbsent2() { + ConcurrentNavigableMap map = dmap5(); + assertEquals("A", map.putIfAbsent(m1, "Z")); + } + + /** + * replace fails when the given key is not present + */ + public void testDescendingReplace() { + ConcurrentNavigableMap map = dmap5(); + assertNull(map.replace(six, "Z")); + assertFalse(map.containsKey(six)); + } + + /** + * replace succeeds if the key is already present + */ + public void testDescendingReplace2() { + ConcurrentNavigableMap map = dmap5(); + assertNotNull(map.replace(m1, "Z")); + assertEquals("Z", map.get(m1)); + } + + /** + * replace value fails when the given key not mapped to expected value + */ + public void testDescendingReplaceValue() { + ConcurrentNavigableMap map = dmap5(); + assertEquals("A", map.get(m1)); + assertFalse(map.replace(m1, "Z", "Z")); + assertEquals("A", map.get(m1)); + } + + /** + * replace value succeeds when the given key mapped to expected value + */ + public void testDescendingReplaceValue2() { + ConcurrentNavigableMap map = dmap5(); + assertEquals("A", map.get(m1)); + assertTrue(map.replace(m1, "A", "Z")); + assertEquals("Z", map.get(m1)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testDescendingRemove() { + ConcurrentNavigableMap map = dmap5(); + map.remove(m5); + assertEquals(4, map.size()); + assertFalse(map.containsKey(m5)); + } + + /** + * remove(key,value) removes only if pair present + */ + public void testDescendingRemove2() { + ConcurrentNavigableMap map = dmap5(); + assertTrue(map.containsKey(m5)); + assertEquals("E", map.get(m5)); + map.remove(m5, "E"); + assertEquals(4, map.size()); + assertFalse(map.containsKey(m5)); + map.remove(m4, "A"); + assertEquals(4, map.size()); + assertTrue(map.containsKey(m4)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testDescendingLowerEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e1 = map.lowerEntry(m3); + assertEquals(m2, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(m6); + assertEquals(m5, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(m1); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testDescendingHigherEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e1 = map.higherEntry(m3); + assertEquals(m4, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(m1, e2.getKey()); + + Map.Entry e3 = map.higherEntry(m5); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(m6); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testDescendingFloorEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e1 = map.floorEntry(m3); + assertEquals(m3, e1.getKey()); + + Map.Entry e2 = map.floorEntry(m6); + assertEquals(m5, e2.getKey()); + + Map.Entry e3 = map.floorEntry(m1); + assertEquals(m1, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testDescendingCeilingEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e1 = map.ceilingEntry(m3); + assertEquals(m3, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(m1, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(m5); + assertEquals(m5, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(m6); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testDescendingPollFirstEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(m1, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(m2, e.getKey()); + map.put(m1, "A"); + e = map.pollFirstEntry(); + assertEquals(m1, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(m3, e.getKey()); + map.remove(m4); + e = map.pollFirstEntry(); + assertEquals(m5, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testDescendingPollLastEntry() { + ConcurrentNavigableMap map = dmap5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(m4, e.getKey()); + map.put(m5, "E"); + e = map.pollLastEntry(); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(m3, e.getKey()); + map.remove(m2); + e = map.pollLastEntry(); + assertEquals(m1, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testDescendingSize() { + ConcurrentNavigableMap map = dmap5(); + ConcurrentNavigableMap empty = dmap0(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testDescendingToString() { + ConcurrentNavigableMap map = dmap5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception testDescendings + + /** + * get(null) of empty map throws NPE + */ + public void testDescendingGet_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) of empty map throws NPE + */ + public void testDescendingContainsKey_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsValue(null) throws NPE + */ + public void testDescendingContainsValue_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap0(); + c.containsValue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testDescendingPut1_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * putIfAbsent(null, x) throws NPE + */ + public void testDescendingPutIfAbsent1_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.putIfAbsent(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x) throws NPE + */ + public void testDescendingReplace_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.replace(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * replace(null, x, y) throws NPE + */ + public void testDescendingReplaceValue_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.replace(null, m1, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE + */ + public void testDescendingRemove1_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null, x) throws NPE + */ + public void testDescendingRemove2_NullPointerException() { + try { + ConcurrentNavigableMap c = dmap5(); + c.remove(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A deserialized map equals original + */ + public void testDescendingSerialization() throws Exception { + NavigableMap x = dmap5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testDescendingSubMapContents() { + ConcurrentNavigableMap map = dmap5(); + SortedMap sm = map.subMap(m2, m4); + assertEquals(m2, sm.firstKey()); + assertEquals(m3, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(m2)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(m3, sm.firstKey()); + assertEquals(m3, sm.lastKey()); + assertEquals("C", sm.remove(m3)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testDescendingSubMapContents2() { + ConcurrentNavigableMap map = dmap5(); + SortedMap sm = map.subMap(m2, m3); + assertEquals(1, sm.size()); + assertEquals(m2, sm.firstKey()); + assertEquals(m2, sm.lastKey()); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertFalse(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(m2)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(m3), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testDescendingHeadMapContents() { + ConcurrentNavigableMap map = dmap5(); + SortedMap sm = map.headMap(m4); + assertTrue(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m1, k); + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(m4, map.firstKey()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testDescendingTailMapContents() { + ConcurrentNavigableMap map = dmap5(); + SortedMap sm = map.tailMap(m2); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertTrue(sm.containsKey(m4)); + assertTrue(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + k = (Integer)(i.next()); + assertEquals(m4, k); + k = (Integer)(i.next()); + assertEquals(m5, k); + assertFalse(i.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(m2, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m3, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m4, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + SortedMap ssm = sm.tailMap(m4); + assertEquals(m4, ssm.firstKey()); + assertEquals(m5, ssm.lastKey()); + assertEquals("D", ssm.remove(m4)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java new file mode 100644 index 00000000000..bad618f3fab --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java @@ -0,0 +1,1141 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.concurrent.ConcurrentSkipListSet; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ConcurrentSkipListSubSetTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ConcurrentSkipListSubSetTest.class); + } + + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * Returns a new set of given size containing consecutive + * Integers 0 ... n. + */ + private NavigableSet populatedSet(int n) { + ConcurrentSkipListSet q = + new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.add(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.add(new Integer(i))); + assertTrue(q.add(new Integer(-n))); + assertTrue(q.add(new Integer(n))); + NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false); + assertFalse(s.isEmpty()); + assertEquals(n, s.size()); + return s; + } + + /** + * Returns a new set of first 5 ints. + */ + private NavigableSet set5() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + q.add(one); + q.add(two); + q.add(three); + q.add(four); + q.add(five); + q.add(zero); + q.add(seven); + NavigableSet s = q.subSet(one, true, seven, false); + assertEquals(5, s.size()); + return s; + } + + /** + * Returns a new set of first 5 negative ints. + */ + private NavigableSet dset5() { + ConcurrentSkipListSet q = new ConcurrentSkipListSet(); + assertTrue(q.isEmpty()); + q.add(m1); + q.add(m2); + q.add(m3); + q.add(m4); + q.add(m5); + NavigableSet s = q.descendingSet(); + assertEquals(5, s.size()); + return s; + } + + private static NavigableSet set0() { + ConcurrentSkipListSet set = new ConcurrentSkipListSet(); + assertTrue(set.isEmpty()); + return set.tailSet(m1, true); + } + + private static NavigableSet dset0() { + ConcurrentSkipListSet set = new ConcurrentSkipListSet(); + assertTrue(set.isEmpty()); + return set; + } + + /** + * A new set has unbounded capacity + */ + public void testConstructor1() { + assertEquals(0, set0().size()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + NavigableSet q = set0(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.pollFirst(); + q.pollFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + NavigableSet q = set0(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Add of comparable element succeeds + */ + public void testAdd() { + NavigableSet q = set0(); + assertTrue(q.add(six)); + } + + /** + * Add of duplicate element fails + */ + public void testAddDup() { + NavigableSet q = set0(); + assertTrue(q.add(six)); + assertFalse(q.add(six)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testAddNonComparable() { + NavigableSet q = set0(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + NavigableSet q = set0(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + NavigableSet q = set0(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + NavigableSet q = set0(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i + SIZE); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + NavigableSet q = set0(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.pollFirst()); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + NavigableSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + NavigableSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = set0(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testLower() { + NavigableSet q = set5(); + Object e1 = q.lower(three); + assertEquals(two, e1); + + Object e2 = q.lower(six); + assertEquals(five, e2); + + Object e3 = q.lower(one); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testHigher() { + NavigableSet q = set5(); + Object e1 = q.higher(three); + assertEquals(four, e1); + + Object e2 = q.higher(zero); + assertEquals(one, e2); + + Object e3 = q.higher(five); + assertNull(e3); + + Object e4 = q.higher(six); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testFloor() { + NavigableSet q = set5(); + Object e1 = q.floor(three); + assertEquals(three, e1); + + Object e2 = q.floor(six); + assertEquals(five, e2); + + Object e3 = q.floor(one); + assertEquals(one, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testCeiling() { + NavigableSet q = set5(); + Object e1 = q.ceiling(three); + assertEquals(three, e1); + + Object e2 = q.ceiling(zero); + assertEquals(one, e2); + + Object e3 = q.ceiling(five); + assertEquals(five, e3); + + Object e4 = q.ceiling(six); + assertNull(e4); + } + + /** + * toArray contains all elements in sorted order + */ + public void testToArray() { + NavigableSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements in sorted order + */ + public void testToArray2() { + NavigableSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + NavigableSet q = populatedSet(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty set has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(set0().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final NavigableSet q = set0(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + NavigableSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testSerialization() throws Exception { + NavigableSet x = populatedSet(SIZE); + NavigableSet y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testSubSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.subSet(two, four); + assertEquals(two, sm.first()); + assertEquals(three, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.first()); + assertEquals(three, sm.last()); + assertTrue(sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testSubSetContents2() { + NavigableSet set = set5(); + SortedSet sm = set.subSet(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.first()); + assertEquals(two, sm.last()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertFalse(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(three)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testHeadSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.headSet(four); + assertTrue(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(four, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testTailSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.tailSet(two); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertTrue(sm.contains(four)); + assertTrue(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(four); + assertEquals(four, ssm.first()); + assertEquals(five, ssm.last()); + assertTrue(ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + + /** + * size changes when elements added and removed + */ + public void testDescendingSize() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * add(null) throws NPE + */ + public void testDescendingAddNull() { + NavigableSet q = dset0(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Add of comparable element succeeds + */ + public void testDescendingAdd() { + NavigableSet q = dset0(); + assertTrue(q.add(m6)); + } + + /** + * Add of duplicate element fails + */ + public void testDescendingAddDup() { + NavigableSet q = dset0(); + assertTrue(q.add(m6)); + assertFalse(q.add(m6)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testDescendingAddNonComparable() { + NavigableSet q = dset0(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testDescendingAddAll1() { + NavigableSet q = dset0(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testDescendingAddAll2() { + NavigableSet q = dset0(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testDescendingAddAll3() { + NavigableSet q = dset0(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i + SIZE); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testDescendingAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + NavigableSet q = dset0(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.pollFirst()); + } + + /** + * poll succeeds unless empty + */ + public void testDescendingPoll() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testDescendingRemoveElement() { + NavigableSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.remove(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2 ) { + assertTrue(q.remove(new Integer(i))); + assertFalse(q.remove(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testDescendingContains() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testDescendingClear() { + NavigableSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testDescendingContainsAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = dset0(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testDescendingRetainAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testDescendingRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testDescendingLower() { + NavigableSet q = dset5(); + Object e1 = q.lower(m3); + assertEquals(m2, e1); + + Object e2 = q.lower(m6); + assertEquals(m5, e2); + + Object e3 = q.lower(m1); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testDescendingHigher() { + NavigableSet q = dset5(); + Object e1 = q.higher(m3); + assertEquals(m4, e1); + + Object e2 = q.higher(zero); + assertEquals(m1, e2); + + Object e3 = q.higher(m5); + assertNull(e3); + + Object e4 = q.higher(m6); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testDescendingFloor() { + NavigableSet q = dset5(); + Object e1 = q.floor(m3); + assertEquals(m3, e1); + + Object e2 = q.floor(m6); + assertEquals(m5, e2); + + Object e3 = q.floor(m1); + assertEquals(m1, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testDescendingCeiling() { + NavigableSet q = dset5(); + Object e1 = q.ceiling(m3); + assertEquals(m3, e1); + + Object e2 = q.ceiling(zero); + assertEquals(m1, e2); + + Object e3 = q.ceiling(m5); + assertEquals(m5, e3); + + Object e4 = q.ceiling(m6); + assertNull(e4); + } + + /** + * toArray contains all elements + */ + public void testDescendingToArray() { + NavigableSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + Arrays.sort(o); + for (int i = 0; i < o.length; i++) + assertEquals(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements + */ + public void testDescendingToArray2() { + NavigableSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + assertSame(ints, q.toArray(ints)); + Arrays.sort(ints); + for (int i = 0; i < ints.length; i++) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testDescendingIterator() { + NavigableSet q = populatedSet(SIZE); + int i = 0; + Iterator it = q.iterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + } + + /** + * iterator of empty set has no elements + */ + public void testDescendingEmptyIterator() { + NavigableSet q = dset0(); + int i = 0; + Iterator it = q.iterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(0, i); + } + + /** + * iterator.remove removes current element + */ + public void testDescendingIteratorRemove() { + final NavigableSet q = dset0(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testDescendingToString() { + NavigableSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testDescendingSerialization() throws Exception { + NavigableSet x = dset5(); + NavigableSet y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testDescendingSubSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.subSet(m2, m4); + assertEquals(m2, sm.first()); + assertEquals(m3, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(m2)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(m3, sm.first()); + assertEquals(m3, sm.last()); + assertTrue(sm.remove(m3)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testDescendingSubSetContents2() { + NavigableSet set = dset5(); + SortedSet sm = set.subSet(m2, m3); + assertEquals(1, sm.size()); + assertEquals(m2, sm.first()); + assertEquals(m2, sm.last()); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertFalse(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(m2)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(m3)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testDescendingHeadSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.headSet(m4); + assertTrue(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m1, k); + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(m4, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testDescendingTailSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.tailSet(m2); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertTrue(sm.contains(m4)); + assertTrue(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + k = (Integer)(i.next()); + assertEquals(m4, k); + k = (Integer)(i.next()); + assertEquals(m5, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(m4); + assertEquals(m4, ssm.first()); + assertEquals(m5, ssm.last()); + assertTrue(ssm.remove(m4)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java b/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java new file mode 100644 index 00000000000..75cfb1435fc --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java @@ -0,0 +1,783 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.concurrent.CopyOnWriteArrayList; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CopyOnWriteArrayListTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(CopyOnWriteArrayListTest.class); + } + + static CopyOnWriteArrayList populatedArray(int n) { + CopyOnWriteArrayList a = new CopyOnWriteArrayList(); + assertTrue(a.isEmpty()); + for (int i = 0; i < n; i++) + a.add(i); + assertFalse(a.isEmpty()); + assertEquals(n, a.size()); + return a; + } + + static CopyOnWriteArrayList populatedArray(Integer[] elements) { + CopyOnWriteArrayList a = new CopyOnWriteArrayList(); + assertTrue(a.isEmpty()); + for (int i = 0; i < elements.length; i++) + a.add(elements[i]); + assertFalse(a.isEmpty()); + assertEquals(elements.length, a.size()); + return a; + } + + /** + * a new list is empty + */ + public void testConstructor() { + CopyOnWriteArrayList a = new CopyOnWriteArrayList(); + assertTrue(a.isEmpty()); + } + + /** + * new list contains all elements of initializing array + */ + public void testConstructor2() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + CopyOnWriteArrayList a = new CopyOnWriteArrayList(ints); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], a.get(i)); + } + + /** + * new list contains all elements of initializing collection + */ + public void testConstructor3() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + CopyOnWriteArrayList a = new CopyOnWriteArrayList(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], a.get(i)); + } + + /** + * addAll adds each element from the given collection, including duplicates + */ + public void testAddAll() { + CopyOnWriteArrayList full = populatedArray(3); + assertTrue(full.addAll(Arrays.asList(three, four, five))); + assertEquals(6, full.size()); + assertTrue(full.addAll(Arrays.asList(three, four, five))); + assertEquals(9, full.size()); + } + + /** + * addAllAbsent adds each element from the given collection that did not + * already exist in the List + */ + public void testAddAllAbsent() { + CopyOnWriteArrayList full = populatedArray(3); + // "one" is duplicate and will not be added + assertEquals(2, full.addAllAbsent(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + assertEquals(0, full.addAllAbsent(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + } + + /** + * addIfAbsent will not add the element if it already exists in the list + */ + public void testAddIfAbsent() { + CopyOnWriteArrayList full = populatedArray(SIZE); + full.addIfAbsent(one); + assertEquals(SIZE, full.size()); + } + + /** + * addIfAbsent adds the element when it does not exist in the list + */ + public void testAddIfAbsent2() { + CopyOnWriteArrayList full = populatedArray(SIZE); + full.addIfAbsent(three); + assertTrue(full.contains(three)); + } + + /** + * clear removes all elements from the list + */ + public void testClear() { + CopyOnWriteArrayList full = populatedArray(SIZE); + full.clear(); + assertEquals(0, full.size()); + } + + /** + * Cloned list is equal + */ + public void testClone() { + CopyOnWriteArrayList l1 = populatedArray(SIZE); + CopyOnWriteArrayList l2 = (CopyOnWriteArrayList)(l1.clone()); + assertEquals(l1, l2); + l1.clear(); + assertFalse(l1.equals(l2)); + } + + /** + * contains is true for added elements + */ + public void testContains() { + CopyOnWriteArrayList full = populatedArray(3); + assertTrue(full.contains(one)); + assertFalse(full.contains(five)); + } + + /** + * adding at an index places it in the indicated index + */ + public void testAddIndex() { + CopyOnWriteArrayList full = populatedArray(3); + full.add(0, m1); + assertEquals(4, full.size()); + assertEquals(m1, full.get(0)); + assertEquals(zero, full.get(1)); + + full.add(2, m2); + assertEquals(5, full.size()); + assertEquals(m2, full.get(2)); + assertEquals(two, full.get(4)); + } + + /** + * lists with same elements are equal and have same hashCode + */ + public void testEquals() { + CopyOnWriteArrayList a = populatedArray(3); + CopyOnWriteArrayList b = populatedArray(3); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertTrue(a.containsAll(b)); + assertTrue(b.containsAll(a)); + assertEquals(a.hashCode(), b.hashCode()); + a.add(m1); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + assertTrue(a.containsAll(b)); + assertFalse(b.containsAll(a)); + b.add(m1); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertTrue(a.containsAll(b)); + assertTrue(b.containsAll(a)); + assertEquals(a.hashCode(), b.hashCode()); + + assertFalse(a.equals(null)); + } + + /** + * containsAll returns true for collections with subset of elements + */ + public void testContainsAll() { + CopyOnWriteArrayList full = populatedArray(3); + assertTrue(full.containsAll(Arrays.asList())); + assertTrue(full.containsAll(Arrays.asList(one))); + assertTrue(full.containsAll(Arrays.asList(one, two))); + assertFalse(full.containsAll(Arrays.asList(one, two, six))); + assertFalse(full.containsAll(Arrays.asList(six))); + + try { + full.containsAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * get returns the value at the given index + */ + public void testGet() { + CopyOnWriteArrayList full = populatedArray(3); + assertEquals(0, full.get(0)); + } + + /** + * indexOf gives the index for the given object + */ + public void testIndexOf() { + CopyOnWriteArrayList full = populatedArray(3); + assertEquals(1, full.indexOf(one)); + assertEquals(-1, full.indexOf("puppies")); + } + + /** + * indexOf gives the index based on the given index + * at which to start searching + */ + public void testIndexOf2() { + CopyOnWriteArrayList full = populatedArray(3); + assertEquals(1, full.indexOf(one, 0)); + assertEquals(-1, full.indexOf(one, 2)); + } + + /** + * isEmpty returns true when empty, else false + */ + public void testIsEmpty() { + CopyOnWriteArrayList empty = new CopyOnWriteArrayList(); + CopyOnWriteArrayList full = populatedArray(SIZE); + assertTrue(empty.isEmpty()); + assertFalse(full.isEmpty()); + } + + /** + * iterator() returns an iterator containing the elements of the + * list in insertion order + */ + public void testIterator() { + Collection empty = new CopyOnWriteArrayList(); + assertFalse(empty.iterator().hasNext()); + try { + empty.iterator().next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedArray(elements); + + Iterator it = full.iterator(); + for (int j = 0; j < SIZE; j++) { + assertTrue(it.hasNext()); + assertEquals(elements[j], it.next()); + } + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + Collection c = new CopyOnWriteArrayList(); + assertIteratorExhausted(c.iterator()); + } + + /** + * iterator.remove throws UnsupportedOperationException + */ + public void testIteratorRemove() { + CopyOnWriteArrayList full = populatedArray(SIZE); + Iterator it = full.iterator(); + it.next(); + try { + it.remove(); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } + + /** + * toString contains toString of elements + */ + public void testToString() { + assertEquals("[]", new CopyOnWriteArrayList().toString()); + CopyOnWriteArrayList full = populatedArray(3); + String s = full.toString(); + for (int i = 0; i < 3; ++i) + assertTrue(s.contains(String.valueOf(i))); + assertEquals(new ArrayList(full).toString(), + full.toString()); + } + + /** + * lastIndexOf returns the index for the given object + */ + public void testLastIndexOf1() { + CopyOnWriteArrayList full = populatedArray(3); + full.add(one); + full.add(three); + assertEquals(3, full.lastIndexOf(one)); + assertEquals(-1, full.lastIndexOf(six)); + } + + /** + * lastIndexOf returns the index from the given starting point + */ + public void testLastIndexOf2() { + CopyOnWriteArrayList full = populatedArray(3); + full.add(one); + full.add(three); + assertEquals(3, full.lastIndexOf(one, 4)); + assertEquals(-1, full.lastIndexOf(three, 3)); + } + + /** + * listIterator traverses all elements + */ + public void testListIterator1() { + CopyOnWriteArrayList full = populatedArray(SIZE); + ListIterator i = full.listIterator(); + int j; + for (j = 0; i.hasNext(); j++) + assertEquals(j, i.next()); + assertEquals(SIZE, j); + } + + /** + * listIterator only returns those elements after the given index + */ + public void testListIterator2() { + CopyOnWriteArrayList full = populatedArray(3); + ListIterator i = full.listIterator(1); + int j; + for (j = 0; i.hasNext(); j++) + assertEquals(j + 1, i.next()); + assertEquals(2, j); + } + + /** + * remove(int) removes and returns the object at the given index + */ + public void testRemove_int() { + int SIZE = 3; + for (int i = 0; i < SIZE; i++) { + CopyOnWriteArrayList full = populatedArray(SIZE); + assertEquals(i, full.remove(i)); + assertEquals(SIZE - 1, full.size()); + assertFalse(full.contains(new Integer(i))); + } + } + + /** + * remove(Object) removes the object if found and returns true + */ + public void testRemove_Object() { + int SIZE = 3; + for (int i = 0; i < SIZE; i++) { + CopyOnWriteArrayList full = populatedArray(SIZE); + assertFalse(full.remove(new Integer(-42))); + assertTrue(full.remove(new Integer(i))); + assertEquals(SIZE - 1, full.size()); + assertFalse(full.contains(new Integer(i))); + } + CopyOnWriteArrayList x = new CopyOnWriteArrayList(Arrays.asList(4, 5, 6)); + assertTrue(x.remove(new Integer(6))); + assertEquals(x, Arrays.asList(4, 5)); + assertTrue(x.remove(new Integer(4))); + assertEquals(x, Arrays.asList(5)); + assertTrue(x.remove(new Integer(5))); + assertEquals(x, Arrays.asList()); + assertFalse(x.remove(new Integer(5))); + } + + /** + * removeAll removes all elements from the given collection + */ + public void testRemoveAll() { + CopyOnWriteArrayList full = populatedArray(3); + assertTrue(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + assertFalse(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + } + + /** + * set changes the element at the given index + */ + public void testSet() { + CopyOnWriteArrayList full = populatedArray(3); + assertEquals(2, full.set(2, four)); + assertEquals(4, full.get(2)); + } + + /** + * size returns the number of elements + */ + public void testSize() { + CopyOnWriteArrayList empty = new CopyOnWriteArrayList(); + CopyOnWriteArrayList full = populatedArray(SIZE); + assertEquals(SIZE, full.size()); + assertEquals(0, empty.size()); + } + + /** + * toArray() returns an Object array containing all elements from + * the list in insertion order + */ + public void testToArray() { + Object[] a = new CopyOnWriteArrayList().toArray(); + assertTrue(Arrays.equals(new Object[0], a)); + assertSame(Object[].class, a.getClass()); + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedArray(elements); + + assertTrue(Arrays.equals(elements, full.toArray())); + assertSame(Object[].class, full.toArray().getClass()); + } + + /** + * toArray(Integer array) returns an Integer array containing all + * elements from the list in insertion order + */ + public void testToArray2() { + Collection empty = new CopyOnWriteArrayList(); + Integer[] a; + + a = new Integer[0]; + assertSame(a, empty.toArray(a)); + + a = new Integer[SIZE / 2]; + Arrays.fill(a, 42); + assertSame(a, empty.toArray(a)); + assertNull(a[0]); + for (int i = 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedArray(elements); + + Arrays.fill(a, 42); + assertTrue(Arrays.equals(elements, full.toArray(a))); + for (int i = 0; i < a.length; i++) + assertEquals(42, (int) a[i]); + assertSame(Integer[].class, full.toArray(a).getClass()); + + a = new Integer[SIZE]; + Arrays.fill(a, 42); + assertSame(a, full.toArray(a)); + assertTrue(Arrays.equals(elements, a)); + + a = new Integer[2 * SIZE]; + Arrays.fill(a, 42); + assertSame(a, full.toArray(a)); + assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE))); + assertNull(a[SIZE]); + for (int i = SIZE + 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + } + + /** + * sublists contains elements at indexes offset from their base + */ + public void testSubList() { + CopyOnWriteArrayList a = populatedArray(10); + assertTrue(a.subList(1,1).isEmpty()); + for (int j = 0; j < 9; ++j) { + for (int i = j ; i < 10; ++i) { + List b = a.subList(j,i); + for (int k = j; k < i; ++k) { + assertEquals(new Integer(k), b.get(k-j)); + } + } + } + + List s = a.subList(2, 5); + assertEquals(3, s.size()); + s.set(2, m1); + assertEquals(a.get(4), m1); + s.clear(); + assertEquals(7, a.size()); + } + + // Exception tests + + /** + * toArray throws an ArrayStoreException when the given array + * can not store the objects inside the list + */ + public void testToArray_ArrayStoreException() { + CopyOnWriteArrayList c = new CopyOnWriteArrayList(); + c.add("zfasdfsdf"); + c.add("asdadasd"); + try { + c.toArray(new Long[5]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * get throws an IndexOutOfBoundsException on a negative index + */ + public void testGet1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.get(-1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * get throws an IndexOutOfBoundsException on a too high index + */ + public void testGet2_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.get(list.size()); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * set throws an IndexOutOfBoundsException on a negative index + */ + public void testSet1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.set(-1, "qwerty"); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * set throws an IndexOutOfBoundsException on a too high index + */ + public void testSet2() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.set(list.size(), "qwerty"); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * add throws an IndexOutOfBoundsException on a negative index + */ + public void testAdd1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.add(-1, "qwerty"); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * add throws an IndexOutOfBoundsException on a too high index + */ + public void testAdd2_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.add(list.size() + 1, "qwerty"); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * remove throws an IndexOutOfBoundsException on a negative index + */ + public void testRemove1_IndexOutOfBounds() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.remove(-1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * remove throws an IndexOutOfBoundsException on a too high index + */ + public void testRemove2_IndexOutOfBounds() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.remove(list.size()); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * addAll throws an IndexOutOfBoundsException on a negative index + */ + public void testAddAll1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.addAll(-1, new LinkedList()); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * addAll throws an IndexOutOfBoundsException on a too high index + */ + public void testAddAll2_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.addAll(list.size() + 1, new LinkedList()); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * listIterator throws an IndexOutOfBoundsException on a negative index + */ + public void testListIterator1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.listIterator(-1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * listIterator throws an IndexOutOfBoundsException on a too high index + */ + public void testListIterator2_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.listIterator(list.size() + 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * subList throws an IndexOutOfBoundsException on a negative index + */ + public void testSubList1_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.subList(-1, list.size()); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * subList throws an IndexOutOfBoundsException on a too high index + */ + public void testSubList2_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.subList(0, list.size() + 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * subList throws IndexOutOfBoundsException when the second index + * is lower then the first + */ + public void testSubList3_IndexOutOfBoundsException() { + CopyOnWriteArrayList c = populatedArray(5); + List[] lists = { c, c.subList(1, c.size() - 1) }; + for (List list : lists) { + try { + list.subList(list.size() - 1, 1); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + } + + /** + * a deserialized serialized list is equal + */ + public void testSerialization() throws Exception { + List x = populatedArray(SIZE); + List y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(0), y.remove(0)); + } + assertTrue(y.isEmpty()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java b/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java new file mode 100644 index 00000000000..bd9a7e3da43 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java @@ -0,0 +1,432 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CopyOnWriteArraySetTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(CopyOnWriteArraySetTest.class); + } + + static CopyOnWriteArraySet populatedSet(int n) { + CopyOnWriteArraySet a = new CopyOnWriteArraySet(); + assertTrue(a.isEmpty()); + for (int i = 0; i < n; i++) + a.add(i); + assertEquals(n == 0, a.isEmpty()); + assertEquals(n, a.size()); + return a; + } + + static CopyOnWriteArraySet populatedSet(Integer[] elements) { + CopyOnWriteArraySet a = new CopyOnWriteArraySet(); + assertTrue(a.isEmpty()); + for (int i = 0; i < elements.length; i++) + a.add(elements[i]); + assertFalse(a.isEmpty()); + assertEquals(elements.length, a.size()); + return a; + } + + /** + * Default-constructed set is empty + */ + public void testConstructor() { + CopyOnWriteArraySet a = new CopyOnWriteArraySet(); + assertTrue(a.isEmpty()); + } + + /** + * Collection-constructed set holds all of its elements + */ + public void testConstructor3() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + CopyOnWriteArraySet a = new CopyOnWriteArraySet(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertTrue(a.contains(ints[i])); + } + + /** + * addAll adds each non-duplicate element from the given collection + */ + public void testAddAll() { + Set full = populatedSet(3); + assertTrue(full.addAll(Arrays.asList(three, four, five))); + assertEquals(6, full.size()); + assertFalse(full.addAll(Arrays.asList(three, four, five))); + assertEquals(6, full.size()); + } + + /** + * addAll adds each non-duplicate element from the given collection + */ + public void testAddAll2() { + Set full = populatedSet(3); + // "one" is duplicate and will not be added + assertTrue(full.addAll(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + assertFalse(full.addAll(Arrays.asList(three, four, one))); + assertEquals(5, full.size()); + } + + /** + * add will not add the element if it already exists in the set + */ + public void testAdd2() { + Set full = populatedSet(3); + full.add(one); + assertEquals(3, full.size()); + } + + /** + * add adds the element when it does not exist in the set + */ + public void testAdd3() { + Set full = populatedSet(3); + full.add(three); + assertTrue(full.contains(three)); + } + + /** + * clear removes all elements from the set + */ + public void testClear() { + Collection full = populatedSet(3); + full.clear(); + assertEquals(0, full.size()); + assertTrue(full.isEmpty()); + } + + /** + * contains returns true for added elements + */ + public void testContains() { + Collection full = populatedSet(3); + assertTrue(full.contains(one)); + assertFalse(full.contains(five)); + } + + /** + * Sets with equal elements are equal + */ + public void testEquals() { + CopyOnWriteArraySet a = populatedSet(3); + CopyOnWriteArraySet b = populatedSet(3); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertTrue(a.containsAll(b)); + assertTrue(b.containsAll(a)); + assertEquals(a.hashCode(), b.hashCode()); + assertEquals(a.size(), b.size()); + + a.add(m1); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + assertTrue(a.containsAll(b)); + assertFalse(b.containsAll(a)); + b.add(m1); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertTrue(a.containsAll(b)); + assertTrue(b.containsAll(a)); + assertEquals(a.hashCode(), b.hashCode()); + + Object x = a.iterator().next(); + a.remove(x); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + assertFalse(a.containsAll(b)); + assertTrue(b.containsAll(a)); + a.add(x); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + assertTrue(a.containsAll(b)); + assertTrue(b.containsAll(a)); + assertEquals(a.hashCode(), b.hashCode()); + assertEquals(a.size(), b.size()); + + CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList()); + CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList()); + assertTrue(empty1.equals(empty1)); + assertTrue(empty1.equals(empty2)); + + assertFalse(empty1.equals(a)); + assertFalse(a.equals(empty1)); + + assertFalse(a.equals(null)); + } + + /** + * containsAll returns true for collections with subset of elements + */ + public void testContainsAll() { + Collection full = populatedSet(3); + assertTrue(full.containsAll(full)); + assertTrue(full.containsAll(Arrays.asList())); + assertTrue(full.containsAll(Arrays.asList(one))); + assertTrue(full.containsAll(Arrays.asList(one, two))); + assertFalse(full.containsAll(Arrays.asList(one, two, six))); + assertFalse(full.containsAll(Arrays.asList(six))); + + CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList()); + CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList()); + assertTrue(empty1.containsAll(empty2)); + assertTrue(empty1.containsAll(empty1)); + assertFalse(empty1.containsAll(full)); + assertTrue(full.containsAll(empty1)); + + try { + full.containsAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * isEmpty is true when empty, else false + */ + public void testIsEmpty() { + assertTrue(populatedSet(0).isEmpty()); + assertFalse(populatedSet(3).isEmpty()); + } + + /** + * iterator() returns an iterator containing the elements of the + * set in insertion order + */ + public void testIterator() { + Collection empty = new CopyOnWriteArraySet(); + assertFalse(empty.iterator().hasNext()); + try { + empty.iterator().next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + Iterator it = full.iterator(); + for (int j = 0; j < SIZE; j++) { + assertTrue(it.hasNext()); + assertEquals(elements[j], it.next()); + } + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new CopyOnWriteArraySet().iterator()); + } + + /** + * iterator remove is unsupported + */ + public void testIteratorRemove() { + Collection full = populatedSet(3); + Iterator it = full.iterator(); + it.next(); + try { + it.remove(); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } + + /** + * toString holds toString of elements + */ + public void testToString() { + assertEquals("[]", new CopyOnWriteArraySet().toString()); + Collection full = populatedSet(3); + String s = full.toString(); + for (int i = 0; i < 3; ++i) + assertTrue(s.contains(String.valueOf(i))); + assertEquals(new ArrayList(full).toString(), + full.toString()); + } + + /** + * removeAll removes all elements from the given collection + */ + public void testRemoveAll() { + Set full = populatedSet(3); + assertTrue(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + assertFalse(full.removeAll(Arrays.asList(one, two))); + assertEquals(1, full.size()); + } + + /** + * remove removes an element + */ + public void testRemove() { + Collection full = populatedSet(3); + full.remove(one); + assertFalse(full.contains(one)); + assertEquals(2, full.size()); + } + + /** + * size returns the number of elements + */ + public void testSize() { + Collection empty = new CopyOnWriteArraySet(); + Collection full = populatedSet(3); + assertEquals(3, full.size()); + assertEquals(0, empty.size()); + } + + /** + * toArray() returns an Object array containing all elements from + * the set in insertion order + */ + public void testToArray() { + Object[] a = new CopyOnWriteArraySet().toArray(); + assertTrue(Arrays.equals(new Object[0], a)); + assertSame(Object[].class, a.getClass()); + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + assertTrue(Arrays.equals(elements, full.toArray())); + assertSame(Object[].class, full.toArray().getClass()); + } + + /** + * toArray(Integer array) returns an Integer array containing all + * elements from the set in insertion order + */ + public void testToArray2() { + Collection empty = new CopyOnWriteArraySet(); + Integer[] a; + + a = new Integer[0]; + assertSame(a, empty.toArray(a)); + + a = new Integer[SIZE / 2]; + Arrays.fill(a, 42); + assertSame(a, empty.toArray(a)); + assertNull(a[0]); + for (int i = 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + + Integer[] elements = new Integer[SIZE]; + for (int i = 0; i < SIZE; i++) + elements[i] = i; + Collections.shuffle(Arrays.asList(elements)); + Collection full = populatedSet(elements); + + Arrays.fill(a, 42); + assertTrue(Arrays.equals(elements, full.toArray(a))); + for (int i = 0; i < a.length; i++) + assertEquals(42, (int) a[i]); + assertSame(Integer[].class, full.toArray(a).getClass()); + + a = new Integer[SIZE]; + Arrays.fill(a, 42); + assertSame(a, full.toArray(a)); + assertTrue(Arrays.equals(elements, a)); + + a = new Integer[2 * SIZE]; + Arrays.fill(a, 42); + assertSame(a, full.toArray(a)); + assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE))); + assertNull(a[SIZE]); + for (int i = SIZE + 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + } + + /** + * toArray throws an ArrayStoreException when the given array can + * not store the objects inside the set + */ + public void testToArray_ArrayStoreException() { + CopyOnWriteArraySet c = new CopyOnWriteArraySet(); + c.add("zfasdfsdf"); + c.add("asdadasd"); + try { + c.toArray(new Long[5]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * A deserialized serialized set is equal + */ + public void testSerialization() throws Exception { + Set x = populatedSet(SIZE); + Set y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * addAll is idempotent + */ + public void testAddAll_idempotent() throws Exception { + Set x = populatedSet(SIZE); + Set y = new CopyOnWriteArraySet(x); + y.addAll(x); + assertEquals(x, y); + assertEquals(y, x); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java b/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java new file mode 100644 index 00000000000..e286d00cdf9 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java @@ -0,0 +1,223 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CountDownLatch; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CountDownLatchTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(CountDownLatchTest.class); + } + + /** + * negative constructor argument throws IAE + */ + public void testConstructor() { + try { + new CountDownLatch(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getCount returns initial count and decreases after countDown + */ + public void testGetCount() { + final CountDownLatch l = new CountDownLatch(2); + assertEquals(2, l.getCount()); + l.countDown(); + assertEquals(1, l.getCount()); + } + + /** + * countDown decrements count when positive and has no effect when zero + */ + public void testCountDown() { + final CountDownLatch l = new CountDownLatch(1); + assertEquals(1, l.getCount()); + l.countDown(); + assertEquals(0, l.getCount()); + l.countDown(); + assertEquals(0, l.getCount()); + } + + /** + * await returns after countDown to zero, but not before + */ + public void testAwait() { + final CountDownLatch l = new CountDownLatch(2); + final CountDownLatch pleaseCountDown = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertEquals(2, l.getCount()); + pleaseCountDown.countDown(); + l.await(); + assertEquals(0, l.getCount()); + }}); + + await(pleaseCountDown); + assertEquals(2, l.getCount()); + l.countDown(); + assertEquals(1, l.getCount()); + assertThreadStaysAlive(t); + l.countDown(); + assertEquals(0, l.getCount()); + awaitTermination(t); + } + + /** + * timed await returns after countDown to zero + */ + public void testTimedAwait() { + final CountDownLatch l = new CountDownLatch(2); + final CountDownLatch pleaseCountDown = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertEquals(2, l.getCount()); + pleaseCountDown.countDown(); + assertTrue(l.await(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, l.getCount()); + }}); + + await(pleaseCountDown); + assertEquals(2, l.getCount()); + l.countDown(); + assertEquals(1, l.getCount()); + assertThreadStaysAlive(t); + l.countDown(); + assertEquals(0, l.getCount()); + awaitTermination(t); + } + + /** + * await throws IE if interrupted before counted down + */ + public void testAwait_Interruptible() { + final CountDownLatch l = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + Thread.currentThread().interrupt(); + try { + l.await(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + l.await(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertEquals(1, l.getCount()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed await throws IE if interrupted before counted down + */ + public void testTimedAwait_Interruptible() { + final CountDownLatch l = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + Thread.currentThread().interrupt(); + try { + l.await(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + l.await(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertEquals(1, l.getCount()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed await times out if not counted down before timeout + */ + public void testAwaitTimeout() throws InterruptedException { + final CountDownLatch l = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertEquals(1, l.getCount()); + assertFalse(l.await(timeoutMillis(), MILLISECONDS)); + assertEquals(1, l.getCount()); + }}); + + awaitTermination(t); + assertEquals(1, l.getCount()); + } + + /** + * toString indicates current count + */ + public void testToString() { + CountDownLatch s = new CountDownLatch(2); + assertTrue(s.toString().contains("Count = 2")); + s.countDown(); + assertTrue(s.toString().contains("Count = 1")); + s.countDown(); + assertTrue(s.toString().contains("Count = 0")); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java b/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java new file mode 100644 index 00000000000..8be66850920 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java @@ -0,0 +1,1872 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.HashSet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountedCompleter; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CountedCompleterTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(CountedCompleterTest.class); + } + + // Runs with "mainPool" use > 1 thread. singletonPool tests use 1 + static final int mainPoolSize = + Math.max(2, Runtime.getRuntime().availableProcessors()); + + private static ForkJoinPool mainPool() { + return new ForkJoinPool(mainPoolSize); + } + + private static ForkJoinPool singletonPool() { + return new ForkJoinPool(1); + } + + private static ForkJoinPool asyncSingletonPool() { + return new ForkJoinPool(1, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + + private void testInvokeOnPool(ForkJoinPool pool, ForkJoinTask a) { + try (PoolCleaner cleaner = cleaner(pool)) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + assertNull(pool.invoke(a)); + + assertTrue(a.isDone()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + } + } + + void checkNotDone(CountedCompleter a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(CountedCompleter a) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + assertNull(a.join()); + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertNull(a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertNull(a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCancelled(CountedCompleter a) { + assertTrue(a.isDone()); + assertTrue(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertTrue(a.getException() instanceof CancellationException); + assertNull(a.getRawResult()); + assertTrue(a.cancel(false)); + assertTrue(a.cancel(true)); + + try { + Thread.currentThread().interrupt(); + a.join(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + Thread.interrupted(); + + { + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + } + + try { + a.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(CountedCompleter a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + + try { + Thread.currentThread().interrupt(); + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(t.getClass(), expected.getClass()); + } + Thread.interrupted(); + + { + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.invoke(); + shouldThrow(); + } catch (Throwable success) { + assertSame(t, success); + } + } + + public static final class FJException extends RuntimeException { + FJException() { super(); } + } + + abstract class CheckedCC extends CountedCompleter { + final AtomicInteger computeN = new AtomicInteger(0); + final AtomicInteger onCompletionN = new AtomicInteger(0); + final AtomicInteger onExceptionalCompletionN = new AtomicInteger(0); + final AtomicInteger setRawResultN = new AtomicInteger(0); + final AtomicReference rawResult = new AtomicReference(null); + int computeN() { return computeN.get(); } + int onCompletionN() { return onCompletionN.get(); } + int onExceptionalCompletionN() { return onExceptionalCompletionN.get(); } + int setRawResultN() { return setRawResultN.get(); } + + CheckedCC() { super(); } + CheckedCC(CountedCompleter p) { super(p); } + CheckedCC(CountedCompleter p, int n) { super(p, n); } + abstract void realCompute(); + public final void compute() { + computeN.incrementAndGet(); + realCompute(); + } + public void onCompletion(CountedCompleter caller) { + onCompletionN.incrementAndGet(); + super.onCompletion(caller); + } + public boolean onExceptionalCompletion(Throwable ex, + CountedCompleter caller) { + onExceptionalCompletionN.incrementAndGet(); + assertNotNull(ex); + assertTrue(isCompletedAbnormally()); + assertTrue(super.onExceptionalCompletion(ex, caller)); + return true; + } + protected void setRawResult(Object t) { + setRawResultN.incrementAndGet(); + rawResult.set(t); + super.setRawResult(t); + } + void checkIncomplete() { + assertEquals(0, computeN()); + assertEquals(0, onCompletionN()); + assertEquals(0, onExceptionalCompletionN()); + assertEquals(0, setRawResultN()); + checkNotDone(this); + } + void checkCompletes(Object rawResult) { + checkIncomplete(); + int pendingCount = getPendingCount(); + complete(rawResult); + assertEquals(pendingCount, getPendingCount()); + assertEquals(0, computeN()); + assertEquals(1, onCompletionN()); + assertEquals(0, onExceptionalCompletionN()); + assertEquals(1, setRawResultN()); + assertSame(rawResult, this.rawResult.get()); + checkCompletedNormally(this); + } + void checkCompletesExceptionally(Throwable ex) { + checkIncomplete(); + completeExceptionally(ex); + checkCompletedExceptionally(ex); + } + void checkCompletedExceptionally(Throwable ex) { + assertEquals(0, computeN()); + assertEquals(0, onCompletionN()); + assertEquals(1, onExceptionalCompletionN()); + assertEquals(0, setRawResultN()); + assertNull(this.rawResult.get()); + checkCompletedAbnormally(this, ex); + } + } + + final class NoopCC extends CheckedCC { + NoopCC() { super(); } + NoopCC(CountedCompleter p) { super(p); } + NoopCC(CountedCompleter p, int initialPendingCount) { + super(p, initialPendingCount); + } + protected void realCompute() {} + } + + /** + * A newly constructed CountedCompleter is not completed; + * complete() causes completion. pendingCount is ignored. + */ + public void testComplete() { + for (Object x : new Object[] { Boolean.TRUE, null }) { + for (int pendingCount : new int[] { 0, 42 }) { + testComplete(new NoopCC(), x, pendingCount); + testComplete(new NoopCC(new NoopCC()), x, pendingCount); + } + } + } + void testComplete(NoopCC cc, Object x, int pendingCount) { + cc.setPendingCount(pendingCount); + cc.checkCompletes(x); + assertEquals(pendingCount, cc.getPendingCount()); + } + + /** + * completeExceptionally completes exceptionally + */ + public void testCompleteExceptionally() { + new NoopCC() + .checkCompletesExceptionally(new FJException()); + new NoopCC(new NoopCC()) + .checkCompletesExceptionally(new FJException()); + } + + /** + * completeExceptionally(null) surprisingly has the same effect as + * completeExceptionally(new RuntimeException()) + */ + public void testCompleteExceptionally_null() { + NoopCC a = new NoopCC(); + a.completeExceptionally(null); + try { + a.invoke(); + shouldThrow(); + } catch (RuntimeException success) { + assertSame(success.getClass(), RuntimeException.class); + assertNull(success.getCause()); + a.checkCompletedExceptionally(success); + } + } + + /** + * setPendingCount sets the reported pending count + */ + public void testSetPendingCount() { + NoopCC a = new NoopCC(); + assertEquals(0, a.getPendingCount()); + int[] vals = { + -1, 0, 1, + Integer.MIN_VALUE, + Integer.MAX_VALUE, + }; + for (int val : vals) { + a.setPendingCount(val); + assertEquals(val, a.getPendingCount()); + } + } + + /** + * addToPendingCount adds to the reported pending count + */ + public void testAddToPendingCount() { + NoopCC a = new NoopCC(); + assertEquals(0, a.getPendingCount()); + a.addToPendingCount(1); + assertEquals(1, a.getPendingCount()); + a.addToPendingCount(27); + assertEquals(28, a.getPendingCount()); + a.addToPendingCount(-28); + assertEquals(0, a.getPendingCount()); + } + + /** + * decrementPendingCountUnlessZero decrements reported pending + * count unless zero + */ + public void testDecrementPendingCountUnlessZero() { + NoopCC a = new NoopCC(null, 2); + assertEquals(2, a.getPendingCount()); + assertEquals(2, a.decrementPendingCountUnlessZero()); + assertEquals(1, a.getPendingCount()); + assertEquals(1, a.decrementPendingCountUnlessZero()); + assertEquals(0, a.getPendingCount()); + assertEquals(0, a.decrementPendingCountUnlessZero()); + assertEquals(0, a.getPendingCount()); + a.setPendingCount(-1); + assertEquals(-1, a.decrementPendingCountUnlessZero()); + assertEquals(-2, a.getPendingCount()); + } + + /** + * compareAndSetPendingCount compares and sets the reported + * pending count + */ + public void testCompareAndSetPendingCount() { + NoopCC a = new NoopCC(); + assertEquals(0, a.getPendingCount()); + assertTrue(a.compareAndSetPendingCount(0, 1)); + assertEquals(1, a.getPendingCount()); + assertTrue(a.compareAndSetPendingCount(1, 2)); + assertEquals(2, a.getPendingCount()); + assertFalse(a.compareAndSetPendingCount(1, 3)); + assertEquals(2, a.getPendingCount()); + } + + /** + * getCompleter returns parent or null if at root + */ + public void testGetCompleter() { + NoopCC a = new NoopCC(); + assertNull(a.getCompleter()); + CountedCompleter b = new NoopCC(a); + assertSame(a, b.getCompleter()); + CountedCompleter c = new NoopCC(b); + assertSame(b, c.getCompleter()); + } + + /** + * getRoot returns self if no parent, else parent's root + */ + public void testGetRoot() { + NoopCC a = new NoopCC(); + NoopCC b = new NoopCC(a); + NoopCC c = new NoopCC(b); + assertSame(a, a.getRoot()); + assertSame(a, b.getRoot()); + assertSame(a, c.getRoot()); + } + + /** + * tryComplete decrements pending count unless zero, in which case + * causes completion + */ + public void testTryComplete() { + NoopCC a = new NoopCC(); + assertEquals(0, a.getPendingCount()); + int n = 3; + a.setPendingCount(n); + for (; n > 0; n--) { + assertEquals(n, a.getPendingCount()); + a.tryComplete(); + a.checkIncomplete(); + assertEquals(n - 1, a.getPendingCount()); + } + a.tryComplete(); + assertEquals(0, a.computeN()); + assertEquals(1, a.onCompletionN()); + assertEquals(0, a.onExceptionalCompletionN()); + assertEquals(0, a.setRawResultN()); + checkCompletedNormally(a); + } + + /** + * propagateCompletion decrements pending count unless zero, in + * which case causes completion, without invoking onCompletion + */ + public void testPropagateCompletion() { + NoopCC a = new NoopCC(); + assertEquals(0, a.getPendingCount()); + int n = 3; + a.setPendingCount(n); + for (; n > 0; n--) { + assertEquals(n, a.getPendingCount()); + a.propagateCompletion(); + a.checkIncomplete(); + assertEquals(n - 1, a.getPendingCount()); + } + a.propagateCompletion(); + assertEquals(0, a.computeN()); + assertEquals(0, a.onCompletionN()); + assertEquals(0, a.onExceptionalCompletionN()); + assertEquals(0, a.setRawResultN()); + checkCompletedNormally(a); + } + + /** + * firstComplete returns this if pending count is zero else null + */ + public void testFirstComplete() { + NoopCC a = new NoopCC(); + a.setPendingCount(1); + assertNull(a.firstComplete()); + a.checkIncomplete(); + assertSame(a, a.firstComplete()); + a.checkIncomplete(); + } + + /** + * firstComplete.nextComplete returns parent if pending count is + * zero else null + */ + public void testNextComplete() { + NoopCC a = new NoopCC(); + NoopCC b = new NoopCC(a); + a.setPendingCount(1); + b.setPendingCount(1); + assertNull(b.firstComplete()); + assertSame(b, b.firstComplete()); + assertNull(b.nextComplete()); + a.checkIncomplete(); + b.checkIncomplete(); + assertSame(a, b.nextComplete()); + assertSame(a, b.nextComplete()); + a.checkIncomplete(); + b.checkIncomplete(); + assertNull(a.nextComplete()); + b.checkIncomplete(); + checkCompletedNormally(a); + } + + /** + * quietlyCompleteRoot completes root task and only root task + */ + public void testQuietlyCompleteRoot() { + NoopCC a = new NoopCC(); + NoopCC b = new NoopCC(a); + NoopCC c = new NoopCC(b); + a.setPendingCount(1); + b.setPendingCount(1); + c.setPendingCount(1); + c.quietlyCompleteRoot(); + assertTrue(a.isDone()); + assertFalse(b.isDone()); + assertFalse(c.isDone()); + } + + // Invocation tests use some interdependent task classes + // to better test propagation etc + + /** + * Version of Fibonacci with different classes for left vs right forks + */ + abstract class CCF extends CheckedCC { + int number; + int rnumber; + + public CCF(CountedCompleter parent, int n) { + super(parent, 1); + this.number = n; + } + + protected final void realCompute() { + CCF f = this; + int n = number; + while (n >= 2) { + new RCCF(f, n - 2).fork(); + f = new LCCF(f, --n); + } + f.complete(null); + } + } + + final class LCCF extends CCF { + public LCCF(int n) { this(null, n); } + public LCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + super.onCompletion(caller); + CCF p = (CCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.number = n; + else + number = n; + } + } + final class RCCF extends CCF { + public RCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + super.onCompletion(caller); + CCF p = (CCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.rnumber = n; + else + number = n; + } + } + + // Version of CCF with forced failure in left completions + abstract class FailingCCF extends CheckedCC { + int number; + int rnumber; + + public FailingCCF(CountedCompleter parent, int n) { + super(parent, 1); + this.number = n; + } + + protected final void realCompute() { + FailingCCF f = this; + int n = number; + while (n >= 2) { + new RFCCF(f, n - 2).fork(); + f = new LFCCF(f, --n); + } + f.complete(null); + } + } + + final class LFCCF extends FailingCCF { + public LFCCF(int n) { this(null, n); } + public LFCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + super.onCompletion(caller); + FailingCCF p = (FailingCCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.number = n; + else + number = n; + } + } + final class RFCCF extends FailingCCF { + public RFCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + super.onCompletion(caller); + completeExceptionally(new FJException()); + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvoke() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertNull(f.invoke()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + f.quietlyInvoke(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPE() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesce() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + helpQuiesce(); + assertEquals(21, f.number); + assertEquals(0, getQueuedTaskCount()); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvoke() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGet() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGet() throws Exception { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoin() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * getPool of executing task returns its pool + */ + public void testGetPool() { + final ForkJoinPool mainPool = mainPool(); + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertSame(mainPool, getPool()); + }}; + testInvokeOnPool(mainPool, a); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertNull(getPool()); + }}; + assertNull(a.invoke()); + } + + /** + * inForkJoinPool of executing task returns true + */ + public void testInForkJoinPool() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertTrue(inForkJoinPool()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * setRawResult(null) succeeds + */ + public void testSetRawResult() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + setRawResult(null); + assertNull(getRawResult()); + }}; + assertNull(a.invoke()); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally2() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF n = new LCCF(8); + CCF f = new LCCF(n, 8); + FJException ex = new FJException(); + f.completeExceptionally(ex); + f.checkCompletedExceptionally(ex); + n.checkCompletedExceptionally(ex); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + invokeAll(f, g); + assertEquals(21, f.number); + assertEquals(34, g.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.number); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + invokeAll(f, g, h); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPE() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + FailingCCF g = new LFCCF(9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF g = new LFCCF(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + FailingCCF g = new LFCCF(9); + CCF h = new LCCF(7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * tryUnfork returns true for most recent unexecuted task, + * and suppresses execution + */ + public void testTryUnfork() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertTrue(f.tryUnfork()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * getSurplusQueuedTaskCount returns > 0 when + * there are more tasks than threads + */ + public void testGetSurplusQueuedTaskCount() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF h = new LCCF(7); + assertSame(h, h.fork()); + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertTrue(getSurplusQueuedTaskCount() > 0); + helpQuiesce(); + assertEquals(0, getSurplusQueuedTaskCount()); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns most recent unexecuted task. + */ + public void testPeekNextLocalTask() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(f, peekNextLocalTask()); + assertNull(f.join()); + checkCompletedNormally(f); + helpQuiesce(); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollNextLocalTask returns most recent unexecuted task without + * executing it + */ + public void testPollNextLocalTask() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(f, pollNextLocalTask()); + helpQuiesce(); + checkNotDone(f); + assertEquals(34, g.number); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it + */ + public void testPollTask() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(f, pollTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns least recent unexecuted task in async mode + */ + public void testPeekNextLocalTaskAsync() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(g, peekNextLocalTask()); + assertNull(f.join()); + helpQuiesce(); + checkCompletedNormally(f); + assertEquals(34, g.number); + checkCompletedNormally(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollNextLocalTask returns least recent unexecuted task without + * executing it, in async mode + */ + public void testPollNextLocalTaskAsync() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(g, pollNextLocalTask()); + helpQuiesce(); + assertEquals(21, f.number); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it, in + * async mode + */ + public void testPollTaskAsync() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF g = new LCCF(9); + assertSame(g, g.fork()); + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertSame(g, pollTask()); + helpQuiesce(); + assertEquals(21, f.number); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + // versions for singleton pools + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvokeSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertNull(f.invoke()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvokeSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + f.quietlyInvoke(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGetSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGetSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPESingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesceSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertSame(f, f.fork()); + helpQuiesce(); + assertEquals(0, getQueuedTaskCount()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvokeSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvokeSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGetSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGetSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvokeSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGetSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGetSingleton() throws Exception { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoinSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionallySingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF n = new LCCF(8); + CCF f = new LCCF(n, 8); + FJException ex = new FJException(); + f.completeExceptionally(ex); + f.checkCompletedExceptionally(ex); + n.checkCompletedExceptionally(ex); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + invokeAll(f, g); + assertEquals(21, f.number); + assertEquals(34, g.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.number); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + invokeAll(f, g, h); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollectionSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPESingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + CCF g = new LCCF(9); + CCF h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + FailingCCF g = new LFCCF(9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF g = new LFCCF(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3Singleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(8); + FailingCCF g = new LFCCF(9); + CCF h = new LCCF(7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollectionSingleton() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(8); + CCF g = new LCCF(9); + CCF h = new LCCF(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java b/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java new file mode 100644 index 00000000000..bfae2ee323b --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java @@ -0,0 +1,491 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CyclicBarrierTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(CyclicBarrierTest.class); + } + + private volatile int countAction; + private class MyAction implements Runnable { + public void run() { ++countAction; } + } + + /** + * Spin-waits till the number of waiters == numberOfWaiters. + */ + void awaitNumberWaiting(CyclicBarrier barrier, int numberOfWaiters) { + long startTime = System.nanoTime(); + while (barrier.getNumberWaiting() != numberOfWaiters) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + + /** + * Creating with negative parties throws IAE + */ + public void testConstructor1() { + try { + new CyclicBarrier(-1, (Runnable)null); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Creating with negative parties and no action throws IAE + */ + public void testConstructor2() { + try { + new CyclicBarrier(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getParties returns the number of parties given in constructor + */ + public void testGetParties() { + CyclicBarrier b = new CyclicBarrier(2); + assertEquals(2, b.getParties()); + assertEquals(0, b.getNumberWaiting()); + } + + /** + * A 1-party barrier triggers after single await + */ + public void testSingleParty() throws Exception { + CyclicBarrier b = new CyclicBarrier(1); + assertEquals(1, b.getParties()); + assertEquals(0, b.getNumberWaiting()); + b.await(); + b.await(); + assertEquals(0, b.getNumberWaiting()); + } + + /** + * The supplied barrier action is run at barrier + */ + public void testBarrierAction() throws Exception { + countAction = 0; + CyclicBarrier b = new CyclicBarrier(1, new MyAction()); + assertEquals(1, b.getParties()); + assertEquals(0, b.getNumberWaiting()); + b.await(); + b.await(); + assertEquals(0, b.getNumberWaiting()); + assertEquals(2, countAction); + } + + /** + * A 2-party/thread barrier triggers after both threads invoke await + */ + public void testTwoParties() throws Exception { + final CyclicBarrier b = new CyclicBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + b.await(); + b.await(); + b.await(); + b.await(); + }}); + + b.await(); + b.await(); + b.await(); + b.await(); + awaitTermination(t); + } + + /** + * An interruption in one party causes others waiting in await to + * throw BrokenBarrierException + */ + public void testAwait1_Interrupted_BrokenBarrier() { + final CyclicBarrier c = new CyclicBarrier(3); + final CountDownLatch pleaseInterrupt = new CountDownLatch(2); + Thread t1 = new ThreadShouldThrow(InterruptedException.class) { + public void realRun() throws Exception { + pleaseInterrupt.countDown(); + c.await(); + }}; + Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + pleaseInterrupt.countDown(); + c.await(); + }}; + + t1.start(); + t2.start(); + await(pleaseInterrupt); + t1.interrupt(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * An interruption in one party causes others waiting in timed await to + * throw BrokenBarrierException + */ + public void testAwait2_Interrupted_BrokenBarrier() throws Exception { + final CyclicBarrier c = new CyclicBarrier(3); + final CountDownLatch pleaseInterrupt = new CountDownLatch(2); + Thread t1 = new ThreadShouldThrow(InterruptedException.class) { + public void realRun() throws Exception { + pleaseInterrupt.countDown(); + c.await(LONG_DELAY_MS, MILLISECONDS); + }}; + Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + pleaseInterrupt.countDown(); + c.await(LONG_DELAY_MS, MILLISECONDS); + }}; + + t1.start(); + t2.start(); + await(pleaseInterrupt); + t1.interrupt(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A timeout in timed await throws TimeoutException + */ + public void testAwait3_TimeoutException() throws InterruptedException { + final CyclicBarrier c = new CyclicBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + long startTime = System.nanoTime(); + try { + c.await(timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) {} + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t); + } + + /** + * A timeout in one party causes others waiting in timed await to + * throw BrokenBarrierException + */ + public void testAwait4_Timeout_BrokenBarrier() throws InterruptedException { + final CyclicBarrier c = new CyclicBarrier(3); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + try { + c.await(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (BrokenBarrierException success) {} + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + awaitNumberWaiting(c, 1); + long startTime = System.nanoTime(); + try { + c.await(timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) {} + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A timeout in one party causes others waiting in await to + * throw BrokenBarrierException + */ + public void testAwait5_Timeout_BrokenBarrier() throws InterruptedException { + final CyclicBarrier c = new CyclicBarrier(3); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + try { + c.await(); + shouldThrow(); + } catch (BrokenBarrierException success) {} + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + awaitNumberWaiting(c, 1); + long startTime = System.nanoTime(); + try { + c.await(timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) {} + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A reset of an active barrier causes waiting threads to throw + * BrokenBarrierException + */ + public void testReset_BrokenBarrier() throws InterruptedException { + final CyclicBarrier c = new CyclicBarrier(3); + final CountDownLatch pleaseReset = new CountDownLatch(2); + Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + pleaseReset.countDown(); + c.await(); + }}; + Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + pleaseReset.countDown(); + c.await(); + }}; + + t1.start(); + t2.start(); + await(pleaseReset); + + awaitNumberWaiting(c, 2); + c.reset(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A reset before threads enter barrier does not throw + * BrokenBarrierException + */ + public void testReset_NoBrokenBarrier() throws Exception { + final CyclicBarrier c = new CyclicBarrier(3); + c.reset(); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + c.await(); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + c.await(); + }}); + + c.await(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * All threads block while a barrier is broken. + */ + public void testReset_Leakage() throws InterruptedException { + final CyclicBarrier c = new CyclicBarrier(2); + final AtomicBoolean done = new AtomicBoolean(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + while (!done.get()) { + try { + while (c.isBroken()) + c.reset(); + + c.await(); + shouldThrow(); + } + catch (BrokenBarrierException ok) {} + catch (InterruptedException ok) {} + }}}); + + for (int i = 0; i < 4; i++) { + delay(timeoutMillis()); + t.interrupt(); + } + done.set(true); + t.interrupt(); + awaitTermination(t); + } + + /** + * Reset of a non-broken barrier does not break barrier + */ + public void testResetWithoutBreakage() throws Exception { + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 3; i++) { + final CyclicBarrier start = new CyclicBarrier(3); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}); + + start.await(); + barrier.await(); + awaitTermination(t1); + awaitTermination(t2); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + if (i == 1) barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + + /** + * Reset of a barrier after interruption reinitializes it. + */ + public void testResetAfterInterrupt() throws Exception { + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + final CyclicBarrier start = new CyclicBarrier(3); + Thread t1 = new ThreadShouldThrow(InterruptedException.class) { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}; + + Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}; + + t1.start(); + t2.start(); + start.await(); + t1.interrupt(); + awaitTermination(t1); + awaitTermination(t2); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + + /** + * Reset of a barrier after timeout reinitializes it. + */ + public void testResetAfterTimeout() throws Exception { + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + assertEquals(0, barrier.getNumberWaiting()); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + try { + barrier.await(); + shouldThrow(); + } catch (BrokenBarrierException success) {} + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + awaitNumberWaiting(barrier, 1); + long startTime = System.nanoTime(); + try { + barrier.await(timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) {} + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t1); + awaitTermination(t2); + assertEquals(0, barrier.getNumberWaiting()); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + + /** + * Reset of a barrier after a failed command reinitializes it. + */ + public void testResetAfterCommandException() throws Exception { + final CyclicBarrier barrier = + new CyclicBarrier(3, new Runnable() { + public void run() { + throw new NullPointerException(); }}); + for (int i = 0; i < 2; i++) { + final CyclicBarrier start = new CyclicBarrier(3); + Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}; + + Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) { + public void realRun() throws Exception { + start.await(); + barrier.await(); + }}; + + t1.start(); + t2.start(); + start.await(); + awaitNumberWaiting(barrier, 2); + try { + barrier.await(); + shouldThrow(); + } catch (NullPointerException success) {} + awaitTermination(t1); + awaitTermination(t2); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/DelayQueueTest.java b/jdk/test/java/util/concurrent/tck/DelayQueueTest.java new file mode 100644 index 00000000000..4d05c864554 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/DelayQueueTest.java @@ -0,0 +1,820 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Delayed; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +import junit.framework.Test; + +public class DelayQueueTest extends JSR166TestCase { + + public static class Generic extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new DelayQueue(); + } + protected PDelay makeElement(int i) { + return new PDelay(i); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(DelayQueueTest.class, + new Generic().testSuite()); + } + + /** + * A delayed implementation for testing. + * Most tests use Pseudodelays, where delays are all elapsed + * (so, no blocking solely for delays) but are still ordered + */ + static class PDelay implements Delayed { + int pseudodelay; + PDelay(int i) { pseudodelay = i; } + public int compareTo(PDelay other) { + int a = this.pseudodelay; + int b = other.pseudodelay; + return (a < b) ? -1 : (a > b) ? 1 : 0; + } + public int compareTo(Delayed y) { + return compareTo((PDelay)y); + } + public boolean equals(Object other) { + return (other instanceof PDelay) && + this.pseudodelay == ((PDelay)other).pseudodelay; + } + // suppress [overrides] javac warning + public int hashCode() { return pseudodelay; } + public long getDelay(TimeUnit ignore) { + return Integer.MIN_VALUE + pseudodelay; + } + public String toString() { + return String.valueOf(pseudodelay); + } + } + + /** + * Delayed implementation that actually delays + */ + static class NanoDelay implements Delayed { + long trigger; + NanoDelay(long i) { + trigger = System.nanoTime() + i; + } + public int compareTo(NanoDelay y) { + long i = trigger; + long j = y.trigger; + if (i < j) return -1; + if (i > j) return 1; + return 0; + } + + public int compareTo(Delayed y) { + return compareTo((NanoDelay)y); + } + + public boolean equals(Object other) { + return equals((NanoDelay)other); + } + public boolean equals(NanoDelay other) { + return other.trigger == trigger; + } + + // suppress [overrides] javac warning + public int hashCode() { return (int) trigger; } + + public long getDelay(TimeUnit unit) { + long n = trigger - System.nanoTime(); + return unit.convert(n, TimeUnit.NANOSECONDS); + } + + public long getTriggerTime() { + return trigger; + } + + public String toString() { + return String.valueOf(trigger); + } + } + + /** + * Returns a new queue of given size containing consecutive + * PDelays 0 ... n. + */ + private DelayQueue populatedQueue(int n) { + DelayQueue q = new DelayQueue(); + assertTrue(q.isEmpty()); + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.offer(new PDelay(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.offer(new PDelay(i))); + assertFalse(q.isEmpty()); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(n, q.size()); + return q; + } + + /** + * A new queue has unbounded capacity + */ + public void testConstructor1() { + assertEquals(Integer.MAX_VALUE, new DelayQueue().remainingCapacity()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new DelayQueue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new DelayQueue(Arrays.asList(new PDelay[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + PDelay[] a = new PDelay[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + a[i] = new PDelay(i); + try { + new DelayQueue(Arrays.asList(a)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + PDelay[] ints = new PDelay[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new PDelay(i); + DelayQueue q = new DelayQueue(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + DelayQueue q = new DelayQueue(); + assertTrue(q.isEmpty()); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + q.add(new PDelay(1)); + assertFalse(q.isEmpty()); + q.add(new PDelay(2)); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * remainingCapacity() always returns Integer.MAX_VALUE + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(SIZE - i, q.size()); + assertTrue(q.remove() instanceof PDelay); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(i, q.size()); + assertTrue(q.add(new PDelay(i))); + } + } + + /** + * offer non-null succeeds + */ + public void testOffer() { + DelayQueue q = new DelayQueue(); + assertTrue(q.offer(new PDelay(0))); + assertTrue(q.offer(new PDelay(1))); + } + + /** + * add succeeds + */ + public void testAdd() { + DelayQueue q = new DelayQueue(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + assertTrue(q.add(new PDelay(i))); + } + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + DelayQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + DelayQueue q = new DelayQueue(); + PDelay[] a = new PDelay[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + a[i] = new PDelay(i); + try { + q.addAll(Arrays.asList(a)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of successful addAll + */ + public void testAddAll5() { + PDelay[] empty = new PDelay[0]; + PDelay[] ints = new PDelay[SIZE]; + for (int i = SIZE - 1; i >= 0; --i) + ints[i] = new PDelay(i); + DelayQueue q = new DelayQueue(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * all elements successfully put are contained + */ + public void testPut() { + DelayQueue q = new DelayQueue(); + for (int i = 0; i < SIZE; ++i) { + PDelay x = new PDelay(i); + q.put(x); + assertTrue(q.contains(x)); + } + assertEquals(SIZE, q.size()); + } + + /** + * put doesn't block waiting for take + */ + public void testPutWithTake() throws InterruptedException { + final DelayQueue q = new DelayQueue(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + q.put(new PDelay(0)); + q.put(new PDelay(0)); + q.put(new PDelay(0)); + q.put(new PDelay(0)); + }}); + + awaitTermination(t); + assertEquals(4, q.size()); + } + + /** + * timed offer does not time out + */ + public void testTimedOffer() throws InterruptedException { + final DelayQueue q = new DelayQueue(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new PDelay(0)); + q.put(new PDelay(0)); + assertTrue(q.offer(new PDelay(0), SHORT_DELAY_MS, MILLISECONDS)); + assertTrue(q.offer(new PDelay(0), LONG_DELAY_MS, MILLISECONDS)); + }}); + + awaitTermination(t); + } + + /** + * take retrieves elements in priority order + */ + public void testTake() throws InterruptedException { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.take()); + } + } + + /** + * Take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final DelayQueue q = populatedQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), ((PDelay)q.take())); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.poll()); + } + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(new PDelay(i), q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final DelayQueue q = populatedQueue(SIZE); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), + ((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS))); + } + + Thread.currentThread().interrupt(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.peek()); + assertEquals(new PDelay(i), q.poll()); + if (q.isEmpty()) + assertNull(q.peek()); + else + assertFalse(new PDelay(i).equals(q.peek())); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.element()); + q.poll(); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(new PDelay(i), q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + DelayQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new PDelay(i))); + q.poll(); + assertFalse(q.contains(new PDelay(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + DelayQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + PDelay x = new PDelay(1); + q.add(x); + assertFalse(q.isEmpty()); + assertTrue(q.contains(x)); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + DelayQueue q = populatedQueue(SIZE); + DelayQueue p = new DelayQueue(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new PDelay(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + DelayQueue q = populatedQueue(SIZE); + DelayQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + DelayQueue q = populatedQueue(SIZE); + DelayQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + PDelay x = (PDelay)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements + */ + public void testToArray() throws InterruptedException { + DelayQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + Arrays.sort(o); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.take()); + } + + /** + * toArray(a) contains all elements + */ + public void testToArray2() { + DelayQueue q = populatedQueue(SIZE); + PDelay[] ints = new PDelay[SIZE]; + PDelay[] array = q.toArray(ints); + assertSame(ints, array); + Arrays.sort(ints); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.remove()); + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + DelayQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + DelayQueue q = populatedQueue(SIZE); + int i = 0; + Iterator it = q.iterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new DelayQueue().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final DelayQueue q = new DelayQueue(); + q.add(new PDelay(2)); + q.add(new PDelay(1)); + q.add(new PDelay(3)); + Iterator it = q.iterator(); + it.next(); + it.remove(); + it = q.iterator(); + assertEquals(new PDelay(2), it.next()); + assertEquals(new PDelay(3), it.next()); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + DelayQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (Object e : q) + assertTrue(s.contains(e.toString())); + } + + /** + * timed poll transfers elements across Executor tasks + */ + public void testPollInExecutor() { + final DelayQueue q = new DelayQueue(); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS)); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(new PDelay(1)); + }}); + } + } + + /** + * Delayed actions do not occur until their delay elapses + */ + public void testDelay() throws InterruptedException { + DelayQueue q = new DelayQueue(); + for (int i = 0; i < SIZE; ++i) + q.add(new NanoDelay(1000000L * (SIZE - i))); + + long last = 0; + for (int i = 0; i < SIZE; ++i) { + NanoDelay e = q.take(); + long tt = e.getTriggerTime(); + assertTrue(System.nanoTime() - tt >= 0); + if (i != 0) + assertTrue(tt >= last); + last = tt; + } + assertTrue(q.isEmpty()); + } + + /** + * peek of a non-empty queue returns non-null even if not expired + */ + public void testPeekDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assertNotNull(q.peek()); + } + + /** + * poll of a non-empty queue returns null if no expired elements. + */ + public void testPollDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assertNull(q.poll()); + } + + /** + * timed poll of a non-empty queue returns null if no expired elements. + */ + public void testTimedPollDelayed() throws InterruptedException { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(LONG_DELAY_MS * 1000000L)); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + } + + /** + * drainTo(c) empties queue into another collection c + */ + public void testDrainTo() { + DelayQueue q = new DelayQueue(); + PDelay[] elems = new PDelay[SIZE]; + for (int i = 0; i < SIZE; ++i) { + elems[i] = new PDelay(i); + q.add(elems[i]); + } + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + for (int i = 0; i < SIZE; ++i) + assertEquals(elems[i], l.get(i)); + q.add(elems[0]); + q.add(elems[1]); + assertFalse(q.isEmpty()); + assertTrue(q.contains(elems[0])); + assertTrue(q.contains(elems[1])); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) + assertEquals(elems[i], l.get(i)); + } + + /** + * drainTo empties queue + */ + public void testDrainToWithActivePut() throws InterruptedException { + final DelayQueue q = populatedQueue(SIZE); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + q.put(new PDelay(SIZE + 1)); + }}); + + t.start(); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + t.join(); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + for (int i = 0; i < SIZE + 2; ++i) { + DelayQueue q = populatedQueue(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(SIZE - k, q.size()); + assertEquals(k, l.size()); + } + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection q = populatedQueue(SIZE); + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } +} diff --git a/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java b/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java new file mode 100644 index 00000000000..0a7fea9eb9d --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java @@ -0,0 +1,183 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Phaser; +import java.util.concurrent.atomic.DoubleAccumulator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class DoubleAccumulatorTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(DoubleAccumulatorTest.class); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals(0.0, ai.get()); + } + + /** + * accumulate accumulates given value to current, and get returns current value + */ + public void testAccumulateAndGet() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + ai.accumulate(2.0); + assertEquals(2.0, ai.get()); + ai.accumulate(-4.0); + assertEquals(2.0, ai.get()); + ai.accumulate(4.0); + assertEquals(4.0, ai.get()); + } + + /** + * reset() causes subsequent get() to return zero + */ + public void testReset() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + ai.accumulate(2.0); + assertEquals(2.0, ai.get()); + ai.reset(); + assertEquals(0.0, ai.get()); + } + + /** + * getThenReset() returns current value; subsequent get() returns zero + */ + public void testGetThenReset() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + ai.accumulate(2.0); + assertEquals(2.0, ai.get()); + assertEquals(2.0, ai.getThenReset()); + assertEquals(0.0, ai.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals("0.0", ai.toString()); + ai.accumulate(1.0); + assertEquals(Double.toString(1.0), ai.toString()); + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals(0, ai.intValue()); + ai.accumulate(1.0); + assertEquals(1, ai.intValue()); + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals(0, ai.longValue()); + ai.accumulate(1.0); + assertEquals(1, ai.longValue()); + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals(0.0f, ai.floatValue()); + ai.accumulate(1.0); + assertEquals(1.0f, ai.floatValue()); + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0); + assertEquals(0.0, ai.doubleValue()); + ai.accumulate(1.0); + assertEquals(1.0, ai.doubleValue()); + } + + /** + * accumulates by multiple threads produce correct result + */ + public void testAccumulateAndGetMT() { + final int incs = 1000000; + final int nthreads = 4; + final ExecutorService pool = Executors.newCachedThreadPool(); + DoubleAccumulator a = new DoubleAccumulator(Double::max, 0.0); + Phaser phaser = new Phaser(nthreads + 1); + for (int i = 0; i < nthreads; ++i) + pool.execute(new AccTask(a, phaser, incs)); + phaser.arriveAndAwaitAdvance(); + phaser.arriveAndAwaitAdvance(); + double expected = incs - 1; + double result = a.get(); + assertEquals(expected, result); + pool.shutdown(); + } + + static final class AccTask implements Runnable { + final DoubleAccumulator acc; + final Phaser phaser; + final int incs; + volatile double result; + AccTask(DoubleAccumulator acc, Phaser phaser, int incs) { + this.acc = acc; + this.phaser = phaser; + this.incs = incs; + } + + public void run() { + phaser.arriveAndAwaitAdvance(); + DoubleAccumulator a = acc; + for (int i = 0; i < incs; ++i) + a.accumulate(i); + result = a.get(); + phaser.arrive(); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java b/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java new file mode 100644 index 00000000000..fc7d1f47834 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java @@ -0,0 +1,197 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.DoubleAdder; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class DoubleAdderTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(DoubleAdderTest.class); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(0.0, ai.sum()); + } + + /** + * add adds given value to current, and sum returns current value + */ + public void testAddAndSum() { + DoubleAdder ai = new DoubleAdder(); + ai.add(2.0); + assertEquals(2.0, ai.sum()); + ai.add(-4.0); + assertEquals(-2.0, ai.sum()); + } + + /** + * reset() causes subsequent sum() to return zero + */ + public void testReset() { + DoubleAdder ai = new DoubleAdder(); + ai.add(2.0); + assertEquals(2.0, ai.sum()); + ai.reset(); + assertEquals(0.0, ai.sum()); + } + + /** + * sumThenReset() returns sum; subsequent sum() returns zero + */ + public void testSumThenReset() { + DoubleAdder ai = new DoubleAdder(); + ai.add(2.0); + assertEquals(2.0, ai.sum()); + assertEquals(2.0, ai.sumThenReset()); + assertEquals(0.0, ai.sum()); + } + + /** + * a deserialized serialized adder holds same value + */ + public void testSerialization() throws Exception { + DoubleAdder x = new DoubleAdder(); + DoubleAdder y = serialClone(x); + assertNotSame(x, y); + x.add(-22.0); + DoubleAdder z = serialClone(x); + assertEquals(-22.0, x.sum()); + assertEquals(0.0, y.sum()); + assertEquals(-22.0, z.sum()); + } + + /** + * toString returns current value. + */ + public void testToString() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(Double.toString(0.0), ai.toString()); + ai.add(1.0); + assertEquals(Double.toString(1.0), ai.toString()); + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(0, ai.intValue()); + ai.add(1.0); + assertEquals(1, ai.intValue()); + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(0, ai.longValue()); + ai.add(1.0); + assertEquals(1, ai.longValue()); + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(0.0f, ai.floatValue()); + ai.add(1.0); + assertEquals(1.0f, ai.floatValue()); + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + DoubleAdder ai = new DoubleAdder(); + assertEquals(0.0, ai.doubleValue()); + ai.add(1.0); + assertEquals(1.0, ai.doubleValue()); + } + + /** + * adds by multiple threads produce correct sum + */ + public void testAddAndSumMT() throws Throwable { + final int incs = 1000000; + final int nthreads = 4; + final ExecutorService pool = Executors.newCachedThreadPool(); + DoubleAdder a = new DoubleAdder(); + CyclicBarrier barrier = new CyclicBarrier(nthreads + 1); + for (int i = 0; i < nthreads; ++i) + pool.execute(new AdderTask(a, barrier, incs)); + barrier.await(); + barrier.await(); + double total = (long)nthreads * incs; + double sum = a.sum(); + assertEquals(sum, total); + pool.shutdown(); + } + + static final class AdderTask implements Runnable { + final DoubleAdder adder; + final CyclicBarrier barrier; + final int incs; + volatile double result; + AdderTask(DoubleAdder adder, CyclicBarrier barrier, int incs) { + this.adder = adder; + this.barrier = barrier; + this.incs = incs; + } + + public void run() { + try { + barrier.await(); + DoubleAdder a = adder; + for (int i = 0; i < incs; ++i) + a.add(1.0); + result = a.sum(); + barrier.await(); + } catch (Throwable t) { throw new Error(t); } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/EntryTest.java b/jdk/test/java/util/concurrent/tck/EntryTest.java new file mode 100644 index 00000000000..dcd696d8d12 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/EntryTest.java @@ -0,0 +1,157 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.AbstractMap; +import java.util.Map; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class EntryTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(EntryTest.class); + } + + static final String k1 = "1"; + static final String v1 = "a"; + static final String k2 = "2"; + static final String v2 = "b"; + + /** + * A new SimpleEntry(k, v) holds k, v. + */ + public void testConstructor1() { + Map.Entry e = new AbstractMap.SimpleEntry(k1, v1); + assertEquals(k1, e.getKey()); + assertEquals(v1, e.getValue()); + } + + /** + * A new SimpleImmutableEntry(k, v) holds k, v. + */ + public void testConstructor2() { + Map.Entry s = new AbstractMap.SimpleImmutableEntry(k1, v1); + assertEquals(k1, s.getKey()); + assertEquals(v1, s.getValue()); + } + + /** + * A new SimpleEntry(entry(k, v)) holds k, v. + */ + public void testConstructor3() { + Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1); + Map.Entry e = new AbstractMap.SimpleEntry(e2); + assertEquals(k1, e.getKey()); + assertEquals(v1, e.getValue()); + } + + /** + * A new SimpleImmutableEntry(entry(k, v)) holds k, v. + */ + public void testConstructor4() { + Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1); + Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2); + assertEquals(k1, s.getKey()); + assertEquals(v1, s.getValue()); + } + + /** + * Entries with same key-value pairs are equal and have same + * hashcodes + */ + public void testEquals() { + Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1); + Map.Entry e = new AbstractMap.SimpleEntry(e2); + Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1); + Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2); + assertEquals(e2, e); + assertEquals(e2.hashCode(), e.hashCode()); + assertEquals(s2, s); + assertEquals(s2.hashCode(), s.hashCode()); + assertEquals(e2, s2); + assertEquals(e2.hashCode(), s2.hashCode()); + assertEquals(e, s); + assertEquals(e.hashCode(), s.hashCode()); + } + + /** + * Entries with different key-value pairs are not equal + */ + public void testNotEquals() { + Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1); + Map.Entry e = new AbstractMap.SimpleEntry(k2, v1); + assertFalse(e2.equals(e)); + e = new AbstractMap.SimpleEntry(k1, v2); + assertFalse(e2.equals(e)); + e = new AbstractMap.SimpleEntry(k2, v2); + assertFalse(e2.equals(e)); + + Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1); + Map.Entry s = new AbstractMap.SimpleImmutableEntry(k2, v1); + assertFalse(s2.equals(s)); + s = new AbstractMap.SimpleImmutableEntry(k1, v2); + assertFalse(s2.equals(s)); + s = new AbstractMap.SimpleImmutableEntry(k2, v2); + assertFalse(s2.equals(s)); + } + + /** + * getValue returns last setValue for SimpleEntry + */ + public void testSetValue1() { + Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1); + Map.Entry e = new AbstractMap.SimpleEntry(e2); + assertEquals(k1, e.getKey()); + assertEquals(v1, e.getValue()); + e.setValue(k2); + assertEquals(k2, e.getValue()); + assertFalse(e2.equals(e)); + } + + /** + * setValue for SimpleImmutableEntry throws UnsupportedOperationException + */ + public void testSetValue2() { + Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1); + Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2); + assertEquals(k1, s.getKey()); + assertEquals(v1, s.getValue()); + try { + s.setValue(k2); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } +} diff --git a/jdk/test/java/util/concurrent/tck/ExchangerTest.java b/jdk/test/java/util/concurrent/tck/ExchangerTest.java new file mode 100644 index 00000000000..ba6c5443623 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ExchangerTest.java @@ -0,0 +1,180 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Exchanger; +import java.util.concurrent.TimeoutException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ExchangerTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ExchangerTest.class); + } + + /** + * exchange exchanges objects across two threads + */ + public void testExchange() { + final Exchanger e = new Exchanger(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertSame(one, e.exchange(two)); + assertSame(two, e.exchange(one)); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertSame(two, e.exchange(one)); + assertSame(one, e.exchange(two)); + }}); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * timed exchange exchanges objects across two threads + */ + public void testTimedExchange() { + final Exchanger e = new Exchanger(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS)); + assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS)); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS)); + assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS)); + }}); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * interrupt during wait for exchange throws IE + */ + public void testExchange_InterruptedException() { + final Exchanger e = new Exchanger(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + e.exchange(one); + }}); + + await(threadStarted); + t.interrupt(); + awaitTermination(t); + } + + /** + * interrupt during wait for timed exchange throws IE + */ + public void testTimedExchange_InterruptedException() { + final Exchanger e = new Exchanger(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws Exception { + threadStarted.countDown(); + e.exchange(null, LONG_DELAY_MS, MILLISECONDS); + }}); + + await(threadStarted); + t.interrupt(); + awaitTermination(t); + } + + /** + * timeout during wait for timed exchange throws TimeoutException + */ + public void testExchange_TimeoutException() { + final Exchanger e = new Exchanger(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + long startTime = System.nanoTime(); + try { + e.exchange(null, timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) {} + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}); + + awaitTermination(t); + } + + /** + * If one exchanging thread is interrupted, another succeeds. + */ + public void testReplacementAfterExchange() { + final Exchanger e = new Exchanger(); + final CountDownLatch exchanged = new CountDownLatch(2); + final CountDownLatch interrupted = new CountDownLatch(1); + Thread t1 = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + assertSame(two, e.exchange(one)); + exchanged.countDown(); + e.exchange(two); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertSame(one, e.exchange(two)); + exchanged.countDown(); + interrupted.await(); + assertSame(three, e.exchange(one)); + }}); + Thread t3 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + interrupted.await(); + assertSame(one, e.exchange(three)); + }}); + + await(exchanged); + t1.interrupt(); + awaitTermination(t1); + interrupted.countDown(); + awaitTermination(t2); + awaitTermination(t3); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java b/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java new file mode 100644 index 00000000000..29db17a7a0d --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java @@ -0,0 +1,241 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ExecutorCompletionServiceTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ExecutorCompletionServiceTest.class); + } + + /** + * Creating a new ECS with null Executor throw NPE + */ + public void testConstructorNPE() { + try { + new ExecutorCompletionService(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Creating a new ECS with null queue throw NPE + */ + public void testConstructorNPE2() { + try { + ExecutorService e = Executors.newCachedThreadPool(); + new ExecutorCompletionService(e, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Submitting a null callable throws NPE + */ + public void testSubmitNPE() { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + Callable c = null; + try { + ecs.submit(c); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * Submitting a null runnable throws NPE + */ + public void testSubmitNPE2() { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + Runnable r = null; + try { + ecs.submit(r, Boolean.TRUE); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * A taken submitted task is completed + */ + public void testTake() throws InterruptedException { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + Callable c = new StringTask(); + ecs.submit(c); + Future f = ecs.take(); + assertTrue(f.isDone()); + } + } + + /** + * Take returns the same future object returned by submit + */ + public void testTake2() throws InterruptedException { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + Callable c = new StringTask(); + Future f1 = ecs.submit(c); + Future f2 = ecs.take(); + assertSame(f1, f2); + } + } + + /** + * If poll returns non-null, the returned task is completed + */ + public void testPoll1() throws Exception { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + assertNull(ecs.poll()); + Callable c = new StringTask(); + ecs.submit(c); + + long startTime = System.nanoTime(); + Future f; + while ((f = ecs.poll()) == null) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + assertTrue(f.isDone()); + assertSame(TEST_STRING, f.get()); + } + } + + /** + * If timed poll returns non-null, the returned task is completed + */ + public void testPoll2() throws InterruptedException { + final ExecutorService e = Executors.newCachedThreadPool(); + final ExecutorCompletionService ecs = new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + assertNull(ecs.poll()); + Callable c = new StringTask(); + ecs.submit(c); + Future f = ecs.poll(SHORT_DELAY_MS, MILLISECONDS); + if (f != null) + assertTrue(f.isDone()); + } + } + + /** + * Submitting to underlying AES that overrides newTaskFor(Callable) + * returns and eventually runs Future returned by newTaskFor. + */ + public void testNewTaskForCallable() throws InterruptedException { + final AtomicBoolean done = new AtomicBoolean(false); + class MyCallableFuture extends FutureTask { + MyCallableFuture(Callable c) { super(c); } + protected void done() { done.set(true); } + } + final ExecutorService e = + new ThreadPoolExecutor(1, 1, + 30L, TimeUnit.SECONDS, + new ArrayBlockingQueue(1)) { + protected RunnableFuture newTaskFor(Callable c) { + return new MyCallableFuture(c); + }}; + ExecutorCompletionService ecs = + new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + assertNull(ecs.poll()); + Callable c = new StringTask(); + Future f1 = ecs.submit(c); + assertTrue("submit must return MyCallableFuture", + f1 instanceof MyCallableFuture); + Future f2 = ecs.take(); + assertSame("submit and take must return same objects", f1, f2); + assertTrue("completed task must have set done", done.get()); + } + } + + /** + * Submitting to underlying AES that overrides newTaskFor(Runnable,T) + * returns and eventually runs Future returned by newTaskFor. + */ + public void testNewTaskForRunnable() throws InterruptedException { + final AtomicBoolean done = new AtomicBoolean(false); + class MyRunnableFuture extends FutureTask { + MyRunnableFuture(Runnable t, V r) { super(t, r); } + protected void done() { done.set(true); } + } + final ExecutorService e = + new ThreadPoolExecutor(1, 1, + 30L, TimeUnit.SECONDS, + new ArrayBlockingQueue(1)) { + protected RunnableFuture newTaskFor(Runnable t, T r) { + return new MyRunnableFuture(t, r); + }}; + final ExecutorCompletionService ecs = + new ExecutorCompletionService(e); + try (PoolCleaner cleaner = cleaner(e)) { + assertNull(ecs.poll()); + Runnable r = new NoOpRunnable(); + Future f1 = ecs.submit(r, null); + assertTrue("submit must return MyRunnableFuture", + f1 instanceof MyRunnableFuture); + Future f2 = ecs.take(); + assertSame("submit and take must return same objects", f1, f2); + assertTrue("completed task must have set done", done.get()); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ExecutorsTest.java b/jdk/test/java/util/concurrent/tck/ExecutorsTest.java new file mode 100644 index 00000000000..ec17b54b507 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ExecutorsTest.java @@ -0,0 +1,623 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ExecutorsTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ExecutorsTest.class); + } + + /** + * A newCachedThreadPool can execute runnables + */ + public void testNewCachedThreadPool1() { + final ExecutorService e = Executors.newCachedThreadPool(); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A newCachedThreadPool with given ThreadFactory can execute runnables + */ + public void testNewCachedThreadPool2() { + final ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A newCachedThreadPool with null ThreadFactory throws NPE + */ + public void testNewCachedThreadPool3() { + try { + ExecutorService e = Executors.newCachedThreadPool(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A new SingleThreadExecutor can execute runnables + */ + public void testNewSingleThreadExecutor1() { + final ExecutorService e = Executors.newSingleThreadExecutor(); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A new SingleThreadExecutor with given ThreadFactory can execute runnables + */ + public void testNewSingleThreadExecutor2() { + final ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A new SingleThreadExecutor with null ThreadFactory throws NPE + */ + public void testNewSingleThreadExecutor3() { + try { + ExecutorService e = Executors.newSingleThreadExecutor(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A new SingleThreadExecutor cannot be casted to concrete implementation + */ + public void testCastNewSingleThreadExecutor() { + final ExecutorService e = Executors.newSingleThreadExecutor(); + try (PoolCleaner cleaner = cleaner(e)) { + try { + ThreadPoolExecutor tpe = (ThreadPoolExecutor)e; + shouldThrow(); + } catch (ClassCastException success) {} + } + } + + /** + * A new newFixedThreadPool can execute runnables + */ + public void testNewFixedThreadPool1() { + final ExecutorService e = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A new newFixedThreadPool with given ThreadFactory can execute runnables + */ + public void testNewFixedThreadPool2() { + final ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * A new newFixedThreadPool with null ThreadFactory throws NPE + */ + public void testNewFixedThreadPool3() { + try { + ExecutorService e = Executors.newFixedThreadPool(2, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A new newFixedThreadPool with 0 threads throws IAE + */ + public void testNewFixedThreadPool4() { + try { + ExecutorService e = Executors.newFixedThreadPool(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * An unconfigurable newFixedThreadPool can execute runnables + */ + public void testUnconfigurableExecutorService() { + final ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2)); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + e.execute(new NoOpRunnable()); + } + } + + /** + * unconfigurableExecutorService(null) throws NPE + */ + public void testUnconfigurableExecutorServiceNPE() { + try { + ExecutorService e = Executors.unconfigurableExecutorService(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * unconfigurableScheduledExecutorService(null) throws NPE + */ + public void testUnconfigurableScheduledExecutorServiceNPE() { + try { + ExecutorService e = Executors.unconfigurableScheduledExecutorService(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * a newSingleThreadScheduledExecutor successfully runs delayed task + */ + public void testNewSingleThreadScheduledExecutor() throws Exception { + final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor(); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch proceed = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { + await(proceed); + }}; + long startTime = System.nanoTime(); + Future f = p.schedule(Executors.callable(task, Boolean.TRUE), + timeoutMillis(), MILLISECONDS); + assertFalse(f.isDone()); + proceed.countDown(); + assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS)); + assertSame(Boolean.TRUE, f.get()); + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * a newScheduledThreadPool successfully runs delayed task + */ + public void testNewScheduledThreadPool() throws Exception { + final ScheduledExecutorService p = Executors.newScheduledThreadPool(2); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch proceed = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { + await(proceed); + }}; + long startTime = System.nanoTime(); + Future f = p.schedule(Executors.callable(task, Boolean.TRUE), + timeoutMillis(), MILLISECONDS); + assertFalse(f.isDone()); + proceed.countDown(); + assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS)); + assertSame(Boolean.TRUE, f.get()); + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * an unconfigurable newScheduledThreadPool successfully runs delayed task + */ + public void testUnconfigurableScheduledExecutorService() throws Exception { + final ScheduledExecutorService p = + Executors.unconfigurableScheduledExecutorService + (Executors.newScheduledThreadPool(2)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch proceed = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { + await(proceed); + }}; + long startTime = System.nanoTime(); + Future f = p.schedule(Executors.callable(task, Boolean.TRUE), + timeoutMillis(), MILLISECONDS); + assertFalse(f.isDone()); + proceed.countDown(); + assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS)); + assertSame(Boolean.TRUE, f.get()); + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * Future.get on submitted tasks will time out if they compute too long. + */ + public void testTimedCallable() throws Exception { + final ExecutorService[] executors = { + Executors.newSingleThreadExecutor(), + Executors.newCachedThreadPool(), + Executors.newFixedThreadPool(2), + Executors.newScheduledThreadPool(2), + }; + + final Runnable sleeper = new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + delay(LONG_DELAY_MS); + }}; + + List threads = new ArrayList(); + for (final ExecutorService executor : executors) { + threads.add(newStartedThread(new CheckedRunnable() { + public void realRun() { + Future future = executor.submit(sleeper); + assertFutureTimesOut(future); + }})); + } + for (Thread thread : threads) + awaitTermination(thread); + for (ExecutorService executor : executors) + joinPool(executor); + } + + /** + * ThreadPoolExecutor using defaultThreadFactory has + * specified group, priority, daemon status, and name + */ + public void testDefaultThreadFactory() throws Exception { + final ThreadGroup egroup = Thread.currentThread().getThreadGroup(); + final CountDownLatch done = new CountDownLatch(1); + Runnable r = new CheckedRunnable() { + public void realRun() { + try { + Thread current = Thread.currentThread(); + assertTrue(!current.isDaemon()); + assertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + assertTrue(g == s.getThreadGroup()); + else + assertTrue(g == egroup); + String name = current.getName(); + assertTrue(name.endsWith("thread-1")); + } catch (SecurityException ok) { + // Also pass if not allowed to change setting + } + done.countDown(); + }}; + ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(r); + await(done); + } + } + + /** + * ThreadPoolExecutor using privilegedThreadFactory has + * specified group, priority, daemon status, name, + * access control context and context class loader + */ + public void testPrivilegedThreadFactory() throws Exception { + final CountDownLatch done = new CountDownLatch(1); + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + final ThreadGroup egroup = Thread.currentThread().getThreadGroup(); + final ClassLoader thisccl = Thread.currentThread().getContextClassLoader(); + final AccessControlContext thisacc = AccessController.getContext(); + Runnable r = new CheckedRunnable() { + public void realRun() { + Thread current = Thread.currentThread(); + assertTrue(!current.isDaemon()); + assertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + assertTrue(g == s.getThreadGroup()); + else + assertTrue(g == egroup); + String name = current.getName(); + assertTrue(name.endsWith("thread-1")); + assertSame(thisccl, current.getContextClassLoader()); + assertEquals(thisacc, AccessController.getContext()); + done.countDown(); + }}; + ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + e.execute(r); + await(done); + } + }}; + + runWithPermissions(r, + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader"), + new RuntimePermission("modifyThread")); + } + + boolean haveCCLPermissions() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + sm.checkPermission(new RuntimePermission("getClassLoader")); + } catch (AccessControlException e) { + return false; + } + } + return true; + } + + void checkCCL() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + } + + class CheckCCL implements Callable { + public Object call() { + checkCCL(); + return null; + } + } + + /** + * Without class loader permissions, creating + * privilegedCallableUsingCurrentClassLoader throws ACE + */ + public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + if (System.getSecurityManager() == null) + return; + try { + Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable()); + shouldThrow(); + } catch (AccessControlException success) {} + }}; + + runWithoutPermissions(r); + } + + /** + * With class loader permissions, calling + * privilegedCallableUsingCurrentClassLoader does not throw ACE + */ + public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + Executors.privilegedCallableUsingCurrentClassLoader + (new NoOpCallable()) + .call(); + }}; + + runWithPermissions(r, + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader")); + } + + /** + * Without permissions, calling privilegedCallable throws ACE + */ + public void testPrivilegedCallableWithNoPrivs() throws Exception { + // Avoid classloader-related SecurityExceptions in swingui.TestRunner + Executors.privilegedCallable(new CheckCCL()); + + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + if (System.getSecurityManager() == null) + return; + Callable task = Executors.privilegedCallable(new CheckCCL()); + try { + task.call(); + shouldThrow(); + } catch (AccessControlException success) {} + }}; + + runWithoutPermissions(r); + + // It seems rather difficult to test that the + // AccessControlContext of the privilegedCallable is used + // instead of its caller. Below is a failed attempt to do + // that, which does not work because the AccessController + // cannot capture the internal state of the current Policy. + // It would be much more work to differentiate based on, + // e.g. CodeSource. + +// final AccessControlContext[] noprivAcc = new AccessControlContext[1]; +// final Callable[] task = new Callable[1]; + +// runWithPermissions +// (new CheckedRunnable() { +// public void realRun() { +// if (System.getSecurityManager() == null) +// return; +// noprivAcc[0] = AccessController.getContext(); +// task[0] = Executors.privilegedCallable(new CheckCCL()); +// try { +// AccessController.doPrivileged(new PrivilegedAction() { +// public Void run() { +// checkCCL(); +// return null; +// }}, noprivAcc[0]); +// shouldThrow(); +// } catch (AccessControlException success) {} +// }}); + +// runWithPermissions +// (new CheckedRunnable() { +// public void realRun() throws Exception { +// if (System.getSecurityManager() == null) +// return; +// // Verify that we have an underprivileged ACC +// try { +// AccessController.doPrivileged(new PrivilegedAction() { +// public Void run() { +// checkCCL(); +// return null; +// }}, noprivAcc[0]); +// shouldThrow(); +// } catch (AccessControlException success) {} + +// try { +// task[0].call(); +// shouldThrow(); +// } catch (AccessControlException success) {} +// }}, +// new RuntimePermission("getClassLoader"), +// new RuntimePermission("setContextClassLoader")); + } + + /** + * With permissions, calling privilegedCallable succeeds + */ + public void testPrivilegedCallableWithPrivs() throws Exception { + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + Executors.privilegedCallable(new CheckCCL()).call(); + }}; + + runWithPermissions(r, + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader")); + } + + /** + * callable(Runnable) returns null when called + */ + public void testCallable1() throws Exception { + Callable c = Executors.callable(new NoOpRunnable()); + assertNull(c.call()); + } + + /** + * callable(Runnable, result) returns result when called + */ + public void testCallable2() throws Exception { + Callable c = Executors.callable(new NoOpRunnable(), one); + assertSame(one, c.call()); + } + + /** + * callable(PrivilegedAction) returns its result when called + */ + public void testCallable3() throws Exception { + Callable c = Executors.callable(new PrivilegedAction() { + public Object run() { return one; }}); + assertSame(one, c.call()); + } + + /** + * callable(PrivilegedExceptionAction) returns its result when called + */ + public void testCallable4() throws Exception { + Callable c = Executors.callable(new PrivilegedExceptionAction() { + public Object run() { return one; }}); + assertSame(one, c.call()); + } + + /** + * callable(null Runnable) throws NPE + */ + public void testCallableNPE1() { + try { + Callable c = Executors.callable((Runnable) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * callable(null, result) throws NPE + */ + public void testCallableNPE2() { + try { + Callable c = Executors.callable((Runnable) null, one); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * callable(null PrivilegedAction) throws NPE + */ + public void testCallableNPE3() { + try { + Callable c = Executors.callable((PrivilegedAction) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * callable(null PrivilegedExceptionAction) throws NPE + */ + public void testCallableNPE4() { + try { + Callable c = Executors.callable((PrivilegedExceptionAction) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java b/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java new file mode 100644 index 00000000000..ee991a21c80 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java @@ -0,0 +1,1615 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.HashSet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountedCompleter; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.TimeoutException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ForkJoinPool8Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ForkJoinPool8Test.class); + } + + /** + * Common pool exists and has expected parallelism. + */ + public void testCommonPoolParallelism() { + assertEquals(ForkJoinPool.getCommonPoolParallelism(), + ForkJoinPool.commonPool().getParallelism()); + } + + /** + * Common pool cannot be shut down + */ + public void testCommonPoolShutDown() { + assertFalse(ForkJoinPool.commonPool().isShutdown()); + assertFalse(ForkJoinPool.commonPool().isTerminating()); + assertFalse(ForkJoinPool.commonPool().isTerminated()); + ForkJoinPool.commonPool().shutdown(); + assertFalse(ForkJoinPool.commonPool().isShutdown()); + assertFalse(ForkJoinPool.commonPool().isTerminating()); + assertFalse(ForkJoinPool.commonPool().isTerminated()); + ForkJoinPool.commonPool().shutdownNow(); + assertFalse(ForkJoinPool.commonPool().isShutdown()); + assertFalse(ForkJoinPool.commonPool().isTerminating()); + assertFalse(ForkJoinPool.commonPool().isTerminated()); + } + + /* + * All of the following test methods are adaptations of those for + * RecursiveAction and CountedCompleter, but with all actions + * executed in the common pool, generally implicitly via + * checkInvoke. + */ + + private void checkInvoke(ForkJoinTask a) { + checkNotDone(a); + assertNull(a.invoke()); + checkCompletedNormally(a); + } + + void checkNotDone(ForkJoinTask a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + if (! ForkJoinTask.inForkJoinPool()) { + Thread.currentThread().interrupt(); + try { + a.get(); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + Thread.currentThread().interrupt(); + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(ForkJoinTask a) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + assertNull(a.join()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertNull(a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertNull(a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCancelled(ForkJoinTask a) { + assertTrue(a.isDone()); + assertTrue(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertTrue(a.getException() instanceof CancellationException); + assertNull(a.getRawResult()); + + try { + a.join(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(ForkJoinTask a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + + try { + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(expected.getClass(), t.getClass()); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + public static final class FJException extends RuntimeException { + public FJException() { super(); } + public FJException(Throwable cause) { super(cause); } + } + + // A simple recursive action for testing + final class FibAction extends CheckedRecursiveAction { + final int number; + int result; + FibAction(int n) { number = n; } + protected void realCompute() { + int n = number; + if (n <= 1) + result = n; + else { + FibAction f1 = new FibAction(n - 1); + FibAction f2 = new FibAction(n - 2); + invokeAll(f1, f2); + result = f1.result + f2.result; + } + } + } + + // A recursive action failing in base case + static final class FailingFibAction extends RecursiveAction { + final int number; + int result; + FailingFibAction(int n) { number = n; } + public void compute() { + int n = number; + if (n <= 1) + throw new FJException(); + else { + FailingFibAction f1 = new FailingFibAction(n - 1); + FailingFibAction f2 = new FailingFibAction(n - 2); + invokeAll(f1, f2); + result = f1.result + f2.result; + } + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks. getRawResult of a RecursiveAction returns null; + */ + public void testInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertNull(f.invoke()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.quietlyInvoke(); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * join/quietlyJoin of a forked task succeeds in the presence of interrupts + */ + public void testJoinIgnoresInterrupts() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + final Thread myself = Thread.currentThread(); + + // test join() + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + assertNull(f.join()); + Thread.interrupted(); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = new FibAction(8); + f.cancel(true); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + Thread.interrupted(); + checkCancelled(f); + } + + f = new FibAction(8); + f.completeExceptionally(new FJException()); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + Thread.interrupted(); + checkCompletedAbnormally(f, success); + } + + // test quietlyJoin() + f = new FibAction(8); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = new FibAction(8); + f.cancel(true); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + checkCancelled(f); + + f = new FibAction(8); + f.completeExceptionally(new FJException()); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + checkCompletedAbnormally(f, f.getException()); + }}; + checkInvoke(a); + a.reinitialize(); + checkInvoke(a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.get(5L, SECONDS)); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + checkInvoke(a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + checkInvoke(a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + checkInvoke(a); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * A reinitialized normally completed task may be re-invoked + */ + public void testReinitialize() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + assertNull(f.invoke()); + assertEquals(21, f.result); + checkCompletedNormally(f); + f.reinitialize(); + checkNotDone(f); + } + }}; + checkInvoke(a); + } + + /** + * A reinitialized abnormally completed task may be re-invoked + */ + public void testReinitializeAbnormal() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + f.reinitialize(); + checkNotDone(f); + } + }}; + checkInvoke(a); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * invoke task suppresses execution invoking complete + */ + public void testComplete() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.complete(null); + assertNull(f.invoke()); + assertEquals(0, f.result); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + invokeAll(f, g); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.result); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + invokeAll(f, g, h); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + checkCompletedNormally(g); + assertEquals(13, h.result); + }}; + checkInvoke(a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + checkCompletedNormally(g); + assertEquals(13, h.result); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + checkInvoke(a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FailingFibAction g = new FailingFibAction(9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction g = new FailingFibAction(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FailingFibAction g = new FailingFibAction(9); + FibAction h = new FibAction(7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + // CountedCompleter versions + + abstract static class CCF extends CountedCompleter { + int number; + int rnumber; + + public CCF(CountedCompleter parent, int n) { + super(parent, 1); + this.number = n; + } + + public final void compute() { + CountedCompleter p; + CCF f = this; + int n = number; + while (n >= 2) { + new RCCF(f, n - 2).fork(); + f = new LCCF(f, --n); + } + f.number = n; + f.onCompletion(f); + if ((p = f.getCompleter()) != null) + p.tryComplete(); + else + f.quietlyComplete(); + } + } + + static final class LCCF extends CCF { + public LCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + CCF p = (CCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.number = n; + else + number = n; + } + } + static final class RCCF extends CCF { + public RCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + CCF p = (CCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.rnumber = n; + else + number = n; + } + } + + // Version of CCF with forced failure in left completions + abstract static class FailingCCF extends CountedCompleter { + int number; + int rnumber; + + public FailingCCF(CountedCompleter parent, int n) { + super(parent, 1); + this.number = n; + } + + public final void compute() { + CountedCompleter p; + FailingCCF f = this; + int n = number; + while (n >= 2) { + new RFCCF(f, n - 2).fork(); + f = new LFCCF(f, --n); + } + f.number = n; + f.onCompletion(f); + if ((p = f.getCompleter()) != null) + p.tryComplete(); + else + f.quietlyComplete(); + } + } + + static final class LFCCF extends FailingCCF { + public LFCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + FailingCCF p = (FailingCCF)getCompleter(); + int n = number + rnumber; + if (p != null) + p.number = n; + else + number = n; + } + } + static final class RFCCF extends FailingCCF { + public RFCCF(CountedCompleter parent, int n) { + super(parent, n); + } + public final void onCompletion(CountedCompleter caller) { + completeExceptionally(new FJException()); + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvokeCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertNull(f.invoke()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvokeCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + f.quietlyInvoke(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGetCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(null, 8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGetCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(null, 8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPECC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(null, 8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + checkInvoke(a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvokeCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(null, 8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvokeCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(null, 8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + checkInvoke(a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(null, 8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGetCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(null, 8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGetCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingCCF f = new LFCCF(null, 8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(null, 8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + checkInvoke(a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvokeCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGetCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(null, 8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGetCC() throws Exception { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + CCF f = new LCCF(null, 8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + checkInvoke(a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoinCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + checkInvoke(a); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertNull(getPool()); + }}; + assertNull(a.invoke()); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * setRawResult(null) succeeds + */ + public void testSetRawResultCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + setRawResult(null); + assertNull(getRawResult()); + }}; + assertNull(a.invoke()); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally2CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + CCF g = new LCCF(null, 9); + invokeAll(f, g); + assertEquals(21, f.number); + assertEquals(34, g.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.number); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + CCF g = new LCCF(null, 9); + CCF h = new LCCF(null, 7); + invokeAll(f, g, h); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + checkInvoke(a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollectionCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + CCF g = new LCCF(null, 9); + CCF h = new LCCF(null, 7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPECC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + CCF g = new LCCF(null, 9); + CCF h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + checkInvoke(a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + FailingCCF g = new LFCCF(null, 9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF g = new LFCCF(null, 9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3CC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + CCF f = new LCCF(null, 8); + FailingCCF g = new LFCCF(null, 9); + CCF h = new LCCF(null, 7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + checkInvoke(a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollectionCC() { + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingCCF f = new LFCCF(null, 8); + CCF g = new LCCF(null, 9); + CCF h = new LCCF(null, 7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + checkInvoke(a); + } + + /** + * awaitQuiescence by a worker is equivalent in effect to + * ForkJoinTask.helpQuiesce() + */ + public void testAwaitQuiescence1() throws Exception { + final ForkJoinPool p = new ForkJoinPool(); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + assertTrue(p.isQuiescent()); + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(p, ForkJoinTask.getPool()); + boolean quiescent = p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS); + assertTrue(quiescent); + assertFalse(p.isQuiescent()); + while (!f.isDone()) { + assertFalse(p.getAsyncMode()); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + Thread.yield(); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + assertFalse(p.isQuiescent()); + assertEquals(0, ForkJoinTask.getQueuedTaskCount()); + assertEquals(21, f.result); + }}; + p.execute(a); + while (!a.isDone() || !p.isQuiescent()) { + assertFalse(p.getAsyncMode()); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + Thread.yield(); + } + assertEquals(0, p.getQueuedTaskCount()); + assertFalse(p.getAsyncMode()); + assertEquals(0, p.getQueuedSubmissionCount()); + assertFalse(p.hasQueuedSubmissions()); + while (p.getActiveThreadCount() != 0 + && millisElapsedSince(startTime) < LONG_DELAY_MS) + Thread.yield(); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * awaitQuiescence returns when pool isQuiescent() or the indicated + * timeout elapsed + */ + public void testAwaitQuiescence2() throws Exception { + /** + * """It is possible to disable or limit the use of threads in the + * common pool by setting the parallelism property to zero. However + * doing so may cause unjoined tasks to never be executed.""" + */ + if ("0".equals(System.getProperty( + "java.util.concurrent.ForkJoinPool.common.parallelism"))) + return; + final ForkJoinPool p = new ForkJoinPool(); + try (PoolCleaner cleaner = cleaner(p)) { + assertTrue(p.isQuiescent()); + final long startTime = System.nanoTime(); + ForkJoinTask a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + while (!f.isDone() + && millisElapsedSince(startTime) < LONG_DELAY_MS) { + assertFalse(p.getAsyncMode()); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + Thread.yield(); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + assertEquals(0, ForkJoinTask.getQueuedTaskCount()); + assertEquals(21, f.result); + }}; + p.execute(a); + assertTrue(p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isQuiescent()); + assertTrue(a.isDone()); + assertEquals(0, p.getQueuedTaskCount()); + assertFalse(p.getAsyncMode()); + assertEquals(0, p.getQueuedSubmissionCount()); + assertFalse(p.hasQueuedSubmissions()); + while (p.getActiveThreadCount() != 0 + && millisElapsedSince(startTime) < LONG_DELAY_MS) + Thread.yield(); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java b/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java new file mode 100644 index 00000000000..282557d97b7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java @@ -0,0 +1,991 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.Future; +import java.util.concurrent.RecursiveTask; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ForkJoinPoolTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ForkJoinPoolTest.class); + } + + /* + * Testing coverage notes: + * + * 1. shutdown and related methods are tested via super.joinPool. + * + * 2. newTaskFor and adapters are tested in submit/invoke tests + * + * 3. We cannot portably test monitoring methods such as + * getStealCount() since they rely ultimately on random task + * stealing that may cause tasks not to be stolen/propagated + * across threads, especially on uniprocessors. + * + * 4. There are no independently testable ForkJoinWorkerThread + * methods, but they are covered here and in task tests. + */ + + // Some classes to test extension and factory methods + + static class MyHandler implements Thread.UncaughtExceptionHandler { + volatile int catches = 0; + public void uncaughtException(Thread t, Throwable e) { + ++catches; + } + } + + static class MyError extends Error {} + + // to test handlers + static class FailingFJWSubclass extends ForkJoinWorkerThread { + public FailingFJWSubclass(ForkJoinPool p) { super(p) ; } + protected void onStart() { super.onStart(); throw new MyError(); } + } + + static class FailingThreadFactory + implements ForkJoinPool.ForkJoinWorkerThreadFactory { + volatile int calls = 0; + public ForkJoinWorkerThread newThread(ForkJoinPool p) { + if (++calls > 1) return null; + return new FailingFJWSubclass(p); + } + } + + static class SubFJP extends ForkJoinPool { // to expose protected + SubFJP() { super(1); } + public int drainTasksTo(Collection> c) { + return super.drainTasksTo(c); + } + public ForkJoinTask pollSubmission() { + return super.pollSubmission(); + } + } + + static class ManagedLocker implements ForkJoinPool.ManagedBlocker { + final ReentrantLock lock; + boolean hasLock = false; + ManagedLocker(ReentrantLock lock) { this.lock = lock; } + public boolean block() { + if (!hasLock) + lock.lock(); + return true; + } + public boolean isReleasable() { + return hasLock || (hasLock = lock.tryLock()); + } + } + + // A simple recursive task for testing + static final class FibTask extends RecursiveTask { + final int number; + FibTask(int n) { number = n; } + protected Integer compute() { + int n = number; + if (n <= 1) + return n; + FibTask f1 = new FibTask(n - 1); + f1.fork(); + return (new FibTask(n - 2)).compute() + f1.join(); + } + } + + // A failing task for testing + static final class FailingTask extends ForkJoinTask { + public final Void getRawResult() { return null; } + protected final void setRawResult(Void mustBeNull) { } + protected final boolean exec() { throw new Error(); } + FailingTask() {} + } + + // Fib needlessly using locking to test ManagedBlockers + static final class LockingFibTask extends RecursiveTask { + final int number; + final ManagedLocker locker; + final ReentrantLock lock; + LockingFibTask(int n, ManagedLocker locker, ReentrantLock lock) { + number = n; + this.locker = locker; + this.lock = lock; + } + protected Integer compute() { + int n; + LockingFibTask f1 = null; + LockingFibTask f2 = null; + locker.block(); + n = number; + if (n > 1) { + f1 = new LockingFibTask(n - 1, locker, lock); + f2 = new LockingFibTask(n - 2, locker, lock); + } + lock.unlock(); + if (n <= 1) + return n; + else { + f1.fork(); + return f2.compute() + f1.join(); + } + } + } + + /** + * Successfully constructed pool reports default factory, + * parallelism and async mode policies, no active threads or + * tasks, and quiescent running state. + */ + public void testDefaultInitialState() { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory, + p.getFactory()); + assertFalse(p.getAsyncMode()); + assertEquals(0, p.getActiveThreadCount()); + assertEquals(0, p.getStealCount()); + assertEquals(0, p.getQueuedTaskCount()); + assertEquals(0, p.getQueuedSubmissionCount()); + assertFalse(p.hasQueuedSubmissions()); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + } + } + + /** + * Constructor throws if size argument is less than zero + */ + public void testConstructor1() { + try { + new ForkJoinPool(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if factory argument is null + */ + public void testConstructor2() { + try { + new ForkJoinPool(1, null, null, false); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getParallelism returns size set in constructor + */ + public void testGetParallelism() { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getParallelism()); + } + } + + /** + * getPoolSize returns number of started workers. + */ + public void testGetPoolSize() { + final CountDownLatch taskStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + final ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(0, p.getActiveThreadCount()); + final Runnable task = new CheckedRunnable() { + public void realRun() throws InterruptedException { + taskStarted.countDown(); + assertEquals(1, p.getPoolSize()); + assertEquals(1, p.getActiveThreadCount()); + done.await(); + }}; + Future future = p.submit(task); + await(taskStarted); + assertEquals(1, p.getPoolSize()); + assertEquals(1, p.getActiveThreadCount()); + done.countDown(); + } + assertEquals(0, p.getPoolSize()); + assertEquals(0, p.getActiveThreadCount()); + } + + /** + * awaitTermination on a non-shutdown pool times out + */ + public void testAwaitTermination_timesOut() throws InterruptedException { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isTerminated()); + assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS)); + assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS)); + assertFalse(p.awaitTermination(-1L, NANOSECONDS)); + assertFalse(p.awaitTermination(-1L, MILLISECONDS)); + assertFalse(p.awaitTermination(0L, NANOSECONDS)); + assertFalse(p.awaitTermination(0L, MILLISECONDS)); + long timeoutNanos = 999999L; + long startTime = System.nanoTime(); + assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS)); + assertTrue(System.nanoTime() - startTime >= timeoutNanos); + assertFalse(p.isTerminated()); + startTime = System.nanoTime(); + long timeoutMillis = timeoutMillis(); + assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + assertFalse(p.isTerminated()); + p.shutdown(); + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + } + + /** + * setUncaughtExceptionHandler changes handler for uncaught exceptions. + * + * Additionally tests: Overriding ForkJoinWorkerThread.onStart + * performs its defined action + */ + public void testSetUncaughtExceptionHandler() throws InterruptedException { + final CountDownLatch uehInvoked = new CountDownLatch(1); + final Thread.UncaughtExceptionHandler ueh = + new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread t, Throwable e) { + threadAssertTrue(e instanceof MyError); + threadAssertTrue(t instanceof FailingFJWSubclass); + uehInvoked.countDown(); + }}; + ForkJoinPool p = new ForkJoinPool(1, new FailingThreadFactory(), + ueh, false); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(ueh, p.getUncaughtExceptionHandler()); + try { + p.execute(new FibTask(8)); + await(uehInvoked); + } finally { + p.shutdownNow(); // failure might have prevented processing task + } + } + } + + /** + * After invoking a single task, isQuiescent eventually becomes + * true, at which time queues are empty, threads are not active, + * the task has completed successfully, and construction + * parameters continue to hold + */ + public void testIsQuiescent() throws Exception { + ForkJoinPool p = new ForkJoinPool(2); + try (PoolCleaner cleaner = cleaner(p)) { + assertTrue(p.isQuiescent()); + long startTime = System.nanoTime(); + FibTask f = new FibTask(20); + p.invoke(f); + assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory, + p.getFactory()); + while (! p.isQuiescent()) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + assertFalse(p.getAsyncMode()); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + Thread.yield(); + } + + assertTrue(p.isQuiescent()); + assertFalse(p.getAsyncMode()); + assertEquals(0, p.getQueuedTaskCount()); + assertEquals(0, p.getQueuedSubmissionCount()); + assertFalse(p.hasQueuedSubmissions()); + while (p.getActiveThreadCount() != 0 + && millisElapsedSince(startTime) < LONG_DELAY_MS) + Thread.yield(); + assertFalse(p.isShutdown()); + assertFalse(p.isTerminating()); + assertFalse(p.isTerminated()); + assertTrue(f.isDone()); + assertEquals(6765, (int) f.get()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * Completed submit(ForkJoinTask) returns result + */ + public void testSubmitForkJoinTask() throws Throwable { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + ForkJoinTask f = p.submit(new FibTask(8)); + assertEquals(21, (int) f.get()); + } + } + + /** + * A task submitted after shutdown is rejected + */ + public void testSubmitAfterShutdown() { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + p.shutdown(); + assertTrue(p.isShutdown()); + try { + ForkJoinTask f = p.submit(new FibTask(8)); + shouldThrow(); + } catch (RejectedExecutionException success) {} + } + } + + /** + * Pool maintains parallelism when using ManagedBlocker + */ + public void testBlockingForkJoinTask() throws Throwable { + ForkJoinPool p = new ForkJoinPool(4); + try { + ReentrantLock lock = new ReentrantLock(); + ManagedLocker locker = new ManagedLocker(lock); + ForkJoinTask f = new LockingFibTask(20, locker, lock); + p.execute(f); + assertEquals(6765, (int) f.get()); + } finally { + p.shutdownNow(); // don't wait out shutdown + } + } + + /** + * pollSubmission returns unexecuted submitted task, if present + */ + public void testPollSubmission() { + final CountDownLatch done = new CountDownLatch(1); + SubFJP p = new SubFJP(); + try (PoolCleaner cleaner = cleaner(p)) { + ForkJoinTask a = p.submit(awaiter(done)); + ForkJoinTask b = p.submit(awaiter(done)); + ForkJoinTask c = p.submit(awaiter(done)); + ForkJoinTask r = p.pollSubmission(); + assertTrue(r == a || r == b || r == c); + assertFalse(r.isDone()); + done.countDown(); + } + } + + /** + * drainTasksTo transfers unexecuted submitted tasks, if present + */ + public void testDrainTasksTo() { + final CountDownLatch done = new CountDownLatch(1); + SubFJP p = new SubFJP(); + try (PoolCleaner cleaner = cleaner(p)) { + ForkJoinTask a = p.submit(awaiter(done)); + ForkJoinTask b = p.submit(awaiter(done)); + ForkJoinTask c = p.submit(awaiter(done)); + ArrayList al = new ArrayList(); + p.drainTasksTo(al); + assertTrue(al.size() > 0); + for (ForkJoinTask r : al) { + assertTrue(r == a || r == b || r == c); + assertFalse(r.isDone()); + } + done.countDown(); + } + } + + // FJ Versions of AbstractExecutorService tests + + /** + * execute(runnable) runs it to completion + */ + public void testExecuteRunnable() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + final AtomicBoolean done = new AtomicBoolean(false); + Future future = e.submit(new CheckedRunnable() { + public void realRun() { + done.set(true); + }}); + assertNull(future.get()); + assertNull(future.get(0, MILLISECONDS)); + assertTrue(done.get()); + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + } + } + + /** + * Completed submit(callable) returns result + */ + public void testSubmitCallable() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new StringTask()); + assertSame(TEST_STRING, future.get()); + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + } + } + + /** + * Completed submit(runnable) returns successfully + */ + public void testSubmitRunnable() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable()); + assertNull(future.get()); + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + } + } + + /** + * Completed submit(runnable, result) returns result + */ + public void testSubmitRunnable2() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + assertSame(TEST_STRING, future.get()); + assertTrue(future.isDone()); + assertFalse(future.isCancelled()); + } + } + + /** + * A submitted privileged action runs to completion + */ + public void testSubmitPrivilegedAction() throws Exception { + final Callable callable = Executors.callable(new PrivilegedAction() { + public Object run() { return TEST_STRING; }}); + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(callable); + assertSame(TEST_STRING, future.get()); + } + }}; + + runWithPermissions(r, new RuntimePermission("modifyThread")); + } + + /** + * A submitted privileged exception action runs to completion + */ + public void testSubmitPrivilegedExceptionAction() throws Exception { + final Callable callable = + Executors.callable(new PrivilegedExceptionAction() { + public Object run() { return TEST_STRING; }}); + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(callable); + assertSame(TEST_STRING, future.get()); + } + }}; + + runWithPermissions(r, new RuntimePermission("modifyThread")); + } + + /** + * A submitted failed privileged exception action reports exception + */ + public void testSubmitFailedPrivilegedExceptionAction() throws Exception { + final Callable callable = + Executors.callable(new PrivilegedExceptionAction() { + public Object run() { throw new IndexOutOfBoundsException(); }}); + Runnable r = new CheckedRunnable() { + public void realRun() throws Exception { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(callable); + try { + future.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof IndexOutOfBoundsException); + } + } + }}; + + runWithPermissions(r, new RuntimePermission("modifyThread")); + } + + /** + * execute(null runnable) throws NullPointerException + */ + public void testExecuteNullRunnable() { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + Future future = e.submit((Runnable) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * submit(null callable) throws NullPointerException + */ + public void testSubmitNullCallable() { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + Future future = e.submit((Callable) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * submit(callable).get() throws InterruptedException if interrupted + */ + public void testInterruptedSubmit() throws InterruptedException { + final CountDownLatch submitted = new CountDownLatch(1); + final CountDownLatch quittingTime = new CountDownLatch(1); + final Callable awaiter = new CheckedCallable() { + public Void realCall() throws InterruptedException { + assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS)); + return null; + }}; + final ExecutorService p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p, quittingTime)) { + Thread t = new Thread(new CheckedInterruptedRunnable() { + public void realRun() throws Exception { + Future future = p.submit(awaiter); + submitted.countDown(); + future.get(); + }}); + t.start(); + await(submitted); + t.interrupt(); + awaitTermination(t); + } + } + + /** + * get of submit(callable) throws ExecutionException if callable + * throws exception + */ + public void testSubmitEE() throws Throwable { + ForkJoinPool p = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.submit(new Callable() { + public Object call() { throw new ArithmeticException(); }}) + .get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof ArithmeticException); + } + } + } + + /** + * invokeAny(null) throws NullPointerException + */ + public void testInvokeAny1() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IllegalArgumentException + */ + public void testInvokeAny2() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NullPointerException if c has a single null element + */ + public void testInvokeAny3() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(c) throws NullPointerException if c has null elements + */ + public void testInvokeAny4() throws Throwable { + CountDownLatch latch = new CountDownLatch(1); + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * invokeAny(c) throws ExecutionException if no task in c completes + */ + public void testInvokeAny5() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task in c if at least one completes + */ + public void testInvokeAny6() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NullPointerException + */ + public void testInvokeAll1() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws InterruptedException { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> r + = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NullPointerException if c has null elements + */ + public void testInvokeAll3() throws InterruptedException { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of returned element of invokeAll(c) throws + * ExecutionException on failed task + */ + public void testInvokeAll4() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks in c + */ + public void testInvokeAll5() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NullPointerException + */ + public void testTimedInvokeAny1() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(null time unit) throws NullPointerException + */ + public void testTimedInvokeAnyNullTimeUnit() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IllegalArgumentException + */ + public void testTimedInvokeAny2() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NullPointerException if c has null elements + */ + public void testTimedInvokeAny3() throws Throwable { + CountDownLatch latch = new CountDownLatch(1); + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task in c + */ + public void testTimedInvokeAny5() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NullPointerException + */ + public void testTimedInvokeAll1() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(null time unit) throws NullPointerException + */ + public void testTimedInvokeAllNullTimeUnit() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws InterruptedException { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> r + = e.invokeAll(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NullPointerException if c has null elements + */ + public void testTimedInvokeAll3() throws InterruptedException { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of returned element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Throwable { + ExecutorService e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures + = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks in c + */ + public void testTimedInvokeAll5() throws Throwable { + ForkJoinPool e = new ForkJoinPool(1); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures + = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java b/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java new file mode 100644 index 00000000000..c8fe1bb9986 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java @@ -0,0 +1,1228 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.TimeoutException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ForkJoinTask8Test extends JSR166TestCase { + + /* + * Testing notes: This differs from ForkJoinTaskTest mainly by + * defining a version of BinaryAsyncAction that uses JDK8 task + * tags for control state, thereby testing getForkJoinTaskTag, + * setForkJoinTaskTag, and compareAndSetForkJoinTaskTag across + * various contexts. Most of the test methods using it are + * otherwise identical, but omitting retest of those dealing with + * cancellation, which is not represented in this tag scheme. + */ + + static final short INITIAL_STATE = -1; + static final short COMPLETE_STATE = 0; + static final short EXCEPTION_STATE = 1; + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ForkJoinTask8Test.class); + } + + // Runs with "mainPool" use > 1 thread. singletonPool tests use 1 + static final int mainPoolSize = + Math.max(2, Runtime.getRuntime().availableProcessors()); + + private static ForkJoinPool mainPool() { + return new ForkJoinPool(mainPoolSize); + } + + private static ForkJoinPool singletonPool() { + return new ForkJoinPool(1); + } + + private static ForkJoinPool asyncSingletonPool() { + return new ForkJoinPool(1, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + + // Compute fib naively and efficiently + final int[] fib; + { + int[] fib = new int[10]; + fib[0] = 0; + fib[1] = 1; + for (int i = 2; i < fib.length; i++) + fib[i] = fib[i - 1] + fib[i - 2]; + this.fib = fib; + } + + private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) { + try (PoolCleaner cleaner = cleaner(pool)) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + assertNull(pool.invoke(a)); + + assertTrue(a.isDone()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + } + } + + void checkNotDone(ForkJoinTask a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + if (a instanceof BinaryAsyncAction) + assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == INITIAL_STATE); + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(ForkJoinTask a) { + checkCompletedNormally(a, null); + } + + void checkCompletedNormally(ForkJoinTask a, T expected) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertSame(expected, a.getRawResult()); + if (a instanceof BinaryAsyncAction) + assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == COMPLETE_STATE); + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + assertSame(expected, a.join()); + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertSame(expected, a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertSame(expected, a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(ForkJoinTask a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + if (a instanceof BinaryAsyncAction) + assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() != INITIAL_STATE); + + try { + Thread.currentThread().interrupt(); + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(t.getClass(), expected.getClass()); + } + Thread.interrupted(); + + { + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + public static final class FJException extends RuntimeException { + FJException() { super(); } + } + + abstract static class BinaryAsyncAction extends ForkJoinTask { + + private volatile BinaryAsyncAction parent; + + private volatile BinaryAsyncAction sibling; + + protected BinaryAsyncAction() { + setForkJoinTaskTag(INITIAL_STATE); + } + + public final Void getRawResult() { return null; } + protected final void setRawResult(Void mustBeNull) { } + + public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { + x.parent = y.parent = this; + x.sibling = y; + y.sibling = x; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + if (this.getForkJoinTaskTag() != COMPLETE_STATE || + x.getForkJoinTaskTag() != COMPLETE_STATE || + y.getForkJoinTaskTag() != COMPLETE_STATE) { + completeThisExceptionally(new FJException()); + } + } + + protected boolean onException() { + return true; + } + + public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { + linkSubtasks(x, y); + y.fork(); + x.fork(); + } + + private void completeThis() { + setForkJoinTaskTag(COMPLETE_STATE); + super.complete(null); + } + + private void completeThisExceptionally(Throwable ex) { + setForkJoinTaskTag(EXCEPTION_STATE); + super.completeExceptionally(ex); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + if (super.cancel(mayInterruptIfRunning)) { + completeExceptionally(new FJException()); + return true; + } + return false; + } + + public final void complete() { + BinaryAsyncAction a = this; + for (;;) { + BinaryAsyncAction s = a.sibling; + BinaryAsyncAction p = a.parent; + a.sibling = null; + a.parent = null; + a.completeThis(); + if (p == null || + p.compareAndSetForkJoinTaskTag(INITIAL_STATE, COMPLETE_STATE)) + break; + try { + p.onComplete(a, s); + } catch (Throwable rex) { + p.completeExceptionally(rex); + return; + } + a = p; + } + } + + public final void completeExceptionally(Throwable ex) { + for (BinaryAsyncAction a = this;;) { + a.completeThisExceptionally(ex); + BinaryAsyncAction s = a.sibling; + if (s != null && !s.isDone()) + s.completeExceptionally(ex); + if ((a = a.parent) == null) + break; + } + } + + public final BinaryAsyncAction getParent() { + return parent; + } + + public BinaryAsyncAction getSibling() { + return sibling; + } + + public void reinitialize() { + parent = sibling = null; + super.reinitialize(); + } + + } + + final class AsyncFib extends BinaryAsyncAction { + int number; + int expectedResult; + public AsyncFib(int number) { + this.number = number; + this.expectedResult = fib[number]; + } + + public final boolean exec() { + try { + AsyncFib f = this; + int n = f.number; + while (n > 1) { + AsyncFib p = f; + AsyncFib r = new AsyncFib(n - 2); + f = new AsyncFib(--n); + p.linkSubtasks(r, f); + r.fork(); + } + f.complete(); + } + catch (Throwable ex) { + compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE); + } + if (getForkJoinTaskTag() == EXCEPTION_STATE) + throw new FJException(); + return false; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + number = ((AsyncFib)x).number + ((AsyncFib)y).number; + super.onComplete(x, y); + } + + public void checkCompletedNormally() { + assertEquals(expectedResult, number); + ForkJoinTask8Test.this.checkCompletedNormally(this); + } + } + + static final class FailingAsyncFib extends BinaryAsyncAction { + int number; + public FailingAsyncFib(int n) { + this.number = n; + } + + public final boolean exec() { + try { + FailingAsyncFib f = this; + int n = f.number; + while (n > 1) { + FailingAsyncFib p = f; + FailingAsyncFib r = new FailingAsyncFib(n - 2); + f = new FailingAsyncFib(--n); + p.linkSubtasks(r, f); + r.fork(); + } + f.complete(); + } + catch (Throwable ex) { + compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE); + } + if (getForkJoinTaskTag() == EXCEPTION_STATE) + throw new FJException(); + return false; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + completeExceptionally(new FJException()); + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvoke() { + testInvoke(mainPool()); + } + public void testInvoke_Singleton() { + testInvoke(singletonPool()); + } + public void testInvoke(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertNull(f.invoke()); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + testQuietlyInvoke(mainPool()); + } + public void testQuietlyInvoke_Singleton() { + testQuietlyInvoke(singletonPool()); + } + public void testQuietlyInvoke(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.quietlyInvoke(); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + testForkJoin(mainPool()); + } + public void testForkJoin_Singleton() { + testForkJoin(singletonPool()); + } + public void testForkJoin(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.join()); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + testForkGet(mainPool()); + } + public void testForkGet_Singleton() { + testForkGet(singletonPool()); + } + public void testForkGet(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get()); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + testForkTimedGet(mainPool()); + } + public void testForkTimedGet_Singleton() { + testForkTimedGet(singletonPool()); + } + public void testForkTimedGet(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * timed get with null time unit throws NullPointerException + */ + public void testForkTimedGetNullTimeUnit() { + testForkTimedGetNullTimeUnit(mainPool()); + } + public void testForkTimedGetNullTimeUnit_Singleton() { + testForkTimedGet(singletonPool()); + } + public void testForkTimedGetNullTimeUnit(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(pool, a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + testForkQuietlyJoin(mainPool()); + } + public void testForkQuietlyJoin_Singleton() { + testForkQuietlyJoin(singletonPool()); + } + public void testForkQuietlyJoin(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesce() { + testForkHelpQuiesce(mainPool()); + } + public void testForkHelpQuiesce_Singleton() { + testForkHelpQuiesce(singletonPool()); + } + public void testForkHelpQuiesce(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + helpQuiesce(); + assertEquals(0, getQueuedTaskCount()); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + testAbnormalInvoke(mainPool()); + } + public void testAbnormalInvoke_Singleton() { + testAbnormalInvoke(singletonPool()); + } + public void testAbnormalInvoke(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + testAbnormalQuietlyInvoke(mainPool()); + } + public void testAbnormalQuietlyInvoke_Singleton() { + testAbnormalQuietlyInvoke(singletonPool()); + } + public void testAbnormalQuietlyInvoke(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(pool, a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + testAbnormalForkJoin(mainPool()); + } + public void testAbnormalForkJoin_Singleton() { + testAbnormalForkJoin(singletonPool()); + } + public void testAbnormalForkJoin(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + testAbnormalForkGet(mainPool()); + } + public void testAbnormalForkGet_Singleton() { + testAbnormalForkJoin(singletonPool()); + } + public void testAbnormalForkGet(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + testAbnormalForkTimedGet(mainPool()); + } + public void testAbnormalForkTimedGet_Singleton() { + testAbnormalForkTimedGet(singletonPool()); + } + public void testAbnormalForkTimedGet(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + testAbnormalForkQuietlyJoin(mainPool()); + } + public void testAbnormalForkQuietlyJoin_Singleton() { + testAbnormalForkQuietlyJoin(singletonPool()); + } + public void testAbnormalForkQuietlyJoin(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(pool, a); + } + + /** + * getPool of executing task returns its pool + */ + public void testGetPool() { + testGetPool(mainPool()); + } + public void testGetPool_Singleton() { + testGetPool(singletonPool()); + } + public void testGetPool(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertSame(pool, getPool()); + }}; + testInvokeOnPool(pool, a); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertNull(getPool()); + }}; + assertNull(a.invoke()); + } + + /** + * inForkJoinPool of executing task returns true + */ + public void testInForkJoinPool() { + testInForkJoinPool(mainPool()); + } + public void testInForkJoinPool_Singleton() { + testInForkJoinPool(singletonPool()); + } + public void testInForkJoinPool(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertTrue(inForkJoinPool()); + }}; + testInvokeOnPool(pool, a); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * setRawResult(null) succeeds + */ + public void testSetRawResult() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + setRawResult(null); + assertNull(getRawResult()); + }}; + assertNull(a.invoke()); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally() { + testCompleteExceptionally(mainPool()); + } + public void testCompleteExceptionally_Singleton() { + testCompleteExceptionally(singletonPool()); + } + public void testCompleteExceptionally(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + testInvokeAll1(mainPool()); + } + public void testInvokeAll1_Singleton() { + testInvokeAll1(singletonPool()); + } + public void testInvokeAll1(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + invokeAll(f); + f.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + testInvokeAll2(mainPool()); + } + public void testInvokeAll2_Singleton() { + testInvokeAll2(singletonPool()); + } + public void testInvokeAll2(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib[] tasks = { + new AsyncFib(8), + new AsyncFib(9), + }; + invokeAll(tasks[0], tasks[1]); + for (AsyncFib task : tasks) assertTrue(task.isDone()); + for (AsyncFib task : tasks) task.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + testInvokeAll3(mainPool()); + } + public void testInvokeAll3_Singleton() { + testInvokeAll3(singletonPool()); + } + public void testInvokeAll3(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib[] tasks = { + new AsyncFib(8), + new AsyncFib(9), + new AsyncFib(7), + }; + invokeAll(tasks[0], tasks[1], tasks[2]); + for (AsyncFib task : tasks) assertTrue(task.isDone()); + for (AsyncFib task : tasks) task.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + testInvokeAllCollection(mainPool()); + } + public void testInvokeAllCollection_Singleton() { + testInvokeAllCollection(singletonPool()); + } + public void testInvokeAllCollection(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib[] tasks = { + new AsyncFib(8), + new AsyncFib(9), + new AsyncFib(7), + }; + invokeAll(Arrays.asList(tasks)); + for (AsyncFib task : tasks) assertTrue(task.isDone()); + for (AsyncFib task : tasks) task.checkCompletedNormally(); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(tasks) with any null task throws NullPointerException + */ + public void testInvokeAllNullTask() { + testInvokeAllNullTask(mainPool()); + } + public void testInvokeAllNullTask_Singleton() { + testInvokeAllNullTask(singletonPool()); + } + public void testInvokeAllNullTask(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib nul = null; + Runnable[] throwingActions = { + () -> invokeAll(nul), + () -> invokeAll(nul, nul), + () -> invokeAll(new AsyncFib(8), new AsyncFib(9), nul), + () -> invokeAll(new AsyncFib(8), nul, new AsyncFib(9)), + () -> invokeAll(nul, new AsyncFib(8), new AsyncFib(9)), + }; + assertThrows(NullPointerException.class, throwingActions); + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + testAbnormalInvokeAll1(mainPool()); + } + public void testAbnormalInvokeAll1_Singleton() { + testAbnormalInvokeAll1(singletonPool()); + } + public void testAbnormalInvokeAll1(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib g = new FailingAsyncFib(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + testAbnormalInvokeAll2(mainPool()); + } + public void testAbnormalInvokeAll2_Singleton() { + testAbnormalInvokeAll2(singletonPool()); + } + public void testAbnormalInvokeAll2(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + ForkJoinTask[] tasks = { f, g }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks[0], tasks[1]); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + testAbnormalInvokeAll3(mainPool()); + } + public void testAbnormalInvokeAll3_Singleton() { + testAbnormalInvokeAll3(singletonPool()); + } + public void testAbnormalInvokeAll3(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks[0], tasks[1], tasks[2]); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + testAbnormalInvokeAllCollection(mainPool()); + } + public void testAbnormalInvokeAllCollection_Singleton() { + testAbnormalInvokeAllCollection(singletonPool()); + } + public void testAbnormalInvokeAllCollection(ForkJoinPool pool) { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(Arrays.asList(tasks)); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(pool, a); + } + + /** + * tryUnfork returns true for most recent unexecuted task, + * and suppresses execution + */ + public void testTryUnfork() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertTrue(f.tryUnfork()); + helpQuiesce(); + checkNotDone(f); + g.checkCompletedNormally(); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * getSurplusQueuedTaskCount returns > 0 when + * there are more tasks than threads + */ + public void testGetSurplusQueuedTaskCount() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib h = new AsyncFib(7); + assertSame(h, h.fork()); + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertTrue(getSurplusQueuedTaskCount() > 0); + helpQuiesce(); + assertEquals(0, getSurplusQueuedTaskCount()); + f.checkCompletedNormally(); + g.checkCompletedNormally(); + h.checkCompletedNormally(); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns most recent unexecuted task. + */ + public void testPeekNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, peekNextLocalTask()); + assertNull(f.join()); + f.checkCompletedNormally(); + helpQuiesce(); + g.checkCompletedNormally(); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollNextLocalTask returns most recent unexecuted task without + * executing it + */ + public void testPollNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, pollNextLocalTask()); + helpQuiesce(); + checkNotDone(f); + g.checkCompletedNormally(); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it + */ + public void testPollTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, pollTask()); + helpQuiesce(); + checkNotDone(f); + g.checkCompletedNormally(); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns least recent unexecuted task in async mode + */ + public void testPeekNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, peekNextLocalTask()); + assertNull(f.join()); + helpQuiesce(); + f.checkCompletedNormally(); + g.checkCompletedNormally(); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollNextLocalTask returns least recent unexecuted task without + * executing it, in async mode + */ + public void testPollNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, pollNextLocalTask()); + helpQuiesce(); + f.checkCompletedNormally(); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it, in + * async mode + */ + public void testPollTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, pollTask()); + helpQuiesce(); + f.checkCompletedNormally(); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * ForkJoinTask.quietlyComplete returns when task completes + * normally without setting a value. The most recent value + * established by setRawResult(V) (or null by default) is returned + * from invoke. + */ + public void testQuietlyComplete() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.quietlyComplete(); + assertEquals(8, f.number); + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertTrue(f.isCompletedNormally()); + assertFalse(f.isCompletedAbnormally()); + assertNull(f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + // jdk9 + + /** + * pollSubmission returns unexecuted submitted task, if present + */ + public void testPollSubmission() { + final CountDownLatch done = new CountDownLatch(1); + final ForkJoinTask a = ForkJoinTask.adapt(awaiter(done)); + final ForkJoinTask b = ForkJoinTask.adapt(awaiter(done)); + final ForkJoinTask c = ForkJoinTask.adapt(awaiter(done)); + final ForkJoinPool p = singletonPool(); + try (PoolCleaner cleaner = cleaner(p, done)) { + Thread external = new Thread(new CheckedRunnable() { + public void realRun() { + p.execute(a); + p.execute(b); + p.execute(c); + }}); + RecursiveAction s = new CheckedRecursiveAction() { + protected void realCompute() { + external.start(); + try { + external.join(); + } catch (Exception ex) { + threadUnexpectedException(ex); + } + assertTrue(p.hasQueuedSubmissions()); + assertTrue(Thread.currentThread() instanceof ForkJoinWorkerThread); + ForkJoinTask r = ForkJoinTask.pollSubmission(); + assertTrue(r == a || r == b || r == c); + assertFalse(r.isDone()); + }}; + p.invoke(s); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java b/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java new file mode 100644 index 00000000000..7000c1a4bb5 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java @@ -0,0 +1,1685 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ForkJoinTaskTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ForkJoinTaskTest.class); + } + + // Runs with "mainPool" use > 1 thread. singletonPool tests use 1 + static final int mainPoolSize = + Math.max(2, Runtime.getRuntime().availableProcessors()); + + private static ForkJoinPool mainPool() { + return new ForkJoinPool(mainPoolSize); + } + + private static ForkJoinPool singletonPool() { + return new ForkJoinPool(1); + } + + private static ForkJoinPool asyncSingletonPool() { + return new ForkJoinPool(1, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + + private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) { + try (PoolCleaner cleaner = cleaner(pool)) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + assertNull(pool.invoke(a)); + + assertTrue(a.isDone()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + } + } + + void checkNotDone(ForkJoinTask a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(ForkJoinTask a) { + checkCompletedNormally(a, null); + } + + void checkCompletedNormally(ForkJoinTask a, T expected) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertSame(expected, a.getRawResult()); + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + assertSame(expected, a.join()); + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + { + Thread.currentThread().interrupt(); + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + Thread.interrupted(); + } + + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertSame(expected, a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertSame(expected, a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCancelled(ForkJoinTask a) { + assertTrue(a.isDone()); + assertTrue(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertTrue(a.getException() instanceof CancellationException); + assertNull(a.getRawResult()); + assertTrue(a.cancel(false)); + assertTrue(a.cancel(true)); + + try { + Thread.currentThread().interrupt(); + a.join(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + Thread.interrupted(); + + { + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + } + + try { + a.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(ForkJoinTask a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + + try { + Thread.currentThread().interrupt(); + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(t.getClass(), expected.getClass()); + } + Thread.interrupted(); + + { + long startTime = System.nanoTime(); + a.quietlyJoin(); // should be no-op + assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + /* + * Testing coverage notes: + * + * To test extension methods and overrides, most tests use + * BinaryAsyncAction extension class that processes joins + * differently than supplied Recursive forms. + */ + + public static final class FJException extends RuntimeException { + FJException() { super(); } + } + + abstract static class BinaryAsyncAction extends ForkJoinTask { + private volatile int controlState; + + static final AtomicIntegerFieldUpdater controlStateUpdater = + AtomicIntegerFieldUpdater.newUpdater(BinaryAsyncAction.class, + "controlState"); + + private volatile BinaryAsyncAction parent; + + private volatile BinaryAsyncAction sibling; + + protected BinaryAsyncAction() { + } + + public final Void getRawResult() { return null; } + protected final void setRawResult(Void mustBeNull) { } + + public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { + x.parent = y.parent = this; + x.sibling = y; + y.sibling = x; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + } + + protected boolean onException() { + return true; + } + + public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { + linkSubtasks(x, y); + y.fork(); + x.fork(); + } + + private void completeThis() { + super.complete(null); + } + + private void completeThisExceptionally(Throwable ex) { + super.completeExceptionally(ex); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + if (super.cancel(mayInterruptIfRunning)) { + completeExceptionally(new FJException()); + return true; + } + return false; + } + + public final void complete() { + BinaryAsyncAction a = this; + for (;;) { + BinaryAsyncAction s = a.sibling; + BinaryAsyncAction p = a.parent; + a.sibling = null; + a.parent = null; + a.completeThis(); + if (p == null || p.compareAndSetControlState(0, 1)) + break; + try { + p.onComplete(a, s); + } catch (Throwable rex) { + p.completeExceptionally(rex); + return; + } + a = p; + } + } + + public final void completeExceptionally(Throwable ex) { + for (BinaryAsyncAction a = this;;) { + a.completeThisExceptionally(ex); + BinaryAsyncAction s = a.sibling; + if (s != null && !s.isDone()) + s.completeExceptionally(ex); + if ((a = a.parent) == null) + break; + } + } + + public final BinaryAsyncAction getParent() { + return parent; + } + + public BinaryAsyncAction getSibling() { + return sibling; + } + + public void reinitialize() { + parent = sibling = null; + super.reinitialize(); + } + + protected final int getControlState() { + return controlState; + } + + protected final boolean compareAndSetControlState(int expect, + int update) { + return controlStateUpdater.compareAndSet(this, expect, update); + } + + protected final void setControlState(int value) { + controlState = value; + } + + protected final void incrementControlState() { + controlStateUpdater.incrementAndGet(this); + } + + protected final void decrementControlState() { + controlStateUpdater.decrementAndGet(this); + } + + } + + static final class AsyncFib extends BinaryAsyncAction { + int number; + public AsyncFib(int n) { + this.number = n; + } + + public final boolean exec() { + AsyncFib f = this; + int n = f.number; + while (n > 1) { + AsyncFib p = f; + AsyncFib r = new AsyncFib(n - 2); + f = new AsyncFib(--n); + p.linkSubtasks(r, f); + r.fork(); + } + f.complete(); + return false; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + number = ((AsyncFib)x).number + ((AsyncFib)y).number; + } + } + + static final class FailingAsyncFib extends BinaryAsyncAction { + int number; + public FailingAsyncFib(int n) { + this.number = n; + } + + public final boolean exec() { + FailingAsyncFib f = this; + int n = f.number; + while (n > 1) { + FailingAsyncFib p = f; + FailingAsyncFib r = new FailingAsyncFib(n - 2); + f = new FailingAsyncFib(--n); + p.linkSubtasks(r, f); + r.fork(); + } + f.complete(); + return false; + } + + protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) { + completeExceptionally(new FJException()); + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertNull(f.invoke()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.quietlyInvoke(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesce() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + helpQuiesce(); + assertEquals(21, f.number); + assertEquals(0, getQueuedTaskCount()); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGet() throws Exception { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * getPool of executing task returns its pool + */ + public void testGetPool() { + final ForkJoinPool mainPool = mainPool(); + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertSame(mainPool, getPool()); + }}; + testInvokeOnPool(mainPool, a); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertNull(getPool()); + }}; + assertNull(a.invoke()); + } + + /** + * inForkJoinPool of executing task returns true + */ + public void testInForkJoinPool() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertTrue(inForkJoinPool()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * setRawResult(null) succeeds + */ + public void testSetRawResult() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + setRawResult(null); + assertNull(getRawResult()); + }}; + assertNull(a.invoke()); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * completeExceptionally(null) surprisingly has the same effect as + * completeExceptionally(new RuntimeException()) + */ + public void testCompleteExceptionally_null() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.completeExceptionally(null); + try { + f.invoke(); + shouldThrow(); + } catch (RuntimeException success) { + assertSame(success.getClass(), RuntimeException.class); + assertNull(success.getCause()); + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + invokeAll(f, g); + assertEquals(21, f.number); + assertEquals(34, g.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.number); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + invokeAll(f, g, h); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + ForkJoinTask[] tasks = { f, g }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib g = new FailingAsyncFib(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + List taskList = Arrays.asList(tasks); + Collections.shuffle(taskList); + try { + invokeAll(taskList); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * tryUnfork returns true for most recent unexecuted task, + * and suppresses execution + */ + public void testTryUnfork() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertTrue(f.tryUnfork()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * getSurplusQueuedTaskCount returns > 0 when + * there are more tasks than threads + */ + public void testGetSurplusQueuedTaskCount() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib h = new AsyncFib(7); + assertSame(h, h.fork()); + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertTrue(getSurplusQueuedTaskCount() > 0); + helpQuiesce(); + assertEquals(0, getSurplusQueuedTaskCount()); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns most recent unexecuted task. + */ + public void testPeekNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, peekNextLocalTask()); + assertNull(f.join()); + checkCompletedNormally(f); + helpQuiesce(); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollNextLocalTask returns most recent unexecuted task without + * executing it + */ + public void testPollNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, pollNextLocalTask()); + helpQuiesce(); + checkNotDone(f); + assertEquals(34, g.number); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it + */ + public void testPollTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(f, pollTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns least recent unexecuted task in async mode + */ + public void testPeekNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, peekNextLocalTask()); + assertNull(f.join()); + helpQuiesce(); + checkCompletedNormally(f); + assertEquals(34, g.number); + checkCompletedNormally(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollNextLocalTask returns least recent unexecuted task without + * executing it, in async mode + */ + public void testPollNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, pollNextLocalTask()); + helpQuiesce(); + assertEquals(21, f.number); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it, in + * async mode + */ + public void testPollTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib g = new AsyncFib(9); + assertSame(g, g.fork()); + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertSame(g, pollTask()); + helpQuiesce(); + assertEquals(21, f.number); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + // versions for singleton pools + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks; getRawResult returns null. + */ + public void testInvokeSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertNull(f.invoke()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvokeSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.quietlyInvoke(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGetSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGetSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPESingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesceSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertSame(f, f.fork()); + helpQuiesce(); + assertEquals(0, getQueuedTaskCount()); + assertEquals(21, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvokeSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvokeSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGetSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGetSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvokeSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGetSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGetSingleton() throws Exception { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoinSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionallySingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + invokeAll(f, g); + assertEquals(21, f.number); + assertEquals(34, g.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.number); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + invokeAll(f, g, h); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollectionSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertEquals(21, f.number); + assertEquals(34, g.number); + assertEquals(13, h.number); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPESingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + ForkJoinTask[] tasks = { f, g }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib g = new FailingAsyncFib(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3Singleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + FailingAsyncFib g = new FailingAsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + Collections.shuffle(Arrays.asList(tasks)); + try { + invokeAll(tasks); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollectionSingleton() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingAsyncFib f = new FailingAsyncFib(8); + AsyncFib g = new AsyncFib(9); + AsyncFib h = new AsyncFib(7); + ForkJoinTask[] tasks = { f, g, h }; + List taskList = Arrays.asList(tasks); + Collections.shuffle(taskList); + try { + invokeAll(taskList); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * ForkJoinTask.quietlyComplete returns when task completes + * normally without setting a value. The most recent value + * established by setRawResult(V) (or null by default) is returned + * from invoke. + */ + public void testQuietlyComplete() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + AsyncFib f = new AsyncFib(8); + f.quietlyComplete(); + assertEquals(8, f.number); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/FutureTaskTest.java b/jdk/test/java/util/concurrent/tck/FutureTaskTest.java new file mode 100644 index 00000000000..1e858bfdbc1 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/FutureTaskTest.java @@ -0,0 +1,866 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class FutureTaskTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(FutureTaskTest.class); + } + + void checkIsDone(Future f) { + assertTrue(f.isDone()); + assertFalse(f.cancel(false)); + assertFalse(f.cancel(true)); + if (f instanceof PublicFutureTask) { + PublicFutureTask pf = (PublicFutureTask) f; + assertEquals(1, pf.doneCount()); + assertFalse(pf.runAndReset()); + assertEquals(1, pf.doneCount()); + Object r = null; Object exInfo = null; + try { + r = f.get(); + } catch (CancellationException t) { + exInfo = CancellationException.class; + } catch (ExecutionException t) { + exInfo = t.getCause(); + } catch (Throwable t) { + threadUnexpectedException(t); + } + + // Check that run and runAndReset have no effect. + int savedRunCount = pf.runCount(); + pf.run(); + pf.runAndReset(); + assertEquals(savedRunCount, pf.runCount()); + try { + assertSame(r, f.get()); + } catch (CancellationException t) { + assertSame(exInfo, CancellationException.class); + } catch (ExecutionException t) { + assertSame(exInfo, t.getCause()); + } catch (Throwable t) { + threadUnexpectedException(t); + } + assertTrue(f.isDone()); + } + } + + void checkNotDone(Future f) { + assertFalse(f.isDone()); + assertFalse(f.isCancelled()); + if (f instanceof PublicFutureTask) { + PublicFutureTask pf = (PublicFutureTask) f; + assertEquals(0, pf.doneCount()); + assertEquals(0, pf.setCount()); + assertEquals(0, pf.setExceptionCount()); + } + } + + void checkIsRunning(Future f) { + checkNotDone(f); + if (f instanceof FutureTask) { + FutureTask ft = (FutureTask) f; + // Check that run methods do nothing + ft.run(); + if (f instanceof PublicFutureTask) { + PublicFutureTask pf = (PublicFutureTask) f; + int savedRunCount = pf.runCount(); + pf.run(); + assertFalse(pf.runAndReset()); + assertEquals(savedRunCount, pf.runCount()); + } + checkNotDone(f); + } + } + + void checkCompletedNormally(Future f, T expected) { + checkIsDone(f); + assertFalse(f.isCancelled()); + + try { + assertSame(expected, f.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertSame(expected, f.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCancelled(Future f) { + checkIsDone(f); + assertTrue(f.isCancelled()); + + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void tryToConfuseDoneTask(PublicFutureTask pf) { + pf.set(new Object()); + pf.setException(new Error()); + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { + pf.cancel(mayInterruptIfRunning); + } + } + + void checkCompletedAbnormally(Future f, Throwable t) { + checkIsDone(f); + assertFalse(f.isCancelled()); + + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t, success.getCause()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + /** + * Subclass to expose protected methods + */ + static class PublicFutureTask extends FutureTask { + private final AtomicInteger runCount; + private final AtomicInteger doneCount = new AtomicInteger(0); + private final AtomicInteger runAndResetCount = new AtomicInteger(0); + private final AtomicInteger setCount = new AtomicInteger(0); + private final AtomicInteger setExceptionCount = new AtomicInteger(0); + public int runCount() { return runCount.get(); } + public int doneCount() { return doneCount.get(); } + public int runAndResetCount() { return runAndResetCount.get(); } + public int setCount() { return setCount.get(); } + public int setExceptionCount() { return setExceptionCount.get(); } + + PublicFutureTask(Runnable runnable) { + this(runnable, seven); + } + PublicFutureTask(Runnable runnable, Object result) { + this(runnable, result, new AtomicInteger(0)); + } + private PublicFutureTask(final Runnable runnable, Object result, + final AtomicInteger runCount) { + super(new Runnable() { + public void run() { + runCount.getAndIncrement(); + runnable.run(); + }}, result); + this.runCount = runCount; + } + PublicFutureTask(Callable callable) { + this(callable, new AtomicInteger(0)); + } + private PublicFutureTask(final Callable callable, + final AtomicInteger runCount) { + super(new Callable() { + public Object call() throws Exception { + runCount.getAndIncrement(); + return callable.call(); + }}); + this.runCount = runCount; + } + @Override public void done() { + assertTrue(isDone()); + doneCount.incrementAndGet(); + super.done(); + } + @Override public boolean runAndReset() { + runAndResetCount.incrementAndGet(); + return super.runAndReset(); + } + @Override public void set(Object x) { + setCount.incrementAndGet(); + super.set(x); + } + @Override public void setException(Throwable t) { + setExceptionCount.incrementAndGet(); + super.setException(t); + } + } + + class Counter extends CheckedRunnable { + final AtomicInteger count = new AtomicInteger(0); + public int get() { return count.get(); } + public void realRun() { + count.getAndIncrement(); + } + } + + /** + * creating a future with a null callable throws NullPointerException + */ + public void testConstructor() { + try { + new FutureTask(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * creating a future with null runnable throws NullPointerException + */ + public void testConstructor2() { + try { + new FutureTask(null, Boolean.TRUE); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * isDone is true when a task completes + */ + public void testIsDone() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + assertFalse(task.isDone()); + task.run(); + assertTrue(task.isDone()); + checkCompletedNormally(task, Boolean.TRUE); + assertEquals(1, task.runCount()); + } + + /** + * runAndReset of a non-cancelled task succeeds + */ + public void testRunAndReset() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + for (int i = 0; i < 3; i++) { + assertTrue(task.runAndReset()); + checkNotDone(task); + assertEquals(i + 1, task.runCount()); + assertEquals(i + 1, task.runAndResetCount()); + assertEquals(0, task.setCount()); + assertEquals(0, task.setExceptionCount()); + } + } + + /** + * runAndReset after cancellation fails + */ + public void testRunAndResetAfterCancel() { + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + assertTrue(task.cancel(mayInterruptIfRunning)); + for (int i = 0; i < 3; i++) { + assertFalse(task.runAndReset()); + assertEquals(0, task.runCount()); + assertEquals(i + 1, task.runAndResetCount()); + assertEquals(0, task.setCount()); + assertEquals(0, task.setExceptionCount()); + } + tryToConfuseDoneTask(task); + checkCancelled(task); + } + } + + /** + * setting value causes get to return it + */ + public void testSet() throws Exception { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + task.set(one); + for (int i = 0; i < 3; i++) { + assertSame(one, task.get()); + assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(1, task.setCount()); + } + tryToConfuseDoneTask(task); + checkCompletedNormally(task, one); + assertEquals(0, task.runCount()); + } + + /** + * setException causes get to throw ExecutionException + */ + public void testSetException_get() throws Exception { + Exception nse = new NoSuchElementException(); + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + task.setException(nse); + + try { + task.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(nse, success.getCause()); + checkCompletedAbnormally(task, nse); + } + + try { + task.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(nse, success.getCause()); + checkCompletedAbnormally(task, nse); + } + + assertEquals(1, task.setExceptionCount()); + assertEquals(0, task.setCount()); + tryToConfuseDoneTask(task); + checkCompletedAbnormally(task, nse); + assertEquals(0, task.runCount()); + } + + /** + * cancel(false) before run succeeds + */ + public void testCancelBeforeRun() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + assertTrue(task.cancel(false)); + task.run(); + assertEquals(0, task.runCount()); + assertEquals(0, task.setCount()); + assertEquals(0, task.setExceptionCount()); + assertTrue(task.isCancelled()); + assertTrue(task.isDone()); + tryToConfuseDoneTask(task); + assertEquals(0, task.runCount()); + checkCancelled(task); + } + + /** + * cancel(true) before run succeeds + */ + public void testCancelBeforeRun2() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + assertTrue(task.cancel(true)); + task.run(); + assertEquals(0, task.runCount()); + assertEquals(0, task.setCount()); + assertEquals(0, task.setExceptionCount()); + assertTrue(task.isCancelled()); + assertTrue(task.isDone()); + tryToConfuseDoneTask(task); + assertEquals(0, task.runCount()); + checkCancelled(task); + } + + /** + * cancel(false) of a completed task fails + */ + public void testCancelAfterRun() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + task.run(); + assertFalse(task.cancel(false)); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCompletedNormally(task, Boolean.TRUE); + assertEquals(1, task.runCount()); + } + + /** + * cancel(true) of a completed task fails + */ + public void testCancelAfterRun2() { + PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); + task.run(); + assertFalse(task.cancel(true)); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCompletedNormally(task, Boolean.TRUE); + assertEquals(1, task.runCount()); + } + + /** + * cancel(true) interrupts a running task that subsequently succeeds + */ + public void testCancelInterrupt() { + final CountDownLatch pleaseCancel = new CountDownLatch(1); + final PublicFutureTask task = + new PublicFutureTask(new CheckedRunnable() { + public void realRun() { + pleaseCancel.countDown(); + try { + delay(LONG_DELAY_MS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + Thread t = newStartedThread(task); + await(pleaseCancel); + assertTrue(task.cancel(true)); + assertTrue(task.isCancelled()); + assertTrue(task.isDone()); + awaitTermination(t); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCancelled(task); + } + + /** + * cancel(true) tries to interrupt a running task, but + * Thread.interrupt throws (simulating a restrictive security + * manager) + */ + public void testCancelInterrupt_ThrowsSecurityException() { + final CountDownLatch pleaseCancel = new CountDownLatch(1); + final CountDownLatch cancelled = new CountDownLatch(1); + final PublicFutureTask task = + new PublicFutureTask(new CheckedRunnable() { + public void realRun() { + pleaseCancel.countDown(); + await(cancelled); + assertFalse(Thread.interrupted()); + }}); + + final Thread t = new Thread(task) { + // Simulate a restrictive security manager. + @Override public void interrupt() { + throw new SecurityException(); + }}; + t.setDaemon(true); + t.start(); + + await(pleaseCancel); + try { + task.cancel(true); + shouldThrow(); + } catch (SecurityException expected) {} + + // We failed to deliver the interrupt, but the world retains + // its sanity, as if we had done task.cancel(false) + assertTrue(task.isCancelled()); + assertTrue(task.isDone()); + assertEquals(1, task.runCount()); + assertEquals(1, task.doneCount()); + assertEquals(0, task.setCount()); + assertEquals(0, task.setExceptionCount()); + cancelled.countDown(); + awaitTermination(t); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCancelled(task); + } + + /** + * cancel(true) interrupts a running task that subsequently throws + */ + public void testCancelInterrupt_taskFails() { + final CountDownLatch pleaseCancel = new CountDownLatch(1); + final PublicFutureTask task = + new PublicFutureTask(new Runnable() { + public void run() { + pleaseCancel.countDown(); + try { + delay(LONG_DELAY_MS); + threadShouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable t) { threadUnexpectedException(t); } + throw new RuntimeException(); + }}); + + Thread t = newStartedThread(task); + await(pleaseCancel); + assertTrue(task.cancel(true)); + assertTrue(task.isCancelled()); + awaitTermination(t); + assertEquals(1, task.runCount()); + assertEquals(0, task.setCount()); + assertEquals(1, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCancelled(task); + } + + /** + * cancel(false) does not interrupt a running task + */ + public void testCancelNoInterrupt() { + final CountDownLatch pleaseCancel = new CountDownLatch(1); + final CountDownLatch cancelled = new CountDownLatch(1); + final PublicFutureTask task = + new PublicFutureTask(new CheckedCallable() { + public Boolean realCall() { + pleaseCancel.countDown(); + await(cancelled); + assertFalse(Thread.interrupted()); + return Boolean.TRUE; + }}); + + Thread t = newStartedThread(task); + await(pleaseCancel); + assertTrue(task.cancel(false)); + assertTrue(task.isCancelled()); + cancelled.countDown(); + awaitTermination(t); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCancelled(task); + } + + /** + * run in one thread causes get in another thread to retrieve value + */ + public void testGetRun() { + final CountDownLatch pleaseRun = new CountDownLatch(2); + + final PublicFutureTask task = + new PublicFutureTask(new CheckedCallable() { + public Object realCall() { + return two; + }}); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + pleaseRun.countDown(); + assertSame(two, task.get()); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + pleaseRun.countDown(); + assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); + }}); + + await(pleaseRun); + checkNotDone(task); + assertTrue(t1.isAlive()); + assertTrue(t2.isAlive()); + task.run(); + checkCompletedNormally(task, two); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + awaitTermination(t1); + awaitTermination(t2); + tryToConfuseDoneTask(task); + checkCompletedNormally(task, two); + } + + /** + * set in one thread causes get in another thread to retrieve value + */ + public void testGetSet() { + final CountDownLatch pleaseSet = new CountDownLatch(2); + + final PublicFutureTask task = + new PublicFutureTask(new CheckedCallable() { + public Object realCall() throws InterruptedException { + return two; + }}); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + pleaseSet.countDown(); + assertSame(two, task.get()); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + pleaseSet.countDown(); + assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); + }}); + + await(pleaseSet); + checkNotDone(task); + assertTrue(t1.isAlive()); + assertTrue(t2.isAlive()); + task.set(two); + assertEquals(0, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCompletedNormally(task, two); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Cancelling a task causes timed get in another thread to throw + * CancellationException + */ + public void testTimedGet_Cancellation() { + testTimedGet_Cancellation(false); + } + public void testTimedGet_Cancellation_interrupt() { + testTimedGet_Cancellation(true); + } + public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) { + final CountDownLatch pleaseCancel = new CountDownLatch(3); + final CountDownLatch cancelled = new CountDownLatch(1); + final Callable callable = + new CheckedCallable() { + public Object realCall() throws InterruptedException { + pleaseCancel.countDown(); + if (mayInterruptIfRunning) { + try { + delay(2*LONG_DELAY_MS); + } catch (InterruptedException success) {} + } else { + await(cancelled); + } + return two; + }}; + final PublicFutureTask task = new PublicFutureTask(callable); + + Thread t1 = new ThreadShouldThrow(CancellationException.class) { + public void realRun() throws Exception { + pleaseCancel.countDown(); + task.get(); + }}; + Thread t2 = new ThreadShouldThrow(CancellationException.class) { + public void realRun() throws Exception { + pleaseCancel.countDown(); + task.get(2*LONG_DELAY_MS, MILLISECONDS); + }}; + t1.start(); + t2.start(); + Thread t3 = newStartedThread(task); + await(pleaseCancel); + checkIsRunning(task); + task.cancel(mayInterruptIfRunning); + checkCancelled(task); + awaitTermination(t1); + awaitTermination(t2); + cancelled.countDown(); + awaitTermination(t3); + assertEquals(1, task.runCount()); + assertEquals(1, task.setCount()); + assertEquals(0, task.setExceptionCount()); + tryToConfuseDoneTask(task); + checkCancelled(task); + } + + /** + * A runtime exception in task causes get to throw ExecutionException + */ + public void testGet_ExecutionException() throws InterruptedException { + final ArithmeticException e = new ArithmeticException(); + final PublicFutureTask task = new PublicFutureTask(new Callable() { + public Object call() { + throw e; + }}); + + task.run(); + assertEquals(1, task.runCount()); + assertEquals(0, task.setCount()); + assertEquals(1, task.setExceptionCount()); + try { + task.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(e, success.getCause()); + tryToConfuseDoneTask(task); + checkCompletedAbnormally(task, success.getCause()); + } + } + + /** + * A runtime exception in task causes timed get to throw ExecutionException + */ + public void testTimedGet_ExecutionException2() throws Exception { + final ArithmeticException e = new ArithmeticException(); + final PublicFutureTask task = new PublicFutureTask(new Callable() { + public Object call() { + throw e; + }}); + + task.run(); + try { + task.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(e, success.getCause()); + tryToConfuseDoneTask(task); + checkCompletedAbnormally(task, success.getCause()); + } + } + + /** + * get is interruptible + */ + public void testGet_interruptible() { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final FutureTask task = new FutureTask(new NoOpCallable()); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + Thread.currentThread().interrupt(); + try { + task.get(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + task.get(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + t.interrupt(); + awaitTermination(t); + checkNotDone(task); + } + + /** + * timed get is interruptible + */ + public void testTimedGet_interruptible() { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final FutureTask task = new FutureTask(new NoOpCallable()); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + Thread.currentThread().interrupt(); + try { + task.get(2*LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + task.get(2*LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + t.interrupt(); + awaitTermination(t); + checkNotDone(task); + } + + /** + * A timed out timed get throws TimeoutException + */ + public void testGet_TimeoutException() throws Exception { + FutureTask task = new FutureTask(new NoOpCallable()); + long startTime = System.nanoTime(); + try { + task.get(timeoutMillis(), MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) { + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * timed get with null TimeUnit throws NullPointerException + */ + public void testGet_NullTimeUnit() throws Exception { + FutureTask task = new FutureTask(new NoOpCallable()); + long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE }; + + for (long timeout : timeouts) { + try { + task.get(timeout, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + task.run(); + + for (long timeout : timeouts) { + try { + task.get(timeout, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed get with most negative timeout works correctly (i.e. no + * underflow bug) + */ + public void testGet_NegativeInfinityTimeout() throws Exception { + final ExecutorService pool = Executors.newFixedThreadPool(10); + final Runnable nop = new Runnable() { public void run() {}}; + final FutureTask task = new FutureTask<>(nop, null); + final List> futures = new ArrayList<>(); + Runnable r = new Runnable() { public void run() { + for (long timeout : new long[] { 0L, -1L, Long.MIN_VALUE }) { + try { + task.get(timeout, NANOSECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) {threadUnexpectedException(fail);}}}}; + for (int i = 0; i < 10; i++) + futures.add(pool.submit(r)); + try { + joinPool(pool); + for (Future future : futures) + checkCompletedNormally(future, null); + } finally { + task.run(); // last resort to help terminate + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java new file mode 100644 index 00000000000..8063047f8b4 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java @@ -0,0 +1,1814 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +/* + * @test + * @summary JSR-166 tck tests + * @build * + * @run junit/othervm/timeout=1000 -Djsr166.testImplementationDetails=true JSR166TestCase + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.security.SecurityPermission; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.PropertyPermission; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.RecursiveTask; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +/** + * Base class for JSR166 Junit TCK tests. Defines some constants, + * utility methods and classes, as well as a simple framework for + * helping to make sure that assertions failing in generated threads + * cause the associated test that generated them to itself fail (which + * JUnit does not otherwise arrange). The rules for creating such + * tests are: + * + *
    + * + *
  1. All assertions in code running in generated threads must use + * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link + * #threadAssertEquals}, or {@link #threadAssertNull}, (not + * {@code fail}, {@code assertTrue}, etc.) It is OK (but not + * particularly recommended) for other code to use these forms too. + * Only the most typically used JUnit assertion methods are defined + * this way, but enough to live with. + * + *
  2. If you override {@link #setUp} or {@link #tearDown}, make sure + * to invoke {@code super.setUp} and {@code super.tearDown} within + * them. These methods are used to clear and check for thread + * assertion failures. + * + *
  3. All delays and timeouts must use one of the constants {@code + * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS}, + * {@code LONG_DELAY_MS}. The idea here is that a SHORT is always + * discriminable from zero time, and always allows enough time for the + * small amounts of computation (creating a thread, calling a few + * methods, etc) needed to reach a timeout point. Similarly, a SMALL + * is always discriminable as larger than SHORT and smaller than + * MEDIUM. And so on. These constants are set to conservative values, + * but even so, if there is ever any doubt, they can all be increased + * in one spot to rerun tests on slower platforms. + * + *
  4. All threads generated must be joined inside each test case + * method (or {@code fail} to do so) before returning from the + * method. The {@code joinPool} method can be used to do this when + * using Executors. + * + *
+ * + *

Other notes + *

    + * + *
  • Usually, there is one testcase method per JSR166 method + * covering "normal" operation, and then as many exception-testing + * methods as there are exceptions the method can throw. Sometimes + * there are multiple tests per JSR166 method when the different + * "normal" behaviors differ significantly. And sometimes testcases + * cover multiple methods when they cannot be tested in isolation. + * + *
  • The documentation style for testcases is to provide as javadoc + * a simple sentence or two describing the property that the testcase + * method purports to test. The javadocs do not say anything about how + * the property is tested. To find out, read the code. + * + *
  • These tests are "conformance tests", and do not attempt to + * test throughput, latency, scalability or other performance factors + * (see the separate "jtreg" tests for a set intended to check these + * for the most central aspects of functionality.) So, most tests use + * the smallest sensible numbers of threads, collection sizes, etc + * needed to check basic conformance. + * + *
  • The test classes currently do not declare inclusion in + * any particular package to simplify things for people integrating + * them in TCK test suites. + * + *
  • As a convenience, the {@code main} of this class (JSR166TestCase) + * runs all JSR166 unit tests. + * + *
+ */ +public class JSR166TestCase extends TestCase { + private static final boolean useSecurityManager = + Boolean.getBoolean("jsr166.useSecurityManager"); + + protected static final boolean expensiveTests = + Boolean.getBoolean("jsr166.expensiveTests"); + + /** + * If true, also run tests that are not part of the official tck + * because they test unspecified implementation details. + */ + protected static final boolean testImplementationDetails = + Boolean.getBoolean("jsr166.testImplementationDetails"); + + /** + * If true, report on stdout all "slow" tests, that is, ones that + * take more than profileThreshold milliseconds to execute. + */ + private static final boolean profileTests = + Boolean.getBoolean("jsr166.profileTests"); + + /** + * The number of milliseconds that tests are permitted for + * execution without being reported, when profileTests is set. + */ + private static final long profileThreshold = + Long.getLong("jsr166.profileThreshold", 100); + + /** + * The number of repetitions per test (for tickling rare bugs). + */ + private static final int runsPerTest = + Integer.getInteger("jsr166.runsPerTest", 1); + + /** + * The number of repetitions of the test suite (for finding leaks?). + */ + private static final int suiteRuns = + Integer.getInteger("jsr166.suiteRuns", 1); + + /** + * The scaling factor to apply to standard delays used in tests. + */ + private static final int delayFactor = + Integer.getInteger("jsr166.delay.factor", 1); + + public JSR166TestCase() { super(); } + public JSR166TestCase(String name) { super(name); } + + /** + * A filter for tests to run, matching strings of the form + * methodName(className), e.g. "testInvokeAll5(ForkJoinPoolTest)" + * Usefully combined with jsr166.runsPerTest. + */ + private static final Pattern methodFilter = methodFilter(); + + private static Pattern methodFilter() { + String regex = System.getProperty("jsr166.methodFilter"); + return (regex == null) ? null : Pattern.compile(regex); + } + + // Instrumentation to debug very rare, but very annoying hung test runs. + static volatile TestCase currentTestCase; + // static volatile int currentRun = 0; + static { + Runnable checkForWedgedTest = new Runnable() { public void run() { + // Avoid spurious reports with enormous runsPerTest. + // A single test case run should never take more than 1 second. + // But let's cap it at the high end too ... + final int timeoutMinutes = + Math.min(15, Math.max(runsPerTest / 60, 1)); + for (TestCase lastTestCase = currentTestCase;;) { + try { MINUTES.sleep(timeoutMinutes); } + catch (InterruptedException unexpected) { break; } + if (lastTestCase == currentTestCase) { + System.err.printf( + "Looks like we're stuck running test: %s%n", + lastTestCase); +// System.err.printf( +// "Looks like we're stuck running test: %s (%d/%d)%n", +// lastTestCase, currentRun, runsPerTest); +// System.err.println("availableProcessors=" + +// Runtime.getRuntime().availableProcessors()); +// System.err.printf("cpu model = %s%n", cpuModel()); + dumpTestThreads(); + // one stack dump is probably enough; more would be spam + break; + } + lastTestCase = currentTestCase; + }}}; + Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest"); + thread.setDaemon(true); + thread.start(); + } + +// public static String cpuModel() { +// try { +// Matcher matcher = Pattern.compile("model name\\s*: (.*)") +// .matcher(new String( +// Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8")); +// matcher.find(); +// return matcher.group(1); +// } catch (Exception ex) { return null; } +// } + + public void runBare() throws Throwable { + currentTestCase = this; + if (methodFilter == null + || methodFilter.matcher(toString()).find()) + super.runBare(); + } + + protected void runTest() throws Throwable { + for (int i = 0; i < runsPerTest; i++) { + // currentRun = i; + if (profileTests) + runTestProfiled(); + else + super.runTest(); + } + } + + protected void runTestProfiled() throws Throwable { + for (int i = 0; i < 2; i++) { + long startTime = System.nanoTime(); + super.runTest(); + long elapsedMillis = millisElapsedSince(startTime); + if (elapsedMillis < profileThreshold) + break; + // Never report first run of any test; treat it as a + // warmup run, notably to trigger all needed classloading, + if (i > 0) + System.out.printf("%n%s: %d%n", toString(), elapsedMillis); + } + } + + /** + * Runs all JSR166 unit tests using junit.textui.TestRunner. + */ + public static void main(String[] args) { + main(suite(), args); + } + + static class PithyResultPrinter extends junit.textui.ResultPrinter { + PithyResultPrinter(java.io.PrintStream writer) { super(writer); } + long runTime; + public void startTest(Test test) {} + protected void printHeader(long runTime) { + this.runTime = runTime; // defer printing for later + } + protected void printFooter(TestResult result) { + if (result.wasSuccessful()) { + getWriter().println("OK (" + result.runCount() + " tests)" + + " Time: " + elapsedTimeAsString(runTime)); + } else { + getWriter().println("Time: " + elapsedTimeAsString(runTime)); + super.printFooter(result); + } + } + } + + /** + * Returns a TestRunner that doesn't bother with unnecessary + * fluff, like printing a "." for each test case. + */ + static junit.textui.TestRunner newPithyTestRunner() { + junit.textui.TestRunner runner = new junit.textui.TestRunner(); + runner.setPrinter(new PithyResultPrinter(System.out)); + return runner; + } + + /** + * Runs all unit tests in the given test suite. + * Actual behavior influenced by jsr166.* system properties. + */ + static void main(Test suite, String[] args) { + if (useSecurityManager) { + System.err.println("Setting a permissive security manager"); + Policy.setPolicy(permissivePolicy()); + System.setSecurityManager(new SecurityManager()); + } + for (int i = 0; i < suiteRuns; i++) { + TestResult result = newPithyTestRunner().doRun(suite); + if (!result.wasSuccessful()) + System.exit(1); + System.gc(); + System.runFinalization(); + } + } + + public static TestSuite newTestSuite(Object... suiteOrClasses) { + TestSuite suite = new TestSuite(); + for (Object suiteOrClass : suiteOrClasses) { + if (suiteOrClass instanceof TestSuite) + suite.addTest((TestSuite) suiteOrClass); + else if (suiteOrClass instanceof Class) + suite.addTest(new TestSuite((Class) suiteOrClass)); + else + throw new ClassCastException("not a test suite or class"); + } + return suite; + } + + public static void addNamedTestClasses(TestSuite suite, + String... testClassNames) { + for (String testClassName : testClassNames) { + try { + Class testClass = Class.forName(testClassName); + Method m = testClass.getDeclaredMethod("suite", + new Class[0]); + suite.addTest(newTestSuite((Test)m.invoke(null))); + } catch (Exception e) { + throw new Error("Missing test class", e); + } + } + } + + public static final double JAVA_CLASS_VERSION; + public static final String JAVA_SPECIFICATION_VERSION; + static { + try { + JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Double run() { + return Double.valueOf(System.getProperty("java.class.version"));}}); + JAVA_SPECIFICATION_VERSION = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + return System.getProperty("java.specification.version");}}); + } catch (Throwable t) { + throw new Error(t); + } + } + + public static boolean atLeastJava6() { return JAVA_CLASS_VERSION >= 50.0; } + public static boolean atLeastJava7() { return JAVA_CLASS_VERSION >= 51.0; } + public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; } + public static boolean atLeastJava9() { + return JAVA_CLASS_VERSION >= 53.0 + // As of 2015-09, java9 still uses 52.0 class file version + || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?(9|[0-9][0-9])$"); + } + public static boolean atLeastJava10() { + return JAVA_CLASS_VERSION >= 54.0 + || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?[0-9][0-9]$"); + } + + /** + * Collects all JSR166 unit tests as one suite. + */ + public static Test suite() { + // Java7+ test classes + TestSuite suite = newTestSuite( + ForkJoinPoolTest.suite(), + ForkJoinTaskTest.suite(), + RecursiveActionTest.suite(), + RecursiveTaskTest.suite(), + LinkedTransferQueueTest.suite(), + PhaserTest.suite(), + ThreadLocalRandomTest.suite(), + AbstractExecutorServiceTest.suite(), + AbstractQueueTest.suite(), + AbstractQueuedSynchronizerTest.suite(), + AbstractQueuedLongSynchronizerTest.suite(), + ArrayBlockingQueueTest.suite(), + ArrayDequeTest.suite(), + AtomicBooleanTest.suite(), + AtomicIntegerArrayTest.suite(), + AtomicIntegerFieldUpdaterTest.suite(), + AtomicIntegerTest.suite(), + AtomicLongArrayTest.suite(), + AtomicLongFieldUpdaterTest.suite(), + AtomicLongTest.suite(), + AtomicMarkableReferenceTest.suite(), + AtomicReferenceArrayTest.suite(), + AtomicReferenceFieldUpdaterTest.suite(), + AtomicReferenceTest.suite(), + AtomicStampedReferenceTest.suite(), + ConcurrentHashMapTest.suite(), + ConcurrentLinkedDequeTest.suite(), + ConcurrentLinkedQueueTest.suite(), + ConcurrentSkipListMapTest.suite(), + ConcurrentSkipListSubMapTest.suite(), + ConcurrentSkipListSetTest.suite(), + ConcurrentSkipListSubSetTest.suite(), + CopyOnWriteArrayListTest.suite(), + CopyOnWriteArraySetTest.suite(), + CountDownLatchTest.suite(), + CyclicBarrierTest.suite(), + DelayQueueTest.suite(), + EntryTest.suite(), + ExchangerTest.suite(), + ExecutorsTest.suite(), + ExecutorCompletionServiceTest.suite(), + FutureTaskTest.suite(), + LinkedBlockingDequeTest.suite(), + LinkedBlockingQueueTest.suite(), + LinkedListTest.suite(), + LockSupportTest.suite(), + PriorityBlockingQueueTest.suite(), + PriorityQueueTest.suite(), + ReentrantLockTest.suite(), + ReentrantReadWriteLockTest.suite(), + ScheduledExecutorTest.suite(), + ScheduledExecutorSubclassTest.suite(), + SemaphoreTest.suite(), + SynchronousQueueTest.suite(), + SystemTest.suite(), + ThreadLocalTest.suite(), + ThreadPoolExecutorTest.suite(), + ThreadPoolExecutorSubclassTest.suite(), + ThreadTest.suite(), + TimeUnitTest.suite(), + TreeMapTest.suite(), + TreeSetTest.suite(), + TreeSubMapTest.suite(), + TreeSubSetTest.suite()); + + // Java8+ test classes + if (atLeastJava8()) { + String[] java8TestClassNames = { + "Atomic8Test", + "CompletableFutureTest", + "ConcurrentHashMap8Test", + "CountedCompleterTest", + "DoubleAccumulatorTest", + "DoubleAdderTest", + "ForkJoinPool8Test", + "ForkJoinTask8Test", + "LongAccumulatorTest", + "LongAdderTest", + "SplittableRandomTest", + "StampedLockTest", + "SubmissionPublisherTest", + "ThreadLocalRandom8Test", + }; + addNamedTestClasses(suite, java8TestClassNames); + } + + // Java9+ test classes + if (atLeastJava9()) { + String[] java9TestClassNames = { + // Currently empty, but expecting varhandle tests + }; + addNamedTestClasses(suite, java9TestClassNames); + } + + return suite; + } + + /** Returns list of junit-style test method names in given class. */ + public static ArrayList testMethodNames(Class testClass) { + Method[] methods = testClass.getDeclaredMethods(); + ArrayList names = new ArrayList(methods.length); + for (Method method : methods) { + if (method.getName().startsWith("test") + && Modifier.isPublic(method.getModifiers()) + // method.getParameterCount() requires jdk8+ + && method.getParameterTypes().length == 0) { + names.add(method.getName()); + } + } + return names; + } + + /** + * Returns junit-style testSuite for the given test class, but + * parameterized by passing extra data to each test. + */ + public static Test parameterizedTestSuite + (Class testClass, + Class dataClass, + ExtraData data) { + try { + TestSuite suite = new TestSuite(); + Constructor c = + testClass.getDeclaredConstructor(dataClass, String.class); + for (String methodName : testMethodNames(testClass)) + suite.addTest((Test) c.newInstance(data, methodName)); + return suite; + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns junit-style testSuite for the jdk8 extension of the + * given test class, but parameterized by passing extra data to + * each test. Uses reflection to allow compilation in jdk7. + */ + public static Test jdk8ParameterizedTestSuite + (Class testClass, + Class dataClass, + ExtraData data) { + if (atLeastJava8()) { + String name = testClass.getName(); + String name8 = name.replaceAll("Test$", "8Test"); + if (name.equals(name8)) throw new Error(name); + try { + return (Test) + Class.forName(name8) + .getMethod("testSuite", new Class[] { dataClass }) + .invoke(null, data); + } catch (Exception e) { + throw new Error(e); + } + } else { + return new TestSuite(); + } + } + + // Delays for timing-dependent tests, in milliseconds. + + public static long SHORT_DELAY_MS; + public static long SMALL_DELAY_MS; + public static long MEDIUM_DELAY_MS; + public static long LONG_DELAY_MS; + + /** + * Returns the shortest timed delay. This can be scaled up for + * slow machines using the jsr166.delay.factor system property. + */ + protected long getShortDelay() { + return 50 * delayFactor; + } + + /** + * Sets delays as multiples of SHORT_DELAY. + */ + protected void setDelays() { + SHORT_DELAY_MS = getShortDelay(); + SMALL_DELAY_MS = SHORT_DELAY_MS * 5; + MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10; + LONG_DELAY_MS = SHORT_DELAY_MS * 200; + } + + /** + * Returns a timeout in milliseconds to be used in tests that + * verify that operations block or time out. + */ + long timeoutMillis() { + return SHORT_DELAY_MS / 4; + } + + /** + * Returns a new Date instance representing a time at least + * delayMillis milliseconds in the future. + */ + Date delayedDate(long delayMillis) { + // Add 1 because currentTimeMillis is known to round into the past. + return new Date(System.currentTimeMillis() + delayMillis + 1); + } + + /** + * The first exception encountered if any threadAssertXXX method fails. + */ + private final AtomicReference threadFailure + = new AtomicReference(null); + + /** + * Records an exception so that it can be rethrown later in the test + * harness thread, triggering a test case failure. Only the first + * failure is recorded; subsequent calls to this method from within + * the same test have no effect. + */ + public void threadRecordFailure(Throwable t) { + System.err.println(t); + dumpTestThreads(); + threadFailure.compareAndSet(null, t); + } + + public void setUp() { + setDelays(); + } + + void tearDownFail(String format, Object... args) { + String msg = toString() + ": " + String.format(format, args); + System.err.println(msg); + dumpTestThreads(); + throw new AssertionFailedError(msg); + } + + /** + * Extra checks that get done for all test cases. + * + * Triggers test case failure if any thread assertions have failed, + * by rethrowing, in the test harness thread, any exception recorded + * earlier by threadRecordFailure. + * + * Triggers test case failure if interrupt status is set in the main thread. + */ + public void tearDown() throws Exception { + Throwable t = threadFailure.getAndSet(null); + if (t != null) { + if (t instanceof Error) + throw (Error) t; + else if (t instanceof RuntimeException) + throw (RuntimeException) t; + else if (t instanceof Exception) + throw (Exception) t; + else { + AssertionFailedError afe = + new AssertionFailedError(t.toString()); + afe.initCause(t); + throw afe; + } + } + + if (Thread.interrupted()) + tearDownFail("interrupt status set in main thread"); + + checkForkJoinPoolThreadLeaks(); + } + + /** + * Finds missing PoolCleaners + */ + void checkForkJoinPoolThreadLeaks() throws InterruptedException { + Thread[] survivors = new Thread[7]; + int count = Thread.enumerate(survivors); + for (int i = 0; i < count; i++) { + Thread thread = survivors[i]; + String name = thread.getName(); + if (name.startsWith("ForkJoinPool-")) { + // give thread some time to terminate + thread.join(LONG_DELAY_MS); + if (thread.isAlive()) + tearDownFail("Found leaked ForkJoinPool thread thread=%s", + thread); + } + } + + if (!ForkJoinPool.commonPool() + .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS)) + tearDownFail("ForkJoin common pool thread stuck"); + } + + /** + * Just like fail(reason), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadFail(String reason) { + try { + fail(reason); + } catch (AssertionFailedError t) { + threadRecordFailure(t); + throw t; + } + } + + /** + * Just like assertTrue(b), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertTrue(boolean b) { + try { + assertTrue(b); + } catch (AssertionFailedError t) { + threadRecordFailure(t); + throw t; + } + } + + /** + * Just like assertFalse(b), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertFalse(boolean b) { + try { + assertFalse(b); + } catch (AssertionFailedError t) { + threadRecordFailure(t); + throw t; + } + } + + /** + * Just like assertNull(x), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertNull(Object x) { + try { + assertNull(x); + } catch (AssertionFailedError t) { + threadRecordFailure(t); + throw t; + } + } + + /** + * Just like assertEquals(x, y), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertEquals(long x, long y) { + try { + assertEquals(x, y); + } catch (AssertionFailedError t) { + threadRecordFailure(t); + throw t; + } + } + + /** + * Just like assertEquals(x, y), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertEquals(Object x, Object y) { + try { + assertEquals(x, y); + } catch (AssertionFailedError fail) { + threadRecordFailure(fail); + throw fail; + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + + /** + * Just like assertSame(x, y), but additionally recording (using + * threadRecordFailure) any AssertionFailedError thrown, so that + * the current testcase will fail. + */ + public void threadAssertSame(Object x, Object y) { + try { + assertSame(x, y); + } catch (AssertionFailedError fail) { + threadRecordFailure(fail); + throw fail; + } + } + + /** + * Calls threadFail with message "should throw exception". + */ + public void threadShouldThrow() { + threadFail("should throw exception"); + } + + /** + * Calls threadFail with message "should throw" + exceptionName. + */ + public void threadShouldThrow(String exceptionName) { + threadFail("should throw " + exceptionName); + } + + /** + * Records the given exception using {@link #threadRecordFailure}, + * then rethrows the exception, wrapping it in an + * AssertionFailedError if necessary. + */ + public void threadUnexpectedException(Throwable t) { + threadRecordFailure(t); + t.printStackTrace(); + if (t instanceof RuntimeException) + throw (RuntimeException) t; + else if (t instanceof Error) + throw (Error) t; + else { + AssertionFailedError afe = + new AssertionFailedError("unexpected exception: " + t); + afe.initCause(t); + throw afe; + } + } + + /** + * Delays, via Thread.sleep, for the given millisecond delay, but + * if the sleep is shorter than specified, may re-sleep or yield + * until time elapses. Ensures that the given time, as measured + * by System.nanoTime(), has elapsed. + */ + static void delay(long millis) throws InterruptedException { + long nanos = millis * (1000 * 1000); + final long wakeupTime = System.nanoTime() + nanos; + do { + if (millis > 0L) + Thread.sleep(millis); + else // too short to sleep + Thread.yield(); + nanos = wakeupTime - System.nanoTime(); + millis = nanos / (1000 * 1000); + } while (nanos >= 0L); + } + + /** + * Allows use of try-with-resources with per-test thread pools. + */ + class PoolCleaner implements AutoCloseable { + private final ExecutorService pool; + public PoolCleaner(ExecutorService pool) { this.pool = pool; } + public void close() { joinPool(pool); } + } + + /** + * An extension of PoolCleaner that has an action to release the pool. + */ + class PoolCleanerWithReleaser extends PoolCleaner { + private final Runnable releaser; + public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) { + super(pool); + this.releaser = releaser; + } + public void close() { + try { + releaser.run(); + } finally { + super.close(); + } + } + } + + PoolCleaner cleaner(ExecutorService pool) { + return new PoolCleaner(pool); + } + + PoolCleaner cleaner(ExecutorService pool, Runnable releaser) { + return new PoolCleanerWithReleaser(pool, releaser); + } + + PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) { + return new PoolCleanerWithReleaser(pool, releaser(latch)); + } + + Runnable releaser(final CountDownLatch latch) { + return new Runnable() { public void run() { + do { latch.countDown(); } + while (latch.getCount() > 0); + }}; + } + + /** + * Waits out termination of a thread pool or fails doing so. + */ + void joinPool(ExecutorService pool) { + try { + pool.shutdown(); + if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) { + try { + threadFail("ExecutorService " + pool + + " did not terminate in a timely manner"); + } finally { + // last resort, for the benefit of subsequent tests + pool.shutdownNow(); + pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS); + } + } + } catch (SecurityException ok) { + // Allowed in case test doesn't have privs + } catch (InterruptedException fail) { + threadFail("Unexpected InterruptedException"); + } + } + + /** Like Runnable, but with the freedom to throw anything */ + interface Action { public void run() throws Throwable; } + + /** + * Runs all the given actions in parallel, failing if any fail. + * Useful for running multiple variants of tests that are + * necessarily individually slow because they must block. + */ + void testInParallel(Action ... actions) { + ExecutorService pool = Executors.newCachedThreadPool(); + try (PoolCleaner cleaner = cleaner(pool)) { + ArrayList> futures = new ArrayList<>(actions.length); + for (final Action action : actions) + futures.add(pool.submit(new CheckedRunnable() { + public void realRun() throws Throwable { action.run();}})); + for (Future future : futures) + try { + assertNull(future.get(LONG_DELAY_MS, MILLISECONDS)); + } catch (ExecutionException ex) { + threadUnexpectedException(ex.getCause()); + } catch (Exception ex) { + threadUnexpectedException(ex); + } + } + } + + /** + * A debugging tool to print stack traces of most threads, as jstack does. + * Uninteresting threads are filtered out. + */ + static void dumpTestThreads() { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + System.err.println("------ stacktrace dump start ------"); + for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { + String name = info.getThreadName(); + if ("Signal Dispatcher".equals(name)) + continue; + if ("Reference Handler".equals(name) + && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) + continue; + if ("Finalizer".equals(name) + && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) + continue; + if ("checkForWedgedTest".equals(name)) + continue; + System.err.print(info); + } + System.err.println("------ stacktrace dump end ------"); + } + + /** + * Checks that thread does not terminate within the default + * millisecond delay of {@code timeoutMillis()}. + */ + void assertThreadStaysAlive(Thread thread) { + assertThreadStaysAlive(thread, timeoutMillis()); + } + + /** + * Checks that thread does not terminate within the given millisecond delay. + */ + void assertThreadStaysAlive(Thread thread, long millis) { + try { + // No need to optimize the failing case via Thread.join. + delay(millis); + assertTrue(thread.isAlive()); + } catch (InterruptedException fail) { + threadFail("Unexpected InterruptedException"); + } + } + + /** + * Checks that the threads do not terminate within the default + * millisecond delay of {@code timeoutMillis()}. + */ + void assertThreadsStayAlive(Thread... threads) { + assertThreadsStayAlive(timeoutMillis(), threads); + } + + /** + * Checks that the threads do not terminate within the given millisecond delay. + */ + void assertThreadsStayAlive(long millis, Thread... threads) { + try { + // No need to optimize the failing case via Thread.join. + delay(millis); + for (Thread thread : threads) + assertTrue(thread.isAlive()); + } catch (InterruptedException fail) { + threadFail("Unexpected InterruptedException"); + } + } + + /** + * Checks that future.get times out, with the default timeout of + * {@code timeoutMillis()}. + */ + void assertFutureTimesOut(Future future) { + assertFutureTimesOut(future, timeoutMillis()); + } + + /** + * Checks that future.get times out, with the given millisecond timeout. + */ + void assertFutureTimesOut(Future future, long timeoutMillis) { + long startTime = System.nanoTime(); + try { + future.get(timeoutMillis, MILLISECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Exception fail) { + threadUnexpectedException(fail); + } finally { future.cancel(true); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + } + + /** + * Fails with message "should throw exception". + */ + public void shouldThrow() { + fail("Should throw exception"); + } + + /** + * Fails with message "should throw " + exceptionName. + */ + public void shouldThrow(String exceptionName) { + fail("Should throw " + exceptionName); + } + + /** + * The number of elements to place in collections, arrays, etc. + */ + public static final int SIZE = 20; + + // Some convenient Integer constants + + public static final Integer zero = new Integer(0); + public static final Integer one = new Integer(1); + public static final Integer two = new Integer(2); + public static final Integer three = new Integer(3); + public static final Integer four = new Integer(4); + public static final Integer five = new Integer(5); + public static final Integer six = new Integer(6); + public static final Integer seven = new Integer(7); + public static final Integer eight = new Integer(8); + public static final Integer nine = new Integer(9); + public static final Integer m1 = new Integer(-1); + public static final Integer m2 = new Integer(-2); + public static final Integer m3 = new Integer(-3); + public static final Integer m4 = new Integer(-4); + public static final Integer m5 = new Integer(-5); + public static final Integer m6 = new Integer(-6); + public static final Integer m10 = new Integer(-10); + + /** + * Runs Runnable r with a security policy that permits precisely + * the specified permissions. If there is no current security + * manager, the runnable is run twice, both with and without a + * security manager. We require that any security manager permit + * getPolicy/setPolicy. + */ + public void runWithPermissions(Runnable r, Permission... permissions) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + r.run(); + } + runWithSecurityManagerWithPermissions(r, permissions); + } + + /** + * Runs Runnable r with a security policy that permits precisely + * the specified permissions. If there is no current security + * manager, a temporary one is set for the duration of the + * Runnable. We require that any security manager permit + * getPolicy/setPolicy. + */ + public void runWithSecurityManagerWithPermissions(Runnable r, + Permission... permissions) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + Policy savedPolicy = Policy.getPolicy(); + try { + Policy.setPolicy(permissivePolicy()); + System.setSecurityManager(new SecurityManager()); + runWithSecurityManagerWithPermissions(r, permissions); + } finally { + System.setSecurityManager(null); + Policy.setPolicy(savedPolicy); + } + } else { + Policy savedPolicy = Policy.getPolicy(); + AdjustablePolicy policy = new AdjustablePolicy(permissions); + Policy.setPolicy(policy); + + try { + r.run(); + } finally { + policy.addPermission(new SecurityPermission("setPolicy")); + Policy.setPolicy(savedPolicy); + } + } + } + + /** + * Runs a runnable without any permissions. + */ + public void runWithoutPermissions(Runnable r) { + runWithPermissions(r); + } + + /** + * A security policy where new permissions can be dynamically added + * or all cleared. + */ + public static class AdjustablePolicy extends java.security.Policy { + Permissions perms = new Permissions(); + AdjustablePolicy(Permission... permissions) { + for (Permission permission : permissions) + perms.add(permission); + } + void addPermission(Permission perm) { perms.add(perm); } + void clearPermissions() { perms = new Permissions(); } + public PermissionCollection getPermissions(CodeSource cs) { + return perms; + } + public PermissionCollection getPermissions(ProtectionDomain pd) { + return perms; + } + public boolean implies(ProtectionDomain pd, Permission p) { + return perms.implies(p); + } + public void refresh() {} + public String toString() { + List ps = new ArrayList(); + for (Enumeration e = perms.elements(); e.hasMoreElements();) + ps.add(e.nextElement()); + return "AdjustablePolicy with permissions " + ps; + } + } + + /** + * Returns a policy containing all the permissions we ever need. + */ + public static Policy permissivePolicy() { + return new AdjustablePolicy + // Permissions j.u.c. needs directly + (new RuntimePermission("modifyThread"), + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader"), + // Permissions needed to change permissions! + new SecurityPermission("getPolicy"), + new SecurityPermission("setPolicy"), + new RuntimePermission("setSecurityManager"), + // Permissions needed by the junit test harness + new RuntimePermission("accessDeclaredMembers"), + new PropertyPermission("*", "read"), + new java.io.FilePermission("<>", "read")); + } + + /** + * Sleeps until the given time has elapsed. + * Throws AssertionFailedError if interrupted. + */ + void sleep(long millis) { + try { + delay(millis); + } catch (InterruptedException fail) { + AssertionFailedError afe = + new AssertionFailedError("Unexpected InterruptedException"); + afe.initCause(fail); + throw afe; + } + } + + /** + * Spin-waits up to the specified number of milliseconds for the given + * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. + */ + void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { + long startTime = System.nanoTime(); + for (;;) { + Thread.State s = thread.getState(); + if (s == Thread.State.BLOCKED || + s == Thread.State.WAITING || + s == Thread.State.TIMED_WAITING) + return; + else if (s == Thread.State.TERMINATED) + fail("Unexpected thread termination"); + else if (millisElapsedSince(startTime) > timeoutMillis) { + threadAssertTrue(thread.isAlive()); + return; + } + Thread.yield(); + } + } + + /** + * Waits up to LONG_DELAY_MS for the given thread to enter a wait + * state: BLOCKED, WAITING, or TIMED_WAITING. + */ + void waitForThreadToEnterWaitState(Thread thread) { + waitForThreadToEnterWaitState(thread, LONG_DELAY_MS); + } + + /** + * Returns the number of milliseconds since time given by + * startNanoTime, which must have been previously returned from a + * call to {@link System#nanoTime()}. + */ + static long millisElapsedSince(long startNanoTime) { + return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime); + } + +// void assertTerminatesPromptly(long timeoutMillis, Runnable r) { +// long startTime = System.nanoTime(); +// try { +// r.run(); +// } catch (Throwable fail) { threadUnexpectedException(fail); } +// if (millisElapsedSince(startTime) > timeoutMillis/2) +// throw new AssertionFailedError("did not return promptly"); +// } + +// void assertTerminatesPromptly(Runnable r) { +// assertTerminatesPromptly(LONG_DELAY_MS/2, r); +// } + + /** + * Checks that timed f.get() returns the expected value, and does not + * wait for the timeout to elapse before returning. + */ + void checkTimedGet(Future f, T expectedValue, long timeoutMillis) { + long startTime = System.nanoTime(); + try { + assertEquals(expectedValue, f.get(timeoutMillis, MILLISECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + if (millisElapsedSince(startTime) > timeoutMillis/2) + throw new AssertionFailedError("timed get did not return promptly"); + } + + void checkTimedGet(Future f, T expectedValue) { + checkTimedGet(f, expectedValue, LONG_DELAY_MS); + } + + /** + * Returns a new started daemon Thread running the given runnable. + */ + Thread newStartedThread(Runnable runnable) { + Thread t = new Thread(runnable); + t.setDaemon(true); + t.start(); + return t; + } + + /** + * Waits for the specified time (in milliseconds) for the thread + * to terminate (using {@link Thread#join(long)}), else interrupts + * the thread (in the hope that it may terminate later) and fails. + */ + void awaitTermination(Thread t, long timeoutMillis) { + try { + t.join(timeoutMillis); + } catch (InterruptedException fail) { + threadUnexpectedException(fail); + } finally { + if (t.getState() != Thread.State.TERMINATED) { + t.interrupt(); + threadFail("timed out waiting for thread to terminate"); + } + } + } + + /** + * Waits for LONG_DELAY_MS milliseconds for the thread to + * terminate (using {@link Thread#join(long)}), else interrupts + * the thread (in the hope that it may terminate later) and fails. + */ + void awaitTermination(Thread t) { + awaitTermination(t, LONG_DELAY_MS); + } + + // Some convenient Runnable classes + + public abstract class CheckedRunnable implements Runnable { + protected abstract void realRun() throws Throwable; + + public final void run() { + try { + realRun(); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + } + + public abstract class RunnableShouldThrow implements Runnable { + protected abstract void realRun() throws Throwable; + + final Class exceptionClass; + + RunnableShouldThrow(Class exceptionClass) { + this.exceptionClass = exceptionClass; + } + + public final void run() { + try { + realRun(); + threadShouldThrow(exceptionClass.getSimpleName()); + } catch (Throwable t) { + if (! exceptionClass.isInstance(t)) + threadUnexpectedException(t); + } + } + } + + public abstract class ThreadShouldThrow extends Thread { + protected abstract void realRun() throws Throwable; + + final Class exceptionClass; + + ThreadShouldThrow(Class exceptionClass) { + this.exceptionClass = exceptionClass; + } + + public final void run() { + try { + realRun(); + threadShouldThrow(exceptionClass.getSimpleName()); + } catch (Throwable t) { + if (! exceptionClass.isInstance(t)) + threadUnexpectedException(t); + } + } + } + + public abstract class CheckedInterruptedRunnable implements Runnable { + protected abstract void realRun() throws Throwable; + + public final void run() { + try { + realRun(); + threadShouldThrow("InterruptedException"); + } catch (InterruptedException success) { + threadAssertFalse(Thread.interrupted()); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + } + + public abstract class CheckedCallable implements Callable { + protected abstract T realCall() throws Throwable; + + public final T call() { + try { + return realCall(); + } catch (Throwable fail) { + threadUnexpectedException(fail); + return null; + } + } + } + + public abstract class CheckedInterruptedCallable + implements Callable { + protected abstract T realCall() throws Throwable; + + public final T call() { + try { + T result = realCall(); + threadShouldThrow("InterruptedException"); + return result; + } catch (InterruptedException success) { + threadAssertFalse(Thread.interrupted()); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + return null; + } + } + + public static class NoOpRunnable implements Runnable { + public void run() {} + } + + public static class NoOpCallable implements Callable { + public Object call() { return Boolean.TRUE; } + } + + public static final String TEST_STRING = "a test string"; + + public static class StringTask implements Callable { + final String value; + public StringTask() { this(TEST_STRING); } + public StringTask(String value) { this.value = value; } + public String call() { return value; } + } + + public Callable latchAwaitingStringTask(final CountDownLatch latch) { + return new CheckedCallable() { + protected String realCall() { + try { + latch.await(); + } catch (InterruptedException quittingTime) {} + return TEST_STRING; + }}; + } + + public Runnable countDowner(final CountDownLatch latch) { + return new CheckedRunnable() { + public void realRun() throws InterruptedException { + latch.countDown(); + }}; + } + + class LatchAwaiter extends CheckedRunnable { + static final int NEW = 0; + static final int RUNNING = 1; + static final int DONE = 2; + final CountDownLatch latch; + int state = NEW; + LatchAwaiter(CountDownLatch latch) { this.latch = latch; } + public void realRun() throws InterruptedException { + state = 1; + await(latch); + state = 2; + } + } + + public LatchAwaiter awaiter(CountDownLatch latch) { + return new LatchAwaiter(latch); + } + + public void await(CountDownLatch latch) { + try { + if (!latch.await(LONG_DELAY_MS, MILLISECONDS)) + fail("timed out waiting for CountDownLatch for " + + (LONG_DELAY_MS/1000) + " sec"); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + + public void await(Semaphore semaphore) { + try { + if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)) + fail("timed out waiting for Semaphore for " + + (LONG_DELAY_MS/1000) + " sec"); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + +// /** +// * Spin-waits up to LONG_DELAY_MS until flag becomes true. +// */ +// public void await(AtomicBoolean flag) { +// await(flag, LONG_DELAY_MS); +// } + +// /** +// * Spin-waits up to the specified timeout until flag becomes true. +// */ +// public void await(AtomicBoolean flag, long timeoutMillis) { +// long startTime = System.nanoTime(); +// while (!flag.get()) { +// if (millisElapsedSince(startTime) > timeoutMillis) +// throw new AssertionFailedError("timed out"); +// Thread.yield(); +// } +// } + + public static class NPETask implements Callable { + public String call() { throw new NullPointerException(); } + } + + public static class CallableOne implements Callable { + public Integer call() { return one; } + } + + public class ShortRunnable extends CheckedRunnable { + protected void realRun() throws Throwable { + delay(SHORT_DELAY_MS); + } + } + + public class ShortInterruptedRunnable extends CheckedInterruptedRunnable { + protected void realRun() throws InterruptedException { + delay(SHORT_DELAY_MS); + } + } + + public class SmallRunnable extends CheckedRunnable { + protected void realRun() throws Throwable { + delay(SMALL_DELAY_MS); + } + } + + public class SmallPossiblyInterruptedRunnable extends CheckedRunnable { + protected void realRun() { + try { + delay(SMALL_DELAY_MS); + } catch (InterruptedException ok) {} + } + } + + public class SmallCallable extends CheckedCallable { + protected Object realCall() throws InterruptedException { + delay(SMALL_DELAY_MS); + return Boolean.TRUE; + } + } + + public class MediumRunnable extends CheckedRunnable { + protected void realRun() throws Throwable { + delay(MEDIUM_DELAY_MS); + } + } + + public class MediumInterruptedRunnable extends CheckedInterruptedRunnable { + protected void realRun() throws InterruptedException { + delay(MEDIUM_DELAY_MS); + } + } + + public Runnable possiblyInterruptedRunnable(final long timeoutMillis) { + return new CheckedRunnable() { + protected void realRun() { + try { + delay(timeoutMillis); + } catch (InterruptedException ok) {} + }}; + } + + public class MediumPossiblyInterruptedRunnable extends CheckedRunnable { + protected void realRun() { + try { + delay(MEDIUM_DELAY_MS); + } catch (InterruptedException ok) {} + } + } + + public class LongPossiblyInterruptedRunnable extends CheckedRunnable { + protected void realRun() { + try { + delay(LONG_DELAY_MS); + } catch (InterruptedException ok) {} + } + } + + /** + * For use as ThreadFactory in constructors + */ + public static class SimpleThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { + return new Thread(r); + } + } + + public interface TrackedRunnable extends Runnable { + boolean isDone(); + } + + public static TrackedRunnable trackedRunnable(final long timeoutMillis) { + return new TrackedRunnable() { + private volatile boolean done = false; + public boolean isDone() { return done; } + public void run() { + try { + delay(timeoutMillis); + done = true; + } catch (InterruptedException ok) {} + } + }; + } + + public static class TrackedShortRunnable implements Runnable { + public volatile boolean done = false; + public void run() { + try { + delay(SHORT_DELAY_MS); + done = true; + } catch (InterruptedException ok) {} + } + } + + public static class TrackedSmallRunnable implements Runnable { + public volatile boolean done = false; + public void run() { + try { + delay(SMALL_DELAY_MS); + done = true; + } catch (InterruptedException ok) {} + } + } + + public static class TrackedMediumRunnable implements Runnable { + public volatile boolean done = false; + public void run() { + try { + delay(MEDIUM_DELAY_MS); + done = true; + } catch (InterruptedException ok) {} + } + } + + public static class TrackedLongRunnable implements Runnable { + public volatile boolean done = false; + public void run() { + try { + delay(LONG_DELAY_MS); + done = true; + } catch (InterruptedException ok) {} + } + } + + public static class TrackedNoOpRunnable implements Runnable { + public volatile boolean done = false; + public void run() { + done = true; + } + } + + public static class TrackedCallable implements Callable { + public volatile boolean done = false; + public Object call() { + try { + delay(SMALL_DELAY_MS); + done = true; + } catch (InterruptedException ok) {} + return Boolean.TRUE; + } + } + + /** + * Analog of CheckedRunnable for RecursiveAction + */ + public abstract class CheckedRecursiveAction extends RecursiveAction { + protected abstract void realCompute() throws Throwable; + + @Override protected final void compute() { + try { + realCompute(); + } catch (Throwable fail) { + threadUnexpectedException(fail); + } + } + } + + /** + * Analog of CheckedCallable for RecursiveTask + */ + public abstract class CheckedRecursiveTask extends RecursiveTask { + protected abstract T realCompute() throws Throwable; + + @Override protected final T compute() { + try { + return realCompute(); + } catch (Throwable fail) { + threadUnexpectedException(fail); + return null; + } + } + } + + /** + * For use as RejectedExecutionHandler in constructors + */ + public static class NoOpREHandler implements RejectedExecutionHandler { + public void rejectedExecution(Runnable r, + ThreadPoolExecutor executor) {} + } + + /** + * A CyclicBarrier that uses timed await and fails with + * AssertionFailedErrors instead of throwing checked exceptions. + */ + public class CheckedBarrier extends CyclicBarrier { + public CheckedBarrier(int parties) { super(parties); } + + public int await() { + try { + return super.await(2 * LONG_DELAY_MS, MILLISECONDS); + } catch (TimeoutException timedOut) { + throw new AssertionFailedError("timed out"); + } catch (Exception fail) { + AssertionFailedError afe = + new AssertionFailedError("Unexpected exception: " + fail); + afe.initCause(fail); + throw afe; + } + } + } + + void checkEmpty(BlockingQueue q) { + try { + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertNull(q.peek()); + assertNull(q.poll()); + assertNull(q.poll(0, MILLISECONDS)); + assertEquals(q.toString(), "[]"); + assertTrue(Arrays.equals(q.toArray(), new Object[0])); + assertFalse(q.iterator().hasNext()); + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + q.iterator().next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + void assertSerialEquals(Object x, Object y) { + assertTrue(Arrays.equals(serialBytes(x), serialBytes(y))); + } + + void assertNotSerialEquals(Object x, Object y) { + assertFalse(Arrays.equals(serialBytes(x), serialBytes(y))); + } + + byte[] serialBytes(Object o) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(o); + oos.flush(); + oos.close(); + return bos.toByteArray(); + } catch (Throwable fail) { + threadUnexpectedException(fail); + return new byte[0]; + } + } + + @SuppressWarnings("unchecked") + T serialClone(T o) { + try { + ObjectInputStream ois = new ObjectInputStream + (new ByteArrayInputStream(serialBytes(o))); + T clone = (T) ois.readObject(); + assertSame(o.getClass(), clone.getClass()); + return clone; + } catch (Throwable fail) { + threadUnexpectedException(fail); + return null; + } + } + + public void assertThrows(Class expectedExceptionClass, + Runnable... throwingActions) { + for (Runnable throwingAction : throwingActions) { + boolean threw = false; + try { throwingAction.run(); } + catch (Throwable t) { + threw = true; + if (!expectedExceptionClass.isInstance(t)) { + AssertionFailedError afe = + new AssertionFailedError + ("Expected " + expectedExceptionClass.getName() + + ", got " + t.getClass().getName()); + afe.initCause(t); + threadUnexpectedException(afe); + } + } + if (!threw) + shouldThrow(expectedExceptionClass.getName()); + } + } + + public void assertIteratorExhausted(Iterator it) { + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertFalse(it.hasNext()); + } +} diff --git a/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java b/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java new file mode 100644 index 00000000000..138799035cb --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java @@ -0,0 +1,1848 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; + +import junit.framework.Test; + +public class LinkedBlockingDequeTest extends JSR166TestCase { + + public static class Unbounded extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new LinkedBlockingDeque(); + } + } + + public static class Bounded extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new LinkedBlockingDeque(SIZE); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(LinkedBlockingDequeTest.class, + new Unbounded().testSuite(), + new Bounded().testSuite()); + } + + /** + * Returns a new deque of given size containing consecutive + * Integers 0 ... n. + */ + private LinkedBlockingDeque populatedDeque(int n) { + LinkedBlockingDeque q = + new LinkedBlockingDeque(n); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; i++) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertEquals(n, q.size()); + return q; + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.removeFirst(); + q.removeFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.removeFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * offerFirst(null) throws NullPointerException + */ + public void testOfferFirstNull() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + try { + q.offerFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offerLast(null) throws NullPointerException + */ + public void testOfferLastNull() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + try { + q.offerLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * OfferFirst succeeds + */ + public void testOfferFirst() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + assertTrue(q.offerFirst(new Integer(0))); + assertTrue(q.offerFirst(new Integer(1))); + } + + /** + * OfferLast succeeds + */ + public void testOfferLast() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + assertTrue(q.offerLast(new Integer(0))); + assertTrue(q.offerLast(new Integer(1))); + } + + /** + * pollFirst succeeds unless empty + */ + public void testPollFirst() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * pollLast succeeds unless empty + */ + public void testPollLast() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollLast()); + } + + /** + * peekFirst returns next element, or null if empty + */ + public void testPeekFirst() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peekFirst()); + assertEquals(i, q.pollFirst()); + assertTrue(q.peekFirst() == null || + !q.peekFirst().equals(i)); + } + assertNull(q.peekFirst()); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.pollFirst()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * peekLast returns next element, or null if empty + */ + public void testPeekLast() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.peekLast()); + assertEquals(i, q.pollLast()); + assertTrue(q.peekLast() == null || + !q.peekLast().equals(i)); + } + assertNull(q.peekLast()); + } + + /** + * getFirst() returns first element, or throws NSEE if empty + */ + public void testFirstElement() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.getFirst()); + assertEquals(i, q.pollFirst()); + } + try { + q.getFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekFirst()); + } + + /** + * getLast() returns last element, or throws NSEE if empty + */ + public void testLastElement() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.getLast()); + assertEquals(i, q.pollLast()); + } + try { + q.getLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirst() removes first element, or throws NSEE if empty + */ + public void testRemoveFirst() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.removeFirst()); + } + try { + q.removeFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekFirst()); + } + + /** + * removeLast() removes last element, or throws NSEE if empty + */ + public void testRemoveLast() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.removeLast()); + } + try { + q.removeLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * removeFirstOccurrence(x) removes x and returns true if present + */ + public void testRemoveFirstOccurrence() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + assertFalse(q.removeFirstOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * removeLastOccurrence(x) removes x and returns true if present + */ + public void testRemoveLastOccurrence() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + assertFalse(q.removeLastOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * peekFirst returns element inserted with addFirst + */ + public void testAddFirst() { + LinkedBlockingDeque q = populatedDeque(3); + q.pollLast(); + q.addFirst(four); + assertSame(four, q.peekFirst()); + } + + /** + * peekLast returns element inserted with addLast + */ + public void testAddLast() { + LinkedBlockingDeque q = populatedDeque(3); + q.pollLast(); + q.addLast(four); + assertSame(four, q.peekLast()); + } + + /** + * A new deque has the indicated capacity, or Integer.MAX_VALUE if + * none given + */ + public void testConstructor1() { + assertEquals(SIZE, new LinkedBlockingDeque(SIZE).remainingCapacity()); + assertEquals(Integer.MAX_VALUE, new LinkedBlockingDeque().remainingCapacity()); + } + + /** + * Constructor throws IllegalArgumentException if capacity argument nonpositive + */ + public void testConstructor2() { + try { + new LinkedBlockingDeque(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Initializing from null Collection throws NullPointerException + */ + public void testConstructor3() { + try { + new LinkedBlockingDeque(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NullPointerException + */ + public void testConstructor4() { + Collection elements = Arrays.asList(new Integer[SIZE]); + try { + new LinkedBlockingDeque(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws + * NullPointerException + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + try { + new LinkedBlockingDeque(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Deque contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + LinkedBlockingDeque q = new LinkedBlockingDeque(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * Deque transitions from empty to full when elements added + */ + public void testEmptyFull() { + LinkedBlockingDeque q = new LinkedBlockingDeque(2); + assertTrue(q.isEmpty()); + assertEquals("should have room for 2", 2, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertFalse(q.offer(three)); + } + + /** + * remainingCapacity decreases on add, increases on remove + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertEquals(i, q.remove()); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertTrue(q.add(i)); + } + } + + /** + * push(null) throws NPE + */ + public void testPushNull() { + LinkedBlockingDeque q = new LinkedBlockingDeque(1); + try { + q.push(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * push succeeds if not full; throws ISE if full + */ + public void testPush() { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.push(x); + assertEquals(x, q.peek()); + } + assertEquals(0, q.remainingCapacity()); + try { + q.push(new Integer(SIZE)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * peekFirst returns element inserted with push + */ + public void testPushWithPeek() { + LinkedBlockingDeque q = populatedDeque(3); + q.pollLast(); + q.push(four); + assertSame(four, q.peekFirst()); + } + + /** + * pop removes next element, or throws NSEE if empty + */ + public void testPop() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pop()); + } + try { + q.pop(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * Offer succeeds if not full; fails if full + */ + public void testOffer() { + LinkedBlockingDeque q = new LinkedBlockingDeque(1); + assertTrue(q.offer(zero)); + assertFalse(q.offer(one)); + } + + /** + * add succeeds if not full; throws ISE if full + */ + public void testAdd() { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) + assertTrue(q.add(new Integer(i))); + assertEquals(0, q.remainingCapacity()); + try { + q.add(new Integer(SIZE)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + LinkedBlockingDeque q = populatedDeque(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + Collection elements = Arrays.asList(ints); + try { + q.addAll(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll throws IllegalStateException if not enough room + */ + public void testAddAll4() { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE - 1); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + Collection elements = Arrays.asList(ints); + try { + q.addAll(elements); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * Deque contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * all elements successfully put are contained + */ + public void testPut() throws InterruptedException { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.put(x); + assertTrue(q.contains(x)); + } + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly if full + */ + public void testBlockingPut() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) + q.put(i); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + + Thread.currentThread().interrupt(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly waiting for take when full + */ + public void testPutWithTake() throws InterruptedException { + final int capacity = 2; + final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < capacity; i++) + q.put(i); + pleaseTake.countDown(); + q.put(86); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + assertEquals(0, q.take()); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offer times out if full and elements not taken + */ + public void testTimedOffer() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Object()); + q.put(new Object()); + long startTime = System.nanoTime(); + assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take retrieves elements in FIFO order + */ + public void testTake() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + } + + /** + * take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final BlockingQueue q = populatedDeque(SIZE); + final CountDownLatch aboutToWait = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + } + aboutToWait.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) { + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + }}); + + aboutToWait.await(); + waitForThreadToEnterWaitState(t, LONG_DELAY_MS); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * putFirst(null) throws NPE + */ + public void testPutFirstNull() throws InterruptedException { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + try { + q.putFirst(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * all elements successfully putFirst are contained + */ + public void testPutFirst() throws InterruptedException { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.putFirst(x); + assertTrue(q.contains(x)); + } + assertEquals(0, q.remainingCapacity()); + } + + /** + * putFirst blocks interruptibly if full + */ + public void testBlockingPutFirst() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) + q.putFirst(i); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + + Thread.currentThread().interrupt(); + try { + q.putFirst(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.putFirst(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + } + + /** + * putFirst blocks interruptibly waiting for take when full + */ + public void testPutFirstWithTake() throws InterruptedException { + final int capacity = 2; + final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < capacity; i++) + q.putFirst(i); + pleaseTake.countDown(); + q.putFirst(86); + + pleaseInterrupt.countDown(); + try { + q.putFirst(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + assertEquals(capacity - 1, q.take()); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offerFirst times out if full and elements not taken + */ + public void testTimedOfferFirst() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.putFirst(new Object()); + q.putFirst(new Object()); + long startTime = System.nanoTime(); + assertFalse(q.offerFirst(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take retrieves elements in FIFO order + */ + public void testTakeFirst() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.takeFirst()); + } + } + + /** + * takeFirst() blocks interruptibly when empty + */ + public void testTakeFirstFromEmptyBlocksInterruptibly() { + final BlockingDeque q = new LinkedBlockingDeque(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + try { + q.takeFirst(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(threadStarted); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * takeFirst() throws InterruptedException immediately if interrupted + * before waiting + */ + public void testTakeFirstFromEmptyAfterInterrupt() { + final BlockingDeque q = new LinkedBlockingDeque(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + try { + q.takeFirst(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + awaitTermination(t); + } + + /** + * takeLast() blocks interruptibly when empty + */ + public void testTakeLastFromEmptyBlocksInterruptibly() { + final BlockingDeque q = new LinkedBlockingDeque(); + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + try { + q.takeLast(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(threadStarted); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * takeLast() throws InterruptedException immediately if interrupted + * before waiting + */ + public void testTakeLastFromEmptyAfterInterrupt() { + final BlockingDeque q = new LinkedBlockingDeque(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + try { + q.takeLast(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + awaitTermination(t); + } + + /** + * takeFirst removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTakeFirst() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.takeFirst()); + } + + Thread.currentThread().interrupt(); + try { + q.takeFirst(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.takeFirst(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed pollFirst with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPollFirst0() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst(0, MILLISECONDS)); + } + assertNull(q.pollFirst(0, MILLISECONDS)); + } + + /** + * timed pollFirst with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPollFirst() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed pollFirst throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPollFirst() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS)); + } + + Thread.currentThread().interrupt(); + try { + q.pollFirst(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.pollFirst(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed pollFirst before a delayed offerFirst fails; after offerFirst succeeds; + * on interruption throws + */ + public void testTimedPollFirstWithOfferFirst() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CheckedBarrier barrier = new CheckedBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + + barrier.await(); + + assertSame(zero, q.pollFirst(LONG_DELAY_MS, MILLISECONDS)); + + Thread.currentThread().interrupt(); + try { + q.pollFirst(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + + barrier.await(); + try { + q.pollFirst(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + barrier.await(); + long startTime = System.nanoTime(); + assertTrue(q.offerFirst(zero, LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + barrier.await(); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * putLast(null) throws NPE + */ + public void testPutLastNull() throws InterruptedException { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + try { + q.putLast(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * all elements successfully putLast are contained + */ + public void testPutLast() throws InterruptedException { + LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.putLast(x); + assertTrue(q.contains(x)); + } + assertEquals(0, q.remainingCapacity()); + } + + /** + * putLast blocks interruptibly if full + */ + public void testBlockingPutLast() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) + q.putLast(i); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + + Thread.currentThread().interrupt(); + try { + q.putLast(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.putLast(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + } + + /** + * putLast blocks interruptibly waiting for take when full + */ + public void testPutLastWithTake() throws InterruptedException { + final int capacity = 2; + final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < capacity; i++) + q.putLast(i); + pleaseTake.countDown(); + q.putLast(86); + + pleaseInterrupt.countDown(); + try { + q.putLast(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + assertEquals(0, q.take()); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offerLast times out if full and elements not taken + */ + public void testTimedOfferLast() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.putLast(new Object()); + q.putLast(new Object()); + long startTime = System.nanoTime(); + assertFalse(q.offerLast(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * takeLast retrieves elements in FIFO order + */ + public void testTakeLast() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i - 1, q.takeLast()); + } + } + + /** + * takeLast removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTakeLast() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i - 1, q.takeLast()); + } + + Thread.currentThread().interrupt(); + try { + q.takeLast(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.takeLast(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timed pollLast with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPollLast0() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i - 1, q.pollLast(0, MILLISECONDS)); + } + assertNull(q.pollLast(0, MILLISECONDS)); + } + + /** + * timed pollLast with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPollLast() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(SIZE - i - 1, q.pollLast(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.pollLast(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed pollLast throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPollLast() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i - 1, + q.pollLast(LONG_DELAY_MS, MILLISECONDS)); + } + + Thread.currentThread().interrupt(); + try { + q.pollLast(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.pollLast(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * timed poll before a delayed offerLast fails; after offerLast succeeds; + * on interruption throws + */ + public void testTimedPollWithOfferLast() throws InterruptedException { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CheckedBarrier barrier = new CheckedBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + + barrier.await(); + + assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS)); + + Thread.currentThread().interrupt(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + barrier.await(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + barrier.await(); + long startTime = System.nanoTime(); + assertTrue(q.offerLast(zero, LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + + barrier.await(); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + q.poll(); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + LinkedBlockingDeque q = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + LinkedBlockingDeque q = populatedDeque(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertEquals(SIZE, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + LinkedBlockingDeque q = populatedDeque(SIZE); + LinkedBlockingDeque p = new LinkedBlockingDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + LinkedBlockingDeque q = populatedDeque(SIZE); + LinkedBlockingDeque p = populatedDeque(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + LinkedBlockingDeque q = populatedDeque(SIZE); + LinkedBlockingDeque p = populatedDeque(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements in FIFO order + */ + public void testToArray() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + LinkedBlockingDeque q = populatedDeque(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.remove()); + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + LinkedBlockingDeque q = populatedDeque(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() throws InterruptedException { + LinkedBlockingDeque q = populatedDeque(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + + it = q.iterator(); + for (i = 0; it.hasNext(); i++) + assertEquals(it.next(), q.take()); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + Deque c = new LinkedBlockingDeque(); + assertIteratorExhausted(c.iterator()); + assertIteratorExhausted(c.descendingIterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(3); + q.add(two); + q.add(one); + q.add(three); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertSame(it.next(), one); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(3); + q.add(one); + q.add(two); + q.add(three); + assertEquals(0, q.remainingCapacity()); + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(3); + q.add(one); + q.add(two); + q.add(three); + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + assertEquals(0, q.size()); + } + + /** + * Descending iterator iterates through all elements + */ + public void testDescendingIterator() { + LinkedBlockingDeque q = populatedDeque(SIZE); + int i = 0; + Iterator it = q.descendingIterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + assertFalse(it.hasNext()); + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * Descending iterator ordering is reverse FIFO + */ + public void testDescendingIteratorOrdering() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(); + for (int iters = 0; iters < 100; ++iters) { + q.add(new Integer(3)); + q.add(new Integer(2)); + q.add(new Integer(1)); + int k = 0; + for (Iterator it = q.descendingIterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + q.remove(); + q.remove(); + q.remove(); + } + } + + /** + * descendingIterator.remove removes current element + */ + public void testDescendingIteratorRemove() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(); + for (int iters = 0; iters < 100; ++iters) { + q.add(new Integer(3)); + q.add(new Integer(2)); + q.add(new Integer(1)); + Iterator it = q.descendingIterator(); + assertEquals(it.next(), new Integer(1)); + it.remove(); + assertEquals(it.next(), new Integer(2)); + it = q.descendingIterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + it.remove(); + assertFalse(it.hasNext()); + q.remove(); + } + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + LinkedBlockingDeque q = populatedDeque(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * offer transfers elements across Executor tasks + */ + public void testOfferInExecutor() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + q.add(one); + q.add(two); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(q.offer(three)); + threadsStarted.await(); + assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, q.remainingCapacity()); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + assertSame(one, q.take()); + }}); + } + } + + /** + * timed poll retrieves elements across Executor threads + */ + public void testPollInExecutor() { + final LinkedBlockingDeque q = new LinkedBlockingDeque(2); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * A deserialized serialized deque has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedDeque(SIZE); + Queue y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * drainTo(c) empties deque into another collection c + */ + public void testDrainTo() { + LinkedBlockingDeque q = populatedDeque(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(SIZE, l.size()); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); + } + + /** + * drainTo empties full deque, unblocking a waiting put. + */ + public void testDrainToWithActivePut() throws InterruptedException { + final LinkedBlockingDeque q = populatedDeque(SIZE); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Integer(SIZE + 1)); + }}); + + t.start(); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + t.join(); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + LinkedBlockingDeque q = new LinkedBlockingDeque(); + for (int i = 0; i < SIZE + 2; ++i) { + for (int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(k, l.size()); + assertEquals(SIZE - k, q.size()); + for (int j = 0; j < k; ++j) + assertEquals(l.get(j), new Integer(j)); + do {} while (q.poll() != null); + } + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Deque[] qs = { + new LinkedBlockingDeque(), + populatedDeque(2), + }; + + for (Deque q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + assertFalse(q.removeFirstOccurrence(null)); + assertFalse(q.removeLastOccurrence(null)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java new file mode 100644 index 00000000000..51246e903a6 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java @@ -0,0 +1,889 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; + +import junit.framework.Test; + +public class LinkedBlockingQueueTest extends JSR166TestCase { + + public static class Unbounded extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new LinkedBlockingQueue(); + } + } + + public static class Bounded extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new LinkedBlockingQueue(SIZE); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(LinkedBlockingQueueTest.class, + new Unbounded().testSuite(), + new Bounded().testSuite()); + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private LinkedBlockingQueue populatedQueue(int n) { + LinkedBlockingQueue q = + new LinkedBlockingQueue(n); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; i++) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertEquals(n, q.size()); + return q; + } + + /** + * A new queue has the indicated capacity, or Integer.MAX_VALUE if + * none given + */ + public void testConstructor1() { + assertEquals(SIZE, new LinkedBlockingQueue(SIZE).remainingCapacity()); + assertEquals(Integer.MAX_VALUE, new LinkedBlockingQueue().remainingCapacity()); + } + + /** + * Constructor throws IllegalArgumentException if capacity argument nonpositive + */ + public void testConstructor2() { + try { + new LinkedBlockingQueue(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Initializing from null Collection throws NullPointerException + */ + public void testConstructor3() { + try { + new LinkedBlockingQueue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NullPointerException + */ + public void testConstructor4() { + Collection elements = Arrays.asList(new Integer[SIZE]); + try { + new LinkedBlockingQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws + * NullPointerException + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + Collection elements = Arrays.asList(ints); + try { + new LinkedBlockingQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + LinkedBlockingQueue q = new LinkedBlockingQueue(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * Queue transitions from empty to full when elements added + */ + public void testEmptyFull() { + LinkedBlockingQueue q = new LinkedBlockingQueue(2); + assertTrue(q.isEmpty()); + assertEquals("should have room for 2", 2, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + assertFalse(q.isEmpty()); + assertEquals(0, q.remainingCapacity()); + assertFalse(q.offer(three)); + } + + /** + * remainingCapacity decreases on add, increases on remove + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertEquals(i, q.remove()); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.remainingCapacity()); + assertEquals(SIZE, q.size() + q.remainingCapacity()); + assertTrue(q.add(i)); + } + } + + /** + * Offer succeeds if not full; fails if full + */ + public void testOffer() { + LinkedBlockingQueue q = new LinkedBlockingQueue(1); + assertTrue(q.offer(zero)); + assertFalse(q.offer(one)); + } + + /** + * add succeeds if not full; throws IllegalStateException if full + */ + public void testAdd() { + LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) + assertTrue(q.add(new Integer(i))); + assertEquals(0, q.remainingCapacity()); + try { + q.add(new Integer(SIZE)); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * addAll(this) throws IllegalArgumentException + */ + public void testAddAllSelf() { + LinkedBlockingQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + Collection elements = Arrays.asList(ints); + try { + q.addAll(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll throws IllegalStateException if not enough room + */ + public void testAddAll4() { + LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE - 1); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + Collection elements = Arrays.asList(ints); + try { + q.addAll(elements); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * Queue contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * all elements successfully put are contained + */ + public void testPut() throws InterruptedException { + LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.put(x); + assertTrue(q.contains(x)); + } + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly if full + */ + public void testBlockingPut() throws InterruptedException { + final LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) + q.put(i); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + + Thread.currentThread().interrupt(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(SIZE, q.size()); + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly waiting for take when full + */ + public void testPutWithTake() throws InterruptedException { + final int capacity = 2; + final LinkedBlockingQueue q = new LinkedBlockingQueue(2); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < capacity; i++) + q.put(i); + pleaseTake.countDown(); + q.put(86); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + assertEquals(0, q.take()); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offer times out if full and elements not taken + */ + public void testTimedOffer() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(2); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Object()); + q.put(new Object()); + long startTime = System.nanoTime(); + assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * take retrieves elements in FIFO order + */ + public void testTake() throws InterruptedException { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + } + + /** + * Take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch aboutToWait = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + } + aboutToWait.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) { + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + }}); + + await(aboutToWait); + waitForThreadToEnterWaitState(t, LONG_DELAY_MS); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * An add following remove(x) succeeds + */ + public void testRemoveElementAndAdd() throws InterruptedException { + LinkedBlockingQueue q = new LinkedBlockingQueue(); + assertTrue(q.add(new Integer(1))); + assertTrue(q.add(new Integer(2))); + assertTrue(q.remove(new Integer(1))); + assertTrue(q.remove(new Integer(2))); + assertTrue(q.add(new Integer(3))); + assertNotNull(q.take()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + LinkedBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + LinkedBlockingQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertEquals(SIZE, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + LinkedBlockingQueue q = populatedQueue(SIZE); + LinkedBlockingQueue p = new LinkedBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + LinkedBlockingQueue q = populatedQueue(SIZE); + LinkedBlockingQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + LinkedBlockingQueue q = populatedQueue(SIZE); + LinkedBlockingQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements in FIFO order + */ + public void testToArray() { + LinkedBlockingQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() throws InterruptedException { + LinkedBlockingQueue q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.poll()); + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + LinkedBlockingQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() throws InterruptedException { + LinkedBlockingQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + + it = q.iterator(); + for (i = 0; it.hasNext(); i++) + assertEquals(it.next(), q.take()); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new LinkedBlockingQueue().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(3); + q.add(two); + q.add(one); + q.add(three); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertSame(it.next(), one); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(3); + q.add(one); + q.add(two); + q.add(three); + assertEquals(0, q.remainingCapacity()); + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(3); + q.add(one); + q.add(two); + q.add(three); + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + assertEquals(0, q.size()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + LinkedBlockingQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * offer transfers elements across Executor tasks + */ + public void testOfferInExecutor() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(2); + q.add(one); + q.add(two); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(q.offer(three)); + threadsStarted.await(); + assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, q.remainingCapacity()); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + assertSame(one, q.take()); + }}); + } + } + + /** + * timed poll retrieves elements across Executor threads + */ + public void testPollInExecutor() { + final LinkedBlockingQueue q = new LinkedBlockingQueue(2); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * A deserialized serialized queue has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * drainTo(c) empties queue into another collection c + */ + public void testDrainTo() { + LinkedBlockingQueue q = populatedQueue(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(SIZE, l.size()); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); + } + + /** + * drainTo empties full queue, unblocking a waiting put. + */ + public void testDrainToWithActivePut() throws InterruptedException { + final LinkedBlockingQueue q = populatedQueue(SIZE); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(new Integer(SIZE + 1)); + }}); + + t.start(); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + t.join(); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + LinkedBlockingQueue q = new LinkedBlockingQueue(); + for (int i = 0; i < SIZE + 2; ++i) { + for (int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(k, l.size()); + assertEquals(SIZE - k, q.size()); + for (int j = 0; j < k; ++j) + assertEquals(l.get(j), new Integer(j)); + do {} while (q.poll() != null); + } + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection[] qs = { + new LinkedBlockingQueue(), + populatedQueue(2), + }; + + for (Collection q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/LinkedListTest.java b/jdk/test/java/util/concurrent/tck/LinkedListTest.java new file mode 100644 index 00000000000..ea0f689dc70 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LinkedListTest.java @@ -0,0 +1,669 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class LinkedListTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(LinkedListTest.class); + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private LinkedList populatedQueue(int n) { + LinkedList q = new LinkedList(); + assertTrue(q.isEmpty()); + for (int i = 0; i < n; ++i) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * new queue is empty + */ + public void testConstructor1() { + assertEquals(0, new LinkedList().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new LinkedList((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + LinkedList q = new LinkedList(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + LinkedList q = new LinkedList(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.remove(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * offer(null) succeeds + */ + public void testOfferNull() { + LinkedList q = new LinkedList(); + q.offer(null); + assertNull(q.get(0)); + assertTrue(q.contains(null)); + } + + /** + * Offer succeeds + */ + public void testOffer() { + LinkedList q = new LinkedList(); + assertTrue(q.offer(new Integer(0))); + assertTrue(q.offer(new Integer(1))); + } + + /** + * add succeeds + */ + public void testAdd() { + LinkedList q = new LinkedList(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + assertTrue(q.add(new Integer(i))); + } + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + LinkedList q = new LinkedList(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + LinkedList q = new LinkedList(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * addAll with too large an index throws IOOBE + */ + public void testAddAll2_IndexOutOfBoundsException() { + LinkedList l = new LinkedList(); + l.add(new Object()); + LinkedList m = new LinkedList(); + m.add(new Object()); + try { + l.addAll(4,m); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + + /** + * addAll with negative index throws IOOBE + */ + public void testAddAll4_BadIndex() { + LinkedList l = new LinkedList(); + l.add(new Object()); + LinkedList m = new LinkedList(); + m.add(new Object()); + try { + l.addAll(-1,m); + shouldThrow(); + } catch (IndexOutOfBoundsException success) {} + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + LinkedList q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove((Integer)i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove((Integer)i)); + assertFalse(q.contains(i)); + assertFalse(q.remove((Integer)(i + 1))); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + LinkedList q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertTrue(q.add(new Integer(1))); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + LinkedList q = populatedQueue(SIZE); + LinkedList p = new LinkedList(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + assertTrue(p.add(new Integer(i))); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + LinkedList q = populatedQueue(SIZE); + LinkedList p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + LinkedList q = populatedQueue(SIZE); + LinkedList p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements in FIFO order + */ + public void testToArray() { + LinkedList q = populatedQueue(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + LinkedList q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.poll()); + } + + /** + * toArray(null) throws NullPointerException + */ + public void testToArray_NullArg() { + LinkedList l = new LinkedList(); + l.add(new Object()); + try { + l.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + LinkedList l = new LinkedList(); + l.add(new Integer(5)); + try { + l.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + LinkedList q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new LinkedList().iterator()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final LinkedList q = new LinkedList(); + q.add(new Integer(1)); + q.add(new Integer(2)); + q.add(new Integer(3)); + int k = 0; + for (Iterator it = q.iterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final LinkedList q = new LinkedList(); + q.add(new Integer(1)); + q.add(new Integer(2)); + q.add(new Integer(3)); + Iterator it = q.iterator(); + assertEquals(1, it.next()); + it.remove(); + it = q.iterator(); + assertEquals(2, it.next()); + assertEquals(3, it.next()); + assertFalse(it.hasNext()); + } + + /** + * Descending iterator iterates through all elements + */ + public void testDescendingIterator() { + LinkedList q = populatedQueue(SIZE); + int i = 0; + Iterator it = q.descendingIterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + assertFalse(it.hasNext()); + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * Descending iterator ordering is reverse FIFO + */ + public void testDescendingIteratorOrdering() { + final LinkedList q = new LinkedList(); + q.add(new Integer(3)); + q.add(new Integer(2)); + q.add(new Integer(1)); + int k = 0; + for (Iterator it = q.descendingIterator(); it.hasNext();) { + assertEquals(++k, it.next()); + } + + assertEquals(3, k); + } + + /** + * descendingIterator.remove removes current element + */ + public void testDescendingIteratorRemove() { + final LinkedList q = new LinkedList(); + q.add(three); + q.add(two); + q.add(one); + Iterator it = q.descendingIterator(); + it.next(); + it.remove(); + it = q.descendingIterator(); + assertSame(it.next(), two); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + LinkedList q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * peek returns element inserted with addFirst + */ + public void testAddFirst() { + LinkedList q = populatedQueue(3); + q.addFirst(four); + assertSame(four, q.peek()); + } + + /** + * peekFirst returns element inserted with push + */ + public void testPush() { + LinkedList q = populatedQueue(3); + q.push(four); + assertSame(four, q.peekFirst()); + } + + /** + * pop removes next element, or throws NSEE if empty + */ + public void testPop() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pop()); + } + try { + q.pop(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * OfferFirst succeeds + */ + public void testOfferFirst() { + LinkedList q = new LinkedList(); + assertTrue(q.offerFirst(new Integer(0))); + assertTrue(q.offerFirst(new Integer(1))); + } + + /** + * OfferLast succeeds + */ + public void testOfferLast() { + LinkedList q = new LinkedList(); + assertTrue(q.offerLast(new Integer(0))); + assertTrue(q.offerLast(new Integer(1))); + } + + /** + * pollLast succeeds unless empty + */ + public void testPollLast() { + LinkedList q = populatedQueue(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollLast()); + } + + /** + * peekFirst returns next element, or null if empty + */ + public void testPeekFirst() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peekFirst()); + assertEquals(i, q.pollFirst()); + assertTrue(q.peekFirst() == null || + !q.peekFirst().equals(i)); + } + assertNull(q.peekFirst()); + } + + /** + * peekLast returns next element, or null if empty + */ + public void testPeekLast() { + LinkedList q = populatedQueue(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.peekLast()); + assertEquals(i, q.pollLast()); + assertTrue(q.peekLast() == null || + !q.peekLast().equals(i)); + } + assertNull(q.peekLast()); + } + + public void testFirstElement() { + LinkedList q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.getFirst()); + assertEquals(i, q.pollFirst()); + } + try { + q.getFirst(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * getLast returns next element, or throws NSEE if empty + */ + public void testLastElement() { + LinkedList q = populatedQueue(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.getLast()); + assertEquals(i, q.pollLast()); + } + try { + q.getLast(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertNull(q.peekLast()); + } + + /** + * removeFirstOccurrence(x) removes x and returns true if present + */ + public void testRemoveFirstOccurrence() { + LinkedList q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeFirstOccurrence(new Integer(i))); + assertFalse(q.removeFirstOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * removeLastOccurrence(x) removes x and returns true if present + */ + public void testRemoveLastOccurrence() { + LinkedList q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.removeLastOccurrence(new Integer(i))); + assertFalse(q.removeLastOccurrence(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java b/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java new file mode 100644 index 00000000000..dd57870a306 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java @@ -0,0 +1,1085 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include John Vint + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedTransferQueue; + +import junit.framework.Test; + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class LinkedTransferQueueTest extends JSR166TestCase { + static class Implementation implements CollectionImplementation { + public Class klazz() { return LinkedTransferQueue.class; } + public Collection emptyCollection() { return new LinkedTransferQueue(); } + public Object makeElement(int i) { return i; } + public boolean isConcurrent() { return true; } + public boolean permitsNulls() { return false; } + } + + public static class Generic extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new LinkedTransferQueue(); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(LinkedTransferQueueTest.class, + new Generic().testSuite(), + CollectionTest.testSuite(new Implementation())); + } + + /** + * Constructor builds new queue with size being zero and empty + * being true + */ + public void testConstructor1() { + assertEquals(0, new LinkedTransferQueue().size()); + assertTrue(new LinkedTransferQueue().isEmpty()); + } + + /** + * Initializing constructor with null collection throws + * NullPointerException + */ + public void testConstructor2() { + try { + new LinkedTransferQueue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws + * NullPointerException + */ + public void testConstructor3() { + Collection elements = Arrays.asList(new Integer[SIZE]); + try { + new LinkedTransferQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing constructor with a collection containing some null elements + * throws NullPointerException + */ + public void testConstructor4() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + try { + new LinkedTransferQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of the collection it is initialized by + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) { + ints[i] = i; + } + List intList = Arrays.asList(ints); + LinkedTransferQueue q + = new LinkedTransferQueue(intList); + assertEquals(q.size(), intList.size()); + assertEquals(q.toString(), intList.toString()); + assertTrue(Arrays.equals(q.toArray(), + intList.toArray())); + assertTrue(Arrays.equals(q.toArray(new Object[0]), + intList.toArray(new Object[0]))); + assertTrue(Arrays.equals(q.toArray(new Object[SIZE]), + intList.toArray(new Object[SIZE]))); + for (int i = 0; i < SIZE; ++i) { + assertEquals(ints[i], q.poll()); + } + } + + /** + * remainingCapacity() always returns Integer.MAX_VALUE + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(SIZE - i, q.size()); + assertEquals(i, q.remove()); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(i, q.size()); + assertTrue(q.add(i)); + } + } + + /** + * addAll(this) throws IllegalArgumentException + */ + public void testAddAllSelf() { + LinkedTransferQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws + * NullPointerException after possibly adding some elements + */ + public void testAddAll3() { + LinkedTransferQueue q = new LinkedTransferQueue(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements, in traversal order, of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) { + ints[i] = i; + } + LinkedTransferQueue q = new LinkedTransferQueue(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) { + assertEquals(ints[i], q.poll()); + } + } + + /** + * all elements successfully put are contained + */ + public void testPut() { + LinkedTransferQueue q = new LinkedTransferQueue(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.put(i); + assertTrue(q.contains(i)); + } + } + + /** + * take retrieves elements in FIFO order + */ + public void testTake() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.take()); + } + } + + /** + * take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll()); + } + assertNull(q.poll()); + checkEmpty(q); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + checkEmpty(q); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + + startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch aboutToWait = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + aboutToWait.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + aboutToWait.await(); + waitForThreadToEnterWaitState(t); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * timed poll after thread interrupted throws InterruptedException + * instead of returning timeout status + */ + public void testTimedPollAfterInterrupt() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + Thread.currentThread().interrupt(); + for (int i = 0; i < SIZE; ++i) + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + awaitTermination(t); + checkEmpty(q); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.peek()); + assertEquals(i, (int) q.poll()); + assertTrue(q.peek() == null || + i != (int) q.peek()); + } + assertNull(q.peek()); + checkEmpty(q); + } + + /** + * element returns next element, or throws NoSuchElementException if empty + */ + public void testElement() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.element()); + assertEquals(i, (int) q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + checkEmpty(q); + } + + /** + * remove removes next element, or throws NoSuchElementException if empty + */ + public void testRemove() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + checkEmpty(q); + } + + /** + * An add following remove(x) succeeds + */ + public void testRemoveElementAndAdd() throws InterruptedException { + LinkedTransferQueue q = new LinkedTransferQueue(); + assertTrue(q.add(one)); + assertTrue(q.add(two)); + assertTrue(q.remove(one)); + assertTrue(q.remove(two)); + assertTrue(q.add(three)); + assertSame(q.take(), three); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + LinkedTransferQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(i)); + assertEquals(i, (int) q.poll()); + assertFalse(q.contains(i)); + } + } + + /** + * clear removes all elements + */ + public void testClear() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + q.clear(); + checkEmpty(q); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + assertEquals(1, q.size()); + assertTrue(q.contains(one)); + q.clear(); + checkEmpty(q); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + LinkedTransferQueue q = populatedQueue(SIZE); + LinkedTransferQueue p = new LinkedTransferQueue(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(i); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true + * if changed + */ + public void testRetainAll() { + LinkedTransferQueue q = populatedQueue(SIZE); + LinkedTransferQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) { + assertFalse(changed); + } else { + assertTrue(changed); + } + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true + * if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + LinkedTransferQueue q = populatedQueue(SIZE); + LinkedTransferQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + assertFalse(q.contains(p.remove())); + } + } + } + + /** + * toArray() contains all elements in FIFO order + */ + public void testToArray() { + LinkedTransferQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) { + assertSame(o[i], q.poll()); + } + } + + /** + * toArray(a) contains all elements in FIFO order + */ + public void testToArray2() { + LinkedTransferQueue q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) { + assertSame(ints[i], q.poll()); + } + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + LinkedTransferQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() throws InterruptedException { + LinkedTransferQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + + it = q.iterator(); + for (i = 0; it.hasNext(); i++) + assertEquals(it.next(), q.take()); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new LinkedTransferQueue().iterator()); + } + + /** + * iterator.remove() removes current element + */ + public void testIteratorRemove() { + final LinkedTransferQueue q = new LinkedTransferQueue(); + q.add(two); + q.add(one); + q.add(three); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertSame(it.next(), one); + assertSame(it.next(), three); + assertFalse(it.hasNext()); + } + + /** + * iterator ordering is FIFO + */ + public void testIteratorOrdering() { + final LinkedTransferQueue q + = new LinkedTransferQueue(); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + q.add(one); + q.add(two); + q.add(three); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + int k = 0; + for (Integer n : q) { + assertEquals(++k, (int) n); + } + assertEquals(3, k); + } + + /** + * Modifications do not cause iterators to fail + */ + public void testWeaklyConsistentIteration() { + final LinkedTransferQueue q = new LinkedTransferQueue(); + q.add(one); + q.add(two); + q.add(three); + for (Iterator it = q.iterator(); it.hasNext();) { + q.remove(); + it.next(); + } + assertEquals(0, q.size()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + LinkedTransferQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * offer transfers elements across Executor tasks + */ + public void testOfferInExecutor() { + final LinkedTransferQueue q = new LinkedTransferQueue(); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + long startTime = System.nanoTime(); + assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + assertSame(one, q.take()); + checkEmpty(q); + }}); + } + } + + /** + * timed poll retrieves elements across Executor threads + */ + public void testPollInExecutor() { + final LinkedTransferQueue q = new LinkedTransferQueue(); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + long startTime = System.nanoTime(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * A deserialized serialized queue has same elements in same order + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(y, x); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * drainTo(c) empties queue into another collection c + */ + public void testDrainTo() { + LinkedTransferQueue q = populatedQueue(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(SIZE, l.size()); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, l.get(i)); + } + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) { + assertEquals(i, l.get(i)); + } + } + + /** + * drainTo(c) empties full queue, unblocking a waiting put. + */ + public void testDrainToWithActivePut() throws InterruptedException { + final LinkedTransferQueue q = populatedQueue(SIZE); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + q.put(SIZE + 1); + }}); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(i, l.get(i)); + awaitTermination(t); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + LinkedTransferQueue q = new LinkedTransferQueue(); + for (int i = 0; i < SIZE + 2; ++i) { + for (int j = 0; j < SIZE; j++) { + assertTrue(q.offer(j)); + } + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(k, l.size()); + assertEquals(SIZE - k, q.size()); + for (int j = 0; j < k; ++j) + assertEquals(j, l.get(j)); + do {} while (q.poll() != null); + } + } + + /** + * timed poll() or take() increments the waiting consumer count; + * offer(e) decrements the waiting consumer count + */ + public void testWaitingConsumer() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + assertEquals(0, q.getWaitingConsumerCount()); + assertFalse(q.hasWaitingConsumer()); + final CountDownLatch threadStarted = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + long startTime = System.nanoTime(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, q.getWaitingConsumerCount()); + assertFalse(q.hasWaitingConsumer()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + threadStarted.await(); + waitForThreadToEnterWaitState(t); + assertEquals(1, q.getWaitingConsumerCount()); + assertTrue(q.hasWaitingConsumer()); + + assertTrue(q.offer(one)); + assertEquals(0, q.getWaitingConsumerCount()); + assertFalse(q.hasWaitingConsumer()); + + awaitTermination(t); + } + + /** + * transfer(null) throws NullPointerException + */ + public void testTransfer1() throws InterruptedException { + try { + LinkedTransferQueue q = new LinkedTransferQueue(); + q.transfer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * transfer waits until a poll occurs. The transfered element + * is returned by this associated poll. + */ + public void testTransfer2() throws InterruptedException { + final LinkedTransferQueue q + = new LinkedTransferQueue(); + final CountDownLatch threadStarted = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + q.transfer(five); + checkEmpty(q); + }}); + + threadStarted.await(); + waitForThreadToEnterWaitState(t); + assertEquals(1, q.size()); + assertSame(five, q.poll()); + checkEmpty(q); + awaitTermination(t); + } + + /** + * transfer waits until a poll occurs, and then transfers in fifo order + */ + public void testTransfer3() throws InterruptedException { + final LinkedTransferQueue q + = new LinkedTransferQueue(); + + Thread first = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.transfer(four); + assertTrue(!q.contains(four)); + assertEquals(1, q.size()); + }}); + + Thread interruptedThread = newStartedThread( + new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + while (q.isEmpty()) + Thread.yield(); + q.transfer(five); + }}); + + while (q.size() < 2) + Thread.yield(); + assertEquals(2, q.size()); + assertSame(four, q.poll()); + first.join(); + assertEquals(1, q.size()); + interruptedThread.interrupt(); + interruptedThread.join(); + checkEmpty(q); + } + + /** + * transfer waits until a poll occurs, at which point the polling + * thread returns the element + */ + public void testTransfer4() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.transfer(four); + assertFalse(q.contains(four)); + assertSame(three, q.poll()); + }}); + + while (q.isEmpty()) + Thread.yield(); + assertFalse(q.isEmpty()); + assertEquals(1, q.size()); + assertTrue(q.offer(three)); + assertSame(four, q.poll()); + awaitTermination(t); + } + + /** + * transfer waits until a take occurs. The transfered element + * is returned by this associated take. + */ + public void testTransfer5() throws InterruptedException { + final LinkedTransferQueue q + = new LinkedTransferQueue(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.transfer(four); + checkEmpty(q); + }}); + + while (q.isEmpty()) + Thread.yield(); + assertFalse(q.isEmpty()); + assertEquals(1, q.size()); + assertSame(four, q.take()); + checkEmpty(q); + awaitTermination(t); + } + + /** + * tryTransfer(null) throws NullPointerException + */ + public void testTryTransfer1() { + final LinkedTransferQueue q = new LinkedTransferQueue(); + try { + q.tryTransfer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * tryTransfer returns false and does not enqueue if there are no + * consumers waiting to poll or take. + */ + public void testTryTransfer2() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + assertFalse(q.tryTransfer(new Object())); + assertFalse(q.hasWaitingConsumer()); + checkEmpty(q); + } + + /** + * If there is a consumer waiting in timed poll, tryTransfer + * returns true while successfully transfering object. + */ + public void testTryTransfer3() throws InterruptedException { + final Object hotPotato = new Object(); + final LinkedTransferQueue q = new LinkedTransferQueue(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + while (! q.hasWaitingConsumer()) + Thread.yield(); + assertTrue(q.hasWaitingConsumer()); + checkEmpty(q); + assertTrue(q.tryTransfer(hotPotato)); + }}); + + long startTime = System.nanoTime(); + assertSame(hotPotato, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + checkEmpty(q); + awaitTermination(t); + } + + /** + * If there is a consumer waiting in take, tryTransfer returns + * true while successfully transfering object. + */ + public void testTryTransfer4() throws InterruptedException { + final Object hotPotato = new Object(); + final LinkedTransferQueue q = new LinkedTransferQueue(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + while (! q.hasWaitingConsumer()) + Thread.yield(); + assertTrue(q.hasWaitingConsumer()); + checkEmpty(q); + assertTrue(q.tryTransfer(hotPotato)); + }}); + + assertSame(q.take(), hotPotato); + checkEmpty(q); + awaitTermination(t); + } + + /** + * tryTransfer blocks interruptibly if no takers + */ + public void testTryTransfer5() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + assertTrue(q.isEmpty()); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + Thread.currentThread().interrupt(); + try { + q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + checkEmpty(q); + } + + /** + * tryTransfer gives up after the timeout and returns false + */ + public void testTryTransfer6() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertFalse(q.tryTransfer(new Object(), + timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + }}); + + awaitTermination(t); + checkEmpty(q); + } + + /** + * tryTransfer waits for any elements previously in to be removed + * before transfering to a poll or take + */ + public void testTryTransfer7() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + assertTrue(q.offer(four)); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertTrue(q.tryTransfer(five, LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + checkEmpty(q); + }}); + + while (q.size() != 2) + Thread.yield(); + assertEquals(2, q.size()); + assertSame(four, q.poll()); + assertSame(five, q.poll()); + checkEmpty(q); + awaitTermination(t); + } + + /** + * tryTransfer attempts to enqueue into the queue and fails + * returning false not enqueueing and the successive poll is null + */ + public void testTryTransfer8() throws InterruptedException { + final LinkedTransferQueue q = new LinkedTransferQueue(); + assertTrue(q.offer(four)); + assertEquals(1, q.size()); + long startTime = System.nanoTime(); + assertFalse(q.tryTransfer(five, timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + assertEquals(1, q.size()); + assertSame(four, q.poll()); + assertNull(q.poll()); + checkEmpty(q); + } + + private LinkedTransferQueue populatedQueue(int n) { + LinkedTransferQueue q = new LinkedTransferQueue(); + checkEmpty(q); + for (int i = 0; i < n; i++) { + assertEquals(i, q.size()); + assertTrue(q.offer(i)); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + } + assertFalse(q.isEmpty()); + return q; + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection[] qs = { + new LinkedTransferQueue(), + populatedQueue(2), + }; + + for (Collection q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/LockSupportTest.java b/jdk/test/java/util/concurrent/tck/LockSupportTest.java new file mode 100644 index 00000000000..02175ad1051 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LockSupportTest.java @@ -0,0 +1,403 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class LockSupportTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(LockSupportTest.class); + } + + static { + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; + } + + /** + * Returns the blocker object used by tests in this file. + * Any old object will do; we'll return a convenient one. + */ + static Object theBlocker() { + return LockSupportTest.class; + } + + enum ParkMethod { + park() { + void park() { + LockSupport.park(); + } + void park(long millis) { + throw new UnsupportedOperationException(); + } + }, + parkUntil() { + void park(long millis) { + LockSupport.parkUntil(deadline(millis)); + } + }, + parkNanos() { + void park(long millis) { + LockSupport.parkNanos(MILLISECONDS.toNanos(millis)); + } + }, + parkBlocker() { + void park() { + LockSupport.park(theBlocker()); + } + void park(long millis) { + throw new UnsupportedOperationException(); + } + }, + parkUntilBlocker() { + void park(long millis) { + LockSupport.parkUntil(theBlocker(), deadline(millis)); + } + }, + parkNanosBlocker() { + void park(long millis) { + LockSupport.parkNanos(theBlocker(), + MILLISECONDS.toNanos(millis)); + } + }; + + void park() { park(2 * LONG_DELAY_MS); } + abstract void park(long millis); + + /** Returns a deadline to use with parkUntil. */ + long deadline(long millis) { + // beware of rounding + return System.currentTimeMillis() + millis + 1; + } + } + + /** + * park is released by subsequent unpark + */ + public void testParkBeforeUnpark_park() { + testParkBeforeUnpark(ParkMethod.park); + } + public void testParkBeforeUnpark_parkNanos() { + testParkBeforeUnpark(ParkMethod.parkNanos); + } + public void testParkBeforeUnpark_parkUntil() { + testParkBeforeUnpark(ParkMethod.parkUntil); + } + public void testParkBeforeUnpark_parkBlocker() { + testParkBeforeUnpark(ParkMethod.parkBlocker); + } + public void testParkBeforeUnpark_parkNanosBlocker() { + testParkBeforeUnpark(ParkMethod.parkNanosBlocker); + } + public void testParkBeforeUnpark_parkUntilBlocker() { + testParkBeforeUnpark(ParkMethod.parkUntilBlocker); + } + public void testParkBeforeUnpark(final ParkMethod parkMethod) { + final CountDownLatch pleaseUnpark = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + pleaseUnpark.countDown(); + parkMethod.park(); + }}); + + await(pleaseUnpark); + LockSupport.unpark(t); + awaitTermination(t); + } + + /** + * park is released by preceding unpark + */ + public void testParkAfterUnpark_park() { + testParkAfterUnpark(ParkMethod.park); + } + public void testParkAfterUnpark_parkNanos() { + testParkAfterUnpark(ParkMethod.parkNanos); + } + public void testParkAfterUnpark_parkUntil() { + testParkAfterUnpark(ParkMethod.parkUntil); + } + public void testParkAfterUnpark_parkBlocker() { + testParkAfterUnpark(ParkMethod.parkBlocker); + } + public void testParkAfterUnpark_parkNanosBlocker() { + testParkAfterUnpark(ParkMethod.parkNanosBlocker); + } + public void testParkAfterUnpark_parkUntilBlocker() { + testParkAfterUnpark(ParkMethod.parkUntilBlocker); + } + public void testParkAfterUnpark(final ParkMethod parkMethod) { + final CountDownLatch pleaseUnpark = new CountDownLatch(1); + final AtomicBoolean pleasePark = new AtomicBoolean(false); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + pleaseUnpark.countDown(); + while (!pleasePark.get()) + Thread.yield(); + parkMethod.park(); + }}); + + await(pleaseUnpark); + LockSupport.unpark(t); + pleasePark.set(true); + awaitTermination(t); + } + + /** + * park is released by subsequent interrupt + */ + public void testParkBeforeInterrupt_park() { + testParkBeforeInterrupt(ParkMethod.park); + } + public void testParkBeforeInterrupt_parkNanos() { + testParkBeforeInterrupt(ParkMethod.parkNanos); + } + public void testParkBeforeInterrupt_parkUntil() { + testParkBeforeInterrupt(ParkMethod.parkUntil); + } + public void testParkBeforeInterrupt_parkBlocker() { + testParkBeforeInterrupt(ParkMethod.parkBlocker); + } + public void testParkBeforeInterrupt_parkNanosBlocker() { + testParkBeforeInterrupt(ParkMethod.parkNanosBlocker); + } + public void testParkBeforeInterrupt_parkUntilBlocker() { + testParkBeforeInterrupt(ParkMethod.parkUntilBlocker); + } + public void testParkBeforeInterrupt(final ParkMethod parkMethod) { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + pleaseInterrupt.countDown(); + do { + parkMethod.park(); + // park may return spuriously + } while (! Thread.currentThread().isInterrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * park is released by preceding interrupt + */ + public void testParkAfterInterrupt_park() { + testParkAfterInterrupt(ParkMethod.park); + } + public void testParkAfterInterrupt_parkNanos() { + testParkAfterInterrupt(ParkMethod.parkNanos); + } + public void testParkAfterInterrupt_parkUntil() { + testParkAfterInterrupt(ParkMethod.parkUntil); + } + public void testParkAfterInterrupt_parkBlocker() { + testParkAfterInterrupt(ParkMethod.parkBlocker); + } + public void testParkAfterInterrupt_parkNanosBlocker() { + testParkAfterInterrupt(ParkMethod.parkNanosBlocker); + } + public void testParkAfterInterrupt_parkUntilBlocker() { + testParkAfterInterrupt(ParkMethod.parkUntilBlocker); + } + public void testParkAfterInterrupt(final ParkMethod parkMethod) { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final AtomicBoolean pleasePark = new AtomicBoolean(false); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + pleaseInterrupt.countDown(); + while (!pleasePark.get()) + Thread.yield(); + assertTrue(Thread.currentThread().isInterrupted()); + parkMethod.park(); + assertTrue(Thread.currentThread().isInterrupted()); + }}); + + await(pleaseInterrupt); + t.interrupt(); + pleasePark.set(true); + awaitTermination(t); + } + + /** + * timed park times out if not unparked + */ + public void testParkTimesOut_parkNanos() { + testParkTimesOut(ParkMethod.parkNanos); + } + public void testParkTimesOut_parkUntil() { + testParkTimesOut(ParkMethod.parkUntil); + } + public void testParkTimesOut_parkNanosBlocker() { + testParkTimesOut(ParkMethod.parkNanosBlocker); + } + public void testParkTimesOut_parkUntilBlocker() { + testParkTimesOut(ParkMethod.parkUntilBlocker); + } + public void testParkTimesOut(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + for (;;) { + long startTime = System.nanoTime(); + parkMethod.park(timeoutMillis()); + // park may return spuriously + if (millisElapsedSince(startTime) >= timeoutMillis()) + return; + } + }}); + + awaitTermination(t); + } + + /** + * getBlocker(null) throws NullPointerException + */ + public void testGetBlockerNull() { + try { + LockSupport.getBlocker(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getBlocker returns the blocker object passed to park + */ + public void testGetBlocker_parkBlocker() { + testGetBlocker(ParkMethod.parkBlocker); + } + public void testGetBlocker_parkNanosBlocker() { + testGetBlocker(ParkMethod.parkNanosBlocker); + } + public void testGetBlocker_parkUntilBlocker() { + testGetBlocker(ParkMethod.parkUntilBlocker); + } + public void testGetBlocker(final ParkMethod parkMethod) { + final CountDownLatch started = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread t = Thread.currentThread(); + started.countDown(); + do { + assertNull(LockSupport.getBlocker(t)); + parkMethod.park(); + assertNull(LockSupport.getBlocker(t)); + // park may return spuriously + } while (! Thread.currentThread().isInterrupted()); + }}); + + long startTime = System.nanoTime(); + await(started); + for (;;) { + Object x = LockSupport.getBlocker(t); + if (x == theBlocker()) { // success + t.interrupt(); + awaitTermination(t); + assertNull(LockSupport.getBlocker(t)); + return; + } else { + assertNull(x); // ok + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + } + + /** + * timed park(0) returns immediately. + * + * Requires hotspot fix for: + * 6763959 java.util.concurrent.locks.LockSupport.parkUntil(0) blocks forever + * which is in jdk7-b118 and 6u25. + */ + public void testPark0_parkNanos() { + testPark0(ParkMethod.parkNanos); + } + public void testPark0_parkUntil() { + testPark0(ParkMethod.parkUntil); + } + public void testPark0_parkNanosBlocker() { + testPark0(ParkMethod.parkNanosBlocker); + } + public void testPark0_parkUntilBlocker() { + testPark0(ParkMethod.parkUntilBlocker); + } + public void testPark0(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + parkMethod.park(0L); + }}); + + awaitTermination(t); + } + + /** + * timed park(Long.MIN_VALUE) returns immediately. + */ + public void testParkNeg_parkNanos() { + testParkNeg(ParkMethod.parkNanos); + } + public void testParkNeg_parkUntil() { + testParkNeg(ParkMethod.parkUntil); + } + public void testParkNeg_parkNanosBlocker() { + testParkNeg(ParkMethod.parkNanosBlocker); + } + public void testParkNeg_parkUntilBlocker() { + testParkNeg(ParkMethod.parkUntilBlocker); + } + public void testParkNeg(final ParkMethod parkMethod) { + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + parkMethod.park(Long.MIN_VALUE); + }}); + + awaitTermination(t); + } +} diff --git a/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java b/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java new file mode 100644 index 00000000000..6acfd27efc7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java @@ -0,0 +1,183 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Phaser; +import java.util.concurrent.atomic.LongAccumulator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class LongAccumulatorTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(LongAccumulatorTest.class); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals(0, ai.get()); + } + + /** + * accumulate accumulates given value to current, and get returns current value + */ + public void testAccumulateAndGet() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + ai.accumulate(2); + assertEquals(2, ai.get()); + ai.accumulate(-4); + assertEquals(2, ai.get()); + ai.accumulate(4); + assertEquals(4, ai.get()); + } + + /** + * reset() causes subsequent get() to return zero + */ + public void testReset() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + ai.accumulate(2); + assertEquals(2, ai.get()); + ai.reset(); + assertEquals(0, ai.get()); + } + + /** + * getThenReset() returns current value; subsequent get() returns zero + */ + public void testGetThenReset() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + ai.accumulate(2); + assertEquals(2, ai.get()); + assertEquals(2, ai.getThenReset()); + assertEquals(0, ai.get()); + } + + /** + * toString returns current value. + */ + public void testToString() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals("0", ai.toString()); + ai.accumulate(1); + assertEquals(Long.toString(1), ai.toString()); + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals(0, ai.intValue()); + ai.accumulate(1); + assertEquals(1, ai.intValue()); + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals(0, ai.longValue()); + ai.accumulate(1); + assertEquals(1, ai.longValue()); + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals(0.0f, ai.floatValue()); + ai.accumulate(1); + assertEquals(1.0f, ai.floatValue()); + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + LongAccumulator ai = new LongAccumulator(Long::max, 0L); + assertEquals(0.0, ai.doubleValue()); + ai.accumulate(1); + assertEquals(1.0, ai.doubleValue()); + } + + /** + * accumulates by multiple threads produce correct result + */ + public void testAccumulateAndGetMT() { + final int incs = 1000000; + final int nthreads = 4; + final ExecutorService pool = Executors.newCachedThreadPool(); + LongAccumulator a = new LongAccumulator(Long::max, 0L); + Phaser phaser = new Phaser(nthreads + 1); + for (int i = 0; i < nthreads; ++i) + pool.execute(new AccTask(a, phaser, incs)); + phaser.arriveAndAwaitAdvance(); + phaser.arriveAndAwaitAdvance(); + long expected = incs - 1; + long result = a.get(); + assertEquals(expected, result); + pool.shutdown(); + } + + static final class AccTask implements Runnable { + final LongAccumulator acc; + final Phaser phaser; + final int incs; + volatile long result; + AccTask(LongAccumulator acc, Phaser phaser, int incs) { + this.acc = acc; + this.phaser = phaser; + this.incs = incs; + } + + public void run() { + phaser.arriveAndAwaitAdvance(); + LongAccumulator a = acc; + for (int i = 0; i < incs; ++i) + a.accumulate(i); + result = a.get(); + phaser.arrive(); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/LongAdderTest.java b/jdk/test/java/util/concurrent/tck/LongAdderTest.java new file mode 100644 index 00000000000..4f0d5e07f75 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/LongAdderTest.java @@ -0,0 +1,220 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.LongAdder; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class LongAdderTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(LongAdderTest.class); + } + + /** + * default constructed initializes to zero + */ + public void testConstructor() { + LongAdder ai = new LongAdder(); + assertEquals(0, ai.sum()); + } + + /** + * add adds given value to current, and sum returns current value + */ + public void testAddAndSum() { + LongAdder ai = new LongAdder(); + ai.add(2); + assertEquals(2, ai.sum()); + ai.add(-4); + assertEquals(-2, ai.sum()); + } + + /** + * decrement decrements and sum returns current value + */ + public void testDecrementAndsum() { + LongAdder ai = new LongAdder(); + ai.decrement(); + assertEquals(-1, ai.sum()); + ai.decrement(); + assertEquals(-2, ai.sum()); + } + + /** + * incrementAndGet increments and returns current value + */ + public void testIncrementAndsum() { + LongAdder ai = new LongAdder(); + ai.increment(); + assertEquals(1, ai.sum()); + ai.increment(); + assertEquals(2, ai.sum()); + } + + /** + * reset() causes subsequent sum() to return zero + */ + public void testReset() { + LongAdder ai = new LongAdder(); + ai.add(2); + assertEquals(2, ai.sum()); + ai.reset(); + assertEquals(0, ai.sum()); + } + + /** + * sumThenReset() returns sum; subsequent sum() returns zero + */ + public void testSumThenReset() { + LongAdder ai = new LongAdder(); + ai.add(2); + assertEquals(2, ai.sum()); + assertEquals(2, ai.sumThenReset()); + assertEquals(0, ai.sum()); + } + + /** + * a deserialized serialized adder holds same value + */ + public void testSerialization() throws Exception { + LongAdder x = new LongAdder(); + LongAdder y = serialClone(x); + assertNotSame(x, y); + x.add(-22); + LongAdder z = serialClone(x); + assertNotSame(y, z); + assertEquals(-22, x.sum()); + assertEquals(0, y.sum()); + assertEquals(-22, z.sum()); + } + + /** + * toString returns current value. + */ + public void testToString() { + LongAdder ai = new LongAdder(); + assertEquals("0", ai.toString()); + ai.increment(); + assertEquals(Long.toString(1), ai.toString()); + } + + /** + * intValue returns current value. + */ + public void testIntValue() { + LongAdder ai = new LongAdder(); + assertEquals(0, ai.intValue()); + ai.increment(); + assertEquals(1, ai.intValue()); + } + + /** + * longValue returns current value. + */ + public void testLongValue() { + LongAdder ai = new LongAdder(); + assertEquals(0, ai.longValue()); + ai.increment(); + assertEquals(1, ai.longValue()); + } + + /** + * floatValue returns current value. + */ + public void testFloatValue() { + LongAdder ai = new LongAdder(); + assertEquals(0.0f, ai.floatValue()); + ai.increment(); + assertEquals(1.0f, ai.floatValue()); + } + + /** + * doubleValue returns current value. + */ + public void testDoubleValue() { + LongAdder ai = new LongAdder(); + assertEquals(0.0, ai.doubleValue()); + ai.increment(); + assertEquals(1.0, ai.doubleValue()); + } + + /** + * adds by multiple threads produce correct sum + */ + public void testAddAndSumMT() throws Throwable { + final int incs = 1000000; + final int nthreads = 4; + final ExecutorService pool = Executors.newCachedThreadPool(); + LongAdder a = new LongAdder(); + CyclicBarrier barrier = new CyclicBarrier(nthreads + 1); + for (int i = 0; i < nthreads; ++i) + pool.execute(new AdderTask(a, barrier, incs)); + barrier.await(); + barrier.await(); + long total = (long)nthreads * incs; + long sum = a.sum(); + assertEquals(sum, total); + pool.shutdown(); + } + + static final class AdderTask implements Runnable { + final LongAdder adder; + final CyclicBarrier barrier; + final int incs; + volatile long result; + AdderTask(LongAdder adder, CyclicBarrier barrier, int incs) { + this.adder = adder; + this.barrier = barrier; + this.incs = incs; + } + + public void run() { + try { + barrier.await(); + LongAdder a = adder; + for (int i = 0; i < incs; ++i) + a.add(1L); + result = a.sum(); + barrier.await(); + } catch (Throwable t) { throw new Error(t); } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/PhaserTest.java b/jdk/test/java/util/concurrent/tck/PhaserTest.java new file mode 100644 index 00000000000..69b6c808ff8 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/PhaserTest.java @@ -0,0 +1,820 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include John Vint + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Phaser; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class PhaserTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(PhaserTest.class); + } + + private static final int maxParties = 65535; + + /** Checks state of unterminated phaser. */ + protected void assertState(Phaser phaser, + int phase, int parties, int unarrived) { + assertEquals(phase, phaser.getPhase()); + assertEquals(parties, phaser.getRegisteredParties()); + assertEquals(unarrived, phaser.getUnarrivedParties()); + assertEquals(parties - unarrived, phaser.getArrivedParties()); + assertFalse(phaser.isTerminated()); + } + + /** Checks state of terminated phaser. */ + protected void assertTerminated(Phaser phaser, int maxPhase, int parties) { + assertTrue(phaser.isTerminated()); + int expectedPhase = maxPhase + Integer.MIN_VALUE; + assertEquals(expectedPhase, phaser.getPhase()); + assertEquals(parties, phaser.getRegisteredParties()); + assertEquals(expectedPhase, phaser.register()); + assertEquals(expectedPhase, phaser.arrive()); + assertEquals(expectedPhase, phaser.arriveAndDeregister()); + } + + protected void assertTerminated(Phaser phaser, int maxPhase) { + assertTerminated(phaser, maxPhase, 0); + } + + /** + * Empty constructor builds a new Phaser with no parent, no registered + * parties and initial phase number of 0 + */ + public void testConstructorDefaultValues() { + Phaser phaser = new Phaser(); + assertNull(phaser.getParent()); + assertEquals(0, phaser.getRegisteredParties()); + assertEquals(0, phaser.getArrivedParties()); + assertEquals(0, phaser.getUnarrivedParties()); + assertEquals(0, phaser.getPhase()); + } + + /** + * Constructing with a negative number of parties throws + * IllegalArgumentException + */ + public void testConstructorNegativeParties() { + try { + new Phaser(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructing with a negative number of parties throws + * IllegalArgumentException + */ + public void testConstructorNegativeParties2() { + try { + new Phaser(new Phaser(), -1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructing with a number of parties > 65535 throws + * IllegalArgumentException + */ + public void testConstructorPartiesExceedsLimit() { + new Phaser(maxParties); + try { + new Phaser(maxParties + 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + + new Phaser(new Phaser(), maxParties); + try { + new Phaser(new Phaser(), maxParties + 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * The parent provided to the constructor should be returned from + * a later call to getParent + */ + public void testConstructor3() { + Phaser parent = new Phaser(); + assertSame(parent, new Phaser(parent).getParent()); + assertNull(new Phaser(null).getParent()); + } + + /** + * The parent being input into the parameter should equal the original + * parent when being returned + */ + public void testConstructor5() { + Phaser parent = new Phaser(); + assertSame(parent, new Phaser(parent, 0).getParent()); + assertNull(new Phaser(null, 0).getParent()); + } + + /** + * register() will increment the number of unarrived parties by + * one and not affect its arrived parties + */ + public void testRegister1() { + Phaser phaser = new Phaser(); + assertState(phaser, 0, 0, 0); + assertEquals(0, phaser.register()); + assertState(phaser, 0, 1, 1); + } + + /** + * Registering more than 65536 parties causes IllegalStateException + */ + public void testRegister2() { + Phaser phaser = new Phaser(0); + assertState(phaser, 0, 0, 0); + assertEquals(0, phaser.bulkRegister(maxParties - 10)); + assertState(phaser, 0, maxParties - 10, maxParties - 10); + for (int i = 0; i < 10; i++) { + assertState(phaser, 0, maxParties - 10 + i, maxParties - 10 + i); + assertEquals(0, phaser.register()); + } + assertState(phaser, 0, maxParties, maxParties); + try { + phaser.register(); + shouldThrow(); + } catch (IllegalStateException success) {} + + try { + phaser.bulkRegister(Integer.MAX_VALUE); + shouldThrow(); + } catch (IllegalStateException success) {} + + assertEquals(0, phaser.bulkRegister(0)); + assertState(phaser, 0, maxParties, maxParties); + } + + /** + * register() correctly returns the current barrier phase number + * when invoked + */ + public void testRegister3() { + Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + assertEquals(0, phaser.arrive()); + assertEquals(1, phaser.register()); + assertState(phaser, 1, 2, 2); + } + + /** + * register causes the next arrive to not increment the phase + * rather retain the phase number + */ + public void testRegister4() { + Phaser phaser = new Phaser(1); + assertEquals(0, phaser.arrive()); + assertEquals(1, phaser.register()); + assertEquals(1, phaser.arrive()); + assertState(phaser, 1, 2, 1); + } + + /** + * register on a subphaser that is currently empty succeeds, even + * in the presence of another non-empty subphaser + */ + public void testRegisterEmptySubPhaser() { + Phaser root = new Phaser(); + Phaser child1 = new Phaser(root, 1); + Phaser child2 = new Phaser(root, 0); + assertEquals(0, child2.register()); + assertState(root, 0, 2, 2); + assertState(child1, 0, 1, 1); + assertState(child2, 0, 1, 1); + assertEquals(0, child2.arriveAndDeregister()); + assertState(root, 0, 1, 1); + assertState(child1, 0, 1, 1); + assertState(child2, 0, 0, 0); + assertEquals(0, child2.register()); + assertEquals(0, child2.arriveAndDeregister()); + assertState(root, 0, 1, 1); + assertState(child1, 0, 1, 1); + assertState(child2, 0, 0, 0); + assertEquals(0, child1.arriveAndDeregister()); + assertTerminated(root, 1); + assertTerminated(child1, 1); + assertTerminated(child2, 1); + } + + /** + * Invoking bulkRegister with a negative parameter throws an + * IllegalArgumentException + */ + public void testBulkRegister1() { + try { + new Phaser().bulkRegister(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * bulkRegister should correctly record the number of unarrived + * parties with the number of parties being registered + */ + public void testBulkRegister2() { + Phaser phaser = new Phaser(); + assertEquals(0, phaser.bulkRegister(0)); + assertState(phaser, 0, 0, 0); + assertEquals(0, phaser.bulkRegister(20)); + assertState(phaser, 0, 20, 20); + } + + /** + * Registering with a number of parties greater than or equal to 1<<16 + * throws IllegalStateException. + */ + public void testBulkRegister3() { + assertEquals(0, new Phaser().bulkRegister((1 << 16) - 1)); + + try { + new Phaser().bulkRegister(1 << 16); + shouldThrow(); + } catch (IllegalStateException success) {} + + try { + new Phaser(2).bulkRegister((1 << 16) - 2); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * the phase number increments correctly when tripping the barrier + */ + public void testPhaseIncrement1() { + for (int size = 1; size < nine; size++) { + final Phaser phaser = new Phaser(size); + for (int index = 0; index <= (1 << size); index++) { + int phase = phaser.arrive(); + assertTrue(index % size == 0 ? (index / size) == phase : index - (phase * size) > 0); + } + } + } + + /** + * arrive() on a registered phaser increments phase. + */ + public void testArrive1() { + Phaser phaser = new Phaser(1); + assertState(phaser, 0, 1, 1); + assertEquals(0, phaser.arrive()); + assertState(phaser, 1, 1, 1); + } + + /** + * arriveAndDeregister does not wait for others to arrive at barrier + */ + public void testArriveAndDeregister() { + final Phaser phaser = new Phaser(1); + for (int i = 0; i < 10; i++) { + assertState(phaser, 0, 1, 1); + assertEquals(0, phaser.register()); + assertState(phaser, 0, 2, 2); + assertEquals(0, phaser.arriveAndDeregister()); + assertState(phaser, 0, 1, 1); + } + assertEquals(0, phaser.arriveAndDeregister()); + assertTerminated(phaser, 1); + } + + /** + * arriveAndDeregister does not wait for others to arrive at barrier + */ + public void testArrive2() { + final Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + List threads = new ArrayList(); + for (int i = 0; i < 10; i++) { + assertEquals(0, phaser.register()); + threads.add(newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.arriveAndDeregister()); + }})); + } + + for (Thread thread : threads) + awaitTermination(thread); + assertState(phaser, 0, 1, 1); + assertEquals(0, phaser.arrive()); + assertState(phaser, 1, 1, 1); + } + + /** + * arrive() returns a negative number if the Phaser is terminated + */ + public void testArrive3() { + Phaser phaser = new Phaser(1); + phaser.forceTermination(); + assertTerminated(phaser, 0, 1); + assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE); + assertTrue(phaser.arrive() < 0); + assertTrue(phaser.register() < 0); + assertTrue(phaser.arriveAndDeregister() < 0); + assertTrue(phaser.awaitAdvance(1) < 0); + assertTrue(phaser.getPhase() < 0); + } + + /** + * arriveAndDeregister() throws IllegalStateException if number of + * registered or unarrived parties would become negative + */ + public void testArriveAndDeregister1() { + Phaser phaser = new Phaser(); + try { + phaser.arriveAndDeregister(); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * arriveAndDeregister reduces the number of arrived parties + */ + public void testArriveAndDeregister2() { + final Phaser phaser = new Phaser(1); + assertEquals(0, phaser.register()); + assertEquals(0, phaser.arrive()); + assertState(phaser, 0, 2, 1); + assertEquals(0, phaser.arriveAndDeregister()); + assertState(phaser, 1, 1, 1); + } + + /** + * arriveAndDeregister arrives at the barrier on a phaser with a parent and + * when a deregistration occurs and causes the phaser to have zero parties + * its parent will be deregistered as well + */ + public void testArriveAndDeregister3() { + Phaser parent = new Phaser(); + Phaser child = new Phaser(parent); + assertState(child, 0, 0, 0); + assertState(parent, 0, 0, 0); + assertEquals(0, child.register()); + assertState(child, 0, 1, 1); + assertState(parent, 0, 1, 1); + assertEquals(0, child.arriveAndDeregister()); + assertTerminated(child, 1); + assertTerminated(parent, 1); + } + + /** + * arriveAndDeregister deregisters one party from its parent when + * the number of parties of child is zero after deregistration + */ + public void testArriveAndDeregister4() { + Phaser parent = new Phaser(); + Phaser child = new Phaser(parent); + assertEquals(0, parent.register()); + assertEquals(0, child.register()); + assertState(child, 0, 1, 1); + assertState(parent, 0, 2, 2); + assertEquals(0, child.arriveAndDeregister()); + assertState(child, 0, 0, 0); + assertState(parent, 0, 1, 1); + } + + /** + * arriveAndDeregister deregisters one party from its parent when + * the number of parties of root is nonzero after deregistration. + */ + public void testArriveAndDeregister5() { + Phaser root = new Phaser(); + Phaser parent = new Phaser(root); + Phaser child = new Phaser(parent); + assertState(root, 0, 0, 0); + assertState(parent, 0, 0, 0); + assertState(child, 0, 0, 0); + assertEquals(0, child.register()); + assertState(root, 0, 1, 1); + assertState(parent, 0, 1, 1); + assertState(child, 0, 1, 1); + assertEquals(0, child.arriveAndDeregister()); + assertTerminated(child, 1); + assertTerminated(parent, 1); + assertTerminated(root, 1); + } + + /** + * arriveAndDeregister returns the phase in which it leaves the + * phaser in after deregistration + */ + public void testArriveAndDeregister6() { + final Phaser phaser = new Phaser(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.arrive()); + }}); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + assertState(phaser, 1, 2, 2); + assertEquals(1, phaser.arriveAndDeregister()); + assertState(phaser, 1, 1, 1); + assertEquals(1, phaser.arriveAndDeregister()); + assertTerminated(phaser, 2); + awaitTermination(t); + } + + /** + * awaitAdvance succeeds upon advance + */ + public void testAwaitAdvance1() { + final Phaser phaser = new Phaser(1); + assertEquals(0, phaser.arrive()); + assertEquals(1, phaser.awaitAdvance(0)); + } + + /** + * awaitAdvance with a negative parameter will return without affecting the + * phaser + */ + public void testAwaitAdvance2() { + Phaser phaser = new Phaser(); + assertTrue(phaser.awaitAdvance(-1) < 0); + assertState(phaser, 0, 0, 0); + } + + /** + * awaitAdvanceInterruptibly blocks interruptibly + */ + public void testAwaitAdvanceInterruptibly_interruptible() throws InterruptedException { + final Phaser phaser = new Phaser(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(2); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + try { + phaser.awaitAdvanceInterruptibly(0); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + phaser.awaitAdvanceInterruptibly(0); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws TimeoutException { + Thread.currentThread().interrupt(); + try { + phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertState(phaser, 0, 1, 1); + assertThreadsStayAlive(t1, t2); + t1.interrupt(); + t2.interrupt(); + awaitTermination(t1); + awaitTermination(t2); + assertState(phaser, 0, 1, 1); + assertEquals(0, phaser.arrive()); + assertState(phaser, 1, 1, 1); + } + + /** + * awaitAdvance continues waiting if interrupted before waiting + */ + public void testAwaitAdvanceAfterInterrupt() { + final Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + final CountDownLatch pleaseArrive = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + assertEquals(0, phaser.register()); + assertEquals(0, phaser.arrive()); + pleaseArrive.countDown(); + assertTrue(Thread.currentThread().isInterrupted()); + assertEquals(1, phaser.awaitAdvance(0)); + assertTrue(Thread.interrupted()); + }}); + + await(pleaseArrive); + waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + assertEquals(0, phaser.arrive()); + awaitTermination(t); + + Thread.currentThread().interrupt(); + assertEquals(1, phaser.awaitAdvance(0)); + assertTrue(Thread.interrupted()); + } + + /** + * awaitAdvance continues waiting if interrupted while waiting + */ + public void testAwaitAdvanceBeforeInterrupt() { + final Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + final CountDownLatch pleaseArrive = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.register()); + assertEquals(0, phaser.arrive()); + assertFalse(Thread.currentThread().isInterrupted()); + pleaseArrive.countDown(); + assertEquals(1, phaser.awaitAdvance(0)); + assertTrue(Thread.interrupted()); + }}); + + await(pleaseArrive); + waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + t.interrupt(); + assertEquals(0, phaser.arrive()); + awaitTermination(t); + + Thread.currentThread().interrupt(); + assertEquals(1, phaser.awaitAdvance(0)); + assertTrue(Thread.interrupted()); + } + + /** + * arriveAndAwaitAdvance continues waiting if interrupted before waiting + */ + public void testArriveAndAwaitAdvanceAfterInterrupt() { + final Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + Thread.currentThread().interrupt(); + assertEquals(0, phaser.register()); + pleaseInterrupt.countDown(); + assertTrue(Thread.currentThread().isInterrupted()); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + assertTrue(Thread.currentThread().isInterrupted()); + }}); + + await(pleaseInterrupt); + waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + Thread.currentThread().interrupt(); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + assertTrue(Thread.interrupted()); + awaitTermination(t); + } + + /** + * arriveAndAwaitAdvance continues waiting if interrupted while waiting + */ + public void testArriveAndAwaitAdvanceBeforeInterrupt() { + final Phaser phaser = new Phaser(); + assertEquals(0, phaser.register()); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.register()); + assertFalse(Thread.currentThread().isInterrupted()); + pleaseInterrupt.countDown(); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + assertTrue(Thread.currentThread().isInterrupted()); + }}); + + await(pleaseInterrupt); + waitForThreadToEnterWaitState(t, SHORT_DELAY_MS); + t.interrupt(); + Thread.currentThread().interrupt(); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + assertTrue(Thread.interrupted()); + awaitTermination(t); + } + + /** + * awaitAdvance atomically waits for all parties within the same phase to + * complete before continuing + */ + public void testAwaitAdvance4() { + final Phaser phaser = new Phaser(4); + final AtomicInteger count = new AtomicInteger(0); + List threads = new ArrayList(); + for (int i = 0; i < 4; i++) + threads.add(newStartedThread(new CheckedRunnable() { + public void realRun() { + for (int k = 0; k < 3; k++) { + assertEquals(2 * k + 1, phaser.arriveAndAwaitAdvance()); + count.incrementAndGet(); + assertEquals(2 * k + 1, phaser.arrive()); + assertEquals(2 * k + 2, phaser.awaitAdvance(2 * k + 1)); + assertEquals(4 * (k + 1), count.get()); + }}})); + + for (Thread thread : threads) + awaitTermination(thread); + } + + /** + * awaitAdvance returns the current phase + */ + public void testAwaitAdvance5() { + final Phaser phaser = new Phaser(1); + assertEquals(1, phaser.awaitAdvance(phaser.arrive())); + assertEquals(1, phaser.getPhase()); + assertEquals(1, phaser.register()); + List threads = new ArrayList(); + for (int i = 0; i < 8; i++) { + final CountDownLatch latch = new CountDownLatch(1); + final boolean goesFirst = ((i & 1) == 0); + threads.add(newStartedThread(new CheckedRunnable() { + public void realRun() { + if (goesFirst) + latch.countDown(); + else + await(latch); + phaser.arrive(); + }})); + if (goesFirst) + await(latch); + else + latch.countDown(); + assertEquals(i + 2, phaser.awaitAdvance(phaser.arrive())); + assertEquals(i + 2, phaser.getPhase()); + } + for (Thread thread : threads) + awaitTermination(thread); + } + + /** + * awaitAdvance returns the current phase in child phasers + */ + public void testAwaitAdvanceTieredPhaser() throws Exception { + final Phaser parent = new Phaser(); + final List zeroPartyChildren = new ArrayList(3); + final List onePartyChildren = new ArrayList(3); + for (int i = 0; i < 3; i++) { + zeroPartyChildren.add(new Phaser(parent, 0)); + onePartyChildren.add(new Phaser(parent, 1)); + } + final List phasers = new ArrayList(); + phasers.addAll(zeroPartyChildren); + phasers.addAll(onePartyChildren); + phasers.add(parent); + for (Phaser phaser : phasers) { + assertEquals(-42, phaser.awaitAdvance(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS)); + } + + for (Phaser child : onePartyChildren) + assertEquals(0, child.arrive()); + for (Phaser phaser : phasers) { + assertEquals(-42, phaser.awaitAdvance(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS)); + assertEquals(1, phaser.awaitAdvance(0)); + assertEquals(1, phaser.awaitAdvanceInterruptibly(0)); + assertEquals(1, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS)); + } + + for (Phaser child : onePartyChildren) + assertEquals(1, child.arrive()); + for (Phaser phaser : phasers) { + assertEquals(-42, phaser.awaitAdvance(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42)); + assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS)); + assertEquals(2, phaser.awaitAdvance(0)); + assertEquals(2, phaser.awaitAdvanceInterruptibly(0)); + assertEquals(2, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS)); + assertEquals(2, phaser.awaitAdvance(1)); + assertEquals(2, phaser.awaitAdvanceInterruptibly(1)); + assertEquals(2, phaser.awaitAdvanceInterruptibly(1, MEDIUM_DELAY_MS, MILLISECONDS)); + } + } + + /** + * awaitAdvance returns when the phaser is externally terminated + */ + public void testAwaitAdvance6() { + final Phaser phaser = new Phaser(3); + final CountDownLatch pleaseForceTermination = new CountDownLatch(2); + final List threads = new ArrayList(); + for (int i = 0; i < 2; i++) { + Runnable r = new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.arrive()); + pleaseForceTermination.countDown(); + assertTrue(phaser.awaitAdvance(0) < 0); + assertTrue(phaser.isTerminated()); + assertTrue(phaser.getPhase() < 0); + assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE); + assertEquals(3, phaser.getRegisteredParties()); + }}; + threads.add(newStartedThread(r)); + } + await(pleaseForceTermination); + phaser.forceTermination(); + assertTrue(phaser.isTerminated()); + assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE); + for (Thread thread : threads) + awaitTermination(thread); + assertEquals(3, phaser.getRegisteredParties()); + } + + /** + * arriveAndAwaitAdvance throws IllegalStateException with no + * unarrived parties + */ + public void testArriveAndAwaitAdvance1() { + Phaser phaser = new Phaser(); + try { + phaser.arriveAndAwaitAdvance(); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * arriveAndAwaitAdvance waits for all threads to arrive, the + * number of arrived parties is the same number that is accounted + * for when the main thread awaitsAdvance + */ + public void testArriveAndAwaitAdvance3() { + final Phaser phaser = new Phaser(1); + final int THREADS = 3; + final CountDownLatch pleaseArrive = new CountDownLatch(THREADS); + final List threads = new ArrayList(); + for (int i = 0; i < THREADS; i++) + threads.add(newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(0, phaser.register()); + pleaseArrive.countDown(); + assertEquals(1, phaser.arriveAndAwaitAdvance()); + }})); + + await(pleaseArrive); + long startTime = System.nanoTime(); + while (phaser.getArrivedParties() < THREADS) + Thread.yield(); + assertEquals(THREADS, phaser.getArrivedParties()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + for (Thread thread : threads) + waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS); + for (Thread thread : threads) + assertTrue(thread.isAlive()); + assertState(phaser, 0, THREADS + 1, 1); + phaser.arriveAndAwaitAdvance(); + for (Thread thread : threads) + awaitTermination(thread); + assertState(phaser, 1, THREADS + 1, THREADS + 1); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java new file mode 100644 index 00000000000..c586837720a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java @@ -0,0 +1,764 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.PriorityBlockingQueue; + +import junit.framework.Test; + +public class PriorityBlockingQueueTest extends JSR166TestCase { + + public static class Generic extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new PriorityBlockingQueue(); + } + } + + public static class InitialCapacity extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new PriorityBlockingQueue(SIZE); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(PriorityBlockingQueueTest.class, + new Generic().testSuite(), + new InitialCapacity().testSuite()); + } + + /** Sample Comparator */ + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private PriorityBlockingQueue populatedQueue(int n) { + PriorityBlockingQueue q = + new PriorityBlockingQueue(n); + assertTrue(q.isEmpty()); + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.offer(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(n, q.size()); + return q; + } + + /** + * A new queue has unbounded capacity + */ + public void testConstructor1() { + assertEquals(Integer.MAX_VALUE, + new PriorityBlockingQueue(SIZE).remainingCapacity()); + } + + /** + * Constructor throws IAE if capacity argument nonpositive + */ + public void testConstructor2() { + try { + new PriorityBlockingQueue(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new PriorityBlockingQueue(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + Collection elements = Arrays.asList(new Integer[SIZE]); + try { + new PriorityBlockingQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = i; + Collection elements = Arrays.asList(ints); + try { + new PriorityBlockingQueue(elements); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = i; + PriorityBlockingQueue q = new PriorityBlockingQueue(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * The comparator used in constructor is used + */ + public void testConstructor7() { + MyReverseComparator cmp = new MyReverseComparator(); + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE, cmp); + assertEquals(cmp, q.comparator()); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + q.addAll(Arrays.asList(ints)); + for (int i = SIZE - 1; i >= 0; --i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + PriorityBlockingQueue q = new PriorityBlockingQueue(2); + assertTrue(q.isEmpty()); + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + q.add(one); + assertFalse(q.isEmpty()); + q.add(two); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * remainingCapacity() always returns Integer.MAX_VALUE + */ + public void testRemainingCapacity() { + BlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(SIZE - i, q.size()); + assertEquals(i, q.remove()); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(Integer.MAX_VALUE, q.remainingCapacity()); + assertEquals(i, q.size()); + assertTrue(q.add(i)); + } + } + + /** + * Offer of comparable element succeeds + */ + public void testOffer() { + PriorityBlockingQueue q = new PriorityBlockingQueue(1); + assertTrue(q.offer(zero)); + assertTrue(q.offer(one)); + } + + /** + * Offer of non-Comparable throws CCE + */ + public void testOfferNonComparable() { + PriorityBlockingQueue q = new PriorityBlockingQueue(1); + try { + q.offer(new Object()); + q.offer(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * add of comparable succeeds + */ + public void testAdd() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + assertTrue(q.add(new Integer(i))); + } + } + + /** + * addAll(this) throws IAE + */ + public void testAddAllSelf() { + PriorityBlockingQueue q = populatedQueue(SIZE); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = SIZE - 1; i >= 0; --i) + ints[i] = new Integer(i); + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * all elements successfully put are contained + */ + public void testPut() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + Integer x = new Integer(i); + q.put(x); + assertTrue(q.contains(x)); + } + assertEquals(SIZE, q.size()); + } + + /** + * put doesn't block waiting for take + */ + public void testPutWithTake() throws InterruptedException { + final PriorityBlockingQueue q = new PriorityBlockingQueue(2); + final int size = 4; + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + for (int i = 0; i < size; i++) + q.put(new Integer(0)); + }}); + + awaitTermination(t); + assertEquals(size, q.size()); + q.take(); + } + + /** + * timed offer does not time out + */ + public void testTimedOffer() throws InterruptedException { + final PriorityBlockingQueue q = new PriorityBlockingQueue(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + q.put(new Integer(0)); + q.put(new Integer(0)); + assertTrue(q.offer(new Integer(0), SHORT_DELAY_MS, MILLISECONDS)); + assertTrue(q.offer(new Integer(0), LONG_DELAY_MS, MILLISECONDS)); + }}); + + awaitTermination(t); + } + + /** + * take retrieves elements in priority order + */ + public void testTake() throws InterruptedException { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + } + + /** + * Take removes existing elements until empty, then blocks interruptibly + */ + public void testBlockingTake() throws InterruptedException { + final PriorityBlockingQueue q = populatedQueue(SIZE); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.take()); + } + + Thread.currentThread().interrupt(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.take(); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll0() throws InterruptedException { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll(0, MILLISECONDS)); + } + assertNull(q.poll(0, MILLISECONDS)); + } + + /** + * timed poll with nonzero timeout succeeds when non-empty, else times out + */ + public void testTimedPoll() throws InterruptedException { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + long startTime = System.nanoTime(); + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + checkEmpty(q); + } + + /** + * Interrupted timed poll throws InterruptedException instead of + * returning timeout status + */ + public void testInterruptedTimedPoll() throws InterruptedException { + final BlockingQueue q = populatedQueue(SIZE); + final CountDownLatch aboutToWait = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS)); + } + aboutToWait.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) { + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + }}); + + aboutToWait.await(); + waitForThreadToEnterWaitState(t, LONG_DELAY_MS); + t.interrupt(); + awaitTermination(t); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + PriorityBlockingQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + PriorityBlockingQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + PriorityBlockingQueue q = populatedQueue(SIZE); + PriorityBlockingQueue p = new PriorityBlockingQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + PriorityBlockingQueue q = populatedQueue(SIZE); + PriorityBlockingQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + PriorityBlockingQueue q = populatedQueue(SIZE); + PriorityBlockingQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements + */ + public void testToArray() throws InterruptedException { + PriorityBlockingQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + Arrays.sort(o); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.take()); + } + + /** + * toArray(a) contains all elements + */ + public void testToArray2() throws InterruptedException { + PriorityBlockingQueue q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + Arrays.sort(ints); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.take()); + } + + /** + * toArray(incompatible array type) throws ArrayStoreException + */ + public void testToArray1_BadArg() { + PriorityBlockingQueue q = populatedQueue(SIZE); + try { + q.toArray(new String[10]); + shouldThrow(); + } catch (ArrayStoreException success) {} + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + PriorityBlockingQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new PriorityBlockingQueue().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final PriorityBlockingQueue q = new PriorityBlockingQueue(3); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + PriorityBlockingQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * timed poll transfers elements across Executor tasks + */ + public void testPollInExecutor() { + final PriorityBlockingQueue q = new PriorityBlockingQueue(2); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + checkEmpty(q); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * A deserialized serialized queue has same elements + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } + + /** + * drainTo(c) empties queue into another collection c + */ + public void testDrainTo() { + PriorityBlockingQueue q = populatedQueue(SIZE); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(SIZE, l.size()); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(2, l.size()); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); + } + + /** + * drainTo empties queue + */ + public void testDrainToWithActivePut() throws InterruptedException { + final PriorityBlockingQueue q = populatedQueue(SIZE); + Thread t = new Thread(new CheckedRunnable() { + public void realRun() { + q.put(new Integer(SIZE + 1)); + }}); + + t.start(); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertTrue(l.size() >= SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + t.join(); + assertTrue(q.size() + l.size() >= SIZE); + } + + /** + * drainTo(c, n) empties first min(n, size) elements of queue into c + */ + public void testDrainToN() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE * 2); + for (int i = 0; i < SIZE + 2; ++i) { + for (int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); + ArrayList l = new ArrayList(); + q.drainTo(l, i); + int k = (i < SIZE) ? i : SIZE; + assertEquals(k, l.size()); + assertEquals(SIZE - k, q.size()); + for (int j = 0; j < k; ++j) + assertEquals(l.get(j), new Integer(j)); + do {} while (q.poll() != null); + } + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection[] qs = { + new PriorityBlockingQueue(), + populatedQueue(2), + }; + + for (Collection q : qs) { + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java b/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java new file mode 100644 index 00000000000..edb2379e2ec --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java @@ -0,0 +1,528 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class PriorityQueueTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(PriorityQueueTest.class); + } + + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * Returns a new queue of given size containing consecutive + * Integers 0 ... n. + */ + private PriorityQueue populatedQueue(int n) { + PriorityQueue q = new PriorityQueue(n); + assertTrue(q.isEmpty()); + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.offer(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.offer(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * A new queue has unbounded capacity + */ + public void testConstructor1() { + assertEquals(0, new PriorityQueue(SIZE).size()); + } + + /** + * Constructor throws IAE if capacity argument nonpositive + */ + public void testConstructor2() { + try { + new PriorityQueue(0); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new PriorityQueue((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new PriorityQueue(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new PriorityQueue(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + PriorityQueue q = new PriorityQueue(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.poll()); + } + + /** + * The comparator used in constructor is used + */ + public void testConstructor7() { + MyReverseComparator cmp = new MyReverseComparator(); + PriorityQueue q = new PriorityQueue(SIZE, cmp); + assertEquals(cmp, q.comparator()); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + q.addAll(Arrays.asList(ints)); + for (int i = SIZE - 1; i >= 0; --i) + assertEquals(ints[i], q.poll()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + PriorityQueue q = new PriorityQueue(2); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.remove(); + q.remove(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.remove(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * offer(null) throws NPE + */ + public void testOfferNull() { + PriorityQueue q = new PriorityQueue(1); + try { + q.offer(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + PriorityQueue q = new PriorityQueue(1); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Offer of comparable element succeeds + */ + public void testOffer() { + PriorityQueue q = new PriorityQueue(1); + assertTrue(q.offer(zero)); + assertTrue(q.offer(one)); + } + + /** + * Offer of non-Comparable throws CCE + */ + public void testOfferNonComparable() { + PriorityQueue q = new PriorityQueue(1); + try { + q.offer(new Object()); + q.offer(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * add of comparable succeeds + */ + public void testAdd() { + PriorityQueue q = new PriorityQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + assertTrue(q.add(new Integer(i))); + } + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + PriorityQueue q = new PriorityQueue(1); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + PriorityQueue q = new PriorityQueue(SIZE); + try { + q.addAll(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + PriorityQueue q = new PriorityQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Queue contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + PriorityQueue q = new PriorityQueue(SIZE); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.poll()); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.poll()); + } + assertNull(q.poll()); + } + + /** + * peek returns next element, or null if empty + */ + public void testPeek() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.peek()); + assertEquals(i, q.poll()); + assertTrue(q.peek() == null || + !q.peek().equals(i)); + } + assertNull(q.peek()); + } + + /** + * element returns next element, or throws NSEE if empty + */ + public void testElement() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.element()); + assertEquals(i, q.poll()); + } + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove removes next element, or throws NSEE if empty + */ + public void testRemove() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.remove()); + } + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + PriorityQueue q = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.poll(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + PriorityQueue q = populatedQueue(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + PriorityQueue q = populatedQueue(SIZE); + PriorityQueue p = new PriorityQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + PriorityQueue q = populatedQueue(SIZE); + PriorityQueue p = populatedQueue(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.remove(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + PriorityQueue q = populatedQueue(SIZE); + PriorityQueue p = populatedQueue(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.remove()); + assertFalse(q.contains(x)); + } + } + } + + /** + * toArray contains all elements + */ + public void testToArray() { + PriorityQueue q = populatedQueue(SIZE); + Object[] o = q.toArray(); + Arrays.sort(o); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.poll()); + } + + /** + * toArray(a) contains all elements + */ + public void testToArray2() { + PriorityQueue q = populatedQueue(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + Arrays.sort(ints); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.poll()); + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + PriorityQueue q = populatedQueue(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty collection has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new PriorityQueue().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final PriorityQueue q = new PriorityQueue(3); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + PriorityQueue q = populatedQueue(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized queue has same elements + */ + public void testSerialization() throws Exception { + Queue x = populatedQueue(SIZE); + Queue y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.remove(), y.remove()); + } + assertTrue(y.isEmpty()); + } +} diff --git a/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java b/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java new file mode 100644 index 00000000000..9ce68c34166 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java @@ -0,0 +1,1272 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeoutException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class RecursiveActionTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(RecursiveActionTest.class); + } + + private static ForkJoinPool mainPool() { + return new ForkJoinPool(); + } + + private static ForkJoinPool singletonPool() { + return new ForkJoinPool(1); + } + + private static ForkJoinPool asyncSingletonPool() { + return new ForkJoinPool(1, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + + private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) { + try (PoolCleaner cleaner = cleaner(pool)) { + checkNotDone(a); + + assertNull(pool.invoke(a)); + + checkCompletedNormally(a); + } + } + + void checkNotDone(RecursiveAction a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + if (! ForkJoinTask.inForkJoinPool()) { + Thread.currentThread().interrupt(); + try { + a.get(); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + Thread.currentThread().interrupt(); + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(RecursiveAction a) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + assertNull(a.join()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertNull(a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertNull(a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCancelled(RecursiveAction a) { + assertTrue(a.isDone()); + assertTrue(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertTrue(a.getException() instanceof CancellationException); + assertNull(a.getRawResult()); + + try { + a.join(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(RecursiveAction a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + + try { + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(expected.getClass(), t.getClass()); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + public static final class FJException extends RuntimeException { + public FJException() { super(); } + public FJException(Throwable cause) { super(cause); } + } + + // A simple recursive action for testing + final class FibAction extends CheckedRecursiveAction { + final int number; + int result; + FibAction(int n) { number = n; } + protected void realCompute() { + int n = number; + if (n <= 1) + result = n; + else { + FibAction f1 = new FibAction(n - 1); + FibAction f2 = new FibAction(n - 2); + invokeAll(f1, f2); + result = f1.result + f2.result; + } + } + } + + // A recursive action failing in base case + static final class FailingFibAction extends RecursiveAction { + final int number; + int result; + FailingFibAction(int n) { number = n; } + public void compute() { + int n = number; + if (n <= 1) + throw new FJException(); + else { + FailingFibAction f1 = new FailingFibAction(n - 1); + FailingFibAction f2 = new FailingFibAction(n - 2); + invokeAll(f1, f2); + result = f1.result + f2.result; + } + } + } + + /** + * invoke returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks. getRawResult of a RecursiveAction returns null; + */ + public void testInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertNull(f.invoke()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.quietlyInvoke(); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.join()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join/quietlyJoin of a forked task succeeds in the presence of interrupts + */ + public void testJoinIgnoresInterrupts() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + final Thread myself = Thread.currentThread(); + + // test join() + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + assertNull(f.join()); + Thread.interrupted(); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = new FibAction(8); + f.cancel(true); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + Thread.interrupted(); + checkCancelled(f); + } + + f = new FibAction(8); + f.completeExceptionally(new FJException()); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + Thread.interrupted(); + checkCompletedAbnormally(f, success); + } + + // test quietlyJoin() + f = new FibAction(8); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = new FibAction(8); + f.cancel(true); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + checkCancelled(f); + + f = new FibAction(8); + f.completeExceptionally(new FJException()); + assertSame(f, f.fork()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + Thread.interrupted(); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + a.reinitialize(); + testInvokeOnPool(singletonPool(), a); + } + + /** + * join/quietlyJoin of a forked task when not in ForkJoinPool + * succeeds in the presence of interrupts + */ + public void testJoinIgnoresInterruptsOutsideForkJoinPool() { + final SynchronousQueue sq = + new SynchronousQueue(); + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws InterruptedException { + FibAction[] fibActions = new FibAction[6]; + for (int i = 0; i < fibActions.length; i++) + fibActions[i] = new FibAction(8); + + fibActions[1].cancel(false); + fibActions[2].completeExceptionally(new FJException()); + fibActions[4].cancel(true); + fibActions[5].completeExceptionally(new FJException()); + + for (int i = 0; i < fibActions.length; i++) + fibActions[i].fork(); + + sq.put(fibActions); + + helpQuiesce(); + }}; + + Runnable r = new CheckedRunnable() { + public void realRun() throws InterruptedException { + FibAction[] fibActions = sq.take(); + FibAction f; + final Thread myself = Thread.currentThread(); + + // test join() ------------ + + f = fibActions[0]; + assertFalse(ForkJoinTask.inForkJoinPool()); + myself.interrupt(); + assertTrue(myself.isInterrupted()); + assertNull(f.join()); + assertTrue(Thread.interrupted()); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = fibActions[1]; + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + assertTrue(Thread.interrupted()); + checkCancelled(f); + } + + f = fibActions[2]; + myself.interrupt(); + assertTrue(myself.isInterrupted()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + assertTrue(Thread.interrupted()); + checkCompletedAbnormally(f, success); + } + + // test quietlyJoin() --------- + + f = fibActions[3]; + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + assertTrue(Thread.interrupted()); + assertEquals(21, f.result); + checkCompletedNormally(f); + + f = fibActions[4]; + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + assertTrue(Thread.interrupted()); + checkCancelled(f); + + f = fibActions[5]; + myself.interrupt(); + assertTrue(myself.isInterrupted()); + f.quietlyJoin(); + assertTrue(Thread.interrupted()); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + + Thread t; + + t = newStartedThread(r); + testInvokeOnPool(mainPool(), a); + awaitTermination(t); + + a.reinitialize(); + t = newStartedThread(r); + testInvokeOnPool(singletonPool(), a); + awaitTermination(t); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.get()); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertNull(f.get(5L, SECONDS)); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get with null time unit throws NPE + */ + public void testForkTimedGetNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + try { + f.get(5L, null); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertEquals(21, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesce() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + helpQuiesce(); + while (!f.isDone()) // wait out race + ; + assertEquals(21, f.result); + assertEquals(0, getQueuedTaskCount()); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvoke() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + try { + f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGet() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() throws Exception { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + f.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoin() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * getPool of executing task returns its pool + */ + public void testGetPool() { + final ForkJoinPool mainPool = mainPool(); + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertSame(mainPool, getPool()); + }}; + testInvokeOnPool(mainPool, a); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertNull(getPool()); + }}; + assertNull(a.invoke()); + } + + /** + * inForkJoinPool of executing task returns true + */ + public void testInForkJoinPool() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertTrue(inForkJoinPool()); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + assertFalse(inForkJoinPool()); + }}; + assertNull(a.invoke()); + } + + /** + * getPool of current thread in pool returns its pool + */ + public void testWorkerGetPool() { + final ForkJoinPool mainPool = mainPool(); + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + ForkJoinWorkerThread w = + (ForkJoinWorkerThread) Thread.currentThread(); + assertSame(mainPool, w.getPool()); + }}; + testInvokeOnPool(mainPool, a); + } + + /** + * getPoolIndex of current thread in pool returns 0 <= value < poolSize + */ + public void testWorkerGetPoolIndex() { + final ForkJoinPool mainPool = mainPool(); + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + ForkJoinWorkerThread w = + (ForkJoinWorkerThread) Thread.currentThread(); + assertTrue(w.getPoolIndex() >= 0); + // pool size can shrink after assigning index, so cannot check + // assertTrue(w.getPoolIndex() < mainPool.getPoolSize()); + }}; + testInvokeOnPool(mainPool, a); + } + + /** + * setRawResult(null) succeeds + */ + public void testSetRawResult() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + setRawResult(null); + assertNull(getRawResult()); + }}; + assertNull(a.invoke()); + } + + /** + * A reinitialized normally completed task may be re-invoked + */ + public void testReinitialize() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + assertNull(f.invoke()); + assertEquals(21, f.result); + checkCompletedNormally(f); + f.reinitialize(); + checkNotDone(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * A reinitialized abnormally completed task may be re-invoked + */ + public void testReinitializeAbnormal() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + f.reinitialize(); + checkNotDone(f); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.completeExceptionally(new FJException()); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invoke task suppresses execution invoking complete + */ + public void testComplete() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + f.complete(null); + assertNull(f.invoke()); + assertEquals(0, f.result); + checkCompletedNormally(f); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + invokeAll(f, g); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + invokeAll(f); + checkCompletedNormally(f); + assertEquals(21, f.result); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + invokeAll(f, g, h); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + checkCompletedNormally(g); + assertEquals(13, h.result); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f); + assertEquals(21, f.result); + checkCompletedNormally(g); + assertEquals(34, g.result); + checkCompletedNormally(g); + assertEquals(13, h.result); + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPE() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FibAction g = new FibAction(9); + FibAction h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FailingFibAction g = new FailingFibAction(9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction g = new FailingFibAction(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction f = new FibAction(8); + FailingFibAction g = new FailingFibAction(9); + FibAction h = new FibAction(7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FailingFibAction f = new FailingFibAction(8); + FibAction g = new FibAction(9); + FibAction h = new FibAction(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + }}; + testInvokeOnPool(mainPool(), a); + } + + /** + * tryUnfork returns true for most recent unexecuted task, + * and suppresses execution + */ + public void testTryUnfork() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertTrue(f.tryUnfork()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * getSurplusQueuedTaskCount returns > 0 when + * there are more tasks than threads + */ + public void testGetSurplusQueuedTaskCount() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction h = new FibAction(7); + assertSame(h, h.fork()); + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertTrue(getSurplusQueuedTaskCount() > 0); + helpQuiesce(); + assertEquals(0, getSurplusQueuedTaskCount()); + checkCompletedNormally(f); + checkCompletedNormally(g); + checkCompletedNormally(h); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns most recent unexecuted task. + */ + public void testPeekNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(f, peekNextLocalTask()); + assertNull(f.join()); + checkCompletedNormally(f); + helpQuiesce(); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollNextLocalTask returns most recent unexecuted task + * without executing it + */ + public void testPollNextLocalTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(f, pollNextLocalTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it + */ + public void testPollTask() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(f, pollTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(singletonPool(), a); + } + + /** + * peekNextLocalTask returns least recent unexecuted task in async mode + */ + public void testPeekNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(g, peekNextLocalTask()); + assertNull(f.join()); + helpQuiesce(); + checkCompletedNormally(f); + checkCompletedNormally(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollNextLocalTask returns least recent unexecuted task without + * executing it, in async mode + */ + public void testPollNextLocalTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(g, pollNextLocalTask()); + helpQuiesce(); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** + * pollTask returns an unexecuted task without executing it, in + * async mode + */ + public void testPollTaskAsync() { + RecursiveAction a = new CheckedRecursiveAction() { + protected void realCompute() { + FibAction g = new FibAction(9); + assertSame(g, g.fork()); + FibAction f = new FibAction(8); + assertSame(f, f.fork()); + assertSame(g, pollTask()); + helpQuiesce(); + checkCompletedNormally(f); + checkNotDone(g); + }}; + testInvokeOnPool(asyncSingletonPool(), a); + } + + /** Demo from RecursiveAction javadoc */ + static class SortTask extends RecursiveAction { + final long[] array; final int lo, hi; + SortTask(long[] array, int lo, int hi) { + this.array = array; this.lo = lo; this.hi = hi; + } + SortTask(long[] array) { this(array, 0, array.length); } + protected void compute() { + if (hi - lo < THRESHOLD) + sortSequentially(lo, hi); + else { + int mid = (lo + hi) >>> 1; + invokeAll(new SortTask(array, lo, mid), + new SortTask(array, mid, hi)); + merge(lo, mid, hi); + } + } + // implementation details follow: + static final int THRESHOLD = 100; + void sortSequentially(int lo, int hi) { + Arrays.sort(array, lo, hi); + } + void merge(int lo, int mid, int hi) { + long[] buf = Arrays.copyOfRange(array, lo, mid); + for (int i = 0, j = lo, k = mid; i < buf.length; j++) + array[j] = (k == hi || buf[i] < array[k]) ? + buf[i++] : array[k++]; + } + } + + /** + * SortTask demo works as advertised + */ + public void testSortTaskDemo() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + long[] array = new long[1007]; + for (int i = 0; i < array.length; i++) + array[i] = rnd.nextLong(); + long[] arrayClone = array.clone(); + testInvokeOnPool(mainPool(), new SortTask(array)); + Arrays.sort(arrayClone); + assertTrue(Arrays.equals(array, arrayClone)); + } +} diff --git a/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java b/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java new file mode 100644 index 00000000000..ce13daa8e7b --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java @@ -0,0 +1,1053 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.HashSet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveTask; +import java.util.concurrent.TimeoutException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class RecursiveTaskTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(RecursiveTaskTest.class); + } + + private static ForkJoinPool mainPool() { + return new ForkJoinPool(); + } + + private static ForkJoinPool singletonPool() { + return new ForkJoinPool(1); + } + + private static ForkJoinPool asyncSingletonPool() { + return new ForkJoinPool(1, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + + private T testInvokeOnPool(ForkJoinPool pool, RecursiveTask a) { + try (PoolCleaner cleaner = cleaner(pool)) { + checkNotDone(a); + + T result = pool.invoke(a); + + checkCompletedNormally(a, result); + return result; + } + } + + void checkNotDone(RecursiveTask a) { + assertFalse(a.isDone()); + assertFalse(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertFalse(a.isCancelled()); + assertNull(a.getException()); + assertNull(a.getRawResult()); + + if (! ForkJoinTask.inForkJoinPool()) { + Thread.currentThread().interrupt(); + try { + a.get(); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + Thread.currentThread().interrupt(); + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + try { + a.get(0L, SECONDS); + shouldThrow(); + } catch (TimeoutException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedNormally(RecursiveTask a, T expected) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertTrue(a.isCompletedNormally()); + assertFalse(a.isCompletedAbnormally()); + assertNull(a.getException()); + assertSame(expected, a.getRawResult()); + assertSame(expected, a.join()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + try { + assertSame(expected, a.get()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + try { + assertSame(expected, a.get(5L, SECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + /** + * Waits for the task to complete, and checks that when it does, + * it will have an Integer result equals to the given int. + */ + void checkCompletesNormally(RecursiveTask a, int expected) { + Integer r = a.join(); + assertEquals(expected, (int) r); + checkCompletedNormally(a, r); + } + + /** + * Like checkCompletesNormally, but verifies that the task has + * already completed. + */ + void checkCompletedNormally(RecursiveTask a, int expected) { + Integer r = a.getRawResult(); + assertEquals(expected, (int) r); + checkCompletedNormally(a, r); + } + + void checkCancelled(RecursiveTask a) { + assertTrue(a.isDone()); + assertTrue(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertTrue(a.getException() instanceof CancellationException); + assertNull(a.getRawResult()); + + try { + a.join(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + void checkCompletedAbnormally(RecursiveTask a, Throwable t) { + assertTrue(a.isDone()); + assertFalse(a.isCancelled()); + assertFalse(a.isCompletedNormally()); + assertTrue(a.isCompletedAbnormally()); + assertSame(t.getClass(), a.getException().getClass()); + assertNull(a.getRawResult()); + assertFalse(a.cancel(false)); + assertFalse(a.cancel(true)); + + try { + a.join(); + shouldThrow(); + } catch (Throwable expected) { + assertSame(t.getClass(), expected.getClass()); + } + + try { + a.get(); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + + try { + a.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertSame(t.getClass(), success.getCause().getClass()); + } catch (Throwable fail) { threadUnexpectedException(fail); } + } + + public static final class FJException extends RuntimeException { + public FJException() { super(); } + } + + // An invalid return value for Fib + static final Integer NoResult = Integer.valueOf(-17); + + // A simple recursive task for testing + final class FibTask extends CheckedRecursiveTask { + final int number; + FibTask(int n) { number = n; } + public Integer realCompute() { + int n = number; + if (n <= 1) + return n; + FibTask f1 = new FibTask(n - 1); + f1.fork(); + return (new FibTask(n - 2)).compute() + f1.join(); + } + + public void publicSetRawResult(Integer result) { + setRawResult(result); + } + } + + // A recursive action failing in base case + final class FailingFibTask extends RecursiveTask { + final int number; + int result; + FailingFibTask(int n) { number = n; } + public Integer compute() { + int n = number; + if (n <= 1) + throw new FJException(); + FailingFibTask f1 = new FailingFibTask(n - 1); + f1.fork(); + return (new FibTask(n - 2)).compute() + f1.join(); + } + } + + /** + * invoke returns value when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks. getRawResult of a completed non-null task + * returns value; + */ + public void testInvoke() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + Integer r = f.invoke(); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + return r; + }}; + assertEquals(21, (int) testInvokeOnPool(mainPool(), a)); + } + + /** + * quietlyInvoke task returns when task completes normally. + * isCompletedAbnormally and isCancelled return false for normally + * completed tasks + */ + public void testQuietlyInvoke() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + f.quietlyInvoke(); + checkCompletedNormally(f, 21); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * join of a forked task returns when task completes + */ + public void testForkJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + Integer r = f.join(); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + return r; + }}; + assertEquals(21, (int) testInvokeOnPool(mainPool(), a)); + } + + /** + * get of a forked task returns when task completes + */ + public void testForkGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + Integer r = f.get(); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + return r; + }}; + assertEquals(21, (int) testInvokeOnPool(mainPool(), a)); + } + + /** + * timed get of a forked task returns when task completes + */ + public void testForkTimedGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + Integer r = f.get(5L, SECONDS); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + return r; + }}; + assertEquals(21, (int) testInvokeOnPool(mainPool(), a)); + } + + /** + * quietlyJoin of a forked task returns when task completes + */ + public void testForkQuietlyJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + Integer r = f.getRawResult(); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + return r; + }}; + assertEquals(21, (int) testInvokeOnPool(mainPool(), a)); + } + + /** + * helpQuiesce returns when tasks are complete. + * getQueuedTaskCount returns 0 when quiescent + */ + public void testForkHelpQuiesce() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + helpQuiesce(); + while (!f.isDone()) // wait out race + ; + assertEquals(0, getQueuedTaskCount()); + checkCompletedNormally(f, 21); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invoke task throws exception when task completes abnormally + */ + public void testAbnormalInvoke() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * quietlyInvoke task returns when task completes abnormally + */ + public void testAbnormalQuietlyInvoke() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + f.quietlyInvoke(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * join of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + assertSame(f, f.fork()); + try { + Integer r = f.join(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FailingFibTask f = new FailingFibTask(8); + assertSame(f, f.fork()); + try { + Integer r = f.get(); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * timed get of a forked task throws exception when task completes abnormally + */ + public void testAbnormalForkTimedGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FailingFibTask f = new FailingFibTask(8); + assertSame(f, f.fork()); + try { + Integer r = f.get(5L, SECONDS); + shouldThrow(); + } catch (ExecutionException success) { + Throwable cause = success.getCause(); + assertTrue(cause instanceof FJException); + checkCompletedAbnormally(f, cause); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * quietlyJoin of a forked task returns when task completes abnormally + */ + public void testAbnormalForkQuietlyJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + assertSame(f, f.fork()); + f.quietlyJoin(); + assertTrue(f.getException() instanceof FJException); + checkCompletedAbnormally(f, f.getException()); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invoke task throws exception when task cancelled + */ + public void testCancelledInvoke() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertTrue(f.cancel(true)); + try { + Integer r = f.invoke(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * join of a forked task throws exception when task cancelled + */ + public void testCancelledForkJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + Integer r = f.join(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * get of a forked task throws exception when task cancelled + */ + public void testCancelledForkGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FibTask f = new FibTask(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + Integer r = f.get(); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * timed get of a forked task throws exception when task cancelled + */ + public void testCancelledForkTimedGet() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() throws Exception { + FibTask f = new FibTask(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + try { + Integer r = f.get(5L, SECONDS); + shouldThrow(); + } catch (CancellationException success) { + checkCancelled(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * quietlyJoin of a forked task returns when task cancelled + */ + public void testCancelledForkQuietlyJoin() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + assertTrue(f.cancel(true)); + assertSame(f, f.fork()); + f.quietlyJoin(); + checkCancelled(f); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * getPool of executing task returns its pool + */ + public void testGetPool() { + final ForkJoinPool mainPool = mainPool(); + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + assertSame(mainPool, getPool()); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool, a)); + } + + /** + * getPool of non-FJ task returns null + */ + public void testGetPool2() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + assertNull(getPool()); + return NoResult; + }}; + assertSame(NoResult, a.invoke()); + } + + /** + * inForkJoinPool of executing task returns true + */ + public void testInForkJoinPool() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + assertTrue(inForkJoinPool()); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * inForkJoinPool of non-FJ task returns false + */ + public void testInForkJoinPool2() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + assertFalse(inForkJoinPool()); + return NoResult; + }}; + assertSame(NoResult, a.invoke()); + } + + /** + * The value set by setRawResult is returned by getRawResult + */ + public void testSetRawResult() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + setRawResult(NoResult); + assertSame(NoResult, getRawResult()); + return NoResult; + } + }; + assertSame(NoResult, a.invoke()); + } + + /** + * A reinitialized normally completed task may be re-invoked + */ + public void testReinitialize() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + Integer r = f.invoke(); + assertEquals(21, (int) r); + checkCompletedNormally(f, r); + f.reinitialize(); + f.publicSetRawResult(null); + checkNotDone(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * A reinitialized abnormally completed task may be re-invoked + */ + public void testReinitializeAbnormal() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + checkNotDone(f); + + for (int i = 0; i < 3; i++) { + try { + f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + f.reinitialize(); + checkNotDone(f); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invoke task throws exception after invoking completeExceptionally + */ + public void testCompleteExceptionally() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + f.completeExceptionally(new FJException()); + try { + Integer r = f.invoke(); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invoke task suppresses execution invoking complete + */ + public void testComplete() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + f.complete(NoResult); + Integer r = f.invoke(); + assertSame(NoResult, r); + checkCompletedNormally(f, NoResult); + return r; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(t1, t2) invokes all task arguments + */ + public void testInvokeAll2() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FibTask g = new FibTask(9); + invokeAll(f, g); + checkCompletedNormally(f, 21); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(tasks) with 1 argument invokes task + */ + public void testInvokeAll1() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + invokeAll(f); + checkCompletedNormally(f, 21); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(tasks) with > 2 argument invokes tasks + */ + public void testInvokeAll3() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FibTask g = new FibTask(9); + FibTask h = new FibTask(7); + invokeAll(f, g, h); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f, 21); + checkCompletedNormally(g, 34); + checkCompletedNormally(h, 13); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(collection) invokes all tasks in the collection + */ + public void testInvokeAllCollection() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FibTask g = new FibTask(9); + FibTask h = new FibTask(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + invokeAll(set); + assertTrue(f.isDone()); + assertTrue(g.isDone()); + assertTrue(h.isDone()); + checkCompletedNormally(f, 21); + checkCompletedNormally(g, 34); + checkCompletedNormally(h, 13); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(tasks) with any null task throws NPE + */ + public void testInvokeAllNPE() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FibTask g = new FibTask(9); + FibTask h = null; + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (NullPointerException success) {} + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(t1, t2) throw exception if any task does + */ + public void testAbnormalInvokeAll2() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FailingFibTask g = new FailingFibTask(9); + try { + invokeAll(f, g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(tasks) with 1 argument throws exception if task does + */ + public void testAbnormalInvokeAll1() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask g = new FailingFibTask(9); + try { + invokeAll(g); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(tasks) with > 2 argument throws exception if any task does + */ + public void testAbnormalInvokeAll3() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask f = new FibTask(8); + FailingFibTask g = new FailingFibTask(9); + FibTask h = new FibTask(7); + try { + invokeAll(f, g, h); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(g, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * invokeAll(collection) throws exception if any task does + */ + public void testAbnormalInvokeAllCollection() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FailingFibTask f = new FailingFibTask(8); + FibTask g = new FibTask(9); + FibTask h = new FibTask(7); + HashSet set = new HashSet(); + set.add(f); + set.add(g); + set.add(h); + try { + invokeAll(set); + shouldThrow(); + } catch (FJException success) { + checkCompletedAbnormally(f, success); + } + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(mainPool(), a)); + } + + /** + * tryUnfork returns true for most recent unexecuted task, + * and suppresses execution + */ + public void testTryUnfork() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertTrue(f.tryUnfork()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(singletonPool(), a)); + } + + /** + * getSurplusQueuedTaskCount returns > 0 when + * there are more tasks than threads + */ + public void testGetSurplusQueuedTaskCount() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask h = new FibTask(7); + assertSame(h, h.fork()); + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertTrue(getSurplusQueuedTaskCount() > 0); + helpQuiesce(); + assertEquals(0, getSurplusQueuedTaskCount()); + checkCompletedNormally(f, 21); + checkCompletedNormally(g, 34); + checkCompletedNormally(h, 13); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(singletonPool(), a)); + } + + /** + * peekNextLocalTask returns most recent unexecuted task. + */ + public void testPeekNextLocalTask() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(f, peekNextLocalTask()); + checkCompletesNormally(f, 21); + helpQuiesce(); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(singletonPool(), a)); + } + + /** + * pollNextLocalTask returns most recent unexecuted task + * without executing it + */ + public void testPollNextLocalTask() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(f, pollNextLocalTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(singletonPool(), a)); + } + + /** + * pollTask returns an unexecuted task without executing it + */ + public void testPollTask() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(f, pollTask()); + helpQuiesce(); + checkNotDone(f); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(singletonPool(), a)); + } + + /** + * peekNextLocalTask returns least recent unexecuted task in async mode + */ + public void testPeekNextLocalTaskAsync() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(g, peekNextLocalTask()); + assertEquals(21, (int) f.join()); + helpQuiesce(); + checkCompletedNormally(f, 21); + checkCompletedNormally(g, 34); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a)); + } + + /** + * pollNextLocalTask returns least recent unexecuted task without + * executing it, in async mode + */ + public void testPollNextLocalTaskAsync() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(g, pollNextLocalTask()); + helpQuiesce(); + checkCompletedNormally(f, 21); + checkNotDone(g); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a)); + } + + /** + * pollTask returns an unexecuted task without executing it, in + * async mode + */ + public void testPollTaskAsync() { + RecursiveTask a = new CheckedRecursiveTask() { + public Integer realCompute() { + FibTask g = new FibTask(9); + assertSame(g, g.fork()); + FibTask f = new FibTask(8); + assertSame(f, f.fork()); + assertSame(g, pollTask()); + helpQuiesce(); + checkCompletedNormally(f, 21); + checkNotDone(g); + return NoResult; + }}; + assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java b/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java new file mode 100644 index 00000000000..8088498562a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java @@ -0,0 +1,1163 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ReentrantLockTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ReentrantLockTest.class); + } + + /** + * A checked runnable calling lockInterruptibly + */ + class InterruptibleLockRunnable extends CheckedRunnable { + final ReentrantLock lock; + InterruptibleLockRunnable(ReentrantLock lock) { this.lock = lock; } + public void realRun() throws InterruptedException { + lock.lockInterruptibly(); + } + } + + /** + * A checked runnable calling lockInterruptibly that expects to be + * interrupted + */ + class InterruptedLockRunnable extends CheckedInterruptedRunnable { + final ReentrantLock lock; + InterruptedLockRunnable(ReentrantLock lock) { this.lock = lock; } + public void realRun() throws InterruptedException { + lock.lockInterruptibly(); + } + } + + /** + * Subclass to expose protected methods + */ + static class PublicReentrantLock extends ReentrantLock { + PublicReentrantLock() { super(); } + PublicReentrantLock(boolean fair) { super(fair); } + public Thread getOwner() { + return super.getOwner(); + } + public Collection getQueuedThreads() { + return super.getQueuedThreads(); + } + public Collection getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); + } + } + + /** + * Releases write lock, checking that it had a hold count of 1. + */ + void releaseLock(PublicReentrantLock lock) { + assertLockedByMoi(lock); + lock.unlock(); + assertFalse(lock.isHeldByCurrentThread()); + assertNotLocked(lock); + } + + /** + * Spin-waits until lock.hasQueuedThread(t) becomes true. + */ + void waitForQueuedThread(PublicReentrantLock lock, Thread t) { + long startTime = System.nanoTime(); + while (!lock.hasQueuedThread(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + assertTrue(t.isAlive()); + assertNotSame(t, lock.getOwner()); + } + + /** + * Checks that lock is not locked. + */ + void assertNotLocked(PublicReentrantLock lock) { + assertFalse(lock.isLocked()); + assertFalse(lock.isHeldByCurrentThread()); + assertNull(lock.getOwner()); + assertEquals(0, lock.getHoldCount()); + } + + /** + * Checks that lock is locked by the given thread. + */ + void assertLockedBy(PublicReentrantLock lock, Thread t) { + assertTrue(lock.isLocked()); + assertSame(t, lock.getOwner()); + assertEquals(t == Thread.currentThread(), + lock.isHeldByCurrentThread()); + assertEquals(t == Thread.currentThread(), + lock.getHoldCount() > 0); + } + + /** + * Checks that lock is locked by the current thread. + */ + void assertLockedByMoi(PublicReentrantLock lock) { + assertLockedBy(lock, Thread.currentThread()); + } + + /** + * Checks that condition c has no waiters. + */ + void assertHasNoWaiters(PublicReentrantLock lock, Condition c) { + assertHasWaiters(lock, c, new Thread[] {}); + } + + /** + * Checks that condition c has exactly the given waiter threads. + */ + void assertHasWaiters(PublicReentrantLock lock, Condition c, + Thread... threads) { + lock.lock(); + assertEquals(threads.length > 0, lock.hasWaiters(c)); + assertEquals(threads.length, lock.getWaitQueueLength(c)); + assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty()); + assertEquals(threads.length, lock.getWaitingThreads(c).size()); + assertEquals(new HashSet(lock.getWaitingThreads(c)), + new HashSet(Arrays.asList(threads))); + lock.unlock(); + } + + enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil } + + /** + * Awaits condition "indefinitely" using the specified AwaitMethod. + */ + void await(Condition c, AwaitMethod awaitMethod) + throws InterruptedException { + long timeoutMillis = 2 * LONG_DELAY_MS; + switch (awaitMethod) { + case await: + c.await(); + break; + case awaitTimed: + assertTrue(c.await(timeoutMillis, MILLISECONDS)); + break; + case awaitNanos: + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); + assertTrue(nanosRemaining > timeoutNanos / 2); + assertTrue(nanosRemaining <= timeoutNanos); + break; + case awaitUntil: + assertTrue(c.awaitUntil(delayedDate(timeoutMillis))); + break; + default: + throw new AssertionError(); + } + } + + /** + * Constructor sets given fairness, and is in unlocked state + */ + public void testConstructor() { + PublicReentrantLock lock; + + lock = new PublicReentrantLock(); + assertFalse(lock.isFair()); + assertNotLocked(lock); + + lock = new PublicReentrantLock(true); + assertTrue(lock.isFair()); + assertNotLocked(lock); + + lock = new PublicReentrantLock(false); + assertFalse(lock.isFair()); + assertNotLocked(lock); + } + + /** + * locking an unlocked lock succeeds + */ + public void testLock() { testLock(false); } + public void testLock_fair() { testLock(true); } + public void testLock(boolean fair) { + PublicReentrantLock lock = new PublicReentrantLock(fair); + lock.lock(); + assertLockedByMoi(lock); + releaseLock(lock); + } + + /** + * Unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testUnlock_IMSE() { testUnlock_IMSE(false); } + public void testUnlock_IMSE_fair() { testUnlock_IMSE(true); } + public void testUnlock_IMSE(boolean fair) { + ReentrantLock lock = new ReentrantLock(fair); + try { + lock.unlock(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * tryLock on an unlocked lock succeeds + */ + public void testTryLock() { testTryLock(false); } + public void testTryLock_fair() { testTryLock(true); } + public void testTryLock(boolean fair) { + PublicReentrantLock lock = new PublicReentrantLock(fair); + assertTrue(lock.tryLock()); + assertLockedByMoi(lock); + assertTrue(lock.tryLock()); + assertLockedByMoi(lock); + lock.unlock(); + releaseLock(lock); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { testHasQueuedThreads(false); } + public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); } + public void testHasQueuedThreads(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertFalse(lock.hasQueuedThreads()); + lock.lock(); + assertFalse(lock.hasQueuedThreads()); + t1.start(); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThreads()); + t2.start(); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(lock.hasQueuedThreads()); + lock.unlock(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThreads()); + } + + /** + * getQueueLength reports number of waiting threads + */ + public void testGetQueueLength() { testGetQueueLength(false); } + public void testGetQueueLength_fair() { testGetQueueLength(true); } + public void testGetQueueLength(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertEquals(0, lock.getQueueLength()); + lock.lock(); + t1.start(); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueueLength()); + t2.start(); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueueLength()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(1, lock.getQueueLength()); + lock.unlock(); + awaitTermination(t2); + assertEquals(0, lock.getQueueLength()); + } + + /** + * hasQueuedThread(null) throws NPE + */ + public void testHasQueuedThreadNPE() { testHasQueuedThreadNPE(false); } + public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); } + public void testHasQueuedThreadNPE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + try { + lock.hasQueuedThread(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasQueuedThread reports whether a thread is queued + */ + public void testHasQueuedThread() { testHasQueuedThread(false); } + public void testHasQueuedThread_fair() { testHasQueuedThread(true); } + public void testHasQueuedThread(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertFalse(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + lock.lock(); + t1.start(); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + t2.start(); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + lock.unlock(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + } + + /** + * getQueuedThreads includes waiting threads + */ + public void testGetQueuedThreads() { testGetQueuedThreads(false); } + public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); } + public void testGetQueuedThreads(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertTrue(lock.getQueuedThreads().isEmpty()); + lock.lock(); + assertTrue(lock.getQueuedThreads().isEmpty()); + t1.start(); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueuedThreads().size()); + assertTrue(lock.getQueuedThreads().contains(t1)); + t2.start(); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueuedThreads().size()); + assertTrue(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + assertEquals(1, lock.getQueuedThreads().size()); + lock.unlock(); + awaitTermination(t2); + assertTrue(lock.getQueuedThreads().isEmpty()); + } + + /** + * timed tryLock is interruptible + */ + public void testTryLock_Interruptible() { testTryLock_Interruptible(false); } + public void testTryLock_Interruptible_fair() { testTryLock_Interruptible(true); } + public void testTryLock_Interruptible(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + lock.lock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.tryLock(2 * LONG_DELAY_MS, MILLISECONDS); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseLock(lock); + } + + /** + * tryLock on a locked lock fails + */ + public void testTryLockWhenLocked() { testTryLockWhenLocked(false); } + public void testTryLockWhenLocked_fair() { testTryLockWhenLocked(true); } + public void testTryLockWhenLocked(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + lock.lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(lock.tryLock()); + }}); + + awaitTermination(t); + releaseLock(lock); + } + + /** + * Timed tryLock on a locked lock times out + */ + public void testTryLock_Timeout() { testTryLock_Timeout(false); } + public void testTryLock_Timeout_fair() { testTryLock_Timeout(true); } + public void testTryLock_Timeout(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + lock.lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long timeoutMillis = 10; + assertFalse(lock.tryLock(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + }}); + + awaitTermination(t); + releaseLock(lock); + } + + /** + * getHoldCount returns number of recursive holds + */ + public void testGetHoldCount() { testGetHoldCount(false); } + public void testGetHoldCount_fair() { testGetHoldCount(true); } + public void testGetHoldCount(boolean fair) { + ReentrantLock lock = new ReentrantLock(fair); + for (int i = 1; i <= SIZE; i++) { + lock.lock(); + assertEquals(i, lock.getHoldCount()); + } + for (int i = SIZE; i > 0; i--) { + lock.unlock(); + assertEquals(i - 1, lock.getHoldCount()); + } + } + + /** + * isLocked is true when locked and false when not + */ + public void testIsLocked() { testIsLocked(false); } + public void testIsLocked_fair() { testIsLocked(true); } + public void testIsLocked(boolean fair) { + try { + final ReentrantLock lock = new ReentrantLock(fair); + assertFalse(lock.isLocked()); + lock.lock(); + assertTrue(lock.isLocked()); + lock.lock(); + assertTrue(lock.isLocked()); + lock.unlock(); + assertTrue(lock.isLocked()); + lock.unlock(); + assertFalse(lock.isLocked()); + final CyclicBarrier barrier = new CyclicBarrier(2); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws Exception { + lock.lock(); + assertTrue(lock.isLocked()); + barrier.await(); + barrier.await(); + lock.unlock(); + }}); + + barrier.await(); + assertTrue(lock.isLocked()); + barrier.await(); + awaitTermination(t); + assertFalse(lock.isLocked()); + } catch (Exception fail) { threadUnexpectedException(fail); } + } + + /** + * lockInterruptibly succeeds when unlocked, else is interruptible + */ + public void testLockInterruptibly() { testLockInterruptibly(false); } + public void testLockInterruptibly_fair() { testLockInterruptibly(true); } + public void testLockInterruptibly(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + try { + lock.lockInterruptibly(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + assertLockedByMoi(lock); + Thread t = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t); + t.interrupt(); + assertTrue(lock.isLocked()); + assertTrue(lock.isHeldByCurrentThread()); + awaitTermination(t); + releaseLock(lock); + } + + /** + * Calling await without holding lock throws IllegalMonitorStateException + */ + public void testAwait_IMSE() { testAwait_IMSE(false); } + public void testAwait_IMSE_fair() { testAwait_IMSE(true); } + public void testAwait_IMSE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + for (AwaitMethod awaitMethod : AwaitMethod.values()) { + long startTime = System.nanoTime(); + try { + await(c, awaitMethod); + shouldThrow(); + } catch (IllegalMonitorStateException success) { + } catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * Calling signal without holding lock throws IllegalMonitorStateException + */ + public void testSignal_IMSE() { testSignal_IMSE(false); } + public void testSignal_IMSE_fair() { testSignal_IMSE(true); } + public void testSignal_IMSE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + try { + c.signal(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * awaitNanos without a signal times out + */ + public void testAwaitNanos_Timeout() { testAwaitNanos_Timeout(false); } + public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); } + public void testAwaitNanos_Timeout(boolean fair) { + try { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + lock.lock(); + long startTime = System.nanoTime(); + long timeoutMillis = 10; + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); + assertTrue(nanosRemaining <= 0); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * timed await without a signal times out + */ + public void testAwait_Timeout() { testAwait_Timeout(false); } + public void testAwait_Timeout_fair() { testAwait_Timeout(true); } + public void testAwait_Timeout(boolean fair) { + try { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + lock.lock(); + long startTime = System.nanoTime(); + long timeoutMillis = 10; + assertFalse(c.await(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * awaitUntil without a signal times out + */ + public void testAwaitUntil_Timeout() { testAwaitUntil_Timeout(false); } + public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); } + public void testAwaitUntil_Timeout(boolean fair) { + try { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + lock.lock(); + // We shouldn't assume that nanoTime and currentTimeMillis + // use the same time source, so don't use nanoTime here. + java.util.Date delayedDate = delayedDate(timeoutMillis()); + assertFalse(c.awaitUntil(delayedDate)); + assertTrue(new java.util.Date().getTime() >= delayedDate.getTime()); + lock.unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * await returns when signalled + */ + public void testAwait() { testAwait(false); } + public void testAwait_fair() { testAwait(true); } + public void testAwait(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch locked = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + locked.countDown(); + c.await(); + lock.unlock(); + }}); + + await(locked); + lock.lock(); + assertHasWaiters(lock, c, t); + c.signal(); + assertHasNoWaiters(lock, c); + assertTrue(t.isAlive()); + lock.unlock(); + awaitTermination(t); + } + + /** + * hasWaiters throws NPE if null + */ + public void testHasWaitersNPE() { testHasWaitersNPE(false); } + public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); } + public void testHasWaitersNPE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + try { + lock.hasWaiters(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitQueueLength throws NPE if null + */ + public void testGetWaitQueueLengthNPE() { testGetWaitQueueLengthNPE(false); } + public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); } + public void testGetWaitQueueLengthNPE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + try { + lock.getWaitQueueLength(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitingThreads throws NPE if null + */ + public void testGetWaitingThreadsNPE() { testGetWaitingThreadsNPE(false); } + public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); } + public void testGetWaitingThreadsNPE(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + try { + lock.getWaitingThreads(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasWaiters throws IllegalArgumentException if not owned + */ + public void testHasWaitersIAE() { testHasWaitersIAE(false); } + public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); } + public void testHasWaitersIAE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + final ReentrantLock lock2 = new ReentrantLock(fair); + try { + lock2.hasWaiters(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * hasWaiters throws IllegalMonitorStateException if not locked + */ + public void testHasWaitersIMSE() { testHasWaitersIMSE(false); } + public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); } + public void testHasWaitersIMSE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + try { + lock.hasWaiters(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * getWaitQueueLength throws IllegalArgumentException if not owned + */ + public void testGetWaitQueueLengthIAE() { testGetWaitQueueLengthIAE(false); } + public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); } + public void testGetWaitQueueLengthIAE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + final ReentrantLock lock2 = new ReentrantLock(fair); + try { + lock2.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getWaitQueueLength throws IllegalMonitorStateException if not locked + */ + public void testGetWaitQueueLengthIMSE() { testGetWaitQueueLengthIMSE(false); } + public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); } + public void testGetWaitQueueLengthIMSE(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + try { + lock.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * getWaitingThreads throws IllegalArgumentException if not owned + */ + public void testGetWaitingThreadsIAE() { testGetWaitingThreadsIAE(false); } + public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); } + public void testGetWaitingThreadsIAE(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final PublicReentrantLock lock2 = new PublicReentrantLock(fair); + try { + lock2.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getWaitingThreads throws IllegalMonitorStateException if not locked + */ + public void testGetWaitingThreadsIMSE() { testGetWaitingThreadsIMSE(false); } + public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); } + public void testGetWaitingThreadsIMSE(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + try { + lock.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * hasWaiters returns true when a thread is waiting, else false + */ + public void testHasWaiters() { testHasWaiters(false); } + public void testHasWaiters_fair() { testHasWaiters(true); } + public void testHasWaiters(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch pleaseSignal = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + pleaseSignal.countDown(); + c.await(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + lock.unlock(); + }}); + + await(pleaseSignal); + lock.lock(); + assertHasWaiters(lock, c, t); + assertTrue(lock.hasWaiters(c)); + c.signal(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + lock.unlock(); + awaitTermination(t); + assertHasNoWaiters(lock, c); + } + + /** + * getWaitQueueLength returns number of waiting threads + */ + public void testGetWaitQueueLength() { testGetWaitQueueLength(false); } + public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); } + public void testGetWaitQueueLength(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch locked1 = new CountDownLatch(1); + final CountDownLatch locked2 = new CountDownLatch(1); + Thread t1 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertFalse(lock.hasWaiters(c)); + assertEquals(0, lock.getWaitQueueLength(c)); + locked1.countDown(); + c.await(); + lock.unlock(); + }}); + + Thread t2 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertTrue(lock.hasWaiters(c)); + assertEquals(1, lock.getWaitQueueLength(c)); + locked2.countDown(); + c.await(); + lock.unlock(); + }}); + + lock.lock(); + assertEquals(0, lock.getWaitQueueLength(c)); + lock.unlock(); + + t1.start(); + await(locked1); + + lock.lock(); + assertHasWaiters(lock, c, t1); + assertEquals(1, lock.getWaitQueueLength(c)); + lock.unlock(); + + t2.start(); + await(locked2); + + lock.lock(); + assertHasWaiters(lock, c, t1, t2); + assertEquals(2, lock.getWaitQueueLength(c)); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.unlock(); + + awaitTermination(t1); + awaitTermination(t2); + + assertHasNoWaiters(lock, c); + } + + /** + * getWaitingThreads returns only and all waiting threads + */ + public void testGetWaitingThreads() { testGetWaitingThreads(false); } + public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); } + public void testGetWaitingThreads(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch locked1 = new CountDownLatch(1); + final CountDownLatch locked2 = new CountDownLatch(1); + Thread t1 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertTrue(lock.getWaitingThreads(c).isEmpty()); + locked1.countDown(); + c.await(); + lock.unlock(); + }}); + + Thread t2 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertFalse(lock.getWaitingThreads(c).isEmpty()); + locked2.countDown(); + c.await(); + lock.unlock(); + }}); + + lock.lock(); + assertTrue(lock.getWaitingThreads(c).isEmpty()); + lock.unlock(); + + t1.start(); + await(locked1); + + lock.lock(); + assertHasWaiters(lock, c, t1); + assertTrue(lock.getWaitingThreads(c).contains(t1)); + assertFalse(lock.getWaitingThreads(c).contains(t2)); + assertEquals(1, lock.getWaitingThreads(c).size()); + lock.unlock(); + + t2.start(); + await(locked2); + + lock.lock(); + assertHasWaiters(lock, c, t1, t2); + assertTrue(lock.getWaitingThreads(c).contains(t1)); + assertTrue(lock.getWaitingThreads(c).contains(t2)); + assertEquals(2, lock.getWaitingThreads(c).size()); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.unlock(); + + awaitTermination(t1); + awaitTermination(t2); + + assertHasNoWaiters(lock, c); + } + + /** + * awaitUninterruptibly is uninterruptible + */ + public void testAwaitUninterruptibly() { testAwaitUninterruptibly(false); } + public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); } + public void testAwaitUninterruptibly(boolean fair) { + final ReentrantLock lock = new ReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch pleaseInterrupt = new CountDownLatch(2); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt before awaitUninterruptibly + lock.lock(); + pleaseInterrupt.countDown(); + Thread.currentThread().interrupt(); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + lock.unlock(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt during awaitUninterruptibly + lock.lock(); + pleaseInterrupt.countDown(); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + lock.unlock(); + }}); + + await(pleaseInterrupt); + lock.lock(); + lock.unlock(); + t2.interrupt(); + + assertThreadStaysAlive(t1); + assertTrue(t2.isAlive()); + + lock.lock(); + c.signalAll(); + lock.unlock(); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * await/awaitNanos/awaitUntil is interruptible + */ + public void testInterruptible_await() { testInterruptible(false, AwaitMethod.await); } + public void testInterruptible_await_fair() { testInterruptible(true, AwaitMethod.await); } + public void testInterruptible_awaitTimed() { testInterruptible(false, AwaitMethod.awaitTimed); } + public void testInterruptible_awaitTimed_fair() { testInterruptible(true, AwaitMethod.awaitTimed); } + public void testInterruptible_awaitNanos() { testInterruptible(false, AwaitMethod.awaitNanos); } + public void testInterruptible_awaitNanos_fair() { testInterruptible(true, AwaitMethod.awaitNanos); } + public void testInterruptible_awaitUntil() { testInterruptible(false, AwaitMethod.awaitUntil); } + public void testInterruptible_awaitUntil_fair() { testInterruptible(true, AwaitMethod.awaitUntil); } + public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) { + final PublicReentrantLock lock = + new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertLockedByMoi(lock); + assertHasNoWaiters(lock, c); + pleaseInterrupt.countDown(); + try { + await(c, awaitMethod); + } finally { + assertLockedByMoi(lock); + assertHasNoWaiters(lock, c); + lock.unlock(); + assertFalse(Thread.interrupted()); + } + }}); + + await(pleaseInterrupt); + assertHasWaiters(lock, c, t); + t.interrupt(); + awaitTermination(t); + assertNotLocked(lock); + } + + /** + * signalAll wakes up all threads + */ + public void testSignalAll_await() { testSignalAll(false, AwaitMethod.await); } + public void testSignalAll_await_fair() { testSignalAll(true, AwaitMethod.await); } + public void testSignalAll_awaitTimed() { testSignalAll(false, AwaitMethod.awaitTimed); } + public void testSignalAll_awaitTimed_fair() { testSignalAll(true, AwaitMethod.awaitTimed); } + public void testSignalAll_awaitNanos() { testSignalAll(false, AwaitMethod.awaitNanos); } + public void testSignalAll_awaitNanos_fair() { testSignalAll(true, AwaitMethod.awaitNanos); } + public void testSignalAll_awaitUntil() { testSignalAll(false, AwaitMethod.awaitUntil); } + public void testSignalAll_awaitUntil_fair() { testSignalAll(true, AwaitMethod.awaitUntil); } + public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch pleaseSignal = new CountDownLatch(2); + class Awaiter extends CheckedRunnable { + public void realRun() throws InterruptedException { + lock.lock(); + pleaseSignal.countDown(); + await(c, awaitMethod); + lock.unlock(); + } + } + + Thread t1 = newStartedThread(new Awaiter()); + Thread t2 = newStartedThread(new Awaiter()); + + await(pleaseSignal); + lock.lock(); + assertHasWaiters(lock, c, t1, t2); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * signal wakes up waiting threads in FIFO order + */ + public void testSignalWakesFifo() { testSignalWakesFifo(false); } + public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); } + public void testSignalWakesFifo(boolean fair) { + final PublicReentrantLock lock = + new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch locked1 = new CountDownLatch(1); + final CountDownLatch locked2 = new CountDownLatch(1); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + locked1.countDown(); + c.await(); + lock.unlock(); + }}); + + await(locked1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + locked2.countDown(); + c.await(); + lock.unlock(); + }}); + + await(locked2); + + lock.lock(); + assertHasWaiters(lock, c, t1, t2); + assertFalse(lock.hasQueuedThreads()); + c.signal(); + assertHasWaiters(lock, c, t2); + assertTrue(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + c.signal(); + assertHasNoWaiters(lock, c); + assertTrue(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + lock.unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * await after multiple reentrant locking preserves lock count + */ + public void testAwaitLockCount() { testAwaitLockCount(false); } + public void testAwaitLockCount_fair() { testAwaitLockCount(true); } + public void testAwaitLockCount(boolean fair) { + final PublicReentrantLock lock = new PublicReentrantLock(fair); + final Condition c = lock.newCondition(); + final CountDownLatch pleaseSignal = new CountDownLatch(2); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + assertLockedByMoi(lock); + assertEquals(1, lock.getHoldCount()); + pleaseSignal.countDown(); + c.await(); + assertLockedByMoi(lock); + assertEquals(1, lock.getHoldCount()); + lock.unlock(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.lock(); + lock.lock(); + assertLockedByMoi(lock); + assertEquals(2, lock.getHoldCount()); + pleaseSignal.countDown(); + c.await(); + assertLockedByMoi(lock); + assertEquals(2, lock.getHoldCount()); + lock.unlock(); + lock.unlock(); + }}); + + await(pleaseSignal); + lock.lock(); + assertHasWaiters(lock, c, t1, t2); + assertEquals(1, lock.getHoldCount()); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A serialized lock deserializes as unlocked + */ + public void testSerialization() { testSerialization(false); } + public void testSerialization_fair() { testSerialization(true); } + public void testSerialization(boolean fair) { + ReentrantLock lock = new ReentrantLock(fair); + lock.lock(); + + ReentrantLock clone = serialClone(lock); + assertEquals(lock.isFair(), clone.isFair()); + assertTrue(lock.isLocked()); + assertFalse(clone.isLocked()); + assertEquals(1, lock.getHoldCount()); + assertEquals(0, clone.getHoldCount()); + clone.lock(); + clone.lock(); + assertTrue(clone.isLocked()); + assertEquals(2, clone.getHoldCount()); + assertEquals(1, lock.getHoldCount()); + clone.unlock(); + clone.unlock(); + assertTrue(lock.isLocked()); + assertFalse(clone.isLocked()); + } + + /** + * toString indicates current lock state + */ + public void testToString() { testToString(false); } + public void testToString_fair() { testToString(true); } + public void testToString(boolean fair) { + ReentrantLock lock = new ReentrantLock(fair); + assertTrue(lock.toString().contains("Unlocked")); + lock.lock(); + assertTrue(lock.toString().contains("Locked")); + lock.unlock(); + assertTrue(lock.toString().contains("Unlocked")); + } +} diff --git a/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java b/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java new file mode 100644 index 00000000000..a2e597e5eb7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java @@ -0,0 +1,1703 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ReentrantReadWriteLockTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ReentrantReadWriteLockTest.class); + } + + /** + * A runnable calling lockInterruptibly + */ + class InterruptibleLockRunnable extends CheckedRunnable { + final ReentrantReadWriteLock lock; + InterruptibleLockRunnable(ReentrantReadWriteLock l) { lock = l; } + public void realRun() throws InterruptedException { + lock.writeLock().lockInterruptibly(); + } + } + + /** + * A runnable calling lockInterruptibly that expects to be + * interrupted + */ + class InterruptedLockRunnable extends CheckedInterruptedRunnable { + final ReentrantReadWriteLock lock; + InterruptedLockRunnable(ReentrantReadWriteLock l) { lock = l; } + public void realRun() throws InterruptedException { + lock.writeLock().lockInterruptibly(); + } + } + + /** + * Subclass to expose protected methods + */ + static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock { + PublicReentrantReadWriteLock() { super(); } + PublicReentrantReadWriteLock(boolean fair) { super(fair); } + public Thread getOwner() { + return super.getOwner(); + } + public Collection getQueuedThreads() { + return super.getQueuedThreads(); + } + public Collection getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); + } + } + + /** + * Releases write lock, checking that it had a hold count of 1. + */ + void releaseWriteLock(PublicReentrantReadWriteLock lock) { + ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + assertWriteLockedByMoi(lock); + assertEquals(1, lock.getWriteHoldCount()); + writeLock.unlock(); + assertNotWriteLocked(lock); + } + + /** + * Spin-waits until lock.hasQueuedThread(t) becomes true. + */ + void waitForQueuedThread(PublicReentrantReadWriteLock lock, Thread t) { + long startTime = System.nanoTime(); + while (!lock.hasQueuedThread(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + assertTrue(t.isAlive()); + assertNotSame(t, lock.getOwner()); + } + + /** + * Checks that lock is not write-locked. + */ + void assertNotWriteLocked(PublicReentrantReadWriteLock lock) { + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isWriteLockedByCurrentThread()); + assertFalse(lock.writeLock().isHeldByCurrentThread()); + assertEquals(0, lock.getWriteHoldCount()); + assertEquals(0, lock.writeLock().getHoldCount()); + assertNull(lock.getOwner()); + } + + /** + * Checks that lock is write-locked by the given thread. + */ + void assertWriteLockedBy(PublicReentrantReadWriteLock lock, Thread t) { + assertTrue(lock.isWriteLocked()); + assertSame(t, lock.getOwner()); + assertEquals(t == Thread.currentThread(), + lock.isWriteLockedByCurrentThread()); + assertEquals(t == Thread.currentThread(), + lock.writeLock().isHeldByCurrentThread()); + assertEquals(t == Thread.currentThread(), + lock.getWriteHoldCount() > 0); + assertEquals(t == Thread.currentThread(), + lock.writeLock().getHoldCount() > 0); + assertEquals(0, lock.getReadLockCount()); + } + + /** + * Checks that lock is write-locked by the current thread. + */ + void assertWriteLockedByMoi(PublicReentrantReadWriteLock lock) { + assertWriteLockedBy(lock, Thread.currentThread()); + } + + /** + * Checks that condition c has no waiters. + */ + void assertHasNoWaiters(PublicReentrantReadWriteLock lock, Condition c) { + assertHasWaiters(lock, c, new Thread[] {}); + } + + /** + * Checks that condition c has exactly the given waiter threads. + */ + void assertHasWaiters(PublicReentrantReadWriteLock lock, Condition c, + Thread... threads) { + lock.writeLock().lock(); + assertEquals(threads.length > 0, lock.hasWaiters(c)); + assertEquals(threads.length, lock.getWaitQueueLength(c)); + assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty()); + assertEquals(threads.length, lock.getWaitingThreads(c).size()); + assertEquals(new HashSet(lock.getWaitingThreads(c)), + new HashSet(Arrays.asList(threads))); + lock.writeLock().unlock(); + } + + enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil } + + /** + * Awaits condition "indefinitely" using the specified AwaitMethod. + */ + void await(Condition c, AwaitMethod awaitMethod) + throws InterruptedException { + long timeoutMillis = 2 * LONG_DELAY_MS; + switch (awaitMethod) { + case await: + c.await(); + break; + case awaitTimed: + assertTrue(c.await(timeoutMillis, MILLISECONDS)); + break; + case awaitNanos: + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); + assertTrue(nanosRemaining > timeoutNanos / 2); + assertTrue(nanosRemaining <= timeoutNanos); + break; + case awaitUntil: + assertTrue(c.awaitUntil(delayedDate(timeoutMillis))); + break; + default: + throw new AssertionError(); + } + } + + /** + * Constructor sets given fairness, and is in unlocked state + */ + public void testConstructor() { + PublicReentrantReadWriteLock lock; + + lock = new PublicReentrantReadWriteLock(); + assertFalse(lock.isFair()); + assertNotWriteLocked(lock); + assertEquals(0, lock.getReadLockCount()); + + lock = new PublicReentrantReadWriteLock(true); + assertTrue(lock.isFair()); + assertNotWriteLocked(lock); + assertEquals(0, lock.getReadLockCount()); + + lock = new PublicReentrantReadWriteLock(false); + assertFalse(lock.isFair()); + assertNotWriteLocked(lock); + assertEquals(0, lock.getReadLockCount()); + } + + /** + * write-locking and read-locking an unlocked lock succeed + */ + public void testLock() { testLock(false); } + public void testLock_fair() { testLock(true); } + public void testLock(boolean fair) { + PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + assertNotWriteLocked(lock); + lock.writeLock().lock(); + assertWriteLockedByMoi(lock); + lock.writeLock().unlock(); + assertNotWriteLocked(lock); + assertEquals(0, lock.getReadLockCount()); + lock.readLock().lock(); + assertNotWriteLocked(lock); + assertEquals(1, lock.getReadLockCount()); + lock.readLock().unlock(); + assertNotWriteLocked(lock); + assertEquals(0, lock.getReadLockCount()); + } + + /** + * getWriteHoldCount returns number of recursive holds + */ + public void testGetWriteHoldCount() { testGetWriteHoldCount(false); } + public void testGetWriteHoldCount_fair() { testGetWriteHoldCount(true); } + public void testGetWriteHoldCount(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + for (int i = 1; i <= SIZE; i++) { + lock.writeLock().lock(); + assertEquals(i,lock.getWriteHoldCount()); + } + for (int i = SIZE; i > 0; i--) { + lock.writeLock().unlock(); + assertEquals(i - 1,lock.getWriteHoldCount()); + } + } + + /** + * writelock.getHoldCount returns number of recursive holds + */ + public void testGetHoldCount() { testGetHoldCount(false); } + public void testGetHoldCount_fair() { testGetHoldCount(true); } + public void testGetHoldCount(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + for (int i = 1; i <= SIZE; i++) { + lock.writeLock().lock(); + assertEquals(i,lock.writeLock().getHoldCount()); + } + for (int i = SIZE; i > 0; i--) { + lock.writeLock().unlock(); + assertEquals(i - 1,lock.writeLock().getHoldCount()); + } + } + + /** + * getReadHoldCount returns number of recursive holds + */ + public void testGetReadHoldCount() { testGetReadHoldCount(false); } + public void testGetReadHoldCount_fair() { testGetReadHoldCount(true); } + public void testGetReadHoldCount(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + for (int i = 1; i <= SIZE; i++) { + lock.readLock().lock(); + assertEquals(i,lock.getReadHoldCount()); + } + for (int i = SIZE; i > 0; i--) { + lock.readLock().unlock(); + assertEquals(i - 1,lock.getReadHoldCount()); + } + } + + /** + * write-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testWriteUnlock_IMSE() { testWriteUnlock_IMSE(false); } + public void testWriteUnlock_IMSE_fair() { testWriteUnlock_IMSE(true); } + public void testWriteUnlock_IMSE(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + try { + lock.writeLock().unlock(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * read-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testReadUnlock_IMSE() { testReadUnlock_IMSE(false); } + public void testReadUnlock_IMSE_fair() { testReadUnlock_IMSE(true); } + public void testReadUnlock_IMSE(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + try { + lock.readLock().unlock(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * write-lockInterruptibly is interruptible + */ + public void testWriteLockInterruptibly_Interruptible() { testWriteLockInterruptibly_Interruptible(false); } + public void testWriteLockInterruptibly_Interruptible_fair() { testWriteLockInterruptibly_Interruptible(true); } + public void testWriteLockInterruptibly_Interruptible(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lockInterruptibly(); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * timed write-tryLock is interruptible + */ + public void testWriteTryLock_Interruptible() { testWriteTryLock_Interruptible(false); } + public void testWriteTryLock_Interruptible_fair() { testWriteTryLock_Interruptible(true); } + public void testWriteTryLock_Interruptible(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * read-lockInterruptibly is interruptible + */ + public void testReadLockInterruptibly_Interruptible() { testReadLockInterruptibly_Interruptible(false); } + public void testReadLockInterruptibly_Interruptible_fair() { testReadLockInterruptibly_Interruptible(true); } + public void testReadLockInterruptibly_Interruptible(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.readLock().lockInterruptibly(); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * timed read-tryLock is interruptible + */ + public void testReadTryLock_Interruptible() { testReadTryLock_Interruptible(false); } + public void testReadTryLock_Interruptible_fair() { testReadTryLock_Interruptible(true); } + public void testReadTryLock_Interruptible(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.readLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * write-tryLock on an unlocked lock succeeds + */ + public void testWriteTryLock() { testWriteTryLock(false); } + public void testWriteTryLock_fair() { testWriteTryLock(true); } + public void testWriteTryLock(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + assertTrue(lock.writeLock().tryLock()); + assertWriteLockedByMoi(lock); + assertTrue(lock.writeLock().tryLock()); + assertWriteLockedByMoi(lock); + lock.writeLock().unlock(); + releaseWriteLock(lock); + } + + /** + * write-tryLock fails if locked + */ + public void testWriteTryLockWhenLocked() { testWriteTryLockWhenLocked(false); } + public void testWriteTryLockWhenLocked_fair() { testWriteTryLockWhenLocked(true); } + public void testWriteTryLockWhenLocked(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(lock.writeLock().tryLock()); + }}); + + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * read-tryLock fails if locked + */ + public void testReadTryLockWhenLocked() { testReadTryLockWhenLocked(false); } + public void testReadTryLockWhenLocked_fair() { testReadTryLockWhenLocked(true); } + public void testReadTryLockWhenLocked(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(lock.readLock().tryLock()); + }}); + + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * Multiple threads can hold a read lock when not write-locked + */ + public void testMultipleReadLocks() { testMultipleReadLocks(false); } + public void testMultipleReadLocks_fair() { testMultipleReadLocks(true); } + public void testMultipleReadLocks(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.readLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + assertTrue(lock.readLock().tryLock(LONG_DELAY_MS, MILLISECONDS)); + lock.readLock().unlock(); + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + + awaitTermination(t); + lock.readLock().unlock(); + } + + /** + * A writelock succeeds only after a reading thread unlocks + */ + public void testWriteAfterReadLock() { testWriteAfterReadLock(false); } + public void testWriteAfterReadLock_fair() { testWriteAfterReadLock(true); } + public void testWriteAfterReadLock(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.readLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(1, lock.getReadLockCount()); + lock.writeLock().lock(); + assertEquals(0, lock.getReadLockCount()); + lock.writeLock().unlock(); + }}); + waitForQueuedThread(lock, t); + assertNotWriteLocked(lock); + assertEquals(1, lock.getReadLockCount()); + lock.readLock().unlock(); + assertEquals(0, lock.getReadLockCount()); + awaitTermination(t); + assertNotWriteLocked(lock); + } + + /** + * A writelock succeeds only after reading threads unlock + */ + public void testWriteAfterMultipleReadLocks() { testWriteAfterMultipleReadLocks(false); } + public void testWriteAfterMultipleReadLocks_fair() { testWriteAfterMultipleReadLocks(true); } + public void testWriteAfterMultipleReadLocks(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.readLock().lock(); + lock.readLock().lock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + assertEquals(3, lock.getReadLockCount()); + lock.readLock().unlock(); + }}); + awaitTermination(t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(2, lock.getReadLockCount()); + lock.writeLock().lock(); + assertEquals(0, lock.getReadLockCount()); + lock.writeLock().unlock(); + }}); + waitForQueuedThread(lock, t2); + assertNotWriteLocked(lock); + assertEquals(2, lock.getReadLockCount()); + lock.readLock().unlock(); + lock.readLock().unlock(); + assertEquals(0, lock.getReadLockCount()); + awaitTermination(t2); + assertNotWriteLocked(lock); + } + + /** + * A thread that tries to acquire a fair read lock (non-reentrantly) + * will block if there is a waiting writer thread + */ + public void testReaderWriterReaderFairFifo() { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(true); + final AtomicBoolean t1GotLock = new AtomicBoolean(false); + + lock.readLock().lock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(1, lock.getReadLockCount()); + lock.writeLock().lock(); + assertEquals(0, lock.getReadLockCount()); + t1GotLock.set(true); + lock.writeLock().unlock(); + }}); + waitForQueuedThread(lock, t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertEquals(1, lock.getReadLockCount()); + lock.readLock().lock(); + assertEquals(1, lock.getReadLockCount()); + assertTrue(t1GotLock.get()); + lock.readLock().unlock(); + }}); + waitForQueuedThread(lock, t2); + assertTrue(t1.isAlive()); + assertNotWriteLocked(lock); + assertEquals(1, lock.getReadLockCount()); + lock.readLock().unlock(); + awaitTermination(t1); + awaitTermination(t2); + assertNotWriteLocked(lock); + } + + /** + * Readlocks succeed only after a writing thread unlocks + */ + public void testReadAfterWriteLock() { testReadAfterWriteLock(false); } + public void testReadAfterWriteLock_fair() { testReadAfterWriteLock(true); } + public void testReadAfterWriteLock(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + + waitForQueuedThread(lock, t1); + waitForQueuedThread(lock, t2); + releaseWriteLock(lock); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Read trylock succeeds if write locked by current thread + */ + public void testReadHoldingWriteLock() { testReadHoldingWriteLock(false); } + public void testReadHoldingWriteLock_fair() { testReadHoldingWriteLock(true); } + public void testReadHoldingWriteLock(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.writeLock().lock(); + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + lock.writeLock().unlock(); + } + + /** + * Read trylock succeeds (barging) even in the presence of waiting + * readers and/or writers + */ + public void testReadTryLockBarging() { testReadTryLockBarging(false); } + public void testReadTryLockBarging_fair() { testReadTryLockBarging(true); } + public void testReadTryLockBarging(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.readLock().lock(); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + }}); + + waitForQueuedThread(lock, t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + + if (fair) + waitForQueuedThread(lock, t2); + + Thread t3 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().tryLock(); + lock.readLock().unlock(); + }}); + + assertTrue(lock.getReadLockCount() > 0); + awaitTermination(t3); + assertTrue(t1.isAlive()); + if (fair) assertTrue(t2.isAlive()); + lock.readLock().unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for readlock + */ + public void testReadHoldingWriteLock2() { testReadHoldingWriteLock2(false); } + public void testReadHoldingWriteLock2_fair() { testReadHoldingWriteLock2(true); } + public void testReadHoldingWriteLock2(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + lock.readLock().lock(); + lock.readLock().unlock(); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.readLock().lock(); + lock.readLock().unlock(); + }}); + + waitForQueuedThread(lock, t1); + waitForQueuedThread(lock, t2); + assertWriteLockedByMoi(lock); + lock.readLock().lock(); + lock.readLock().unlock(); + releaseWriteLock(lock); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testReadHoldingWriteLock3() { testReadHoldingWriteLock3(false); } + public void testReadHoldingWriteLock3_fair() { testReadHoldingWriteLock3(true); } + public void testReadHoldingWriteLock3(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + lock.readLock().lock(); + lock.readLock().unlock(); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + }}); + + waitForQueuedThread(lock, t1); + waitForQueuedThread(lock, t2); + assertWriteLockedByMoi(lock); + lock.readLock().lock(); + lock.readLock().unlock(); + assertWriteLockedByMoi(lock); + lock.writeLock().unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Write lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testWriteHoldingWriteLock4() { testWriteHoldingWriteLock4(false); } + public void testWriteHoldingWriteLock4_fair() { testWriteHoldingWriteLock4(true); } + public void testWriteHoldingWriteLock4(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + lock.writeLock().lock(); + lock.writeLock().unlock(); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + }}); + + waitForQueuedThread(lock, t1); + waitForQueuedThread(lock, t2); + assertWriteLockedByMoi(lock); + assertEquals(1, lock.getWriteHoldCount()); + lock.writeLock().lock(); + assertWriteLockedByMoi(lock); + assertEquals(2, lock.getWriteHoldCount()); + lock.writeLock().unlock(); + assertWriteLockedByMoi(lock); + assertEquals(1, lock.getWriteHoldCount()); + lock.writeLock().unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * Read tryLock succeeds if readlocked but not writelocked + */ + public void testTryLockWhenReadLocked() { testTryLockWhenReadLocked(false); } + public void testTryLockWhenReadLocked_fair() { testTryLockWhenReadLocked(true); } + public void testTryLockWhenReadLocked(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.readLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + }}); + + awaitTermination(t); + lock.readLock().unlock(); + } + + /** + * write tryLock fails when readlocked + */ + public void testWriteTryLockWhenReadLocked() { testWriteTryLockWhenReadLocked(false); } + public void testWriteTryLockWhenReadLocked_fair() { testWriteTryLockWhenReadLocked(true); } + public void testWriteTryLockWhenReadLocked(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.readLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + assertFalse(lock.writeLock().tryLock()); + }}); + + awaitTermination(t); + lock.readLock().unlock(); + } + + /** + * write timed tryLock times out if locked + */ + public void testWriteTryLock_Timeout() { testWriteTryLock_Timeout(false); } + public void testWriteTryLock_Timeout_fair() { testWriteTryLock_Timeout(true); } + public void testWriteTryLock_Timeout(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long timeoutMillis = 10; + assertFalse(lock.writeLock().tryLock(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + }}); + + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * read timed tryLock times out if write-locked + */ + public void testReadTryLock_Timeout() { testReadTryLock_Timeout(false); } + public void testReadTryLock_Timeout_fair() { testReadTryLock_Timeout(true); } + public void testReadTryLock_Timeout(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.writeLock().lock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long timeoutMillis = 10; + assertFalse(lock.readLock().tryLock(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + }}); + + awaitTermination(t); + assertTrue(lock.writeLock().isHeldByCurrentThread()); + lock.writeLock().unlock(); + } + + /** + * write lockInterruptibly succeeds if unlocked, else is interruptible + */ + public void testWriteLockInterruptibly() { testWriteLockInterruptibly(false); } + public void testWriteLockInterruptibly_fair() { testWriteLockInterruptibly(true); } + public void testWriteLockInterruptibly(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + try { + lock.writeLock().lockInterruptibly(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lockInterruptibly(); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + assertTrue(lock.writeLock().isHeldByCurrentThread()); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * read lockInterruptibly succeeds if lock free else is interruptible + */ + public void testReadLockInterruptibly() { testReadLockInterruptibly(false); } + public void testReadLockInterruptibly_fair() { testReadLockInterruptibly(true); } + public void testReadLockInterruptibly(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + try { + lock.readLock().lockInterruptibly(); + lock.readLock().unlock(); + lock.writeLock().lockInterruptibly(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.readLock().lockInterruptibly(); + }}); + + waitForQueuedThread(lock, t); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock); + } + + /** + * Calling await without holding lock throws IllegalMonitorStateException + */ + public void testAwait_IMSE() { testAwait_IMSE(false); } + public void testAwait_IMSE_fair() { testAwait_IMSE(true); } + public void testAwait_IMSE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + for (AwaitMethod awaitMethod : AwaitMethod.values()) { + long startTime = System.nanoTime(); + try { + await(c, awaitMethod); + shouldThrow(); + } catch (IllegalMonitorStateException success) { + } catch (InterruptedException fail) { + threadUnexpectedException(fail); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * Calling signal without holding lock throws IllegalMonitorStateException + */ + public void testSignal_IMSE() { testSignal_IMSE(false); } + public void testSignal_IMSE_fair() { testSignal_IMSE(true); } + public void testSignal_IMSE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + try { + c.signal(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * Calling signalAll without holding lock throws IllegalMonitorStateException + */ + public void testSignalAll_IMSE() { testSignalAll_IMSE(false); } + public void testSignalAll_IMSE_fair() { testSignalAll_IMSE(true); } + public void testSignalAll_IMSE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + try { + c.signalAll(); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * awaitNanos without a signal times out + */ + public void testAwaitNanos_Timeout() { testAwaitNanos_Timeout(false); } + public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); } + public void testAwaitNanos_Timeout(boolean fair) { + try { + final ReentrantReadWriteLock lock = + new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + lock.writeLock().lock(); + long startTime = System.nanoTime(); + long timeoutMillis = 10; + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); + assertTrue(nanosRemaining <= 0); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.writeLock().unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * timed await without a signal times out + */ + public void testAwait_Timeout() { testAwait_Timeout(false); } + public void testAwait_Timeout_fair() { testAwait_Timeout(true); } + public void testAwait_Timeout(boolean fair) { + try { + final ReentrantReadWriteLock lock = + new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + lock.writeLock().lock(); + long startTime = System.nanoTime(); + long timeoutMillis = 10; + assertFalse(c.await(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.writeLock().unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * awaitUntil without a signal times out + */ + public void testAwaitUntil_Timeout() { testAwaitUntil_Timeout(false); } + public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); } + public void testAwaitUntil_Timeout(boolean fair) { + try { + final ReentrantReadWriteLock lock = + new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + lock.writeLock().lock(); + // We shouldn't assume that nanoTime and currentTimeMillis + // use the same time source, so don't use nanoTime here. + java.util.Date delayedDate = delayedDate(timeoutMillis()); + assertFalse(c.awaitUntil(delayedDate)); + assertTrue(new java.util.Date().getTime() >= delayedDate.getTime()); + lock.writeLock().unlock(); + } catch (InterruptedException fail) { threadUnexpectedException(fail); } + } + + /** + * await returns when signalled + */ + public void testAwait() { testAwait(false); } + public void testAwait_fair() { testAwait(true); } + public void testAwait(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + locked.countDown(); + c.await(); + lock.writeLock().unlock(); + }}); + + await(locked); + lock.writeLock().lock(); + assertHasWaiters(lock, c, t); + c.signal(); + assertHasNoWaiters(lock, c); + assertTrue(t.isAlive()); + lock.writeLock().unlock(); + awaitTermination(t); + } + + /** + * awaitUninterruptibly is uninterruptible + */ + public void testAwaitUninterruptibly() { testAwaitUninterruptibly(false); } + public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); } + public void testAwaitUninterruptibly(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch pleaseInterrupt = new CountDownLatch(2); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt before awaitUninterruptibly + lock.writeLock().lock(); + pleaseInterrupt.countDown(); + Thread.currentThread().interrupt(); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + lock.writeLock().unlock(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt during awaitUninterruptibly + lock.writeLock().lock(); + pleaseInterrupt.countDown(); + c.awaitUninterruptibly(); + assertTrue(Thread.interrupted()); + lock.writeLock().unlock(); + }}); + + await(pleaseInterrupt); + lock.writeLock().lock(); + lock.writeLock().unlock(); + t2.interrupt(); + + assertThreadStaysAlive(t1); + assertTrue(t2.isAlive()); + + lock.writeLock().lock(); + c.signalAll(); + lock.writeLock().unlock(); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * await/awaitNanos/awaitUntil is interruptible + */ + public void testInterruptible_await() { testInterruptible(false, AwaitMethod.await); } + public void testInterruptible_await_fair() { testInterruptible(true, AwaitMethod.await); } + public void testInterruptible_awaitTimed() { testInterruptible(false, AwaitMethod.awaitTimed); } + public void testInterruptible_awaitTimed_fair() { testInterruptible(true, AwaitMethod.awaitTimed); } + public void testInterruptible_awaitNanos() { testInterruptible(false, AwaitMethod.awaitNanos); } + public void testInterruptible_awaitNanos_fair() { testInterruptible(true, AwaitMethod.awaitNanos); } + public void testInterruptible_awaitUntil() { testInterruptible(false, AwaitMethod.awaitUntil); } + public void testInterruptible_awaitUntil_fair() { testInterruptible(true, AwaitMethod.awaitUntil); } + public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertWriteLockedByMoi(lock); + assertHasNoWaiters(lock, c); + locked.countDown(); + try { + await(c, awaitMethod); + } finally { + assertWriteLockedByMoi(lock); + assertHasNoWaiters(lock, c); + lock.writeLock().unlock(); + assertFalse(Thread.interrupted()); + } + }}); + + await(locked); + assertHasWaiters(lock, c, t); + t.interrupt(); + awaitTermination(t); + assertNotWriteLocked(lock); + } + + /** + * signalAll wakes up all threads + */ + public void testSignalAll_await() { testSignalAll(false, AwaitMethod.await); } + public void testSignalAll_await_fair() { testSignalAll(true, AwaitMethod.await); } + public void testSignalAll_awaitTimed() { testSignalAll(false, AwaitMethod.awaitTimed); } + public void testSignalAll_awaitTimed_fair() { testSignalAll(true, AwaitMethod.awaitTimed); } + public void testSignalAll_awaitNanos() { testSignalAll(false, AwaitMethod.awaitNanos); } + public void testSignalAll_awaitNanos_fair() { testSignalAll(true, AwaitMethod.awaitNanos); } + public void testSignalAll_awaitUntil() { testSignalAll(false, AwaitMethod.awaitUntil); } + public void testSignalAll_awaitUntil_fair() { testSignalAll(true, AwaitMethod.awaitUntil); } + public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(2); + final Lock writeLock = lock.writeLock(); + class Awaiter extends CheckedRunnable { + public void realRun() throws InterruptedException { + writeLock.lock(); + locked.countDown(); + await(c, awaitMethod); + writeLock.unlock(); + } + } + + Thread t1 = newStartedThread(new Awaiter()); + Thread t2 = newStartedThread(new Awaiter()); + + await(locked); + writeLock.lock(); + assertHasWaiters(lock, c, t1, t2); + c.signalAll(); + assertHasNoWaiters(lock, c); + writeLock.unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * signal wakes up waiting threads in FIFO order + */ + public void testSignalWakesFifo() { testSignalWakesFifo(false); } + public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); } + public void testSignalWakesFifo(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked1 = new CountDownLatch(1); + final CountDownLatch locked2 = new CountDownLatch(1); + final Lock writeLock = lock.writeLock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + writeLock.lock(); + locked1.countDown(); + c.await(); + writeLock.unlock(); + }}); + + await(locked1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + writeLock.lock(); + locked2.countDown(); + c.await(); + writeLock.unlock(); + }}); + + await(locked2); + + writeLock.lock(); + assertHasWaiters(lock, c, t1, t2); + assertFalse(lock.hasQueuedThreads()); + c.signal(); + assertHasWaiters(lock, c, t2); + assertTrue(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + c.signal(); + assertHasNoWaiters(lock, c); + assertTrue(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + writeLock.unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * await after multiple reentrant locking preserves lock count + */ + public void testAwaitLockCount() { testAwaitLockCount(false); } + public void testAwaitLockCount_fair() { testAwaitLockCount(true); } + public void testAwaitLockCount(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(2); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertWriteLockedByMoi(lock); + assertEquals(1, lock.writeLock().getHoldCount()); + locked.countDown(); + c.await(); + assertWriteLockedByMoi(lock); + assertEquals(1, lock.writeLock().getHoldCount()); + lock.writeLock().unlock(); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + lock.writeLock().lock(); + assertWriteLockedByMoi(lock); + assertEquals(2, lock.writeLock().getHoldCount()); + locked.countDown(); + c.await(); + assertWriteLockedByMoi(lock); + assertEquals(2, lock.writeLock().getHoldCount()); + lock.writeLock().unlock(); + lock.writeLock().unlock(); + }}); + + await(locked); + lock.writeLock().lock(); + assertHasWaiters(lock, c, t1, t2); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.writeLock().unlock(); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * A serialized lock deserializes as unlocked + */ + public void testSerialization() { testSerialization(false); } + public void testSerialization_fair() { testSerialization(true); } + public void testSerialization(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + lock.writeLock().lock(); + lock.readLock().lock(); + + ReentrantReadWriteLock clone = serialClone(lock); + assertEquals(lock.isFair(), clone.isFair()); + assertTrue(lock.isWriteLocked()); + assertFalse(clone.isWriteLocked()); + assertEquals(1, lock.getReadLockCount()); + assertEquals(0, clone.getReadLockCount()); + clone.writeLock().lock(); + clone.readLock().lock(); + assertTrue(clone.isWriteLocked()); + assertEquals(1, clone.getReadLockCount()); + clone.readLock().unlock(); + clone.writeLock().unlock(); + assertFalse(clone.isWriteLocked()); + assertEquals(1, lock.getReadLockCount()); + assertEquals(0, clone.getReadLockCount()); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { testHasQueuedThreads(false); } + public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); } + public void testHasQueuedThreads(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertFalse(lock.hasQueuedThreads()); + lock.writeLock().lock(); + assertFalse(lock.hasQueuedThreads()); + t1.start(); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThreads()); + t2.start(); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(lock.hasQueuedThreads()); + lock.writeLock().unlock(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThreads()); + } + + /** + * hasQueuedThread(null) throws NPE + */ + public void testHasQueuedThreadNPE() { testHasQueuedThreadNPE(false); } + public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); } + public void testHasQueuedThreadNPE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + try { + lock.hasQueuedThread(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasQueuedThread reports whether a thread is queued + */ + public void testHasQueuedThread() { testHasQueuedThread(false); } + public void testHasQueuedThread_fair() { testHasQueuedThread(true); } + public void testHasQueuedThread(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertFalse(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + lock.writeLock().lock(); + t1.start(); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + t2.start(); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.hasQueuedThread(t1)); + assertTrue(lock.hasQueuedThread(t2)); + lock.writeLock().unlock(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThread(t1)); + assertFalse(lock.hasQueuedThread(t2)); + } + + /** + * getQueueLength reports number of waiting threads + */ + public void testGetQueueLength() { testGetQueueLength(false); } + public void testGetQueueLength_fair() { testGetQueueLength(true); } + public void testGetQueueLength(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertEquals(0, lock.getQueueLength()); + lock.writeLock().lock(); + t1.start(); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueueLength()); + t2.start(); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueueLength()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(1, lock.getQueueLength()); + lock.writeLock().unlock(); + awaitTermination(t2); + assertEquals(0, lock.getQueueLength()); + } + + /** + * getQueuedThreads includes waiting threads + */ + public void testGetQueuedThreads() { testGetQueuedThreads(false); } + public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); } + public void testGetQueuedThreads(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + Thread t1 = new Thread(new InterruptedLockRunnable(lock)); + Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); + assertTrue(lock.getQueuedThreads().isEmpty()); + lock.writeLock().lock(); + assertTrue(lock.getQueuedThreads().isEmpty()); + t1.start(); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueuedThreads().size()); + assertTrue(lock.getQueuedThreads().contains(t1)); + t2.start(); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueuedThreads().size()); + assertTrue(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + assertEquals(1, lock.getQueuedThreads().size()); + lock.writeLock().unlock(); + awaitTermination(t2); + assertTrue(lock.getQueuedThreads().isEmpty()); + } + + /** + * hasWaiters throws NPE if null + */ + public void testHasWaitersNPE() { testHasWaitersNPE(false); } + public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); } + public void testHasWaitersNPE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + try { + lock.hasWaiters(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitQueueLength throws NPE if null + */ + public void testGetWaitQueueLengthNPE() { testGetWaitQueueLengthNPE(false); } + public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); } + public void testGetWaitQueueLengthNPE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + try { + lock.getWaitQueueLength(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * getWaitingThreads throws NPE if null + */ + public void testGetWaitingThreadsNPE() { testGetWaitingThreadsNPE(false); } + public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); } + public void testGetWaitingThreadsNPE(boolean fair) { + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(fair); + try { + lock.getWaitingThreads(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * hasWaiters throws IllegalArgumentException if not owned + */ + public void testHasWaitersIAE() { testHasWaitersIAE(false); } + public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); } + public void testHasWaitersIAE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair); + try { + lock2.hasWaiters(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * hasWaiters throws IllegalMonitorStateException if not locked + */ + public void testHasWaitersIMSE() { testHasWaitersIMSE(false); } + public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); } + public void testHasWaitersIMSE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + try { + lock.hasWaiters(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * getWaitQueueLength throws IllegalArgumentException if not owned + */ + public void testGetWaitQueueLengthIAE() { testGetWaitQueueLengthIAE(false); } + public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); } + public void testGetWaitQueueLengthIAE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair); + try { + lock2.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getWaitQueueLength throws IllegalMonitorStateException if not locked + */ + public void testGetWaitQueueLengthIMSE() { testGetWaitQueueLengthIMSE(false); } + public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); } + public void testGetWaitQueueLengthIMSE(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + try { + lock.getWaitQueueLength(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * getWaitingThreads throws IllegalArgumentException if not owned + */ + public void testGetWaitingThreadsIAE() { testGetWaitingThreadsIAE(false); } + public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); } + public void testGetWaitingThreadsIAE(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final PublicReentrantReadWriteLock lock2 = + new PublicReentrantReadWriteLock(fair); + try { + lock2.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * getWaitingThreads throws IllegalMonitorStateException if not locked + */ + public void testGetWaitingThreadsIMSE() { testGetWaitingThreadsIMSE(false); } + public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); } + public void testGetWaitingThreadsIMSE(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + try { + lock.getWaitingThreads(c); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * hasWaiters returns true when a thread is waiting, else false + */ + public void testHasWaiters() { testHasWaiters(false); } + public void testHasWaiters_fair() { testHasWaiters(true); } + public void testHasWaiters(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + locked.countDown(); + c.await(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + lock.writeLock().unlock(); + }}); + + await(locked); + lock.writeLock().lock(); + assertHasWaiters(lock, c, t); + assertTrue(lock.hasWaiters(c)); + c.signal(); + assertHasNoWaiters(lock, c); + assertFalse(lock.hasWaiters(c)); + lock.writeLock().unlock(); + awaitTermination(t); + assertHasNoWaiters(lock, c); + } + + /** + * getWaitQueueLength returns number of waiting threads + */ + public void testGetWaitQueueLength() { testGetWaitQueueLength(false); } + public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); } + public void testGetWaitQueueLength(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertEquals(0, lock.getWaitQueueLength(c)); + locked.countDown(); + c.await(); + lock.writeLock().unlock(); + }}); + + await(locked); + lock.writeLock().lock(); + assertHasWaiters(lock, c, t); + assertEquals(1, lock.getWaitQueueLength(c)); + c.signal(); + assertHasNoWaiters(lock, c); + assertEquals(0, lock.getWaitQueueLength(c)); + lock.writeLock().unlock(); + awaitTermination(t); + } + + /** + * getWaitingThreads returns only and all waiting threads + */ + public void testGetWaitingThreads() { testGetWaitingThreads(false); } + public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); } + public void testGetWaitingThreads(boolean fair) { + final PublicReentrantReadWriteLock lock = + new PublicReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final CountDownLatch locked1 = new CountDownLatch(1); + final CountDownLatch locked2 = new CountDownLatch(1); + Thread t1 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertTrue(lock.getWaitingThreads(c).isEmpty()); + locked1.countDown(); + c.await(); + lock.writeLock().unlock(); + }}); + + Thread t2 = new Thread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLock().lock(); + assertFalse(lock.getWaitingThreads(c).isEmpty()); + locked2.countDown(); + c.await(); + lock.writeLock().unlock(); + }}); + + lock.writeLock().lock(); + assertTrue(lock.getWaitingThreads(c).isEmpty()); + lock.writeLock().unlock(); + + t1.start(); + await(locked1); + t2.start(); + await(locked2); + + lock.writeLock().lock(); + assertTrue(lock.hasWaiters(c)); + assertTrue(lock.getWaitingThreads(c).contains(t1)); + assertTrue(lock.getWaitingThreads(c).contains(t2)); + assertEquals(2, lock.getWaitingThreads(c).size()); + c.signalAll(); + assertHasNoWaiters(lock, c); + lock.writeLock().unlock(); + + awaitTermination(t1); + awaitTermination(t2); + + assertHasNoWaiters(lock, c); + } + + /** + * toString indicates current lock state + */ + public void testToString() { testToString(false); } + public void testToString_fair() { testToString(true); } + public void testToString(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + assertTrue(lock.toString().contains("Write locks = 0")); + assertTrue(lock.toString().contains("Read locks = 0")); + lock.writeLock().lock(); + assertTrue(lock.toString().contains("Write locks = 1")); + assertTrue(lock.toString().contains("Read locks = 0")); + lock.writeLock().unlock(); + lock.readLock().lock(); + lock.readLock().lock(); + assertTrue(lock.toString().contains("Write locks = 0")); + assertTrue(lock.toString().contains("Read locks = 2")); + } + + /** + * readLock.toString indicates current lock state + */ + public void testReadLockToString() { testReadLockToString(false); } + public void testReadLockToString_fair() { testReadLockToString(true); } + public void testReadLockToString(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + assertTrue(lock.readLock().toString().contains("Read locks = 0")); + lock.readLock().lock(); + lock.readLock().lock(); + assertTrue(lock.readLock().toString().contains("Read locks = 2")); + } + + /** + * writeLock.toString indicates current lock state + */ + public void testWriteLockToString() { testWriteLockToString(false); } + public void testWriteLockToString_fair() { testWriteLockToString(true); } + public void testWriteLockToString(boolean fair) { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + assertTrue(lock.writeLock().toString().contains("Unlocked")); + lock.writeLock().lock(); + assertTrue(lock.writeLock().toString().contains("Locked")); + lock.writeLock().unlock(); + assertTrue(lock.writeLock().toString().contains("Unlocked")); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java b/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java new file mode 100644 index 00000000000..50b7b171140 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java @@ -0,0 +1,1286 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.RunnableScheduledFuture; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ScheduledExecutorSubclassTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ScheduledExecutorSubclassTest.class); + } + + static class CustomTask implements RunnableScheduledFuture { + RunnableScheduledFuture task; + volatile boolean ran; + CustomTask(RunnableScheduledFuture t) { task = t; } + public boolean isPeriodic() { return task.isPeriodic(); } + public void run() { + ran = true; + task.run(); + } + public long getDelay(TimeUnit unit) { return task.getDelay(unit); } + public int compareTo(Delayed t) { + return task.compareTo(((CustomTask)t).task); + } + public boolean cancel(boolean mayInterruptIfRunning) { + return task.cancel(mayInterruptIfRunning); + } + public boolean isCancelled() { return task.isCancelled(); } + public boolean isDone() { return task.isDone(); } + public V get() throws InterruptedException, ExecutionException { + V v = task.get(); + assertTrue(ran); + return v; + } + public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + V v = task.get(time, unit); + assertTrue(ran); + return v; + } + } + + public class CustomExecutor extends ScheduledThreadPoolExecutor { + + protected RunnableScheduledFuture decorateTask(Runnable r, RunnableScheduledFuture task) { + return new CustomTask(task); + } + + protected RunnableScheduledFuture decorateTask(Callable c, RunnableScheduledFuture task) { + return new CustomTask(task); + } + CustomExecutor(int corePoolSize) { super(corePoolSize); } + CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) { + super(corePoolSize, handler); + } + + CustomExecutor(int corePoolSize, ThreadFactory threadFactory) { + super(corePoolSize, threadFactory); + } + CustomExecutor(int corePoolSize, ThreadFactory threadFactory, + RejectedExecutionHandler handler) { + super(corePoolSize, threadFactory, handler); + } + + } + + /** + * execute successfully executes a runnable + */ + public void testExecute() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + p.execute(task); + await(done); + } + } + + /** + * delayed schedule of callable successfully executes after delay + */ + public void testSchedule1() throws Exception { + final CountDownLatch done = new CountDownLatch(1); + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final long startTime = System.nanoTime(); + Callable task = new CheckedCallable() { + public Boolean realCall() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + return Boolean.TRUE; + }}; + Future f = p.schedule(task, timeoutMillis(), MILLISECONDS); + assertSame(Boolean.TRUE, f.get()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * delayed schedule of runnable successfully executes after delay + */ + public void testSchedule3() throws Exception { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + Future f = p.schedule(task, timeoutMillis(), MILLISECONDS); + await(done); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * scheduleAtFixedRate executes runnable after given initial delay + */ + public void testSchedule4() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + ScheduledFuture f = + p.scheduleAtFixedRate(task, timeoutMillis(), + LONG_DELAY_MS, MILLISECONDS); + await(done); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + f.cancel(true); + } + } + + /** + * scheduleWithFixedDelay executes runnable after given initial delay + */ + public void testSchedule5() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + ScheduledFuture f = + p.scheduleWithFixedDelay(task, timeoutMillis(), + LONG_DELAY_MS, MILLISECONDS); + await(done); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + f.cancel(true); + } + } + + static class RunnableCounter implements Runnable { + AtomicInteger count = new AtomicInteger(0); + public void run() { count.getAndIncrement(); } + } + + /** + * scheduleAtFixedRate executes series of tasks at given rate + */ + public void testFixedRateSequence() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) { + long startTime = System.nanoTime(); + int cycles = 10; + final CountDownLatch done = new CountDownLatch(cycles); + Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + ScheduledFuture h = + p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS); + await(done); + h.cancel(true); + double normalizedTime = + (double) millisElapsedSince(startTime) / delay; + if (normalizedTime >= cycles - 1 && + normalizedTime <= cycles) + return; + } + fail("unexpected execution rate"); + } + } + + /** + * scheduleWithFixedDelay executes series of tasks with given period + */ + public void testFixedDelaySequence() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) { + long startTime = System.nanoTime(); + int cycles = 10; + final CountDownLatch done = new CountDownLatch(cycles); + Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + ScheduledFuture h = + p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS); + await(done); + h.cancel(true); + double normalizedTime = + (double) millisElapsedSince(startTime) / delay; + if (normalizedTime >= cycles - 1 && + normalizedTime <= cycles) + return; + } + fail("unexpected execution rate"); + } + } + + /** + * execute(null) throws NPE + */ + public void testExecuteNull() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * schedule(null) throws NPE + */ + public void testScheduleNull() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + TrackedCallable callable = null; + Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * execute throws RejectedExecutionException if shutdown + */ + public void testSchedule1_RejectedExecutionException() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpRunnable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * schedule throws RejectedExecutionException if shutdown + */ + public void testSchedule2_RejectedExecutionException() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpCallable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * schedule callable throws RejectedExecutionException if shutdown + */ + public void testSchedule3_RejectedExecutionException() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpCallable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * scheduleAtFixedRate throws RejectedExecutionException if shutdown + */ + public void testScheduleAtFixedRate1_RejectedExecutionException() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.scheduleAtFixedRate(new NoOpRunnable(), + MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * scheduleWithFixedDelay throws RejectedExecutionException if shutdown + */ + public void testScheduleWithFixedDelay1_RejectedExecutionException() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.scheduleWithFixedDelay(new NoOpRunnable(), + MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * getActiveCount increases but doesn't overestimate, when a + * thread becomes active + */ + public void testGetActiveCount() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getActiveCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getActiveCount()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getActiveCount()); + } + } + + /** + * getCompletedTaskCount increases, but doesn't overestimate, + * when tasks complete + */ + public void testGetCompletedTaskCount() throws InterruptedException { + final ThreadPoolExecutor p = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch threadProceed = new CountDownLatch(1); + final CountDownLatch threadDone = new CountDownLatch(1); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.await(); + threadDone.countDown(); + }}); + await(threadStarted); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.countDown(); + threadDone.await(); + long startTime = System.nanoTime(); + while (p.getCompletedTaskCount() != 1) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + } + + /** + * getCorePoolSize returns size given in constructor if not otherwise set + */ + public void testGetCorePoolSize() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getCorePoolSize()); + } + } + + /** + * getLargestPoolSize increases, but doesn't overestimate, when + * multiple threads active + */ + public void testGetLargestPoolSize() throws InterruptedException { + final int THREADS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(THREADS); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadsStarted = new CountDownLatch(THREADS); + assertEquals(0, p.getLargestPoolSize()); + for (int i = 0; i < THREADS; i++) + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.countDown(); + await(done); + assertEquals(THREADS, p.getLargestPoolSize()); + }}); + await(threadsStarted); + assertEquals(THREADS, p.getLargestPoolSize()); + } + assertEquals(THREADS, p.getLargestPoolSize()); + } + + /** + * getPoolSize increases, but doesn't overestimate, when threads + * become active + */ + public void testGetPoolSize() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getPoolSize()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getPoolSize()); + } + } + + /** + * getTaskCount increases, but doesn't overestimate, when tasks + * submitted + */ + public void testGetTaskCount() throws InterruptedException { + final int TASKS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + for (int i = 0; i < TASKS; i++) { + assertEquals(1 + i, p.getTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1 + TASKS, p.getTaskCount()); + await(done); + }}); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(1 + TASKS, p.getCompletedTaskCount()); + } + + /** + * getThreadFactory returns factory in constructor if not set + */ + public void testGetThreadFactory() { + final ThreadFactory threadFactory = new SimpleThreadFactory(); + final CustomExecutor p = new CustomExecutor(1, threadFactory); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory sets the thread factory returned by getThreadFactory + */ + public void testSetThreadFactory() { + final ThreadFactory threadFactory = new SimpleThreadFactory(); + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + p.setThreadFactory(threadFactory); + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory(null) throws NPE + */ + public void testSetThreadFactoryNull() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setThreadFactory(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * isShutdown is false before shutdown, true after + */ + public void testIsShutdown() { + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isShutdown()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.isShutdown()); + } + } + + /** + * isTerminated is false before termination, true after + */ + public void testIsTerminated() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminated()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminated()); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + } + + /** + * isTerminating is not true when running or when terminated + */ + public void testIsTerminating() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * getQueue returns the work queue, which contains queued tasks + */ + public void testGetQueue() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + ScheduledFuture[] tasks = new ScheduledFuture[5]; + for (int i = 0; i < tasks.length; i++) { + Runnable r = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + tasks[i] = p.schedule(r, 1, MILLISECONDS); + } + await(threadStarted); + BlockingQueue q = p.getQueue(); + assertTrue(q.contains(tasks[tasks.length - 1])); + assertFalse(q.contains(tasks[0])); + } + } + + /** + * remove(task) removes queued task, and fails to remove active task + */ + public void testRemove() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + ScheduledFuture[] tasks = new ScheduledFuture[5]; + final CountDownLatch threadStarted = new CountDownLatch(1); + for (int i = 0; i < tasks.length; i++) { + Runnable r = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + tasks[i] = p.schedule(r, 1, MILLISECONDS); + } + await(threadStarted); + BlockingQueue q = p.getQueue(); + assertFalse(p.remove((Runnable)tasks[0])); + assertTrue(q.contains((Runnable)tasks[4])); + assertTrue(q.contains((Runnable)tasks[3])); + assertTrue(p.remove((Runnable)tasks[4])); + assertFalse(p.remove((Runnable)tasks[4])); + assertFalse(q.contains((Runnable)tasks[4])); + assertTrue(q.contains((Runnable)tasks[3])); + assertTrue(p.remove((Runnable)tasks[3])); + assertFalse(q.contains((Runnable)tasks[3])); + } + } + + /** + * purge removes cancelled tasks from the queue + */ + public void testPurge() throws InterruptedException { + final ScheduledFuture[] tasks = new ScheduledFuture[5]; + final Runnable releaser = new Runnable() { public void run() { + for (ScheduledFuture task : tasks) + if (task != null) task.cancel(true); }}; + final CustomExecutor p = new CustomExecutor(1); + try (PoolCleaner cleaner = cleaner(p, releaser)) { + for (int i = 0; i < tasks.length; i++) + tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(), + LONG_DELAY_MS, MILLISECONDS); + int max = tasks.length; + if (tasks[4].cancel(true)) --max; + if (tasks[3].cancel(true)) --max; + // There must eventually be an interference-free point at + // which purge will not fail. (At worst, when queue is empty.) + long startTime = System.nanoTime(); + do { + p.purge(); + long count = p.getTaskCount(); + if (count == max) + return; + } while (millisElapsedSince(startTime) < LONG_DELAY_MS); + fail("Purge failed to remove cancelled tasks"); + } + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow() throws InterruptedException { + final int poolSize = 2; + final int count = 5; + final AtomicInteger ran = new AtomicInteger(0); + final CustomExecutor p = new CustomExecutor(poolSize); + final CountDownLatch threadsStarted = new CountDownLatch(poolSize); + Runnable waiter = new CheckedRunnable() { public void realRun() { + threadsStarted.countDown(); + try { + MILLISECONDS.sleep(2 * LONG_DELAY_MS); + } catch (InterruptedException success) {} + ran.getAndIncrement(); + }}; + for (int i = 0; i < count; i++) + p.execute(waiter); + await(threadsStarted); + assertEquals(poolSize, p.getActiveCount()); + assertEquals(0, p.getCompletedTaskCount()); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + assertEquals(count - poolSize, queuedTasks.size()); + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(poolSize, ran.get()); + assertEquals(poolSize, p.getCompletedTaskCount()); + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow_delayedTasks() throws InterruptedException { + final CustomExecutor p = new CustomExecutor(1); + List tasks = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Runnable r = new NoOpRunnable(); + tasks.add(p.schedule(r, 9, SECONDS)); + tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS)); + tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS)); + } + if (testImplementationDetails) + assertEquals(new HashSet(tasks), new HashSet(p.getQueue())); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + if (testImplementationDetails) + assertEquals(new HashSet(tasks), new HashSet(queuedTasks)); + assertEquals(tasks.size(), queuedTasks.size()); + for (ScheduledFuture task : tasks) { + assertFalse(((CustomTask)task).ran); + assertFalse(task.isDone()); + assertFalse(task.isCancelled()); + } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + + /** + * By default, periodic tasks are cancelled at shutdown. + * By default, delayed tasks keep running after shutdown. + * Check that changing the default values work: + * - setExecuteExistingDelayedTasksAfterShutdownPolicy + * - setContinueExistingPeriodicTasksAfterShutdownPolicy + */ + public void testShutdown_cancellation() throws Exception { + Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE }; + for (Boolean policy : allBooleans) + { + final int poolSize = 2; + final CustomExecutor p = new CustomExecutor(poolSize); + final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE); + final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE); + final boolean effectiveRemovePolicy = (policy == Boolean.TRUE); + if (policy != null) { + p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy); + p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy); + p.setRemoveOnCancelPolicy(policy); + } + assertEquals(effectiveDelayedPolicy, + p.getExecuteExistingDelayedTasksAfterShutdownPolicy()); + assertEquals(effectivePeriodicPolicy, + p.getContinueExistingPeriodicTasksAfterShutdownPolicy()); + assertEquals(effectiveRemovePolicy, + p.getRemoveOnCancelPolicy()); + // Strategy: Wedge the pool with poolSize "blocker" threads + final AtomicInteger ran = new AtomicInteger(0); + final CountDownLatch poolBlocked = new CountDownLatch(poolSize); + final CountDownLatch unblock = new CountDownLatch(1); + final CountDownLatch periodicLatch1 = new CountDownLatch(2); + final CountDownLatch periodicLatch2 = new CountDownLatch(2); + Runnable task = new CheckedRunnable() { public void realRun() + throws InterruptedException { + poolBlocked.countDown(); + assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS)); + ran.getAndIncrement(); + }}; + List> blockers = new ArrayList<>(); + List> periodics = new ArrayList<>(); + List> delayeds = new ArrayList<>(); + for (int i = 0; i < poolSize; i++) + blockers.add(p.submit(task)); + assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS)); + + periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1), + 1, 1, MILLISECONDS)); + periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2), + 1, 1, MILLISECONDS)); + delayeds.add(p.schedule(task, 1, MILLISECONDS)); + + assertTrue(p.getQueue().containsAll(periodics)); + assertTrue(p.getQueue().containsAll(delayeds)); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.isShutdown()); + assertFalse(p.isTerminated()); + for (Future periodic : periodics) { + assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled()); + assertTrue(effectivePeriodicPolicy ^ periodic.isDone()); + } + for (Future delayed : delayeds) { + assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled()); + assertTrue(effectiveDelayedPolicy ^ delayed.isDone()); + } + if (testImplementationDetails) { + assertEquals(effectivePeriodicPolicy, + p.getQueue().containsAll(periodics)); + assertEquals(effectiveDelayedPolicy, + p.getQueue().containsAll(delayeds)); + } + // Release all pool threads + unblock.countDown(); + + for (Future delayed : delayeds) { + if (effectiveDelayedPolicy) { + assertNull(delayed.get()); + } + } + if (effectivePeriodicPolicy) { + assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS)); + for (Future periodic : periodics) { + assertTrue(periodic.cancel(false)); + assertTrue(periodic.isCancelled()); + assertTrue(periodic.isDone()); + } + } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get()); + }} + + /** + * completed submit of callable returns result + */ + public void testSubmitCallable() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new StringTask()); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * completed submit of runnable returns successfully + */ + public void testSubmitRunnable() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable()); + future.get(); + assertTrue(future.isDone()); + } + } + + /** + * completed submit of (runnable, result) returns result + */ + public void testSubmitRunnable2() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAny(null) throws NPE + */ + public void testInvokeAny1() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IAE + */ + public void testInvokeAny2() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NPE if c has null elements + */ + public void testInvokeAny3() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * invokeAny(c) throws ExecutionException if no task completes + */ + public void testInvokeAny4() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task + */ + public void testInvokeAny5() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NPE + */ + public void testInvokeAll1() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NPE if c has null elements + */ + public void testInvokeAll3() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of invokeAll(c) throws exception on failed task + */ + public void testInvokeAll4() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks + */ + public void testInvokeAll5() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NPE + */ + public void testTimedInvokeAny1() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(,,null) throws NPE + */ + public void testTimedInvokeAnyNullTimeUnit() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IAE + */ + public void testTimedInvokeAny2() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NPE if c has null elements + */ + public void testTimedInvokeAny3() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task + */ + public void testTimedInvokeAny5() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NPE + */ + public void testTimedInvokeAll1() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(,,null) throws NPE + */ + public void testTimedInvokeAllNullTimeUnit() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>(), MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NPE if c has null elements + */ + public void testTimedInvokeAll3() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks + */ + public void testTimedInvokeAll5() throws Exception { + final ExecutorService e = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAll(c) cancels tasks not completed by timeout + */ + public void testTimedInvokeAll6() throws Exception { + for (long timeout = timeoutMillis();;) { + final CountDownLatch done = new CountDownLatch(1); + final Callable waiter = new CheckedCallable() { + public String realCall() { + try { done.await(LONG_DELAY_MS, MILLISECONDS); } + catch (InterruptedException ok) {} + return "1"; }}; + final ExecutorService p = new CustomExecutor(2); + try (PoolCleaner cleaner = cleaner(p, done)) { + List> tasks = new ArrayList<>(); + tasks.add(new StringTask("0")); + tasks.add(waiter); + tasks.add(new StringTask("2")); + long startTime = System.nanoTime(); + List> futures = + p.invokeAll(tasks, timeout, MILLISECONDS); + assertEquals(tasks.size(), futures.size()); + assertTrue(millisElapsedSince(startTime) >= timeout); + for (Future future : futures) + assertTrue(future.isDone()); + assertTrue(futures.get(1).isCancelled()); + try { + assertEquals("0", futures.get(0).get()); + assertEquals("2", futures.get(2).get()); + break; + } catch (CancellationException retryWithLongerTimeout) { + timeout *= 2; + if (timeout >= LONG_DELAY_MS / 2) + fail("expected exactly one task to be cancelled"); + } + } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java b/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java new file mode 100644 index 00000000000..797dee3a845 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java @@ -0,0 +1,1259 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ScheduledExecutorTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ScheduledExecutorTest.class); + } + + /** + * execute successfully executes a runnable + */ + public void testExecute() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + p.execute(task); + assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); + } + } + + /** + * delayed schedule of callable successfully executes after delay + */ + public void testSchedule1() throws Exception { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Callable task = new CheckedCallable() { + public Boolean realCall() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + return Boolean.TRUE; + }}; + Future f = p.schedule(task, timeoutMillis(), MILLISECONDS); + assertSame(Boolean.TRUE, f.get()); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + assertTrue(done.await(0L, MILLISECONDS)); + } + } + + /** + * delayed schedule of runnable successfully executes after delay + */ + public void testSchedule3() throws Exception { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + Future f = p.schedule(task, timeoutMillis(), MILLISECONDS); + await(done); + assertNull(f.get(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + } + + /** + * scheduleAtFixedRate executes runnable after given initial delay + */ + public void testSchedule4() throws Exception { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + ScheduledFuture f = + p.scheduleAtFixedRate(task, timeoutMillis(), + LONG_DELAY_MS, MILLISECONDS); + await(done); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + f.cancel(true); + } + } + + /** + * scheduleWithFixedDelay executes runnable after given initial delay + */ + public void testSchedule5() throws Exception { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final long startTime = System.nanoTime(); + final CountDownLatch done = new CountDownLatch(1); + Runnable task = new CheckedRunnable() { + public void realRun() { + done.countDown(); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }}; + ScheduledFuture f = + p.scheduleWithFixedDelay(task, timeoutMillis(), + LONG_DELAY_MS, MILLISECONDS); + await(done); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + f.cancel(true); + } + } + + static class RunnableCounter implements Runnable { + AtomicInteger count = new AtomicInteger(0); + public void run() { count.getAndIncrement(); } + } + + /** + * scheduleAtFixedRate executes series of tasks at given rate + */ + public void testFixedRateSequence() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) { + long startTime = System.nanoTime(); + int cycles = 10; + final CountDownLatch done = new CountDownLatch(cycles); + Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + ScheduledFuture h = + p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS); + await(done); + h.cancel(true); + double normalizedTime = + (double) millisElapsedSince(startTime) / delay; + if (normalizedTime >= cycles - 1 && + normalizedTime <= cycles) + return; + } + fail("unexpected execution rate"); + } + } + + /** + * scheduleWithFixedDelay executes series of tasks with given period + */ + public void testFixedDelaySequence() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) { + long startTime = System.nanoTime(); + int cycles = 10; + final CountDownLatch done = new CountDownLatch(cycles); + Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + ScheduledFuture h = + p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS); + await(done); + h.cancel(true); + double normalizedTime = + (double) millisElapsedSince(startTime) / delay; + if (normalizedTime >= cycles - 1 && + normalizedTime <= cycles) + return; + } + fail("unexpected execution rate"); + } + } + + /** + * execute(null) throws NPE + */ + public void testExecuteNull() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * schedule(null) throws NPE + */ + public void testScheduleNull() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + TrackedCallable callable = null; + Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * execute throws RejectedExecutionException if shutdown + */ + public void testSchedule1_RejectedExecutionException() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpRunnable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * schedule throws RejectedExecutionException if shutdown + */ + public void testSchedule2_RejectedExecutionException() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpCallable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * schedule callable throws RejectedExecutionException if shutdown + */ + public void testSchedule3_RejectedExecutionException() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.schedule(new NoOpCallable(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * scheduleAtFixedRate throws RejectedExecutionException if shutdown + */ + public void testScheduleAtFixedRate1_RejectedExecutionException() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.scheduleAtFixedRate(new NoOpRunnable(), + MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * scheduleWithFixedDelay throws RejectedExecutionException if shutdown + */ + public void testScheduleWithFixedDelay1_RejectedExecutionException() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.shutdown(); + p.scheduleWithFixedDelay(new NoOpRunnable(), + MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (RejectedExecutionException success) { + } catch (SecurityException ok) {} + } + } + + /** + * getActiveCount increases but doesn't overestimate, when a + * thread becomes active + */ + public void testGetActiveCount() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getActiveCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getActiveCount()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getActiveCount()); + } + } + + /** + * getCompletedTaskCount increases, but doesn't overestimate, + * when tasks complete + */ + public void testGetCompletedTaskCount() throws InterruptedException { + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch threadProceed = new CountDownLatch(1); + final CountDownLatch threadDone = new CountDownLatch(1); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.await(); + threadDone.countDown(); + }}); + await(threadStarted); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.countDown(); + threadDone.await(); + long startTime = System.nanoTime(); + while (p.getCompletedTaskCount() != 1) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + } + + /** + * getCorePoolSize returns size given in constructor if not otherwise set + */ + public void testGetCorePoolSize() throws InterruptedException { + ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getCorePoolSize()); + } + } + + /** + * getLargestPoolSize increases, but doesn't overestimate, when + * multiple threads active + */ + public void testGetLargestPoolSize() throws InterruptedException { + final int THREADS = 3; + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(THREADS); + final CountDownLatch threadsStarted = new CountDownLatch(THREADS); + final CountDownLatch done = new CountDownLatch(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getLargestPoolSize()); + for (int i = 0; i < THREADS; i++) + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.countDown(); + await(done); + assertEquals(THREADS, p.getLargestPoolSize()); + }}); + await(threadsStarted); + assertEquals(THREADS, p.getLargestPoolSize()); + } + assertEquals(THREADS, p.getLargestPoolSize()); + } + + /** + * getPoolSize increases, but doesn't overestimate, when threads + * become active + */ + public void testGetPoolSize() throws InterruptedException { + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getPoolSize()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getPoolSize()); + } + } + + /** + * getTaskCount increases, but doesn't overestimate, when tasks + * submitted + */ + public void testGetTaskCount() throws InterruptedException { + final int TASKS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + for (int i = 0; i < TASKS; i++) { + assertEquals(1 + i, p.getTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1 + TASKS, p.getTaskCount()); + await(done); + }}); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(1 + TASKS, p.getCompletedTaskCount()); + } + + /** + * getThreadFactory returns factory in constructor if not set + */ + public void testGetThreadFactory() throws InterruptedException { + final ThreadFactory threadFactory = new SimpleThreadFactory(); + final ScheduledThreadPoolExecutor p = + new ScheduledThreadPoolExecutor(1, threadFactory); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory sets the thread factory returned by getThreadFactory + */ + public void testSetThreadFactory() throws InterruptedException { + ThreadFactory threadFactory = new SimpleThreadFactory(); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + p.setThreadFactory(threadFactory); + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory(null) throws NPE + */ + public void testSetThreadFactoryNull() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setThreadFactory(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * isShutdown is false before shutdown, true after + */ + public void testIsShutdown() { + + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try { + assertFalse(p.isShutdown()); + } + finally { + try { p.shutdown(); } catch (SecurityException ok) { return; } + } + assertTrue(p.isShutdown()); + } + + /** + * isTerminated is false before termination, true after + */ + public void testIsTerminated() throws InterruptedException { + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + assertFalse(p.isTerminated()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminated()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + } + + /** + * isTerminating is not true when running or when terminated + */ + public void testIsTerminating() throws InterruptedException { + final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * getQueue returns the work queue, which contains queued tasks + */ + public void testGetQueue() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + ScheduledFuture[] tasks = new ScheduledFuture[5]; + for (int i = 0; i < tasks.length; i++) { + Runnable r = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + tasks[i] = p.schedule(r, 1, MILLISECONDS); + } + await(threadStarted); + BlockingQueue q = p.getQueue(); + assertTrue(q.contains(tasks[tasks.length - 1])); + assertFalse(q.contains(tasks[0])); + } + } + + /** + * remove(task) removes queued task, and fails to remove active task + */ + public void testRemove() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p, done)) { + ScheduledFuture[] tasks = new ScheduledFuture[5]; + final CountDownLatch threadStarted = new CountDownLatch(1); + for (int i = 0; i < tasks.length; i++) { + Runnable r = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + tasks[i] = p.schedule(r, 1, MILLISECONDS); + } + await(threadStarted); + BlockingQueue q = p.getQueue(); + assertFalse(p.remove((Runnable)tasks[0])); + assertTrue(q.contains((Runnable)tasks[4])); + assertTrue(q.contains((Runnable)tasks[3])); + assertTrue(p.remove((Runnable)tasks[4])); + assertFalse(p.remove((Runnable)tasks[4])); + assertFalse(q.contains((Runnable)tasks[4])); + assertTrue(q.contains((Runnable)tasks[3])); + assertTrue(p.remove((Runnable)tasks[3])); + assertFalse(q.contains((Runnable)tasks[3])); + } + } + + /** + * purge eventually removes cancelled tasks from the queue + */ + public void testPurge() throws InterruptedException { + final ScheduledFuture[] tasks = new ScheduledFuture[5]; + final Runnable releaser = new Runnable() { public void run() { + for (ScheduledFuture task : tasks) + if (task != null) task.cancel(true); }}; + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p, releaser)) { + for (int i = 0; i < tasks.length; i++) + tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(), + LONG_DELAY_MS, MILLISECONDS); + int max = tasks.length; + if (tasks[4].cancel(true)) --max; + if (tasks[3].cancel(true)) --max; + // There must eventually be an interference-free point at + // which purge will not fail. (At worst, when queue is empty.) + long startTime = System.nanoTime(); + do { + p.purge(); + long count = p.getTaskCount(); + if (count == max) + return; + } while (millisElapsedSince(startTime) < LONG_DELAY_MS); + fail("Purge failed to remove cancelled tasks"); + } + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow() throws InterruptedException { + final int poolSize = 2; + final int count = 5; + final AtomicInteger ran = new AtomicInteger(0); + final ScheduledThreadPoolExecutor p = + new ScheduledThreadPoolExecutor(poolSize); + final CountDownLatch threadsStarted = new CountDownLatch(poolSize); + Runnable waiter = new CheckedRunnable() { public void realRun() { + threadsStarted.countDown(); + try { + MILLISECONDS.sleep(2 * LONG_DELAY_MS); + } catch (InterruptedException success) {} + ran.getAndIncrement(); + }}; + for (int i = 0; i < count; i++) + p.execute(waiter); + await(threadsStarted); + assertEquals(poolSize, p.getActiveCount()); + assertEquals(0, p.getCompletedTaskCount()); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + assertEquals(count - poolSize, queuedTasks.size()); + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(poolSize, ran.get()); + assertEquals(poolSize, p.getCompletedTaskCount()); + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow_delayedTasks() throws InterruptedException { + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + List tasks = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + Runnable r = new NoOpRunnable(); + tasks.add(p.schedule(r, 9, SECONDS)); + tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS)); + tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS)); + } + if (testImplementationDetails) + assertEquals(new HashSet(tasks), new HashSet(p.getQueue())); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + if (testImplementationDetails) + assertEquals(new HashSet(tasks), new HashSet(queuedTasks)); + assertEquals(tasks.size(), queuedTasks.size()); + for (ScheduledFuture task : tasks) { + assertFalse(task.isDone()); + assertFalse(task.isCancelled()); + } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + + /** + * By default, periodic tasks are cancelled at shutdown. + * By default, delayed tasks keep running after shutdown. + * Check that changing the default values work: + * - setExecuteExistingDelayedTasksAfterShutdownPolicy + * - setContinueExistingPeriodicTasksAfterShutdownPolicy + */ + public void testShutdown_cancellation() throws Exception { + Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE }; + for (Boolean policy : allBooleans) + { + final int poolSize = 2; + final ScheduledThreadPoolExecutor p + = new ScheduledThreadPoolExecutor(poolSize); + final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE); + final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE); + final boolean effectiveRemovePolicy = (policy == Boolean.TRUE); + if (policy != null) { + p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy); + p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy); + p.setRemoveOnCancelPolicy(policy); + } + assertEquals(effectiveDelayedPolicy, + p.getExecuteExistingDelayedTasksAfterShutdownPolicy()); + assertEquals(effectivePeriodicPolicy, + p.getContinueExistingPeriodicTasksAfterShutdownPolicy()); + assertEquals(effectiveRemovePolicy, + p.getRemoveOnCancelPolicy()); + // Strategy: Wedge the pool with poolSize "blocker" threads + final AtomicInteger ran = new AtomicInteger(0); + final CountDownLatch poolBlocked = new CountDownLatch(poolSize); + final CountDownLatch unblock = new CountDownLatch(1); + final CountDownLatch periodicLatch1 = new CountDownLatch(2); + final CountDownLatch periodicLatch2 = new CountDownLatch(2); + Runnable task = new CheckedRunnable() { public void realRun() + throws InterruptedException { + poolBlocked.countDown(); + assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS)); + ran.getAndIncrement(); + }}; + List> blockers = new ArrayList<>(); + List> periodics = new ArrayList<>(); + List> delayeds = new ArrayList<>(); + for (int i = 0; i < poolSize; i++) + blockers.add(p.submit(task)); + assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS)); + + periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1), + 1, 1, MILLISECONDS)); + periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2), + 1, 1, MILLISECONDS)); + delayeds.add(p.schedule(task, 1, MILLISECONDS)); + + assertTrue(p.getQueue().containsAll(periodics)); + assertTrue(p.getQueue().containsAll(delayeds)); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.isShutdown()); + assertFalse(p.isTerminated()); + for (Future periodic : periodics) { + assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled()); + assertTrue(effectivePeriodicPolicy ^ periodic.isDone()); + } + for (Future delayed : delayeds) { + assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled()); + assertTrue(effectiveDelayedPolicy ^ delayed.isDone()); + } + if (testImplementationDetails) { + assertEquals(effectivePeriodicPolicy, + p.getQueue().containsAll(periodics)); + assertEquals(effectiveDelayedPolicy, + p.getQueue().containsAll(delayeds)); + } + // Release all pool threads + unblock.countDown(); + + for (Future delayed : delayeds) { + if (effectiveDelayedPolicy) { + assertNull(delayed.get()); + } + } + if (effectivePeriodicPolicy) { + assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS)); + for (Future periodic : periodics) { + assertTrue(periodic.cancel(false)); + assertTrue(periodic.isCancelled()); + assertTrue(periodic.isDone()); + } + } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get()); + }} + + /** + * completed submit of callable returns result + */ + public void testSubmitCallable() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new StringTask()); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * completed submit of runnable returns successfully + */ + public void testSubmitRunnable() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable()); + future.get(); + assertTrue(future.isDone()); + } + } + + /** + * completed submit of (runnable, result) returns result + */ + public void testSubmitRunnable2() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAny(null) throws NPE + */ + public void testInvokeAny1() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IAE + */ + public void testInvokeAny2() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NPE if c has null elements + */ + public void testInvokeAny3() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * invokeAny(c) throws ExecutionException if no task completes + */ + public void testInvokeAny4() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task + */ + public void testInvokeAny5() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NPE + */ + public void testInvokeAll1() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NPE if c has null elements + */ + public void testInvokeAll3() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of invokeAll(c) throws exception on failed task + */ + public void testInvokeAll4() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks + */ + public void testInvokeAll5() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NPE + */ + public void testTimedInvokeAny1() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(,,null) throws NPE + */ + public void testTimedInvokeAnyNullTimeUnit() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IAE + */ + public void testTimedInvokeAny2() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NPE if c has null elements + */ + public void testTimedInvokeAny3() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task + */ + public void testTimedInvokeAny5() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NPE + */ + public void testTimedInvokeAll1() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(,,null) throws NPE + */ + public void testTimedInvokeAllNullTimeUnit() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NPE if c has null elements + */ + public void testTimedInvokeAll3() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks + */ + public void testTimedInvokeAll5() throws Exception { + final ExecutorService e = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAll(c) cancels tasks not completed by timeout + */ + public void testTimedInvokeAll6() throws Exception { + for (long timeout = timeoutMillis();;) { + final CountDownLatch done = new CountDownLatch(1); + final Callable waiter = new CheckedCallable() { + public String realCall() { + try { done.await(LONG_DELAY_MS, MILLISECONDS); } + catch (InterruptedException ok) {} + return "1"; }}; + final ExecutorService p = new ScheduledThreadPoolExecutor(2); + try (PoolCleaner cleaner = cleaner(p, done)) { + List> tasks = new ArrayList<>(); + tasks.add(new StringTask("0")); + tasks.add(waiter); + tasks.add(new StringTask("2")); + long startTime = System.nanoTime(); + List> futures = + p.invokeAll(tasks, timeout, MILLISECONDS); + assertEquals(tasks.size(), futures.size()); + assertTrue(millisElapsedSince(startTime) >= timeout); + for (Future future : futures) + assertTrue(future.isDone()); + assertTrue(futures.get(1).isCancelled()); + try { + assertEquals("0", futures.get(0).get()); + assertEquals("2", futures.get(2).get()); + break; + } catch (CancellationException retryWithLongerTimeout) { + timeout *= 2; + if (timeout >= LONG_DELAY_MS / 2) + fail("expected exactly one task to be cancelled"); + } + } + } + } + + /** + * A fixed delay task with overflowing period should not prevent a + * one-shot task from executing. + * https://bugs.openjdk.java.net/browse/JDK-8051859 + */ + public void testScheduleWithFixedDelay_overflow() throws Exception { + final CountDownLatch delayedDone = new CountDownLatch(1); + final CountDownLatch immediateDone = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final Runnable immediate = new Runnable() { public void run() { + immediateDone.countDown(); + }}; + final Runnable delayed = new Runnable() { public void run() { + delayedDone.countDown(); + p.submit(immediate); + }}; + p.scheduleWithFixedDelay(delayed, 0L, Long.MAX_VALUE, SECONDS); + await(delayedDone); + await(immediateDone); + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/SemaphoreTest.java b/jdk/test/java/util/concurrent/tck/SemaphoreTest.java new file mode 100644 index 00000000000..0e85d7c73fc --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/SemaphoreTest.java @@ -0,0 +1,669 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class SemaphoreTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(SemaphoreTest.class); + } + + /** + * Subclass to expose protected methods + */ + static class PublicSemaphore extends Semaphore { + PublicSemaphore(int permits) { super(permits); } + PublicSemaphore(int permits, boolean fair) { super(permits, fair); } + public Collection getQueuedThreads() { + return super.getQueuedThreads(); + } + public boolean hasQueuedThread(Thread t) { + return super.getQueuedThreads().contains(t); + } + public void reducePermits(int reduction) { + super.reducePermits(reduction); + } + } + + /** + * A runnable calling acquire + */ + class InterruptibleLockRunnable extends CheckedRunnable { + final Semaphore lock; + InterruptibleLockRunnable(Semaphore s) { lock = s; } + public void realRun() { + try { + lock.acquire(); + } + catch (InterruptedException ignored) {} + } + } + + /** + * A runnable calling acquire that expects to be interrupted + */ + class InterruptedLockRunnable extends CheckedInterruptedRunnable { + final Semaphore lock; + InterruptedLockRunnable(Semaphore s) { lock = s; } + public void realRun() throws InterruptedException { + lock.acquire(); + } + } + + /** + * Spin-waits until s.hasQueuedThread(t) becomes true. + */ + void waitForQueuedThread(PublicSemaphore s, Thread t) { + long startTime = System.nanoTime(); + while (!s.hasQueuedThread(t)) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + assertTrue(s.hasQueuedThreads()); + assertTrue(t.isAlive()); + } + + /** + * Spin-waits until s.hasQueuedThreads() becomes true. + */ + void waitForQueuedThreads(Semaphore s) { + long startTime = System.nanoTime(); + while (!s.hasQueuedThreads()) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + throw new AssertionFailedError("timed out"); + Thread.yield(); + } + } + + enum AcquireMethod { + acquire() { + void acquire(Semaphore s) throws InterruptedException { + s.acquire(); + } + }, + acquireN() { + void acquire(Semaphore s, int permits) throws InterruptedException { + s.acquire(permits); + } + }, + acquireUninterruptibly() { + void acquire(Semaphore s) { + s.acquireUninterruptibly(); + } + }, + acquireUninterruptiblyN() { + void acquire(Semaphore s, int permits) { + s.acquireUninterruptibly(permits); + } + }, + tryAcquire() { + void acquire(Semaphore s) { + assertTrue(s.tryAcquire()); + } + }, + tryAcquireN() { + void acquire(Semaphore s, int permits) { + assertTrue(s.tryAcquire(permits)); + } + }, + tryAcquireTimed() { + void acquire(Semaphore s) throws InterruptedException { + assertTrue(s.tryAcquire(2 * LONG_DELAY_MS, MILLISECONDS)); + } + }, + tryAcquireTimedN { + void acquire(Semaphore s, int permits) throws InterruptedException { + assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS)); + } + }; + + // Intentionally meta-circular + + /** Acquires 1 permit. */ + void acquire(Semaphore s) throws InterruptedException { + acquire(s, 1); + } + /** Acquires the given number of permits. */ + void acquire(Semaphore s, int permits) throws InterruptedException { + for (int i = 0; i < permits; i++) + acquire(s); + } + } + + /** + * Zero, negative, and positive initial values are allowed in constructor + */ + public void testConstructor() { testConstructor(false); } + public void testConstructor_fair() { testConstructor(true); } + public void testConstructor(boolean fair) { + for (int permits : new int[] { -42, -1, 0, 1, 42 }) { + Semaphore s = new Semaphore(permits, fair); + assertEquals(permits, s.availablePermits()); + assertEquals(fair, s.isFair()); + } + } + + /** + * Constructor without fairness argument behaves as nonfair + */ + public void testConstructorDefaultsToNonFair() { + for (int permits : new int[] { -42, -1, 0, 1, 42 }) { + Semaphore s = new Semaphore(permits); + assertEquals(permits, s.availablePermits()); + assertFalse(s.isFair()); + } + } + + /** + * tryAcquire succeeds when sufficient permits, else fails + */ + public void testTryAcquireInSameThread() { testTryAcquireInSameThread(false); } + public void testTryAcquireInSameThread_fair() { testTryAcquireInSameThread(true); } + public void testTryAcquireInSameThread(boolean fair) { + Semaphore s = new Semaphore(2, fair); + assertEquals(2, s.availablePermits()); + assertTrue(s.tryAcquire()); + assertTrue(s.tryAcquire()); + assertEquals(0, s.availablePermits()); + assertFalse(s.tryAcquire()); + assertFalse(s.tryAcquire()); + assertEquals(0, s.availablePermits()); + } + + /** + * timed tryAcquire times out + */ + public void testTryAcquire_timeout() { testTryAcquire_timeout(false); } + public void testTryAcquire_timeout_fair() { testTryAcquire_timeout(true); } + public void testTryAcquire_timeout(boolean fair) { + Semaphore s = new Semaphore(0, fair); + long startTime = System.nanoTime(); + try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + + /** + * timed tryAcquire(N) times out + */ + public void testTryAcquireN_timeout() { testTryAcquireN_timeout(false); } + public void testTryAcquireN_timeout_fair() { testTryAcquireN_timeout(true); } + public void testTryAcquireN_timeout(boolean fair) { + Semaphore s = new Semaphore(2, fair); + long startTime = System.nanoTime(); + try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + + /** + * acquire(), acquire(N), timed tryAcquired, timed tryAcquire(N) + * are interruptible + */ + public void testInterruptible_acquire() { testInterruptible(false, AcquireMethod.acquire); } + public void testInterruptible_acquire_fair() { testInterruptible(true, AcquireMethod.acquire); } + public void testInterruptible_acquireN() { testInterruptible(false, AcquireMethod.acquireN); } + public void testInterruptible_acquireN_fair() { testInterruptible(true, AcquireMethod.acquireN); } + public void testInterruptible_tryAcquireTimed() { testInterruptible(false, AcquireMethod.tryAcquireTimed); } + public void testInterruptible_tryAcquireTimed_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimed); } + public void testInterruptible_tryAcquireTimedN() { testInterruptible(false, AcquireMethod.tryAcquireTimedN); } + public void testInterruptible_tryAcquireTimedN_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimedN); } + public void testInterruptible(boolean fair, final AcquireMethod acquirer) { + final PublicSemaphore s = new PublicSemaphore(0, fair); + final Semaphore pleaseInterrupt = new Semaphore(0, fair); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + // Interrupt before acquire + Thread.currentThread().interrupt(); + try { + acquirer.acquire(s); + shouldThrow(); + } catch (InterruptedException success) {} + + // Interrupt during acquire + try { + acquirer.acquire(s); + shouldThrow(); + } catch (InterruptedException success) {} + + // Interrupt before acquire(N) + Thread.currentThread().interrupt(); + try { + acquirer.acquire(s, 3); + shouldThrow(); + } catch (InterruptedException success) {} + + pleaseInterrupt.release(); + + // Interrupt during acquire(N) + try { + acquirer.acquire(s, 3); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + waitForQueuedThread(s, t); + t.interrupt(); + await(pleaseInterrupt); + waitForQueuedThread(s, t); + t.interrupt(); + awaitTermination(t); + } + + /** + * acquireUninterruptibly(), acquireUninterruptibly(N) are + * uninterruptible + */ + public void testUninterruptible_acquireUninterruptibly() { testUninterruptible(false, AcquireMethod.acquireUninterruptibly); } + public void testUninterruptible_acquireUninterruptibly_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptibly); } + public void testUninterruptible_acquireUninterruptiblyN() { testUninterruptible(false, AcquireMethod.acquireUninterruptiblyN); } + public void testUninterruptible_acquireUninterruptiblyN_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptiblyN); } + public void testUninterruptible(boolean fair, final AcquireMethod acquirer) { + final PublicSemaphore s = new PublicSemaphore(0, fair); + final Semaphore pleaseInterrupt = new Semaphore(-1, fair); + + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Interrupt before acquire + pleaseInterrupt.release(); + Thread.currentThread().interrupt(); + acquirer.acquire(s); + assertTrue(Thread.interrupted()); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Interrupt during acquire + pleaseInterrupt.release(); + acquirer.acquire(s); + assertTrue(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + waitForQueuedThread(s, t1); + waitForQueuedThread(s, t2); + t2.interrupt(); + + assertThreadStaysAlive(t1); + assertTrue(t2.isAlive()); + + s.release(2); + + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * hasQueuedThreads reports whether there are waiting threads + */ + public void testHasQueuedThreads() { testHasQueuedThreads(false); } + public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); } + public void testHasQueuedThreads(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertFalse(lock.hasQueuedThreads()); + lock.acquireUninterruptibly(); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertTrue(lock.hasQueuedThreads()); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertTrue(lock.hasQueuedThreads()); + t1.interrupt(); + awaitTermination(t1); + assertTrue(lock.hasQueuedThreads()); + lock.release(); + awaitTermination(t2); + assertFalse(lock.hasQueuedThreads()); + } + + /** + * getQueueLength reports number of waiting threads + */ + public void testGetQueueLength() { testGetQueueLength(false); } + public void testGetQueueLength_fair() { testGetQueueLength(true); } + public void testGetQueueLength(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertEquals(0, lock.getQueueLength()); + lock.acquireUninterruptibly(); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertEquals(1, lock.getQueueLength()); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertEquals(2, lock.getQueueLength()); + t1.interrupt(); + awaitTermination(t1); + assertEquals(1, lock.getQueueLength()); + lock.release(); + awaitTermination(t2); + assertEquals(0, lock.getQueueLength()); + } + + /** + * getQueuedThreads includes waiting threads + */ + public void testGetQueuedThreads() { testGetQueuedThreads(false); } + public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); } + public void testGetQueuedThreads(boolean fair) { + final PublicSemaphore lock = new PublicSemaphore(1, fair); + assertTrue(lock.getQueuedThreads().isEmpty()); + lock.acquireUninterruptibly(); + assertTrue(lock.getQueuedThreads().isEmpty()); + Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); + waitForQueuedThread(lock, t1); + assertTrue(lock.getQueuedThreads().contains(t1)); + Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); + waitForQueuedThread(lock, t2); + assertTrue(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + t1.interrupt(); + awaitTermination(t1); + assertFalse(lock.getQueuedThreads().contains(t1)); + assertTrue(lock.getQueuedThreads().contains(t2)); + lock.release(); + awaitTermination(t2); + assertTrue(lock.getQueuedThreads().isEmpty()); + } + + /** + * drainPermits reports and removes given number of permits + */ + public void testDrainPermits() { testDrainPermits(false); } + public void testDrainPermits_fair() { testDrainPermits(true); } + public void testDrainPermits(boolean fair) { + Semaphore s = new Semaphore(0, fair); + assertEquals(0, s.availablePermits()); + assertEquals(0, s.drainPermits()); + s.release(10); + assertEquals(10, s.availablePermits()); + assertEquals(10, s.drainPermits()); + assertEquals(0, s.availablePermits()); + assertEquals(0, s.drainPermits()); + } + + /** + * release(-N) throws IllegalArgumentException + */ + public void testReleaseIAE() { testReleaseIAE(false); } + public void testReleaseIAE_fair() { testReleaseIAE(true); } + public void testReleaseIAE(boolean fair) { + Semaphore s = new Semaphore(10, fair); + try { + s.release(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * reducePermits(-N) throws IllegalArgumentException + */ + public void testReducePermitsIAE() { testReducePermitsIAE(false); } + public void testReducePermitsIAE_fair() { testReducePermitsIAE(true); } + public void testReducePermitsIAE(boolean fair) { + PublicSemaphore s = new PublicSemaphore(10, fair); + try { + s.reducePermits(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * reducePermits reduces number of permits + */ + public void testReducePermits() { testReducePermits(false); } + public void testReducePermits_fair() { testReducePermits(true); } + public void testReducePermits(boolean fair) { + PublicSemaphore s = new PublicSemaphore(10, fair); + assertEquals(10, s.availablePermits()); + s.reducePermits(0); + assertEquals(10, s.availablePermits()); + s.reducePermits(1); + assertEquals(9, s.availablePermits()); + s.reducePermits(10); + assertEquals(-1, s.availablePermits()); + s.reducePermits(10); + assertEquals(-11, s.availablePermits()); + s.reducePermits(0); + assertEquals(-11, s.availablePermits()); + } + + /** + * a reserialized semaphore has same number of permits and + * fairness, but no queued threads + */ + public void testSerialization() { testSerialization(false); } + public void testSerialization_fair() { testSerialization(true); } + public void testSerialization(boolean fair) { + try { + Semaphore s = new Semaphore(3, fair); + s.acquire(); + s.acquire(); + s.release(); + + Semaphore clone = serialClone(s); + assertEquals(fair, s.isFair()); + assertEquals(fair, clone.isFair()); + assertEquals(2, s.availablePermits()); + assertEquals(2, clone.availablePermits()); + clone.acquire(); + clone.acquire(); + clone.release(); + assertEquals(2, s.availablePermits()); + assertEquals(1, clone.availablePermits()); + assertFalse(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); + } catch (InterruptedException e) { threadUnexpectedException(e); } + + { + PublicSemaphore s = new PublicSemaphore(0, fair); + Thread t = newStartedThread(new InterruptibleLockRunnable(s)); + // waitForQueuedThreads(s); // suffers from "flicker", so ... + waitForQueuedThread(s, t); // ... we use this instead + PublicSemaphore clone = serialClone(s); + assertEquals(fair, s.isFair()); + assertEquals(fair, clone.isFair()); + assertEquals(0, s.availablePermits()); + assertEquals(0, clone.availablePermits()); + assertTrue(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); + s.release(); + awaitTermination(t); + assertFalse(s.hasQueuedThreads()); + assertFalse(clone.hasQueuedThreads()); + } + } + + /** + * tryAcquire(n) succeeds when sufficient permits, else fails + */ + public void testTryAcquireNInSameThread() { testTryAcquireNInSameThread(false); } + public void testTryAcquireNInSameThread_fair() { testTryAcquireNInSameThread(true); } + public void testTryAcquireNInSameThread(boolean fair) { + Semaphore s = new Semaphore(2, fair); + assertEquals(2, s.availablePermits()); + assertFalse(s.tryAcquire(3)); + assertEquals(2, s.availablePermits()); + assertTrue(s.tryAcquire(2)); + assertEquals(0, s.availablePermits()); + assertFalse(s.tryAcquire(1)); + assertFalse(s.tryAcquire(2)); + assertEquals(0, s.availablePermits()); + } + + /** + * acquire succeeds if permits available + */ + public void testReleaseAcquireSameThread_acquire() { testReleaseAcquireSameThread(false, AcquireMethod.acquire); } + public void testReleaseAcquireSameThread_acquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquire); } + public void testReleaseAcquireSameThread_acquireN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireN); } + public void testReleaseAcquireSameThread_acquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireN); } + public void testReleaseAcquireSameThread_acquireUninterruptibly() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptibly_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptiblyN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_acquireUninterruptiblyN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireSameThread_tryAcquire() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquire); } + public void testReleaseAcquireSameThread_tryAcquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquire); } + public void testReleaseAcquireSameThread_tryAcquireN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireN); } + public void testReleaseAcquireSameThread_tryAcquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireN); } + public void testReleaseAcquireSameThread_tryAcquireTimed() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireSameThread_tryAcquireTimed_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireSameThread_tryAcquireTimedN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireSameThread_tryAcquireTimedN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireSameThread(boolean fair, + final AcquireMethod acquirer) { + Semaphore s = new Semaphore(1, fair); + for (int i = 1; i < 6; i++) { + s.release(i); + assertEquals(1 + i, s.availablePermits()); + try { + acquirer.acquire(s, i); + } catch (InterruptedException e) { threadUnexpectedException(e); } + assertEquals(1, s.availablePermits()); + } + } + + /** + * release in one thread enables acquire in another thread + */ + public void testReleaseAcquireDifferentThreads_acquire() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquire); } + public void testReleaseAcquireDifferentThreads_acquire_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquire); } + public void testReleaseAcquireDifferentThreads_acquireN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireN); } + public void testReleaseAcquireDifferentThreads_acquireN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireN); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptibly() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptibly_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimed() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimed_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimed); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimedN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireDifferentThreads_tryAcquireTimedN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimedN); } + public void testReleaseAcquireDifferentThreads(boolean fair, + final AcquireMethod acquirer) { + final Semaphore s = new Semaphore(0, fair); + final int rounds = 4; + long startTime = System.nanoTime(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + for (int i = 0; i < rounds; i++) { + assertFalse(s.hasQueuedThreads()); + if (i % 2 == 0) + acquirer.acquire(s); + else + acquirer.acquire(s, 3); + }}}); + + for (int i = 0; i < rounds; i++) { + while (! (s.availablePermits() == 0 && s.hasQueuedThreads())) + Thread.yield(); + assertTrue(t.isAlive()); + if (i % 2 == 0) + s.release(); + else + s.release(3); + } + awaitTermination(t); + assertEquals(0, s.availablePermits()); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + + /** + * fair locks are strictly FIFO + */ + public void testFairLocksFifo() { + final PublicSemaphore s = new PublicSemaphore(1, true); + final CountDownLatch pleaseRelease = new CountDownLatch(1); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Will block; permits are available, but not three + s.acquire(3); + }}); + + waitForQueuedThread(s, t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + // Will fail, even though 1 permit is available + assertFalse(s.tryAcquire(0L, MILLISECONDS)); + assertFalse(s.tryAcquire(1, 0L, MILLISECONDS)); + + // untimed tryAcquire will barge and succeed + assertTrue(s.tryAcquire()); + s.release(2); + assertTrue(s.tryAcquire(2)); + s.release(); + + pleaseRelease.countDown(); + // Will queue up behind t1, even though 1 permit is available + s.acquire(); + }}); + + await(pleaseRelease); + waitForQueuedThread(s, t2); + s.release(2); + awaitTermination(t1); + assertTrue(t2.isAlive()); + s.release(); + awaitTermination(t2); + } + + /** + * toString indicates current number of permits + */ + public void testToString() { testToString(false); } + public void testToString_fair() { testToString(true); } + public void testToString(boolean fair) { + PublicSemaphore s = new PublicSemaphore(0, fair); + assertTrue(s.toString().contains("Permits = 0")); + s.release(); + assertTrue(s.toString().contains("Permits = 1")); + s.release(2); + assertTrue(s.toString().contains("Permits = 3")); + s.reducePermits(5); + assertTrue(s.toString().contains("Permits = -2")); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java b/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java new file mode 100644 index 00000000000..74baf387653 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java @@ -0,0 +1,555 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.SplittableRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class SplittableRandomTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(SplittableRandomTest.class); + } + + /* + * Testing coverage notes: + * + * 1. Many of the test methods are adapted from ThreadLocalRandomTest. + * + * 2. These tests do not check for random number generator quality. + * But we check for minimal API compliance by requiring that + * repeated calls to nextX methods, up to NCALLS tries, produce at + * least two distinct results. (In some possible universe, a + * "correct" implementation might fail, but the odds are vastly + * less than that of encountering a hardware failure while running + * the test.) For bounded nextX methods, we sample various + * intervals across multiples of primes. In other tests, we repeat + * under REPS different values. + */ + + // max numbers of calls to detect getting stuck on one value + static final int NCALLS = 10000; + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 26); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 40); + + // Number of replications for other checks + static final int REPS = + Integer.getInteger("SplittableRandomTest.reps", 4); + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + SplittableRandom sr = new SplittableRandom(); + int f = sr.nextInt(); + int i = 0; + while (i < NCALLS && sr.nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + SplittableRandom sr = new SplittableRandom(); + long f = sr.nextLong(); + int i = 0; + while (i < NCALLS && sr.nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + SplittableRandom sr = new SplittableRandom(); + double f = sr.nextDouble(); + int i = 0; + while (i < NCALLS && sr.nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Two SplittableRandoms created with the same seed produce the + * same values for nextLong. + */ + public void testSeedConstructor() { + for (long seed = 2; seed < MAX_LONG_BOUND; seed += 15485863) { + SplittableRandom sr1 = new SplittableRandom(seed); + SplittableRandom sr2 = new SplittableRandom(seed); + for (int i = 0; i < REPS; ++i) + assertEquals(sr1.nextLong(), sr2.nextLong()); + } + } + + /** + * A SplittableRandom produced by split() of a default-constructed + * SplittableRandom generates a different sequence + */ + public void testSplit1() { + SplittableRandom sr = new SplittableRandom(); + for (int reps = 0; reps < REPS; ++reps) { + SplittableRandom sc = sr.split(); + int i = 0; + while (i < NCALLS && sr.nextLong() == sc.nextLong()) + ++i; + assertTrue(i < NCALLS); + } + } + + /** + * A SplittableRandom produced by split() of a seeded-constructed + * SplittableRandom generates a different sequence + */ + public void testSplit2() { + SplittableRandom sr = new SplittableRandom(12345); + for (int reps = 0; reps < REPS; ++reps) { + SplittableRandom sc = sr.split(); + int i = 0; + while (i < NCALLS && sr.nextLong() == sc.nextLong()) + ++i; + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(non-positive) throws IllegalArgumentException + */ + public void testNextIntBoundNonPositive() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextInt(-17), + () -> sr.nextInt(0), + () -> sr.nextInt(Integer.MIN_VALUE), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + public void testNextIntBadBounds() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextInt(17, 2), + () -> sr.nextInt(-42, -42), + () -> sr.nextInt(Integer.MAX_VALUE, Integer.MIN_VALUE), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * nextInt(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded() { + SplittableRandom sr = new SplittableRandom(); + for (int i = 0; i < 2; i++) assertEquals(0, sr.nextInt(1)); + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = sr.nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = sr.nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = sr.nextInt(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = sr.nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(non-positive) throws IllegalArgumentException + */ + public void testNextLongBoundNonPositive() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextLong(-17L), + () -> sr.nextLong(0L), + () -> sr.nextLong(Long.MIN_VALUE), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + public void testNextLongBadBounds() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextLong(17L, 2L), + () -> sr.nextLong(-42L, -42L), + () -> sr.nextLong(Long.MAX_VALUE, Long.MIN_VALUE), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * nextLong(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded() { + SplittableRandom sr = new SplittableRandom(); + for (int i = 0; i < 2; i++) assertEquals(0L, sr.nextLong(1L)); + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = sr.nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = sr.nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = sr.nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = sr.nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(non-positive) throws IllegalArgumentException + */ + public void testNextDoubleBoundNonPositive() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextDouble(-17.0d), + () -> sr.nextDouble(0.0d), + () -> sr.nextDouble(-Double.MIN_VALUE), + () -> sr.nextDouble(Double.NEGATIVE_INFINITY), + () -> sr.nextDouble(Double.NaN), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * nextDouble(! (least < bound)) throws IllegalArgumentException + */ + public void testNextDoubleBadBounds() { + SplittableRandom sr = new SplittableRandom(); + Runnable[] throwingActions = { + () -> sr.nextDouble(17.0d, 2.0d), + () -> sr.nextDouble(-42.0d, -42.0d), + () -> sr.nextDouble(Double.MAX_VALUE, Double.MIN_VALUE), + () -> sr.nextDouble(Double.NaN, 0.0d), + () -> sr.nextDouble(0.0d, Double.NaN), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + // TODO: Test infinite bounds! + //() -> sr.nextDouble(Double.NEGATIVE_INFINITY, 0.0d), + //() -> sr.nextDouble(0.0d, Double.POSITIVE_INFINITY), + + /** + * nextDouble(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextDoubleBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = sr.nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = sr.nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + SplittableRandom r = new SplittableRandom(); + Runnable[] throwingActions = { + () -> { java.util.stream.IntStream x = r.ints(-1L); }, + () -> { java.util.stream.IntStream x = r.ints(-1L, 2, 3); }, + () -> { java.util.stream.LongStream x = r.longs(-1L); }, + () -> { java.util.stream.LongStream x = r.longs(-1L, -1L, 1L); }, + () -> { java.util.stream.DoubleStream x = r.doubles(-1L); }, + () -> { java.util.stream.DoubleStream x = r.doubles(-1L, .5, .6); }, + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + SplittableRandom r = new SplittableRandom(); + Runnable[] throwingActions = { + () -> { java.util.stream.IntStream x = r.ints(2, 1); }, + () -> { java.util.stream.IntStream x = r.ints(10, 42, 42); }, + () -> { java.util.stream.LongStream x = r.longs(-1L, -1L); }, + () -> { java.util.stream.LongStream x = r.longs(10, 1L, -2L); }, + () -> { java.util.stream.DoubleStream x = r.doubles(0.0, 0.0); }, + () -> { java.util.stream.DoubleStream x = r.doubles(10, .5, .4); }, + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * A parallel sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * A parallel sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * A parallel sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * Each of a parallel sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 12345L; + for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) { + final int lo = least, hi = bound; + r.ints(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * Each of a parallel sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 123L; + for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + final long lo = least, hi = bound; + r.longs(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * Each of a parallel sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 456; + for (double least = 0.00011; least < 1.0e20; least *= 9) { + for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) { + final double lo = least, hi = bound; + r.doubles(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * A parallel unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.ints().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A parallel unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.longs().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A parallel unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.doubles().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.ints().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.longs().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.doubles().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/StampedLockTest.java b/jdk/test/java/util/concurrent/tck/StampedLockTest.java new file mode 100644 index 00000000000..84026409d7b --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java @@ -0,0 +1,906 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz + * with assistance from members of JCP JSR-166 Expert Group and + * released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.StampedLock; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class StampedLockTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(StampedLockTest.class); + } + + /** + * A runnable calling writeLockInterruptibly + */ + class InterruptibleLockRunnable extends CheckedRunnable { + final StampedLock lock; + InterruptibleLockRunnable(StampedLock l) { lock = l; } + public void realRun() throws InterruptedException { + lock.writeLockInterruptibly(); + } + } + + /** + * A runnable calling writeLockInterruptibly that expects to be + * interrupted + */ + class InterruptedLockRunnable extends CheckedInterruptedRunnable { + final StampedLock lock; + InterruptedLockRunnable(StampedLock l) { lock = l; } + public void realRun() throws InterruptedException { + lock.writeLockInterruptibly(); + } + } + + /** + * Releases write lock, checking isWriteLocked before and after + */ + void releaseWriteLock(StampedLock lock, long s) { + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + assertFalse(lock.isWriteLocked()); + } + + /** + * Constructed StampedLock is in unlocked state + */ + public void testConstructor() { + StampedLock lock; + lock = new StampedLock(); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + } + + /** + * write-locking and read-locking an unlocked lock succeed + */ + public void testLock() { + StampedLock lock = new StampedLock(); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long s = lock.writeLock(); + assertTrue(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + lock.unlockWrite(s); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long rs = lock.readLock(); + assertFalse(lock.isWriteLocked()); + assertTrue(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 1); + lock.unlockRead(rs); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + } + + /** + * unlock releases either a read or write lock + */ + public void testUnlock() { + StampedLock lock = new StampedLock(); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long s = lock.writeLock(); + assertTrue(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + lock.unlock(s); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long rs = lock.readLock(); + assertFalse(lock.isWriteLocked()); + assertTrue(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 1); + lock.unlock(rs); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + } + + /** + * tryUnlockRead/Write succeeds if locked in associated mode else + * returns false + */ + public void testTryUnlock() { + StampedLock lock = new StampedLock(); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long s = lock.writeLock(); + assertTrue(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + assertFalse(lock.tryUnlockRead()); + assertTrue(lock.tryUnlockWrite()); + assertFalse(lock.tryUnlockWrite()); + assertFalse(lock.tryUnlockRead()); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + long rs = lock.readLock(); + assertFalse(lock.isWriteLocked()); + assertTrue(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 1); + assertFalse(lock.tryUnlockWrite()); + assertTrue(lock.tryUnlockRead()); + assertFalse(lock.tryUnlockRead()); + assertFalse(lock.tryUnlockWrite()); + assertFalse(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); + assertEquals(lock.getReadLockCount(), 0); + } + + /** + * write-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testWriteUnlock_IMSE() { + StampedLock lock = new StampedLock(); + try { + lock.unlockWrite(0L); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * write-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testWriteUnlock_IMSE2() { + StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + lock.unlockWrite(s); + try { + lock.unlockWrite(s); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * write-unlocking after readlock throws IllegalMonitorStateException + */ + public void testWriteUnlock_IMSE3() { + StampedLock lock = new StampedLock(); + long s = lock.readLock(); + try { + lock.unlockWrite(s); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * read-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testReadUnlock_IMSE() { + StampedLock lock = new StampedLock(); + long s = lock.readLock(); + lock.unlockRead(s); + try { + lock.unlockRead(s); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * read-unlocking an unlocked lock throws IllegalMonitorStateException + */ + public void testReadUnlock_IMSE2() { + StampedLock lock = new StampedLock(); + try { + lock.unlockRead(0L); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * read-unlocking after writeLock throws IllegalMonitorStateException + */ + public void testReadUnlock_IMSE3() { + StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + try { + lock.unlockRead(s); + shouldThrow(); + } catch (IllegalMonitorStateException success) {} + } + + /** + * validate(0) fails + */ + public void testValidate0() { + StampedLock lock = new StampedLock(); + assertFalse(lock.validate(0L)); + } + + /** + * A stamp obtained from a successful lock operation validates + */ + public void testValidate() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + s = lock.readLock(); + assertTrue(lock.validate(s)); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock()) != 0L); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock()) != 0L); + assertTrue(lock.validate(s)); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + lock.unlockRead(s); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + } + + /** + * A stamp obtained from an unsuccessful lock operation does not validate + */ + public void testValidate2() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s; + assertTrue((s = lock.writeLock()) != 0L); + assertTrue(lock.validate(s)); + assertFalse(lock.validate(lock.tryWriteLock())); + assertFalse(lock.validate(lock.tryWriteLock(10L, MILLISECONDS))); + assertFalse(lock.validate(lock.tryReadLock())); + assertFalse(lock.validate(lock.tryReadLock(10L, MILLISECONDS))); + assertFalse(lock.validate(lock.tryOptimisticRead())); + lock.unlockWrite(s); + } + + /** + * writeLockInterruptibly is interruptible + */ + public void testWriteLockInterruptibly_Interruptible() + throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.writeLockInterruptibly(); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * timed tryWriteLock is interruptible + */ + public void testWriteTryLock_Interruptible() throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.tryWriteLock(2 * LONG_DELAY_MS, MILLISECONDS); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * readLockInterruptibly is interruptible + */ + public void testReadLockInterruptibly_Interruptible() + throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.readLockInterruptibly(); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * timed tryReadLock is interruptible + */ + public void testReadTryLock_Interruptible() throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.tryReadLock(2 * LONG_DELAY_MS, MILLISECONDS); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * tryWriteLock on an unlocked lock succeeds + */ + public void testWriteTryLock() { + final StampedLock lock = new StampedLock(); + long s = lock.tryWriteLock(); + assertTrue(s != 0L); + assertTrue(lock.isWriteLocked()); + long s2 = lock.tryWriteLock(); + assertEquals(s2, 0L); + releaseWriteLock(lock, s); + } + + /** + * tryWriteLock fails if locked + */ + public void testWriteTryLockWhenLocked() { + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + long ws = lock.tryWriteLock(); + assertTrue(ws == 0L); + }}); + + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * tryReadLock fails if write-locked + */ + public void testReadTryLockWhenLocked() { + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + long rs = lock.tryReadLock(); + assertEquals(rs, 0L); + }}); + + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * Multiple threads can hold a read lock when not write-locked + */ + public void testMultipleReadLocks() { + final StampedLock lock = new StampedLock(); + final long s = lock.readLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long s2 = lock.tryReadLock(); + assertTrue(s2 != 0L); + lock.unlockRead(s2); + long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS); + assertTrue(s3 != 0L); + lock.unlockRead(s3); + long s4 = lock.readLock(); + lock.unlockRead(s4); + }}); + + awaitTermination(t); + lock.unlockRead(s); + } + + /** + * A writelock succeeds only after a reading thread unlocks + */ + public void testWriteAfterReadLock() throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long rs = lock.readLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + running.countDown(); + long s = lock.writeLock(); + lock.unlockWrite(s); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + assertFalse(lock.isWriteLocked()); + lock.unlockRead(rs); + awaitTermination(t); + assertFalse(lock.isWriteLocked()); + } + + /** + * A writelock succeeds only after reading threads unlock + */ + public void testWriteAfterMultipleReadLocks() { + final StampedLock lock = new StampedLock(); + long s = lock.readLock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + long rs = lock.readLock(); + lock.unlockRead(rs); + }}); + + awaitTermination(t1); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + long ws = lock.writeLock(); + lock.unlockWrite(ws); + }}); + + assertFalse(lock.isWriteLocked()); + lock.unlockRead(s); + awaitTermination(t2); + assertFalse(lock.isWriteLocked()); + } + + /** + * Readlocks succeed only after a writing thread unlocks + */ + public void testReadAfterWriteLock() { + final StampedLock lock = new StampedLock(); + final long s = lock.writeLock(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() { + long rs = lock.readLock(); + lock.unlockRead(rs); + }}); + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() { + long rs = lock.readLock(); + lock.unlockRead(rs); + }}); + + releaseWriteLock(lock, s); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * tryReadLock succeeds if readlocked but not writelocked + */ + public void testTryLockWhenReadLocked() { + final StampedLock lock = new StampedLock(); + long s = lock.readLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + long rs = lock.tryReadLock(); + threadAssertTrue(rs != 0L); + lock.unlockRead(rs); + }}); + + awaitTermination(t); + lock.unlockRead(s); + } + + /** + * tryWriteLock fails when readlocked + */ + public void testWriteTryLockWhenReadLocked() { + final StampedLock lock = new StampedLock(); + long s = lock.readLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() { + long ws = lock.tryWriteLock(); + threadAssertEquals(ws, 0L); + }}); + + awaitTermination(t); + lock.unlockRead(s); + } + + /** + * timed tryWriteLock times out if locked + */ + public void testWriteTryLock_Timeout() { + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long timeoutMillis = 10; + long ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS); + assertEquals(ws, 0L); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + }}); + + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * timed tryReadLock times out if write-locked + */ + public void testReadTryLock_Timeout() { + final StampedLock lock = new StampedLock(); + long s = lock.writeLock(); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + long timeoutMillis = 10; + long rs = lock.tryReadLock(timeoutMillis, MILLISECONDS); + assertEquals(rs, 0L); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + }}); + + awaitTermination(t); + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + } + + /** + * writeLockInterruptibly succeeds if unlocked, else is interruptible + */ + public void testWriteLockInterruptibly() throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s = lock.writeLockInterruptibly(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.writeLockInterruptibly(); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + assertTrue(lock.isWriteLocked()); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * readLockInterruptibly succeeds if lock free else is interruptible + */ + public void testReadLockInterruptibly() throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s; + s = lock.readLockInterruptibly(); + lock.unlockRead(s); + s = lock.writeLockInterruptibly(); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + running.countDown(); + lock.readLockInterruptibly(); + }}); + + running.await(); + waitForThreadToEnterWaitState(t, 100); + t.interrupt(); + awaitTermination(t); + releaseWriteLock(lock, s); + } + + /** + * A serialized lock deserializes as unlocked + */ + public void testSerialization() { + StampedLock lock = new StampedLock(); + lock.writeLock(); + StampedLock clone = serialClone(lock); + assertTrue(lock.isWriteLocked()); + assertFalse(clone.isWriteLocked()); + long s = clone.writeLock(); + assertTrue(clone.isWriteLocked()); + clone.unlockWrite(s); + assertFalse(clone.isWriteLocked()); + } + + /** + * toString indicates current lock state + */ + public void testToString() { + StampedLock lock = new StampedLock(); + assertTrue(lock.toString().contains("Unlocked")); + long s = lock.writeLock(); + assertTrue(lock.toString().contains("Write-locked")); + lock.unlockWrite(s); + s = lock.readLock(); + assertTrue(lock.toString().contains("Read-locks")); + } + + /** + * tryOptimisticRead succeeds and validates if unlocked, fails if locked + */ + public void testValidateOptimistic() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s, p; + assertTrue((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.writeLock()) != 0L); + assertFalse((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + assertTrue((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.readLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock()) != 0L); + assertTrue(lock.validate(s)); + assertFalse((p = lock.tryOptimisticRead()) != 0L); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryOptimisticRead()) != 0L); + lock.unlockRead(s); + assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); + assertFalse((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryOptimisticRead()) != 0L); + lock.unlockRead(s); + assertTrue((p = lock.tryOptimisticRead()) != 0L); + } + + /** + * tryOptimisticRead stamp does not validate if a write lock intervenes + */ + public void testValidateOptimisticWriteLocked() { + StampedLock lock = new StampedLock(); + long s, p; + assertTrue((p = lock.tryOptimisticRead()) != 0L); + assertTrue((s = lock.writeLock()) != 0L); + assertFalse(lock.validate(p)); + assertFalse((p = lock.tryOptimisticRead()) != 0L); + assertTrue(lock.validate(s)); + lock.unlockWrite(s); + } + + /** + * tryOptimisticRead stamp does not validate if a write lock + * intervenes in another thread + */ + public void testValidateOptimisticWriteLocked2() + throws InterruptedException { + final CountDownLatch running = new CountDownLatch(1); + final StampedLock lock = new StampedLock(); + long s, p; + assertTrue((p = lock.tryOptimisticRead()) != 0L); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + lock.writeLockInterruptibly(); + running.countDown(); + lock.writeLockInterruptibly(); + }}); + + running.await(); + assertFalse(lock.validate(p)); + assertFalse((p = lock.tryOptimisticRead()) != 0L); + t.interrupt(); + awaitTermination(t); + } + + /** + * tryConvertToOptimisticRead succeeds and validates if successfully locked, + */ + public void testTryConvertToOptimisticRead() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s, p; + s = 0L; + assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue((s = lock.writeLock()) != 0L); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.readLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertTrue(lock.validate(p)); + } + + /** + * tryConvertToReadLock succeeds and validates if successfully locked + * or lock free; + */ + public void testTryConvertToReadLock() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s, p; + s = 0L; + assertFalse((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + lock.unlockRead(p); + assertTrue((s = lock.writeLock()) != 0L); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + assertTrue((s = lock.readLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + assertTrue((s = lock.tryWriteLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + assertTrue((s = lock.tryReadLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockRead(p); + } + + /** + * tryConvertToWriteLock succeeds and validates if successfully locked + * or lock free; + */ + public void testTryConvertToWriteLock() throws InterruptedException { + StampedLock lock = new StampedLock(); + long s, p; + s = 0L; + assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + lock.unlockWrite(p); + assertTrue((s = lock.writeLock()) != 0L); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + assertTrue((s = lock.readLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + assertTrue((s = lock.tryReadLock()) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); + assertTrue(lock.validate(s)); + assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.validate(p)); + lock.unlockWrite(p); + } + + /** + * asWriteLock can be locked and unlocked + */ + public void testAsWriteLock() { + StampedLock sl = new StampedLock(); + Lock lock = sl.asWriteLock(); + lock.lock(); + assertFalse(lock.tryLock()); + lock.unlock(); + assertTrue(lock.tryLock()); + } + + /** + * asReadLock can be locked and unlocked + */ + public void testAsReadLock() { + StampedLock sl = new StampedLock(); + Lock lock = sl.asReadLock(); + lock.lock(); + lock.unlock(); + assertTrue(lock.tryLock()); + } + + /** + * asReadWriteLock.writeLock can be locked and unlocked + */ + public void testAsReadWriteLockWriteLock() { + StampedLock sl = new StampedLock(); + Lock lock = sl.asReadWriteLock().writeLock(); + lock.lock(); + assertFalse(lock.tryLock()); + lock.unlock(); + assertTrue(lock.tryLock()); + } + + /** + * asReadWriteLock.readLock can be locked and unlocked + */ + public void testAsReadWriteLockReadLock() { + StampedLock sl = new StampedLock(); + Lock lock = sl.asReadWriteLock().readLock(); + lock.lock(); + lock.unlock(); + assertTrue(lock.tryLock()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java b/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java new file mode 100644 index 00000000000..ab4c9e90438 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java @@ -0,0 +1,1010 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.stream.Stream; +import junit.framework.Test; +import junit.framework.TestSuite; + +import static java.util.concurrent.Flow.Publisher; +import static java.util.concurrent.Flow.Subscriber; +import static java.util.concurrent.Flow.Subscription; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +public class SubmissionPublisherTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(SubmissionPublisherTest.class); + } + + final Executor basicExecutor = basicPublisher().getExecutor(); + + static SubmissionPublisher basicPublisher() { + return new SubmissionPublisher(); + } + + static class SPException extends RuntimeException {} + + class TestSubscriber implements Subscriber { + volatile Subscription sn; + int last; // Requires that onNexts are in numeric order + volatile int nexts; + volatile int errors; + volatile int completes; + volatile boolean throwOnCall = false; + volatile boolean request = true; + volatile Throwable lastError; + + public synchronized void onSubscribe(Subscription s) { + threadAssertTrue(sn == null); + sn = s; + notifyAll(); + if (throwOnCall) + throw new SPException(); + if (request) + sn.request(1L); + } + public synchronized void onNext(Integer t) { + ++nexts; + notifyAll(); + int current = t.intValue(); + threadAssertTrue(current >= last); + last = current; + if (request) + sn.request(1L); + if (throwOnCall) + throw new SPException(); + } + public synchronized void onError(Throwable t) { + threadAssertTrue(completes == 0); + threadAssertTrue(errors == 0); + lastError = t; + ++errors; + notifyAll(); + } + public synchronized void onComplete() { + threadAssertTrue(completes == 0); + ++completes; + notifyAll(); + } + + synchronized void awaitSubscribe() { + while (sn == null) { + try { + wait(); + } catch (Exception ex) { + threadUnexpectedException(ex); + break; + } + } + } + synchronized void awaitNext(int n) { + while (nexts < n) { + try { + wait(); + } catch (Exception ex) { + threadUnexpectedException(ex); + break; + } + } + } + synchronized void awaitComplete() { + while (completes == 0 && errors == 0) { + try { + wait(); + } catch (Exception ex) { + threadUnexpectedException(ex); + break; + } + } + } + synchronized void awaitError() { + while (errors == 0) { + try { + wait(); + } catch (Exception ex) { + threadUnexpectedException(ex); + break; + } + } + } + + } + + /** + * A new SubmissionPublisher has no subscribers, a non-null + * executor, a power-of-two capacity, is not closed, and reports + * zero demand and lag + */ + void checkInitialState(SubmissionPublisher p) { + assertFalse(p.hasSubscribers()); + assertEquals(0, p.getNumberOfSubscribers()); + assertTrue(p.getSubscribers().isEmpty()); + assertFalse(p.isClosed()); + assertNull(p.getClosedException()); + int n = p.getMaxBufferCapacity(); + assertTrue((n & (n - 1)) == 0); // power of two + assertNotNull(p.getExecutor()); + assertEquals(0, p.estimateMinimumDemand()); + assertEquals(0, p.estimateMaximumLag()); + } + + /** + * A default-constructed SubmissionPublisher has no subscribers, + * is not closed, has default buffer size, and uses the + * defaultExecutor + */ + public void testConstructor1() { + SubmissionPublisher p = new SubmissionPublisher(); + checkInitialState(p); + assertEquals(p.getMaxBufferCapacity(), Flow.defaultBufferSize()); + Executor e = p.getExecutor(), c = ForkJoinPool.commonPool(); + if (ForkJoinPool.getCommonPoolParallelism() > 1) + assertSame(e, c); + else + assertNotSame(e, c); + } + + /** + * A new SubmissionPublisher has no subscribers, is not closed, + * has the given buffer size, and uses the given executor + */ + public void testConstructor2() { + Executor e = Executors.newFixedThreadPool(1); + SubmissionPublisher p = new SubmissionPublisher(e, 8); + checkInitialState(p); + assertSame(p.getExecutor(), e); + assertEquals(8, p.getMaxBufferCapacity()); + } + + /** + * A null Executor argument to SubmissionPublisher constructor throws NPE + */ + public void testConstructor3() { + try { + new SubmissionPublisher(null, 8); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A negative capacity argument to SubmissionPublisher constructor + * throws IAE + */ + public void testConstructor4() { + Executor e = Executors.newFixedThreadPool(1); + try { + new SubmissionPublisher(e, -1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * A closed publisher reports isClosed with no closedException and + * throws ISE upon attempted submission; a subsequent close or + * closeExceptionally has no additional effect. + */ + public void testClose() { + SubmissionPublisher p = basicPublisher(); + checkInitialState(p); + p.close(); + assertTrue(p.isClosed()); + assertNull(p.getClosedException()); + try { + p.submit(1); + shouldThrow(); + } catch (IllegalStateException success) {} + Throwable ex = new SPException(); + p.closeExceptionally(ex); + assertTrue(p.isClosed()); + assertNull(p.getClosedException()); + } + + /** + * A publisher closedExceptionally reports isClosed with the + * closedException and throws ISE upon attempted submission; a + * subsequent close or closeExceptionally has no additional + * effect. + */ + public void testCloseExceptionally() { + SubmissionPublisher p = basicPublisher(); + checkInitialState(p); + Throwable ex = new SPException(); + p.closeExceptionally(ex); + assertTrue(p.isClosed()); + assertSame(p.getClosedException(), ex); + try { + p.submit(1); + shouldThrow(); + } catch (IllegalStateException success) {} + p.close(); + assertTrue(p.isClosed()); + assertSame(p.getClosedException(), ex); + } + + /** + * Upon subscription, the subscriber's onSubscribe is called, no + * other Subscriber methods are invoked, the publisher + * hasSubscribers, isSubscribed is true, and existing + * subscriptions are unaffected. + */ + public void testSubscribe1() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + p.subscribe(s); + assertTrue(p.hasSubscribers()); + assertEquals(1, p.getNumberOfSubscribers()); + assertTrue(p.getSubscribers().contains(s)); + assertTrue(p.isSubscribed(s)); + s.awaitSubscribe(); + assertNotNull(s.sn); + assertEquals(0, s.nexts); + assertEquals(0, s.errors); + assertEquals(0, s.completes); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s2); + assertTrue(p.hasSubscribers()); + assertEquals(2, p.getNumberOfSubscribers()); + assertTrue(p.getSubscribers().contains(s)); + assertTrue(p.getSubscribers().contains(s2)); + assertTrue(p.isSubscribed(s)); + assertTrue(p.isSubscribed(s2)); + s2.awaitSubscribe(); + assertNotNull(s2.sn); + assertEquals(0, s2.nexts); + assertEquals(0, s2.errors); + assertEquals(0, s2.completes); + p.close(); + } + + /** + * If closed, upon subscription, the subscriber's onComplete + * method is invoked + */ + public void testSubscribe2() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + p.close(); + p.subscribe(s); + s.awaitComplete(); + assertEquals(0, s.nexts); + assertEquals(0, s.errors); + assertEquals(1, s.completes, 1); + } + + /** + * If closedExceptionally, upon subscription, the subscriber's + * onError method is invoked + */ + public void testSubscribe3() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + Throwable ex = new SPException(); + p.closeExceptionally(ex); + assertTrue(p.isClosed()); + assertSame(p.getClosedException(), ex); + p.subscribe(s); + s.awaitError(); + assertEquals(0, s.nexts); + assertEquals(1, s.errors); + } + + /** + * Upon attempted resubscription, the subscriber's onError is + * called and the subscription is cancelled. + */ + public void testSubscribe4() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + p.subscribe(s); + assertTrue(p.hasSubscribers()); + assertEquals(1, p.getNumberOfSubscribers()); + assertTrue(p.getSubscribers().contains(s)); + assertTrue(p.isSubscribed(s)); + s.awaitSubscribe(); + assertNotNull(s.sn); + assertEquals(0, s.nexts); + assertEquals(0, s.errors); + assertEquals(0, s.completes); + p.subscribe(s); + s.awaitError(); + assertEquals(0, s.nexts); + assertEquals(1, s.errors); + assertFalse(p.isSubscribed(s)); + } + + /** + * An exception thrown in onSubscribe causes onError + */ + public void testSubscribe5() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + s.throwOnCall = true; + try { + p.subscribe(s); + } catch (Exception ok) {} + s.awaitError(); + assertEquals(0, s.nexts); + assertEquals(1, s.errors); + assertEquals(0, s.completes); + } + + /** + * subscribe(null) throws NPE + */ + public void testSubscribe6() { + SubmissionPublisher p = basicPublisher(); + try { + p.subscribe(null); + shouldThrow(); + } catch (NullPointerException success) {} + checkInitialState(p); + } + + /** + * Closing a publisher causes onComplete to subscribers + */ + public void testCloseCompletes() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + p.submit(1); + p.close(); + assertTrue(p.isClosed()); + assertNull(p.getClosedException()); + s1.awaitComplete(); + assertEquals(1, s1.nexts); + assertEquals(1, s1.completes); + s2.awaitComplete(); + assertEquals(1, s2.nexts); + assertEquals(1, s2.completes); + } + + /** + * Closing a publisher exceptionally causes onError to subscribers + */ + public void testCloseExceptionallyError() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + p.submit(1); + p.closeExceptionally(new SPException()); + assertTrue(p.isClosed()); + s1.awaitError(); + assertTrue(s1.nexts <= 1); + assertEquals(1, s1.errors); + s2.awaitError(); + assertTrue(s2.nexts <= 1); + assertEquals(1, s2.errors); + } + + /** + * Cancelling a subscription eventually causes no more onNexts to be issued + */ + public void testCancel() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + s1.awaitSubscribe(); + p.submit(1); + s1.sn.cancel(); + for (int i = 2; i <= 20; ++i) + p.submit(i); + p.close(); + s2.awaitComplete(); + assertEquals(20, s2.nexts); + assertEquals(1, s2.completes); + assertTrue(s1.nexts < 20); + assertFalse(p.isSubscribed(s1)); + } + + /** + * Throwing an exception in onNext causes onError + */ + public void testThrowOnNext() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + s1.awaitSubscribe(); + p.submit(1); + s1.throwOnCall = true; + p.submit(2); + p.close(); + s2.awaitComplete(); + assertEquals(2, s2.nexts); + s1.awaitComplete(); + assertEquals(1, s1.errors); + } + + /** + * If a handler is supplied in constructor, it is invoked when + * subscriber throws an exception in onNext + */ + public void testThrowOnNextHandler() { + AtomicInteger calls = new AtomicInteger(); + SubmissionPublisher p = new SubmissionPublisher + (basicExecutor, 8, + (s, e) -> calls.getAndIncrement()); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + s1.awaitSubscribe(); + p.submit(1); + s1.throwOnCall = true; + p.submit(2); + p.close(); + s2.awaitComplete(); + assertEquals(2, s2.nexts); + assertEquals(1, s2.completes); + s1.awaitError(); + assertEquals(1, s1.errors); + assertEquals(1, calls.get()); + } + + /** + * onNext items are issued in the same order to each subscriber + */ + public void testOrder() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + for (int i = 1; i <= 20; ++i) + p.submit(i); + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertEquals(20, s2.nexts); + assertEquals(1, s2.completes); + assertEquals(20, s1.nexts); + assertEquals(1, s1.completes); + } + + /** + * onNext is issued only if requested + */ + public void testRequest1() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + p.subscribe(s1); + s1.awaitSubscribe(); + assertTrue(p.estimateMinimumDemand() == 0); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s2); + p.submit(1); + p.submit(2); + s2.awaitNext(1); + assertEquals(0, s1.nexts); + s1.sn.request(3); + p.submit(3); + p.close(); + s2.awaitComplete(); + assertEquals(3, s2.nexts); + assertEquals(1, s2.completes); + s1.awaitComplete(); + assertTrue(s1.nexts > 0); + assertEquals(1, s1.completes); + } + + /** + * onNext is not issued when requests become zero + */ + public void testRequest2() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + s1.request = false; + p.submit(1); + p.submit(2); + p.close(); + s2.awaitComplete(); + assertEquals(2, s2.nexts); + assertEquals(1, s2.completes); + s1.awaitNext(1); + assertEquals(1, s1.nexts); + } + + /** + * Negative request causes error + */ + public void testRequest3() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + s1.sn.request(-1L); + p.submit(1); + p.submit(2); + p.close(); + s2.awaitComplete(); + assertEquals(2, s2.nexts); + assertEquals(1, s2.completes); + s1.awaitError(); + assertEquals(1, s1.errors); + assertTrue(s1.lastError instanceof IllegalArgumentException); + } + + /** + * estimateMinimumDemand reports 0 until request, nonzero after + * request, and zero again after delivery + */ + public void testEstimateMinimumDemand() { + TestSubscriber s = new TestSubscriber(); + SubmissionPublisher p = basicPublisher(); + s.request = false; + p.subscribe(s); + s.awaitSubscribe(); + assertEquals(0, p.estimateMinimumDemand()); + s.sn.request(1); + assertEquals(1, p.estimateMinimumDemand()); + p.submit(1); + s.awaitNext(1); + assertEquals(0, p.estimateMinimumDemand()); + } + + /** + * submit to a publisher with no subscribers returns lag 0 + */ + public void testEmptySubmit() { + SubmissionPublisher p = basicPublisher(); + assertEquals(0, p.submit(1)); + } + + /** + * submit(null) throws NPE + */ + public void testNullSubmit() { + SubmissionPublisher p = basicPublisher(); + try { + p.submit(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * submit returns number of lagged items, compatible with result + * of estimateMaximumLag. + */ + public void testLaggedSubmit() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + assertEquals(1, p.submit(1)); + assertTrue(p.estimateMaximumLag() >= 1); + assertTrue(p.submit(2) >= 2); + assertTrue(p.estimateMaximumLag() >= 2); + s1.sn.request(4); + assertTrue(p.submit(3) >= 3); + assertTrue(p.estimateMaximumLag() >= 3); + s2.sn.request(4); + p.submit(4); + p.close(); + s2.awaitComplete(); + assertEquals(4, s2.nexts); + s1.awaitComplete(); + assertEquals(4, s2.nexts); + } + + /** + * submit eventually issues requested items when buffer capacity is 1 + */ + public void testCap1Submit() { + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 1); + TestSubscriber s1 = new TestSubscriber(); + TestSubscriber s2 = new TestSubscriber(); + p.subscribe(s1); + p.subscribe(s2); + for (int i = 1; i <= 20; ++i) { + assertTrue(p.estimateMinimumDemand() <= 1); + assertTrue(p.submit(i) >= 0); + } + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertEquals(20, s2.nexts); + assertEquals(1, s2.completes); + assertEquals(20, s1.nexts); + assertEquals(1, s1.completes); + } + + static boolean noopHandle(AtomicInteger count) { + count.getAndIncrement(); + return false; + } + + static boolean reqHandle(AtomicInteger count, Subscriber s) { + count.getAndIncrement(); + ((TestSubscriber)s).sn.request(Long.MAX_VALUE); + return true; + } + + /** + * offer to a publisher with no subscribers returns lag 0 + */ + public void testEmptyOffer() { + SubmissionPublisher p = basicPublisher(); + assertEquals(0, p.offer(1, null)); + } + + /** + * offer(null) throws NPE + */ + public void testNullOffer() { + SubmissionPublisher p = basicPublisher(); + try { + p.offer(null, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * offer returns number of lagged items if not saturated + */ + public void testLaggedOffer() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + assertTrue(p.offer(1, null) >= 1); + assertTrue(p.offer(2, null) >= 2); + s1.sn.request(4); + assertTrue(p.offer(3, null) >= 3); + s2.sn.request(4); + p.offer(4, null); + p.close(); + s2.awaitComplete(); + assertEquals(4, s2.nexts); + s1.awaitComplete(); + assertEquals(4, s2.nexts); + } + + /** + * offer reports drops if saturated + */ + public void testDroppedOffer() { + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + for (int i = 1; i <= 4; ++i) + assertTrue(p.offer(i, null) >= 0); + p.offer(5, null); + assertTrue(p.offer(6, null) < 0); + s1.sn.request(64); + assertTrue(p.offer(7, null) < 0); + s2.sn.request(64); + p.close(); + s2.awaitComplete(); + assertTrue(s2.nexts >= 4); + s1.awaitComplete(); + assertTrue(s1.nexts >= 4); + } + + /** + * offer invokes drop handler if saturated + */ + public void testHandledDroppedOffer() { + AtomicInteger calls = new AtomicInteger(); + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + for (int i = 1; i <= 4; ++i) + assertTrue(p.offer(i, (s, x) -> noopHandle(calls)) >= 0); + p.offer(4, (s, x) -> noopHandle(calls)); + assertTrue(p.offer(6, (s, x) -> noopHandle(calls)) < 0); + s1.sn.request(64); + assertTrue(p.offer(7, (s, x) -> noopHandle(calls)) < 0); + s2.sn.request(64); + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertTrue(calls.get() >= 4); + } + + /** + * offer succeeds if drop handler forces request + */ + public void testRecoveredHandledDroppedOffer() { + AtomicInteger calls = new AtomicInteger(); + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + int n = 0; + for (int i = 1; i <= 8; ++i) { + int d = p.offer(i, (s, x) -> reqHandle(calls, s)); + n = n + 2 + (d < 0 ? d : 0); + } + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertEquals(n, s1.nexts + s2.nexts); + assertTrue(calls.get() >= 2); + } + + /** + * Timed offer to a publisher with no subscribers returns lag 0 + */ + public void testEmptyTimedOffer() { + SubmissionPublisher p = basicPublisher(); + long startTime = System.nanoTime(); + assertEquals(0, p.offer(1, LONG_DELAY_MS, MILLISECONDS, null)); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + } + + /** + * Timed offer with null item or TimeUnit throws NPE + */ + public void testNullTimedOffer() { + SubmissionPublisher p = basicPublisher(); + long startTime = System.nanoTime(); + try { + p.offer(null, LONG_DELAY_MS, MILLISECONDS, null); + shouldThrow(); + } catch (NullPointerException success) {} + try { + p.offer(1, LONG_DELAY_MS, null, null); + shouldThrow(); + } catch (NullPointerException success) {} + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + } + + /** + * Timed offer returns number of lagged items if not saturated + */ + public void testLaggedTimedOffer() { + SubmissionPublisher p = basicPublisher(); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + long startTime = System.nanoTime(); + assertTrue(p.offer(1, LONG_DELAY_MS, MILLISECONDS, null) >= 1); + assertTrue(p.offer(2, LONG_DELAY_MS, MILLISECONDS, null) >= 2); + s1.sn.request(4); + assertTrue(p.offer(3, LONG_DELAY_MS, MILLISECONDS, null) >= 3); + s2.sn.request(4); + p.offer(4, LONG_DELAY_MS, MILLISECONDS, null); + p.close(); + s2.awaitComplete(); + assertEquals(4, s2.nexts); + s1.awaitComplete(); + assertEquals(4, s2.nexts); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); + } + + /** + * Timed offer reports drops if saturated + */ + public void testDroppedTimedOffer() { + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + long delay = timeoutMillis(); + for (int i = 1; i <= 4; ++i) + assertTrue(p.offer(i, delay, MILLISECONDS, null) >= 0); + long startTime = System.nanoTime(); + assertTrue(p.offer(5, delay, MILLISECONDS, null) < 0); + s1.sn.request(64); + assertTrue(p.offer(6, delay, MILLISECONDS, null) < 0); + // 2 * delay should elapse but check only 1 * delay to allow timer slop + assertTrue(millisElapsedSince(startTime) >= delay); + s2.sn.request(64); + p.close(); + s2.awaitComplete(); + assertTrue(s2.nexts >= 2); + s1.awaitComplete(); + assertTrue(s1.nexts >= 2); + } + + /** + * Timed offer invokes drop handler if saturated + */ + public void testHandledDroppedTimedOffer() { + AtomicInteger calls = new AtomicInteger(); + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + long delay = timeoutMillis(); + for (int i = 1; i <= 4; ++i) + assertTrue(p.offer(i, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) >= 0); + long startTime = System.nanoTime(); + assertTrue(p.offer(5, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0); + s1.sn.request(64); + assertTrue(p.offer(6, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0); + assertTrue(millisElapsedSince(startTime) >= delay); + s2.sn.request(64); + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertTrue(calls.get() >= 2); + } + + /** + * Timed offer succeeds if drop handler forces request + */ + public void testRecoveredHandledDroppedTimedOffer() { + AtomicInteger calls = new AtomicInteger(); + SubmissionPublisher p = new SubmissionPublisher( + basicExecutor, 4); + TestSubscriber s1 = new TestSubscriber(); + s1.request = false; + TestSubscriber s2 = new TestSubscriber(); + s2.request = false; + p.subscribe(s1); + p.subscribe(s2); + s2.awaitSubscribe(); + s1.awaitSubscribe(); + int n = 0; + long delay = timeoutMillis(); + long startTime = System.nanoTime(); + for (int i = 1; i <= 6; ++i) { + int d = p.offer(i, delay, MILLISECONDS, (s, x) -> reqHandle(calls, s)); + n = n + 2 + (d < 0 ? d : 0); + } + assertTrue(millisElapsedSince(startTime) >= delay); + p.close(); + s2.awaitComplete(); + s1.awaitComplete(); + assertEquals(n, s1.nexts + s2.nexts); + assertTrue(calls.get() >= 2); + } + + /** + * consume returns a CompletableFuture that is done when + * publisher completes + */ + public void testConsume() { + AtomicInteger sum = new AtomicInteger(); + SubmissionPublisher p = basicPublisher(); + CompletableFuture f = + p.consume((Integer x) -> { sum.getAndAdd(x.intValue()); }); + int n = 20; + for (int i = 1; i <= n; ++i) + p.submit(i); + p.close(); + f.join(); + assertEquals((n * (n + 1)) / 2, sum.get()); + } + + /** + * consume(null) throws NPE + */ + public void testConsumeNPE() { + SubmissionPublisher p = basicPublisher(); + try { + CompletableFuture f = p.consume(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * consume eventually stops processing published items if cancelled + */ + public void testCancelledConsume() { + AtomicInteger count = new AtomicInteger(); + SubmissionPublisher p = basicPublisher(); + CompletableFuture f = p.consume(x -> count.getAndIncrement()); + f.cancel(true); + int n = 1000000; // arbitrary limit + for (int i = 1; i <= n; ++i) + p.submit(i); + assertTrue(count.get() < n); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java b/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java new file mode 100644 index 00000000000..b4529801928 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java @@ -0,0 +1,643 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.SynchronousQueue; + +import junit.framework.Test; + +public class SynchronousQueueTest extends JSR166TestCase { + + public static class Fair extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new SynchronousQueue(true); + } + } + + public static class NonFair extends BlockingQueueTest { + protected BlockingQueue emptyCollection() { + return new SynchronousQueue(false); + } + } + + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return newTestSuite(SynchronousQueueTest.class, + new Fair().testSuite(), + new NonFair().testSuite()); + } + + /** + * Any SynchronousQueue is both empty and full + */ + public void testEmptyFull() { testEmptyFull(false); } + public void testEmptyFull_fair() { testEmptyFull(true); } + public void testEmptyFull(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertEquals(0, q.remainingCapacity()); + assertFalse(q.offer(zero)); + } + + /** + * offer fails if no active taker + */ + public void testOffer() { testOffer(false); } + public void testOffer_fair() { testOffer(true); } + public void testOffer(boolean fair) { + SynchronousQueue q = new SynchronousQueue(fair); + assertFalse(q.offer(one)); + } + + /** + * add throws IllegalStateException if no active taker + */ + public void testAdd() { testAdd(false); } + public void testAdd_fair() { testAdd(true); } + public void testAdd(boolean fair) { + SynchronousQueue q = new SynchronousQueue(fair); + assertEquals(0, q.remainingCapacity()); + try { + q.add(one); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * addAll(this) throws IllegalArgumentException + */ + public void testAddAll_self() { testAddAll_self(false); } + public void testAddAll_self_fair() { testAddAll_self(true); } + public void testAddAll_self(boolean fair) { + SynchronousQueue q = new SynchronousQueue(fair); + try { + q.addAll(q); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * addAll throws ISE if no active taker + */ + public void testAddAll_ISE() { testAddAll_ISE(false); } + public void testAddAll_ISE_fair() { testAddAll_ISE(true); } + public void testAddAll_ISE(boolean fair) { + SynchronousQueue q = new SynchronousQueue(fair); + Integer[] ints = new Integer[1]; + for (int i = 0; i < ints.length; i++) + ints[i] = i; + Collection coll = Arrays.asList(ints); + try { + q.addAll(coll); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * put blocks interruptibly if no active taker + */ + public void testBlockingPut() { testBlockingPut(false); } + public void testBlockingPut_fair() { testBlockingPut(true); } + public void testBlockingPut(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + Thread.currentThread().interrupt(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * put blocks interruptibly waiting for take + */ + public void testPutWithTake() { testPutWithTake(false); } + public void testPutWithTake_fair() { testPutWithTake(true); } + public void testPutWithTake(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CountDownLatch pleaseTake = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + pleaseTake.countDown(); + q.put(one); + + pleaseInterrupt.countDown(); + try { + q.put(99); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseTake); + assertEquals(0, q.remainingCapacity()); + try { assertSame(one, q.take()); } + catch (InterruptedException e) { threadUnexpectedException(e); } + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + assertEquals(0, q.remainingCapacity()); + } + + /** + * timed offer times out if elements not taken + */ + public void testTimedOffer() { testTimedOffer(false); } + public void testTimedOffer_fair() { testTimedOffer(true); } + public void testTimedOffer(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + pleaseInterrupt.countDown(); + try { + q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * poll return null if no active putter + */ + public void testPoll() { testPoll(false); } + public void testPoll_fair() { testPoll(true); } + public void testPoll(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + assertNull(q.poll()); + } + + /** + * timed poll with zero timeout times out if no active putter + */ + public void testTimedPoll0() { testTimedPoll0(false); } + public void testTimedPoll0_fair() { testTimedPoll0(true); } + public void testTimedPoll0(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + try { assertNull(q.poll(0, MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + } + + /** + * timed poll with nonzero timeout times out if no active putter + */ + public void testTimedPoll() { testTimedPoll(false); } + public void testTimedPoll_fair() { testTimedPoll(true); } + public void testTimedPoll(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + long startTime = System.nanoTime(); + try { assertNull(q.poll(timeoutMillis(), MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + } + + /** + * timed poll before a delayed offer times out, returning null; + * after offer succeeds; on interruption throws + */ + public void testTimedPollWithOffer() { testTimedPollWithOffer(false); } + public void testTimedPollWithOffer_fair() { testTimedPollWithOffer(true); } + public void testTimedPollWithOffer(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CountDownLatch pleaseOffer = new CountDownLatch(1); + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + assertNull(q.poll(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + + pleaseOffer.countDown(); + startTime = System.nanoTime(); + assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS)); + + Thread.currentThread().interrupt(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + q.poll(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + }}); + + await(pleaseOffer); + long startTime = System.nanoTime(); + try { assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); } + catch (InterruptedException e) { threadUnexpectedException(e); } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * peek() returns null if no active putter + */ + public void testPeek() { testPeek(false); } + public void testPeek_fair() { testPeek(true); } + public void testPeek(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + assertNull(q.peek()); + } + + /** + * element() throws NoSuchElementException if no active putter + */ + public void testElement() { testElement(false); } + public void testElement_fair() { testElement(true); } + public void testElement(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + try { + q.element(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * remove() throws NoSuchElementException if no active putter + */ + public void testRemove() { testRemove(false); } + public void testRemove_fair() { testRemove(true); } + public void testRemove(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + try { + q.remove(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + + /** + * contains returns false + */ + public void testContains() { testContains(false); } + public void testContains_fair() { testContains(true); } + public void testContains(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + assertFalse(q.contains(zero)); + } + + /** + * clear ensures isEmpty + */ + public void testClear() { testClear(false); } + public void testClear_fair() { testClear(true); } + public void testClear(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll returns false unless empty + */ + public void testContainsAll() { testContainsAll(false); } + public void testContainsAll_fair() { testContainsAll(true); } + public void testContainsAll(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Integer[] empty = new Integer[0]; + assertTrue(q.containsAll(Arrays.asList(empty))); + Integer[] ints = new Integer[1]; ints[0] = zero; + assertFalse(q.containsAll(Arrays.asList(ints))); + } + + /** + * retainAll returns false + */ + public void testRetainAll() { testRetainAll(false); } + public void testRetainAll_fair() { testRetainAll(true); } + public void testRetainAll(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Integer[] empty = new Integer[0]; + assertFalse(q.retainAll(Arrays.asList(empty))); + Integer[] ints = new Integer[1]; ints[0] = zero; + assertFalse(q.retainAll(Arrays.asList(ints))); + } + + /** + * removeAll returns false + */ + public void testRemoveAll() { testRemoveAll(false); } + public void testRemoveAll_fair() { testRemoveAll(true); } + public void testRemoveAll(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Integer[] empty = new Integer[0]; + assertFalse(q.removeAll(Arrays.asList(empty))); + Integer[] ints = new Integer[1]; ints[0] = zero; + assertFalse(q.containsAll(Arrays.asList(ints))); + } + + /** + * toArray is empty + */ + public void testToArray() { testToArray(false); } + public void testToArray_fair() { testToArray(true); } + public void testToArray(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Object[] o = q.toArray(); + assertEquals(0, o.length); + } + + /** + * toArray(Integer array) returns its argument with the first + * element (if present) nulled out + */ + public void testToArray2() { testToArray2(false); } + public void testToArray2_fair() { testToArray2(true); } + public void testToArray2(boolean fair) { + final SynchronousQueue q + = new SynchronousQueue(fair); + Integer[] a; + + a = new Integer[0]; + assertSame(a, q.toArray(a)); + + a = new Integer[3]; + Arrays.fill(a, 42); + assertSame(a, q.toArray(a)); + assertNull(a[0]); + for (int i = 1; i < a.length; i++) + assertEquals(42, (int) a[i]); + } + + /** + * toArray(null) throws NPE + */ + public void testToArray_null() { testToArray_null(false); } + public void testToArray_null_fair() { testToArray_null(true); } + public void testToArray_null(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + try { + Object[] o = q.toArray(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * iterator does not traverse any elements + */ + public void testIterator() { testIterator(false); } + public void testIterator_fair() { testIterator(true); } + public void testIterator(boolean fair) { + assertIteratorExhausted(new SynchronousQueue(fair).iterator()); + } + + /** + * iterator remove throws ISE + */ + public void testIteratorRemove() { testIteratorRemove(false); } + public void testIteratorRemove_fair() { testIteratorRemove(true); } + public void testIteratorRemove(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Iterator it = q.iterator(); + try { + it.remove(); + shouldThrow(); + } catch (IllegalStateException success) {} + } + + /** + * toString returns a non-null string + */ + public void testToString() { testToString(false); } + public void testToString_fair() { testToString(true); } + public void testToString(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + String s = q.toString(); + assertNotNull(s); + } + + /** + * offer transfers elements across Executor tasks + */ + public void testOfferInExecutor() { testOfferInExecutor(false); } + public void testOfferInExecutor_fair() { testOfferInExecutor(true); } + public void testOfferInExecutor(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(q.offer(one)); + threadsStarted.await(); + assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS)); + assertEquals(0, q.remainingCapacity()); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + assertSame(one, q.take()); + }}); + } + } + + /** + * timed poll retrieves elements across Executor threads + */ + public void testPollInExecutor() { testPollInExecutor(false); } + public void testPollInExecutor_fair() { testPollInExecutor(true); } + public void testPollInExecutor(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + final CheckedBarrier threadsStarted = new CheckedBarrier(2); + final ExecutorService executor = Executors.newFixedThreadPool(2); + try (PoolCleaner cleaner = cleaner(executor)) { + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertNull(q.poll()); + threadsStarted.await(); + assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(q.isEmpty()); + }}); + + executor.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.await(); + q.put(one); + }}); + } + } + + /** + * a deserialized serialized queue is usable + */ + public void testSerialization() { + final SynchronousQueue x = new SynchronousQueue(); + final SynchronousQueue y = new SynchronousQueue(false); + final SynchronousQueue z = new SynchronousQueue(true); + assertSerialEquals(x, y); + assertNotSerialEquals(x, z); + SynchronousQueue[] qs = { x, y, z }; + for (SynchronousQueue q : qs) { + SynchronousQueue clone = serialClone(q); + assertNotSame(q, clone); + assertSerialEquals(q, clone); + assertTrue(clone.isEmpty()); + assertEquals(0, clone.size()); + assertEquals(0, clone.remainingCapacity()); + assertFalse(clone.offer(zero)); + } + } + + /** + * drainTo(c) of empty queue doesn't transfer elements + */ + public void testDrainTo() { testDrainTo(false); } + public void testDrainTo_fair() { testDrainTo(true); } + public void testDrainTo(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + ArrayList l = new ArrayList(); + q.drainTo(l); + assertEquals(0, q.size()); + assertEquals(0, l.size()); + } + + /** + * drainTo empties queue, unblocking a waiting put. + */ + public void testDrainToWithActivePut() { testDrainToWithActivePut(false); } + public void testDrainToWithActivePut_fair() { testDrainToWithActivePut(true); } + public void testDrainToWithActivePut(boolean fair) { + final SynchronousQueue q = new SynchronousQueue(fair); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(one); + }}); + + ArrayList l = new ArrayList(); + long startTime = System.nanoTime(); + while (l.isEmpty()) { + q.drainTo(l); + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + assertTrue(l.size() == 1); + assertSame(one, l.get(0)); + awaitTermination(t); + } + + /** + * drainTo(c, n) empties up to n elements of queue into c + */ + public void testDrainToN() throws InterruptedException { + final SynchronousQueue q = new SynchronousQueue(); + Thread t1 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(one); + }}); + + Thread t2 = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + q.put(two); + }}); + + ArrayList l = new ArrayList(); + int drained; + while ((drained = q.drainTo(l, 1)) == 0) Thread.yield(); + assertEquals(1, drained); + assertEquals(1, l.size()); + while ((drained = q.drainTo(l, 1)) == 0) Thread.yield(); + assertEquals(1, drained); + assertEquals(2, l.size()); + assertTrue(l.contains(one)); + assertTrue(l.contains(two)); + awaitTermination(t1); + awaitTermination(t2); + } + + /** + * remove(null), contains(null) always return false + */ + public void testNeverContainsNull() { + Collection q = new SynchronousQueue(); + assertFalse(q.contains(null)); + assertFalse(q.remove(null)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/SystemTest.java b/jdk/test/java/util/concurrent/tck/SystemTest.java new file mode 100644 index 00000000000..abf3b49cdd5 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/SystemTest.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class SystemTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(SystemTest.class); + } + + /** + * Worst case rounding for millisecs; set for 60 cycle millis clock. + * This value might need to be changed on JVMs with coarser + * System.currentTimeMillis clocks. + */ + static final long MILLIS_ROUND = 17; + + /** + * Nanos between readings of millis is no longer than millis (plus + * possible rounding). + * This shows only that nano timing not (much) worse than milli. + */ + public void testNanoTime1() throws InterruptedException { + long m1 = System.currentTimeMillis(); + Thread.sleep(1); + long n1 = System.nanoTime(); + Thread.sleep(SHORT_DELAY_MS); + long n2 = System.nanoTime(); + Thread.sleep(1); + long m2 = System.currentTimeMillis(); + long millis = m2 - m1; + long nanos = n2 - n1; + assertTrue(nanos >= 0); + long nanosAsMillis = nanos / 1000000; + assertTrue(nanosAsMillis <= millis + MILLIS_ROUND); + } + + /** + * Millis between readings of nanos is less than nanos, adjusting + * for rounding. + * This shows only that nano timing not (much) worse than milli. + */ + public void testNanoTime2() throws InterruptedException { + long n1 = System.nanoTime(); + Thread.sleep(1); + long m1 = System.currentTimeMillis(); + Thread.sleep(SHORT_DELAY_MS); + long m2 = System.currentTimeMillis(); + Thread.sleep(1); + long n2 = System.nanoTime(); + long millis = m2 - m1; + long nanos = n2 - n1; + + assertTrue(nanos >= 0); + long nanosAsMillis = nanos / 1000000; + assertTrue(millis <= nanosAsMillis + MILLIS_ROUND); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java b/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java new file mode 100644 index 00000000000..c80af6290ec --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java @@ -0,0 +1,262 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadLocalRandom8Test extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ThreadLocalRandom8Test.class); + } + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 26); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 42); + + // Number of replications for other checks + static final int REPS = + Integer.getInteger("ThreadLocalRandom8Test.reps", 4); + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + Runnable[] throwingActions = { + () -> r.ints(-1L), + () -> r.ints(-1L, 2, 3), + () -> r.longs(-1L), + () -> r.longs(-1L, -1L, 1L), + () -> r.doubles(-1L), + () -> r.doubles(-1L, .5, .6), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + Runnable[] throwingActions = { + () -> r.ints(2, 1), + () -> r.ints(10, 42, 42), + () -> r.longs(-1L, -1L), + () -> r.longs(10, 1L, -2L), + () -> r.doubles(0.0, 0.0), + () -> r.doubles(10, .5, .4), + }; + assertThrows(IllegalArgumentException.class, throwingActions); + } + + /** + * A parallel sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * A parallel sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * A parallel sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + size += 524959; + } + } + + /** + * Each of a parallel sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 12345L; + for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) { + final int lo = least, hi = bound; + r.ints(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * Each of a parallel sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 123L; + for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + final long lo = least, hi = bound; + r.longs(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * Each of a parallel sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 456; + for (double least = 0.00011; least < 1.0e20; least *= 9) { + for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) { + final double lo = least, hi = bound; + r.doubles(size, lo, hi).parallel().forEach( + x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(0, fails.get()); + } + + /** + * A parallel unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.ints().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A parallel unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.longs().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A parallel unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.doubles().limit(size).parallel().forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.ints().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.longs().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + + /** + * A sequential unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.doubles().limit(size).forEach(x -> counter.increment()); + assertEquals(size, counter.sum()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java b/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java new file mode 100644 index 00000000000..da50bbe2495 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java @@ -0,0 +1,368 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadLocalRandomTest extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ThreadLocalRandomTest.class); + } + + /* + * Testing coverage notes: + * + * We don't test randomness properties, but only that repeated + * calls, up to NCALLS tries, produce at least one different + * result. For bounded versions, we sample various intervals + * across multiples of primes. + */ + + // max numbers of calls to detect getting stuck on one value + static final int NCALLS = 10000; + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 28); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 42); + + // Number of replications for other checks + static final int REPS = 20; + + /** + * setSeed throws UnsupportedOperationException + */ + public void testSetSeed() { + try { + ThreadLocalRandom.current().setSeed(17); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + } + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + int f = ThreadLocalRandom.current().nextInt(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + long f = ThreadLocalRandom.current().nextLong(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextBoolean produce at least two distinct results + */ + public void testNextBoolean() { + boolean f = ThreadLocalRandom.current().nextBoolean(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextFloat produce at least two distinct results + */ + public void testNextFloat() { + float f = ThreadLocalRandom.current().nextFloat(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + double f = ThreadLocalRandom.current().nextDouble(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextGaussian produce at least two distinct results + */ + public void testNextGaussian() { + double f = ThreadLocalRandom.current().nextGaussian(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * nextInt(non-positive) throws IllegalArgumentException + */ + public void testNextIntBoundNonPositive() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + for (int bound : new int[] { 0, -17, Integer.MIN_VALUE }) { + try { + rnd.nextInt(bound); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + public void testNextIntBadBounds() { + int[][] badBoundss = { + { 17, 2 }, + { -42, -42 }, + { Integer.MAX_VALUE, Integer.MIN_VALUE }, + }; + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + for (int[] badBounds : badBoundss) { + try { + rnd.nextInt(badBounds[0], badBounds[1]); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * nextInt(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded() { + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = ThreadLocalRandom.current().nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded2() { + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = ThreadLocalRandom.current().nextInt(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(non-positive) throws IllegalArgumentException + */ + public void testNextLongBoundNonPositive() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + for (long bound : new long[] { 0L, -17L, Long.MIN_VALUE }) { + try { + rnd.nextLong(bound); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + public void testNextLongBadBounds() { + long[][] badBoundss = { + { 17L, 2L }, + { -42L, -42L }, + { Long.MAX_VALUE, Long.MIN_VALUE }, + }; + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + for (long[] badBounds : badBoundss) { + try { + rnd.nextLong(badBounds[0], badBounds[1]); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * nextLong(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded() { + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = ThreadLocalRandom.current().nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded2() { + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = ThreadLocalRandom.current().nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(non-positive) throws IllegalArgumentException + */ + public void testNextDoubleBoundNonPositive() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + double[] badBounds = { + 0.0d, + -17.0d, + -Double.MIN_VALUE, + Double.NEGATIVE_INFINITY, + Double.NaN, + }; + for (double bound : badBounds) { + try { + rnd.nextDouble(bound); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * nextDouble(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextDoubleBounded2() { + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = ThreadLocalRandom.current().nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Different threads produce different pseudo-random sequences + */ + public void testDifferentSequences() { + // Don't use main thread's ThreadLocalRandom - it is likely to + // be polluted by previous tests. + final AtomicReference threadLocalRandom = + new AtomicReference(); + final AtomicLong rand = new AtomicLong(); + + long firstRand = 0; + ThreadLocalRandom firstThreadLocalRandom = null; + + Runnable getRandomState = new CheckedRunnable() { + public void realRun() { + ThreadLocalRandom current = ThreadLocalRandom.current(); + assertSame(current, ThreadLocalRandom.current()); + // test bug: the following is not guaranteed and not true in JDK8 + // assertNotSame(current, threadLocalRandom.get()); + rand.set(current.nextLong()); + threadLocalRandom.set(current); + }}; + + Thread first = newStartedThread(getRandomState); + awaitTermination(first); + firstRand = rand.get(); + firstThreadLocalRandom = threadLocalRandom.get(); + + for (int i = 0; i < NCALLS; i++) { + Thread t = newStartedThread(getRandomState); + awaitTermination(t); + if (firstRand != rand.get()) + return; + } + fail("all threads generate the same pseudo-random sequence"); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java b/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java new file mode 100644 index 00000000000..51d2197f4d7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java @@ -0,0 +1,128 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadLocalTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ThreadLocalTest.class); + } + + static ThreadLocal tl = new ThreadLocal() { + public Integer initialValue() { + return one; + } + }; + + static InheritableThreadLocal itl = + new InheritableThreadLocal() { + protected Integer initialValue() { + return zero; + } + + protected Integer childValue(Integer parentValue) { + return new Integer(parentValue.intValue() + 1); + } + }; + + /** + * remove causes next access to return initial value + */ + public void testRemove() { + assertSame(tl.get(), one); + tl.set(two); + assertSame(tl.get(), two); + tl.remove(); + assertSame(tl.get(), one); + } + + /** + * remove in InheritableThreadLocal causes next access to return + * initial value + */ + public void testRemoveITL() { + assertSame(itl.get(), zero); + itl.set(two); + assertSame(itl.get(), two); + itl.remove(); + assertSame(itl.get(), zero); + } + + private class ITLThread extends Thread { + final int[] x; + ITLThread(int[] array) { x = array; } + public void run() { + Thread child = null; + if (itl.get().intValue() < x.length - 1) { + child = new ITLThread(x); + child.start(); + } + Thread.yield(); + + int threadId = itl.get().intValue(); + for (int j = 0; j < threadId; j++) { + x[threadId]++; + Thread.yield(); + } + + if (child != null) { // Wait for child (if any) + try { + child.join(); + } catch (InterruptedException e) { + threadUnexpectedException(e); + } + } + } + } + + /** + * InheritableThreadLocal propagates generic values. + */ + public void testGenericITL() throws InterruptedException { + final int threadCount = 10; + final int[] x = new int[threadCount]; + Thread progenitor = new ITLThread(x); + progenitor.start(); + progenitor.join(); + for (int i = 0; i < threadCount; i++) { + assertEquals(i, x[i]); + } + } +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java new file mode 100644 index 00000000000..3959527af70 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java @@ -0,0 +1,2062 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadPoolExecutorSubclassTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ThreadPoolExecutorSubclassTest.class); + } + + static class CustomTask implements RunnableFuture { + final Callable callable; + final ReentrantLock lock = new ReentrantLock(); + final Condition cond = lock.newCondition(); + boolean done; + boolean cancelled; + V result; + Thread thread; + Exception exception; + CustomTask(Callable c) { + if (c == null) throw new NullPointerException(); + callable = c; + } + CustomTask(final Runnable r, final V res) { + if (r == null) throw new NullPointerException(); + callable = new Callable() { + public V call() throws Exception { r.run(); return res; }}; + } + public boolean isDone() { + lock.lock(); try { return done; } finally { lock.unlock() ; } + } + public boolean isCancelled() { + lock.lock(); try { return cancelled; } finally { lock.unlock() ; } + } + public boolean cancel(boolean mayInterrupt) { + lock.lock(); + try { + if (!done) { + cancelled = true; + done = true; + if (mayInterrupt && thread != null) + thread.interrupt(); + return true; + } + return false; + } + finally { lock.unlock() ; } + } + public void run() { + lock.lock(); + try { + if (done) + return; + thread = Thread.currentThread(); + } + finally { lock.unlock() ; } + V v = null; + Exception e = null; + try { + v = callable.call(); + } + catch (Exception ex) { + e = ex; + } + lock.lock(); + try { + if (!done) { + result = v; + exception = e; + done = true; + thread = null; + cond.signalAll(); + } + } + finally { lock.unlock(); } + } + public V get() throws InterruptedException, ExecutionException { + lock.lock(); + try { + while (!done) + cond.await(); + if (cancelled) + throw new CancellationException(); + if (exception != null) + throw new ExecutionException(exception); + return result; + } + finally { lock.unlock(); } + } + public V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + long nanos = unit.toNanos(timeout); + lock.lock(); + try { + while (!done) { + if (nanos <= 0L) + throw new TimeoutException(); + nanos = cond.awaitNanos(nanos); + } + if (cancelled) + throw new CancellationException(); + if (exception != null) + throw new ExecutionException(exception); + return result; + } + finally { lock.unlock(); } + } + } + + static class CustomTPE extends ThreadPoolExecutor { + protected RunnableFuture newTaskFor(Callable c) { + return new CustomTask(c); + } + protected RunnableFuture newTaskFor(Runnable r, V v) { + return new CustomTask(r, v); + } + + CustomTPE(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, + workQueue); + } + CustomTPE(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + threadFactory); + } + + CustomTPE(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + handler); + } + CustomTPE(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory, + RejectedExecutionHandler handler) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, + workQueue, threadFactory, handler); + } + + final CountDownLatch beforeCalled = new CountDownLatch(1); + final CountDownLatch afterCalled = new CountDownLatch(1); + final CountDownLatch terminatedCalled = new CountDownLatch(1); + + public CustomTPE() { + super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue()); + } + protected void beforeExecute(Thread t, Runnable r) { + beforeCalled.countDown(); + } + protected void afterExecute(Runnable r, Throwable t) { + afterCalled.countDown(); + } + protected void terminated() { + terminatedCalled.countDown(); + } + + public boolean beforeCalled() { + return beforeCalled.getCount() == 0; + } + public boolean afterCalled() { + return afterCalled.getCount() == 0; + } + public boolean terminatedCalled() { + return terminatedCalled.getCount() == 0; + } + } + + static class FailingThreadFactory implements ThreadFactory { + int calls = 0; + public Thread newThread(Runnable r) { + if (++calls > 1) return null; + return new Thread(r); + } + } + + /** + * execute successfully executes a runnable + */ + public void testExecute() throws InterruptedException { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + 2 * LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + p.execute(task); + assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); + } + } + + /** + * getActiveCount increases but doesn't overestimate, when a + * thread becomes active + */ + public void testGetActiveCount() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getActiveCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getActiveCount()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getActiveCount()); + } + } + + /** + * prestartCoreThread starts a thread if under corePoolSize, else doesn't + */ + public void testPrestartCoreThread() { + final ThreadPoolExecutor p = + new CustomTPE(2, 6, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(0, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(1, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(2, p.getPoolSize()); + assertFalse(p.prestartCoreThread()); + assertEquals(2, p.getPoolSize()); + p.setCorePoolSize(4); + assertTrue(p.prestartCoreThread()); + assertEquals(3, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(4, p.getPoolSize()); + assertFalse(p.prestartCoreThread()); + assertEquals(4, p.getPoolSize()); + } + } + + /** + * prestartAllCoreThreads starts all corePoolSize threads + */ + public void testPrestartAllCoreThreads() { + final ThreadPoolExecutor p = + new CustomTPE(2, 6, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(0, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(2, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(2, p.getPoolSize()); + p.setCorePoolSize(4); + p.prestartAllCoreThreads(); + assertEquals(4, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(4, p.getPoolSize()); + } + } + + /** + * getCompletedTaskCount increases, but doesn't overestimate, + * when tasks complete + */ + public void testGetCompletedTaskCount() throws InterruptedException { + final ThreadPoolExecutor p = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch threadProceed = new CountDownLatch(1); + final CountDownLatch threadDone = new CountDownLatch(1); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.await(); + threadDone.countDown(); + }}); + await(threadStarted); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.countDown(); + threadDone.await(); + long startTime = System.nanoTime(); + while (p.getCompletedTaskCount() != 1) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + } + + /** + * getCorePoolSize returns size given in constructor if not otherwise set + */ + public void testGetCorePoolSize() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getCorePoolSize()); + } + } + + /** + * getKeepAliveTime returns value given in constructor if not otherwise set + */ + public void testGetKeepAliveTime() { + final ThreadPoolExecutor p = + new CustomTPE(2, 2, + 1000, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getKeepAliveTime(SECONDS)); + } + } + + /** + * getThreadFactory returns factory in constructor if not set + */ + public void testGetThreadFactory() { + final ThreadFactory threadFactory = new SimpleThreadFactory(); + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10), + threadFactory, + new NoOpREHandler()); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory sets the thread factory returned by getThreadFactory + */ + public void testSetThreadFactory() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + ThreadFactory threadFactory = new SimpleThreadFactory(); + p.setThreadFactory(threadFactory); + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory(null) throws NPE + */ + public void testSetThreadFactoryNull() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setThreadFactory(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * getRejectedExecutionHandler returns handler in constructor if not set + */ + public void testGetRejectedExecutionHandler() { + final RejectedExecutionHandler handler = new NoOpREHandler(); + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10), + handler); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(handler, p.getRejectedExecutionHandler()); + } + } + + /** + * setRejectedExecutionHandler sets the handler returned by + * getRejectedExecutionHandler + */ + public void testSetRejectedExecutionHandler() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + RejectedExecutionHandler handler = new NoOpREHandler(); + p.setRejectedExecutionHandler(handler); + assertSame(handler, p.getRejectedExecutionHandler()); + } + } + + /** + * setRejectedExecutionHandler(null) throws NPE + */ + public void testSetRejectedExecutionHandlerNull() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setRejectedExecutionHandler(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * getLargestPoolSize increases, but doesn't overestimate, when + * multiple threads active + */ + public void testGetLargestPoolSize() throws InterruptedException { + final int THREADS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(THREADS, THREADS, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getLargestPoolSize()); + final CountDownLatch threadsStarted = new CountDownLatch(THREADS); + for (int i = 0; i < THREADS; i++) + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.countDown(); + await(done); + assertEquals(THREADS, p.getLargestPoolSize()); + }}); + await(threadsStarted); + assertEquals(THREADS, p.getLargestPoolSize()); + } + assertEquals(THREADS, p.getLargestPoolSize()); + } + + /** + * getMaximumPoolSize returns value given in constructor if not + * otherwise set + */ + public void testGetMaximumPoolSize() { + final ThreadPoolExecutor p = + new CustomTPE(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(3, p.getMaximumPoolSize()); + p.setMaximumPoolSize(5); + assertEquals(5, p.getMaximumPoolSize()); + p.setMaximumPoolSize(4); + assertEquals(4, p.getMaximumPoolSize()); + } + } + + /** + * getPoolSize increases, but doesn't overestimate, when threads + * become active + */ + public void testGetPoolSize() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getPoolSize()); + final CountDownLatch threadStarted = new CountDownLatch(1); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getPoolSize()); + } + } + + /** + * getTaskCount increases, but doesn't overestimate, when tasks submitted + */ + public void testGetTaskCount() throws InterruptedException { + final int TASKS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + for (int i = 0; i < TASKS; i++) { + assertEquals(1 + i, p.getTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1 + TASKS, p.getTaskCount()); + await(done); + }}); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(1 + TASKS, p.getCompletedTaskCount()); + } + + /** + * isShutdown is false before shutdown, true after + */ + public void testIsShutdown() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isShutdown()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.isShutdown()); + } + } + + /** + * isTerminated is false before termination, true after + */ + public void testIsTerminated() throws InterruptedException { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * isTerminating is not true when running or when terminated + */ + public void testIsTerminating() throws InterruptedException { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * getQueue returns the work queue, which contains queued tasks + */ + public void testGetQueue() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + FutureTask[] tasks = new FutureTask[5]; + for (int i = 0; i < tasks.length; i++) { + Callable task = new CheckedCallable() { + public Boolean realCall() throws InterruptedException { + threadStarted.countDown(); + assertSame(q, p.getQueue()); + await(done); + return Boolean.TRUE; + }}; + tasks[i] = new FutureTask(task); + p.execute(tasks[i]); + } + await(threadStarted); + assertSame(q, p.getQueue()); + assertFalse(q.contains(tasks[0])); + assertTrue(q.contains(tasks[tasks.length - 1])); + assertEquals(tasks.length - 1, q.size()); + } + } + + /** + * remove(task) removes queued task, and fails to remove active task + */ + public void testRemove() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable[] tasks = new Runnable[6]; + final CountDownLatch threadStarted = new CountDownLatch(1); + for (int i = 0; i < tasks.length; i++) { + tasks[i] = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + p.execute(tasks[i]); + } + await(threadStarted); + assertFalse(p.remove(tasks[0])); + assertTrue(q.contains(tasks[4])); + assertTrue(q.contains(tasks[3])); + assertTrue(p.remove(tasks[4])); + assertFalse(p.remove(tasks[4])); + assertFalse(q.contains(tasks[4])); + assertTrue(q.contains(tasks[3])); + assertTrue(p.remove(tasks[3])); + assertFalse(q.contains(tasks[3])); + } + } + + /** + * purge removes cancelled tasks from the queue + */ + public void testPurge() throws InterruptedException { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + final BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + FutureTask[] tasks = new FutureTask[5]; + for (int i = 0; i < tasks.length; i++) { + Callable task = new CheckedCallable() { + public Boolean realCall() throws InterruptedException { + threadStarted.countDown(); + await(done); + return Boolean.TRUE; + }}; + tasks[i] = new FutureTask(task); + p.execute(tasks[i]); + } + await(threadStarted); + assertEquals(tasks.length, p.getTaskCount()); + assertEquals(tasks.length - 1, q.size()); + assertEquals(1L, p.getActiveCount()); + assertEquals(0L, p.getCompletedTaskCount()); + tasks[4].cancel(true); + tasks[3].cancel(false); + p.purge(); + assertEquals(tasks.length - 3, q.size()); + assertEquals(tasks.length - 2, p.getTaskCount()); + p.purge(); // Nothing to do + assertEquals(tasks.length - 3, q.size()); + assertEquals(tasks.length - 2, p.getTaskCount()); + } + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow() throws InterruptedException { + final int poolSize = 2; + final int count = 5; + final AtomicInteger ran = new AtomicInteger(0); + final ThreadPoolExecutor p = + new CustomTPE(poolSize, poolSize, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + final CountDownLatch threadsStarted = new CountDownLatch(poolSize); + Runnable waiter = new CheckedRunnable() { public void realRun() { + threadsStarted.countDown(); + try { + MILLISECONDS.sleep(2 * LONG_DELAY_MS); + } catch (InterruptedException success) {} + ran.getAndIncrement(); + }}; + for (int i = 0; i < count; i++) + p.execute(waiter); + await(threadsStarted); + assertEquals(poolSize, p.getActiveCount()); + assertEquals(0, p.getCompletedTaskCount()); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + assertEquals(count - poolSize, queuedTasks.size()); + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(poolSize, ran.get()); + assertEquals(poolSize, p.getCompletedTaskCount()); + } + + // Exception Tests + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor1() { + try { + new CustomTPE(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor2() { + try { + new CustomTPE(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor3() { + try { + new CustomTPE(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor4() { + try { + new CustomTPE(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor5() { + try { + new CustomTPE(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException() { + try { + new CustomTPE(1, 2, 1L, SECONDS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor6() { + try { + new CustomTPE(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor7() { + try { + new CustomTPE(1,-1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor8() { + try { + new CustomTPE(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor9() { + try { + new CustomTPE(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor10() { + try { + new CustomTPE(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException2() { + try { + new CustomTPE(1, 2, 1L, SECONDS, null, new SimpleThreadFactory()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if threadFactory is set to null + */ + public void testConstructorNullPointerException3() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (ThreadFactory) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor11() { + try { + new CustomTPE(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor12() { + try { + new CustomTPE(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor13() { + try { + new CustomTPE(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor14() { + try { + new CustomTPE(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor15() { + try { + new CustomTPE(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException4() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + null, + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if handler is set to null + */ + public void testConstructorNullPointerException5() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (RejectedExecutionHandler) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor16() { + try { + new CustomTPE(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor17() { + try { + new CustomTPE(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor18() { + try { + new CustomTPE(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor19() { + try { + new CustomTPE(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor20() { + try { + new CustomTPE(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is null + */ + public void testConstructorNullPointerException6() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + null, + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if handler is null + */ + public void testConstructorNullPointerException7() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + (RejectedExecutionHandler) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if ThreadFactory is null + */ + public void testConstructorNullPointerException8() { + try { + new CustomTPE(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (ThreadFactory) null, + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * execute throws RejectedExecutionException if saturated. + */ + public void testSaturatedExecute() { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable task = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + for (int i = 0; i < 2; ++i) + p.execute(task); + for (int i = 0; i < 2; ++i) { + try { + p.execute(task); + shouldThrow(); + } catch (RejectedExecutionException success) {} + assertTrue(p.getTaskCount() <= 2); + } + } + } + + /** + * executor using CallerRunsPolicy runs task if saturated. + */ + public void testSaturatedExecute2() { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.CallerRunsPolicy()); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable blocker = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + p.execute(blocker); + TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5]; + for (int i = 0; i < tasks.length; i++) + tasks[i] = new TrackedNoOpRunnable(); + for (int i = 0; i < tasks.length; i++) + p.execute(tasks[i]); + for (int i = 1; i < tasks.length; i++) + assertTrue(tasks[i].done); + assertFalse(tasks[0].done); // waiting in queue + } + } + + /** + * executor using DiscardPolicy drops task if saturated. + */ + public void testSaturatedExecute3() { + final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5]; + for (int i = 0; i < tasks.length; ++i) + tasks[i] = new TrackedNoOpRunnable(); + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.DiscardPolicy()); + try (PoolCleaner cleaner = cleaner(p, done)) { + p.execute(awaiter(done)); + + for (TrackedNoOpRunnable task : tasks) + p.execute(task); + for (int i = 1; i < tasks.length; i++) + assertFalse(tasks[i].done); + } + for (int i = 1; i < tasks.length; i++) + assertFalse(tasks[i].done); + assertTrue(tasks[0].done); // was waiting in queue + } + + /** + * executor using DiscardOldestPolicy drops oldest task if saturated. + */ + public void testSaturatedExecute4() { + final CountDownLatch done = new CountDownLatch(1); + LatchAwaiter r1 = awaiter(done); + LatchAwaiter r2 = awaiter(done); + LatchAwaiter r3 = awaiter(done); + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.DiscardOldestPolicy()); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(LatchAwaiter.NEW, r1.state); + assertEquals(LatchAwaiter.NEW, r2.state); + assertEquals(LatchAwaiter.NEW, r3.state); + p.execute(r1); + p.execute(r2); + assertTrue(p.getQueue().contains(r2)); + p.execute(r3); + assertFalse(p.getQueue().contains(r2)); + assertTrue(p.getQueue().contains(r3)); + } + assertEquals(LatchAwaiter.DONE, r1.state); + assertEquals(LatchAwaiter.NEW, r2.state); + assertEquals(LatchAwaiter.DONE, r3.state); + } + + /** + * execute throws RejectedExecutionException if shutdown + */ + public void testRejectedExecutionExceptionOnShutdown() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(new NoOpRunnable()); + shouldThrow(); + } catch (RejectedExecutionException success) {} + } + } + + /** + * execute using CallerRunsPolicy drops task on shutdown + */ + public void testCallerRunsOnShutdown() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.CallerRunsPolicy()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute using DiscardPolicy drops task on shutdown + */ + public void testDiscardOnShutdown() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.DiscardPolicy()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute using DiscardOldestPolicy drops task on shutdown + */ + public void testDiscardOldestOnShutdown() { + final ThreadPoolExecutor p = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new CustomTPE.DiscardOldestPolicy()); + + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute(null) throws NPE + */ + public void testExecuteNull() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + 1L, SECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * setCorePoolSize of negative value throws IllegalArgumentException + */ + public void testCorePoolSizeIllegalArgumentException() { + final ThreadPoolExecutor p = + new CustomTPE(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setCorePoolSize(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * setMaximumPoolSize(int) throws IllegalArgumentException + * if given a value less the core pool size + */ + public void testMaximumPoolSizeIllegalArgumentException() { + final ThreadPoolExecutor p = + new CustomTPE(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setMaximumPoolSize(1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * setMaximumPoolSize throws IllegalArgumentException + * if given a negative value + */ + public void testMaximumPoolSizeIllegalArgumentException2() { + final ThreadPoolExecutor p = + new CustomTPE(2, 3, + LONG_DELAY_MS, + MILLISECONDS,new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setMaximumPoolSize(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * setKeepAliveTime throws IllegalArgumentException + * when given a negative value + */ + public void testKeepAliveTimeIllegalArgumentException() { + final ThreadPoolExecutor p = + new CustomTPE(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setKeepAliveTime(-1, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * terminated() is called on termination + */ + public void testTerminated() { + CustomTPE p = new CustomTPE(); + try (PoolCleaner cleaner = cleaner(p)) { + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.terminatedCalled()); + assertTrue(p.isShutdown()); + } + } + + /** + * beforeExecute and afterExecute are called when executing task + */ + public void testBeforeAfter() throws InterruptedException { + CustomTPE p = new CustomTPE(); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + p.execute(new CheckedRunnable() { + public void realRun() { + done.countDown(); + }}); + await(p.afterCalled); + assertEquals(0, done.getCount()); + assertTrue(p.afterCalled()); + assertTrue(p.beforeCalled()); + } + } + + /** + * completed submit of callable returns result + */ + public void testSubmitCallable() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new StringTask()); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * completed submit of runnable returns successfully + */ + public void testSubmitRunnable() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable()); + future.get(); + assertTrue(future.isDone()); + } + } + + /** + * completed submit of (runnable, result) returns result + */ + public void testSubmitRunnable2() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAny(null) throws NPE + */ + public void testInvokeAny1() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IAE + */ + public void testInvokeAny2() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NPE if c has null elements + */ + public void testInvokeAny3() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * invokeAny(c) throws ExecutionException if no task completes + */ + public void testInvokeAny4() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task + */ + public void testInvokeAny5() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NPE + */ + public void testInvokeAll1() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NPE if c has null elements + */ + public void testInvokeAll3() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testInvokeAll4() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks + */ + public void testInvokeAll5() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NPE + */ + public void testTimedInvokeAny1() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(,,null) throws NPE + */ + public void testTimedInvokeAnyNullTimeUnit() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IAE + */ + public void testTimedInvokeAny2() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NPE if c has null elements + */ + public void testTimedInvokeAny3() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task + */ + public void testTimedInvokeAny5() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NPE + */ + public void testTimedInvokeAll1() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(,,null) throws NPE + */ + public void testTimedInvokeAllNullTimeUnit() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NPE if c has null elements + */ + public void testTimedInvokeAll3() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks + */ + public void testTimedInvokeAll5() throws Exception { + final ExecutorService e = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAll(c) cancels tasks not completed by timeout + */ + public void testTimedInvokeAll6() throws Exception { + for (long timeout = timeoutMillis();;) { + final CountDownLatch done = new CountDownLatch(1); + final Callable waiter = new CheckedCallable() { + public String realCall() { + try { done.await(LONG_DELAY_MS, MILLISECONDS); } + catch (InterruptedException ok) {} + return "1"; }}; + final ExecutorService p = + new CustomTPE(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + List> tasks = new ArrayList<>(); + tasks.add(new StringTask("0")); + tasks.add(waiter); + tasks.add(new StringTask("2")); + long startTime = System.nanoTime(); + List> futures = + p.invokeAll(tasks, timeout, MILLISECONDS); + assertEquals(tasks.size(), futures.size()); + assertTrue(millisElapsedSince(startTime) >= timeout); + for (Future future : futures) + assertTrue(future.isDone()); + assertTrue(futures.get(1).isCancelled()); + try { + assertEquals("0", futures.get(0).get()); + assertEquals("2", futures.get(2).get()); + break; + } catch (CancellationException retryWithLongerTimeout) { + timeout *= 2; + if (timeout >= LONG_DELAY_MS / 2) + fail("expected exactly one task to be cancelled"); + } + } + } + } + + /** + * Execution continues if there is at least one thread even if + * thread factory fails to create more + */ + public void testFailingThreadFactory() throws InterruptedException { + final ExecutorService e = + new CustomTPE(100, 100, + LONG_DELAY_MS, MILLISECONDS, + new LinkedBlockingQueue(), + new FailingThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + final int TASKS = 100; + final CountDownLatch done = new CountDownLatch(TASKS); + for (int k = 0; k < TASKS; ++k) + e.execute(new CheckedRunnable() { + public void realRun() { + done.countDown(); + }}); + assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); + } + } + + /** + * allowsCoreThreadTimeOut is by default false. + */ + public void testAllowsCoreThreadTimeOut() { + final ThreadPoolExecutor p = + new CustomTPE(2, 2, + 1000, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.allowsCoreThreadTimeOut()); + } + } + + /** + * allowCoreThreadTimeOut(true) causes idle threads to time out + */ + public void testAllowCoreThreadTimeOut_true() throws Exception { + long keepAliveTime = timeoutMillis(); + final ThreadPoolExecutor p = + new CustomTPE(2, 10, + keepAliveTime, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + p.allowCoreThreadTimeOut(true); + p.execute(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + }}); + await(threadStarted); + delay(keepAliveTime); + long startTime = System.nanoTime(); + while (p.getPoolSize() > 0 + && millisElapsedSince(startTime) < LONG_DELAY_MS) + Thread.yield(); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + assertEquals(0, p.getPoolSize()); + } + } + + /** + * allowCoreThreadTimeOut(false) causes idle threads not to time out + */ + public void testAllowCoreThreadTimeOut_false() throws Exception { + long keepAliveTime = timeoutMillis(); + final ThreadPoolExecutor p = + new CustomTPE(2, 10, + keepAliveTime, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + p.allowCoreThreadTimeOut(false); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertTrue(p.getPoolSize() >= 1); + }}); + delay(2 * keepAliveTime); + assertTrue(p.getPoolSize() >= 1); + } + } + + /** + * get(cancelled task) throws CancellationException + * (in part, a test of CustomTPE itself) + */ + public void testGet_cancelled() throws Exception { + final CountDownLatch done = new CountDownLatch(1); + final ExecutorService e = + new CustomTPE(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new LinkedBlockingQueue()); + try (PoolCleaner cleaner = cleaner(e, done)) { + final CountDownLatch blockerStarted = new CountDownLatch(1); + final List> futures = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + Runnable r = new CheckedRunnable() { public void realRun() + throws Throwable { + blockerStarted.countDown(); + assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS)); + }}; + futures.add(e.submit(r)); + } + await(blockerStarted); + for (Future future : futures) future.cancel(false); + for (Future future : futures) { + try { + future.get(); + shouldThrow(); + } catch (CancellationException success) {} + try { + future.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) {} + assertTrue(future.isCancelled()); + assertTrue(future.isDone()); + } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java new file mode 100644 index 00000000000..5df3c94b120 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java @@ -0,0 +1,2093 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadPoolExecutorTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(ThreadPoolExecutorTest.class); + } + + static class ExtendedTPE extends ThreadPoolExecutor { + final CountDownLatch beforeCalled = new CountDownLatch(1); + final CountDownLatch afterCalled = new CountDownLatch(1); + final CountDownLatch terminatedCalled = new CountDownLatch(1); + + public ExtendedTPE() { + super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue()); + } + protected void beforeExecute(Thread t, Runnable r) { + beforeCalled.countDown(); + } + protected void afterExecute(Runnable r, Throwable t) { + afterCalled.countDown(); + } + protected void terminated() { + terminatedCalled.countDown(); + } + + public boolean beforeCalled() { + return beforeCalled.getCount() == 0; + } + public boolean afterCalled() { + return afterCalled.getCount() == 0; + } + public boolean terminatedCalled() { + return terminatedCalled.getCount() == 0; + } + } + + static class FailingThreadFactory implements ThreadFactory { + int calls = 0; + public Thread newThread(Runnable r) { + if (++calls > 1) return null; + return new Thread(r); + } + } + + /** + * execute successfully executes a runnable + */ + public void testExecute() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + final Runnable task = new CheckedRunnable() { + public void realRun() { done.countDown(); }}; + p.execute(task); + assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); + } + } + + /** + * getActiveCount increases but doesn't overestimate, when a + * thread becomes active + */ + public void testGetActiveCount() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getActiveCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getActiveCount()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getActiveCount()); + } + } + + /** + * prestartCoreThread starts a thread if under corePoolSize, else doesn't + */ + public void testPrestartCoreThread() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 6, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(0, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(1, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(2, p.getPoolSize()); + assertFalse(p.prestartCoreThread()); + assertEquals(2, p.getPoolSize()); + p.setCorePoolSize(4); + assertTrue(p.prestartCoreThread()); + assertEquals(3, p.getPoolSize()); + assertTrue(p.prestartCoreThread()); + assertEquals(4, p.getPoolSize()); + assertFalse(p.prestartCoreThread()); + assertEquals(4, p.getPoolSize()); + } + } + + /** + * prestartAllCoreThreads starts all corePoolSize threads + */ + public void testPrestartAllCoreThreads() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 6, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(0, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(2, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(2, p.getPoolSize()); + p.setCorePoolSize(4); + p.prestartAllCoreThreads(); + assertEquals(4, p.getPoolSize()); + p.prestartAllCoreThreads(); + assertEquals(4, p.getPoolSize()); + } + } + + /** + * getCompletedTaskCount increases, but doesn't overestimate, + * when tasks complete + */ + public void testGetCompletedTaskCount() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch threadProceed = new CountDownLatch(1); + final CountDownLatch threadDone = new CountDownLatch(1); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.await(); + threadDone.countDown(); + }}); + await(threadStarted); + assertEquals(0, p.getCompletedTaskCount()); + threadProceed.countDown(); + threadDone.await(); + long startTime = System.nanoTime(); + while (p.getCompletedTaskCount() != 1) { + if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out"); + Thread.yield(); + } + } + } + + /** + * getCorePoolSize returns size given in constructor if not otherwise set + */ + public void testGetCorePoolSize() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getCorePoolSize()); + } + } + + /** + * getKeepAliveTime returns value given in constructor if not otherwise set + */ + public void testGetKeepAliveTime() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 2, + 1000, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(1, p.getKeepAliveTime(SECONDS)); + } + } + + /** + * getThreadFactory returns factory in constructor if not set + */ + public void testGetThreadFactory() { + ThreadFactory threadFactory = new SimpleThreadFactory(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10), + threadFactory, + new NoOpREHandler()); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory sets the thread factory returned by getThreadFactory + */ + public void testSetThreadFactory() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + ThreadFactory threadFactory = new SimpleThreadFactory(); + p.setThreadFactory(threadFactory); + assertSame(threadFactory, p.getThreadFactory()); + } + } + + /** + * setThreadFactory(null) throws NPE + */ + public void testSetThreadFactoryNull() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setThreadFactory(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * getRejectedExecutionHandler returns handler in constructor if not set + */ + public void testGetRejectedExecutionHandler() { + final RejectedExecutionHandler handler = new NoOpREHandler(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10), + handler); + try (PoolCleaner cleaner = cleaner(p)) { + assertSame(handler, p.getRejectedExecutionHandler()); + } + } + + /** + * setRejectedExecutionHandler sets the handler returned by + * getRejectedExecutionHandler + */ + public void testSetRejectedExecutionHandler() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + RejectedExecutionHandler handler = new NoOpREHandler(); + p.setRejectedExecutionHandler(handler); + assertSame(handler, p.getRejectedExecutionHandler()); + } + } + + /** + * setRejectedExecutionHandler(null) throws NPE + */ + public void testSetRejectedExecutionHandlerNull() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setRejectedExecutionHandler(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * getLargestPoolSize increases, but doesn't overestimate, when + * multiple threads active + */ + public void testGetLargestPoolSize() throws InterruptedException { + final int THREADS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(THREADS, THREADS, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getLargestPoolSize()); + final CountDownLatch threadsStarted = new CountDownLatch(THREADS); + for (int i = 0; i < THREADS; i++) + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadsStarted.countDown(); + await(done); + assertEquals(THREADS, p.getLargestPoolSize()); + }}); + await(threadsStarted); + assertEquals(THREADS, p.getLargestPoolSize()); + } + assertEquals(THREADS, p.getLargestPoolSize()); + } + + /** + * getMaximumPoolSize returns value given in constructor if not + * otherwise set + */ + public void testGetMaximumPoolSize() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertEquals(3, p.getMaximumPoolSize()); + p.setMaximumPoolSize(5); + assertEquals(5, p.getMaximumPoolSize()); + p.setMaximumPoolSize(4); + assertEquals(4, p.getMaximumPoolSize()); + } + } + + /** + * getPoolSize increases, but doesn't overestimate, when threads + * become active + */ + public void testGetPoolSize() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(0, p.getPoolSize()); + final CountDownLatch threadStarted = new CountDownLatch(1); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getPoolSize()); + } + } + + /** + * getTaskCount increases, but doesn't overestimate, when tasks submitted + */ + public void testGetTaskCount() throws InterruptedException { + final int TASKS = 3; + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + assertEquals(0, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertEquals(1, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + for (int i = 0; i < TASKS; i++) { + assertEquals(1 + i, p.getTaskCount()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertEquals(1 + TASKS, p.getTaskCount()); + await(done); + }}); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(0, p.getCompletedTaskCount()); + } + assertEquals(1 + TASKS, p.getTaskCount()); + assertEquals(1 + TASKS, p.getCompletedTaskCount()); + } + + /** + * isShutdown is false before shutdown, true after + */ + public void testIsShutdown() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isShutdown()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.isShutdown()); + } + } + + /** + * awaitTermination on a non-shutdown pool times out + */ + public void testAwaitTermination_timesOut() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.isTerminated()); + assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS)); + assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS)); + assertFalse(p.awaitTermination(-1L, NANOSECONDS)); + assertFalse(p.awaitTermination(-1L, MILLISECONDS)); + assertFalse(p.awaitTermination(0L, NANOSECONDS)); + assertFalse(p.awaitTermination(0L, MILLISECONDS)); + long timeoutNanos = 999999L; + long startTime = System.nanoTime(); + assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS)); + assertTrue(System.nanoTime() - startTime >= timeoutNanos); + assertFalse(p.isTerminated()); + startTime = System.nanoTime(); + long timeoutMillis = timeoutMillis(); + assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + assertFalse(p.isTerminated()); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + } + } + + /** + * isTerminated is false before termination, true after + */ + public void testIsTerminated() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * isTerminating is not true when running or when terminated + */ + public void testIsTerminating() throws InterruptedException { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + assertFalse(p.isTerminating()); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + assertFalse(p.isTerminating()); + threadStarted.countDown(); + await(done); + }}); + await(threadStarted); + assertFalse(p.isTerminating()); + done.countDown(); + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertFalse(p.isTerminating()); + } + } + + /** + * getQueue returns the work queue, which contains queued tasks + */ + public void testGetQueue() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + FutureTask[] tasks = new FutureTask[5]; + for (int i = 0; i < tasks.length; i++) { + Callable task = new CheckedCallable() { + public Boolean realCall() throws InterruptedException { + threadStarted.countDown(); + assertSame(q, p.getQueue()); + await(done); + return Boolean.TRUE; + }}; + tasks[i] = new FutureTask(task); + p.execute(tasks[i]); + } + await(threadStarted); + assertSame(q, p.getQueue()); + assertFalse(q.contains(tasks[0])); + assertTrue(q.contains(tasks[tasks.length - 1])); + assertEquals(tasks.length - 1, q.size()); + } + } + + /** + * remove(task) removes queued task, and fails to remove active task + */ + public void testRemove() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable[] tasks = new Runnable[6]; + final CountDownLatch threadStarted = new CountDownLatch(1); + for (int i = 0; i < tasks.length; i++) { + tasks[i] = new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + await(done); + }}; + p.execute(tasks[i]); + } + await(threadStarted); + assertFalse(p.remove(tasks[0])); + assertTrue(q.contains(tasks[4])); + assertTrue(q.contains(tasks[3])); + assertTrue(p.remove(tasks[4])); + assertFalse(p.remove(tasks[4])); + assertFalse(q.contains(tasks[4])); + assertTrue(q.contains(tasks[3])); + assertTrue(p.remove(tasks[3])); + assertFalse(q.contains(tasks[3])); + } + } + + /** + * purge removes cancelled tasks from the queue + */ + public void testPurge() throws InterruptedException { + final CountDownLatch threadStarted = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(1); + final BlockingQueue q = new ArrayBlockingQueue(10); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + q); + try (PoolCleaner cleaner = cleaner(p, done)) { + FutureTask[] tasks = new FutureTask[5]; + for (int i = 0; i < tasks.length; i++) { + Callable task = new CheckedCallable() { + public Boolean realCall() throws InterruptedException { + threadStarted.countDown(); + await(done); + return Boolean.TRUE; + }}; + tasks[i] = new FutureTask(task); + p.execute(tasks[i]); + } + await(threadStarted); + assertEquals(tasks.length, p.getTaskCount()); + assertEquals(tasks.length - 1, q.size()); + assertEquals(1L, p.getActiveCount()); + assertEquals(0L, p.getCompletedTaskCount()); + tasks[4].cancel(true); + tasks[3].cancel(false); + p.purge(); + assertEquals(tasks.length - 3, q.size()); + assertEquals(tasks.length - 2, p.getTaskCount()); + p.purge(); // Nothing to do + assertEquals(tasks.length - 3, q.size()); + assertEquals(tasks.length - 2, p.getTaskCount()); + } + } + + /** + * shutdownNow returns a list containing tasks that were not run, + * and those tasks are drained from the queue + */ + public void testShutdownNow() throws InterruptedException { + final int poolSize = 2; + final int count = 5; + final AtomicInteger ran = new AtomicInteger(0); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(poolSize, poolSize, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + final CountDownLatch threadsStarted = new CountDownLatch(poolSize); + Runnable waiter = new CheckedRunnable() { public void realRun() { + threadsStarted.countDown(); + try { + MILLISECONDS.sleep(2 * LONG_DELAY_MS); + } catch (InterruptedException success) {} + ran.getAndIncrement(); + }}; + for (int i = 0; i < count; i++) + p.execute(waiter); + await(threadsStarted); + assertEquals(poolSize, p.getActiveCount()); + assertEquals(0, p.getCompletedTaskCount()); + final List queuedTasks; + try { + queuedTasks = p.shutdownNow(); + } catch (SecurityException ok) { + return; // Allowed in case test doesn't have privs + } + assertTrue(p.isShutdown()); + assertTrue(p.getQueue().isEmpty()); + assertEquals(count - poolSize, queuedTasks.size()); + assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS)); + assertTrue(p.isTerminated()); + assertEquals(poolSize, ran.get()); + assertEquals(poolSize, p.getCompletedTaskCount()); + } + + // Exception Tests + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor1() { + try { + new ThreadPoolExecutor(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor2() { + try { + new ThreadPoolExecutor(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor3() { + try { + new ThreadPoolExecutor(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor4() { + try { + new ThreadPoolExecutor(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor5() { + try { + new ThreadPoolExecutor(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10)); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + (BlockingQueue) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor6() { + try { + new ThreadPoolExecutor(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor7() { + try { + new ThreadPoolExecutor(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor8() { + try { + new ThreadPoolExecutor(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor9() { + try { + new ThreadPoolExecutor(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor10() { + try { + new ThreadPoolExecutor(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException2() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + (BlockingQueue) null, + new SimpleThreadFactory()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if threadFactory is set to null + */ + public void testConstructorNullPointerException3() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (ThreadFactory) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor11() { + try { + new ThreadPoolExecutor(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor12() { + try { + new ThreadPoolExecutor(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor13() { + try { + new ThreadPoolExecutor(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor14() { + try { + new ThreadPoolExecutor(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor15() { + try { + new ThreadPoolExecutor(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is set to null + */ + public void testConstructorNullPointerException4() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + (BlockingQueue) null, + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if handler is set to null + */ + public void testConstructorNullPointerException5() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (RejectedExecutionHandler) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if corePoolSize argument is less than zero + */ + public void testConstructor16() { + try { + new ThreadPoolExecutor(-1, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is less than zero + */ + public void testConstructor17() { + try { + new ThreadPoolExecutor(1, -1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if maximumPoolSize is equal to zero + */ + public void testConstructor18() { + try { + new ThreadPoolExecutor(1, 0, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if keepAliveTime is less than zero + */ + public void testConstructor19() { + try { + new ThreadPoolExecutor(1, 2, -1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if corePoolSize is greater than the maximumPoolSize + */ + public void testConstructor20() { + try { + new ThreadPoolExecutor(2, 1, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** + * Constructor throws if workQueue is null + */ + public void testConstructorNullPointerException6() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + (BlockingQueue) null, + new SimpleThreadFactory(), + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if handler is null + */ + public void testConstructorNullPointerException7() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + new SimpleThreadFactory(), + (RejectedExecutionHandler) null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Constructor throws if ThreadFactory is null + */ + public void testConstructorNullPointerException8() { + try { + new ThreadPoolExecutor(1, 2, 1L, SECONDS, + new ArrayBlockingQueue(10), + (ThreadFactory) null, + new NoOpREHandler()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * get of submitted callable throws InterruptedException if interrupted + */ + public void testInterruptedSubmit() throws InterruptedException { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + 60, SECONDS, + new ArrayBlockingQueue(10)); + + try (PoolCleaner cleaner = cleaner(p, done)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws Exception { + Callable task = new CheckedCallable() { + public Boolean realCall() throws InterruptedException { + threadStarted.countDown(); + await(done); + return Boolean.TRUE; + }}; + p.submit(task).get(); + }}); + + await(threadStarted); + t.interrupt(); + awaitTermination(t); + } + } + + /** + * execute throws RejectedExecutionException if saturated. + */ + public void testSaturatedExecute() { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable task = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + for (int i = 0; i < 2; ++i) + p.execute(task); + for (int i = 0; i < 2; ++i) { + try { + p.execute(task); + shouldThrow(); + } catch (RejectedExecutionException success) {} + assertTrue(p.getTaskCount() <= 2); + } + } + } + + /** + * submit(runnable) throws RejectedExecutionException if saturated. + */ + public void testSaturatedSubmitRunnable() { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable task = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + for (int i = 0; i < 2; ++i) + p.submit(task); + for (int i = 0; i < 2; ++i) { + try { + p.execute(task); + shouldThrow(); + } catch (RejectedExecutionException success) {} + assertTrue(p.getTaskCount() <= 2); + } + } + } + + /** + * submit(callable) throws RejectedExecutionException if saturated. + */ + public void testSaturatedSubmitCallable() { + final CountDownLatch done = new CountDownLatch(1); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try (PoolCleaner cleaner = cleaner(p, done)) { + Runnable task = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + for (int i = 0; i < 2; ++i) + p.submit(Executors.callable(task)); + for (int i = 0; i < 2; ++i) { + try { + p.execute(task); + shouldThrow(); + } catch (RejectedExecutionException success) {} + assertTrue(p.getTaskCount() <= 2); + } + } + } + + /** + * executor using CallerRunsPolicy runs task if saturated. + */ + public void testSaturatedExecute2() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, + MILLISECONDS, + new ArrayBlockingQueue(1), + new ThreadPoolExecutor.CallerRunsPolicy()); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + Runnable blocker = new CheckedRunnable() { + public void realRun() throws InterruptedException { + await(done); + }}; + p.execute(blocker); + TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5]; + for (int i = 0; i < tasks.length; i++) + tasks[i] = new TrackedNoOpRunnable(); + for (int i = 0; i < tasks.length; i++) + p.execute(tasks[i]); + for (int i = 1; i < tasks.length; i++) + assertTrue(tasks[i].done); + assertFalse(tasks[0].done); // waiting in queue + done.countDown(); + } + } + + /** + * executor using DiscardPolicy drops task if saturated. + */ + public void testSaturatedExecute3() { + final CountDownLatch done = new CountDownLatch(1); + final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5]; + for (int i = 0; i < tasks.length; ++i) + tasks[i] = new TrackedNoOpRunnable(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new ThreadPoolExecutor.DiscardPolicy()); + try (PoolCleaner cleaner = cleaner(p, done)) { + p.execute(awaiter(done)); + + for (TrackedNoOpRunnable task : tasks) + p.execute(task); + for (int i = 1; i < tasks.length; i++) + assertFalse(tasks[i].done); + } + for (int i = 1; i < tasks.length; i++) + assertFalse(tasks[i].done); + assertTrue(tasks[0].done); // was waiting in queue + } + + /** + * executor using DiscardOldestPolicy drops oldest task if saturated. + */ + public void testSaturatedExecute4() { + final CountDownLatch done = new CountDownLatch(1); + LatchAwaiter r1 = awaiter(done); + LatchAwaiter r2 = awaiter(done); + LatchAwaiter r3 = awaiter(done); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new ThreadPoolExecutor.DiscardOldestPolicy()); + try (PoolCleaner cleaner = cleaner(p, done)) { + assertEquals(LatchAwaiter.NEW, r1.state); + assertEquals(LatchAwaiter.NEW, r2.state); + assertEquals(LatchAwaiter.NEW, r3.state); + p.execute(r1); + p.execute(r2); + assertTrue(p.getQueue().contains(r2)); + p.execute(r3); + assertFalse(p.getQueue().contains(r2)); + assertTrue(p.getQueue().contains(r3)); + } + assertEquals(LatchAwaiter.DONE, r1.state); + assertEquals(LatchAwaiter.NEW, r2.state); + assertEquals(LatchAwaiter.DONE, r3.state); + } + + /** + * execute throws RejectedExecutionException if shutdown + */ + public void testRejectedExecutionExceptionOnShutdown() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1)); + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(new NoOpRunnable()); + shouldThrow(); + } catch (RejectedExecutionException success) {} + } + } + + /** + * execute using CallerRunsPolicy drops task on shutdown + */ + public void testCallerRunsOnShutdown() { + RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), h); + + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute using DiscardPolicy drops task on shutdown + */ + public void testDiscardOnShutdown() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new ThreadPoolExecutor.DiscardPolicy()); + + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute using DiscardOldestPolicy drops task on shutdown + */ + public void testDiscardOldestOnShutdown() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(1), + new ThreadPoolExecutor.DiscardOldestPolicy()); + + try { p.shutdown(); } catch (SecurityException ok) { return; } + try (PoolCleaner cleaner = cleaner(p)) { + TrackedNoOpRunnable r = new TrackedNoOpRunnable(); + p.execute(r); + assertFalse(r.done); + } + } + + /** + * execute(null) throws NPE + */ + public void testExecuteNull() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + 1L, SECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.execute(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * setCorePoolSize of negative value throws IllegalArgumentException + */ + public void testCorePoolSizeIllegalArgumentException() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setCorePoolSize(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * setMaximumPoolSize(int) throws IllegalArgumentException if + * given a value less the core pool size + */ + public void testMaximumPoolSizeIllegalArgumentException() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setMaximumPoolSize(1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * setMaximumPoolSize throws IllegalArgumentException + * if given a negative value + */ + public void testMaximumPoolSizeIllegalArgumentException2() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setMaximumPoolSize(-1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * Configuration changes that allow core pool size greater than + * max pool size result in IllegalArgumentException. + */ + public void testPoolSizeInvariants() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + for (int s = 1; s < 5; s++) { + p.setMaximumPoolSize(s); + p.setCorePoolSize(s); + try { + p.setMaximumPoolSize(s - 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertEquals(s, p.getCorePoolSize()); + assertEquals(s, p.getMaximumPoolSize()); + try { + p.setCorePoolSize(s + 1); + shouldThrow(); + } catch (IllegalArgumentException success) {} + assertEquals(s, p.getCorePoolSize()); + assertEquals(s, p.getMaximumPoolSize()); + } + } + } + + /** + * setKeepAliveTime throws IllegalArgumentException + * when given a negative value + */ + public void testKeepAliveTimeIllegalArgumentException() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 3, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + try { + p.setKeepAliveTime(-1, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * terminated() is called on termination + */ + public void testTerminated() { + ExtendedTPE p = new ExtendedTPE(); + try (PoolCleaner cleaner = cleaner(p)) { + try { p.shutdown(); } catch (SecurityException ok) { return; } + assertTrue(p.terminatedCalled()); + assertTrue(p.isShutdown()); + } + } + + /** + * beforeExecute and afterExecute are called when executing task + */ + public void testBeforeAfter() throws InterruptedException { + ExtendedTPE p = new ExtendedTPE(); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch done = new CountDownLatch(1); + p.execute(new CheckedRunnable() { + public void realRun() { + done.countDown(); + }}); + await(p.afterCalled); + assertEquals(0, done.getCount()); + assertTrue(p.afterCalled()); + assertTrue(p.beforeCalled()); + } + } + + /** + * completed submit of callable returns result + */ + public void testSubmitCallable() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new StringTask()); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * completed submit of runnable returns successfully + */ + public void testSubmitRunnable() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable()); + future.get(); + assertTrue(future.isDone()); + } + } + + /** + * completed submit of (runnable, result) returns result + */ + public void testSubmitRunnable2() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + Future future = e.submit(new NoOpRunnable(), TEST_STRING); + String result = future.get(); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAny(null) throws NPE + */ + public void testInvokeAny1() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAny(empty collection) throws IAE + */ + public void testInvokeAny2() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>()); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * invokeAny(c) throws NPE if c has null elements + */ + public void testInvokeAny3() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * invokeAny(c) throws ExecutionException if no task completes + */ + public void testInvokeAny4() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAny(c) returns result of some task + */ + public void testInvokeAny5() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l); + assertSame(TEST_STRING, result); + } + } + + /** + * invokeAll(null) throws NPE + */ + public void testInvokeAll1() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * invokeAll(empty collection) returns empty collection + */ + public void testInvokeAll2() throws InterruptedException { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>()); + assertTrue(r.isEmpty()); + } + } + + /** + * invokeAll(c) throws NPE if c has null elements + */ + public void testInvokeAll3() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testInvokeAll4() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = e.invokeAll(l); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * invokeAll(c) returns results of all completed tasks + */ + public void testInvokeAll5() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = e.invokeAll(l); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAny(null) throws NPE + */ + public void testTimedInvokeAny1() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(,,null) throws NPE + */ + public void testTimedInvokeAnyNullTimeUnit() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAny(empty collection) throws IAE + */ + public void testTimedInvokeAny2() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAny(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + + /** + * timed invokeAny(c) throws NPE if c has null elements + */ + public void testTimedInvokeAny3() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(latchAwaitingStringTask(latch)); + l.add(null); + try { + e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + latch.countDown(); + } + } + + /** + * timed invokeAny(c) throws ExecutionException if no task completes + */ + public void testTimedInvokeAny4() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new NPETask()); + try { + e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAny(c) returns result of some task + */ + public void testTimedInvokeAny5() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + long startTime = System.nanoTime(); + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS); + assertSame(TEST_STRING, result); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + } + } + + /** + * timed invokeAll(null) throws NPE + */ + public void testTimedInvokeAll1() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + try { + e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(,,null) throws NPE + */ + public void testTimedInvokeAllNullTimeUnit() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, null); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * timed invokeAll(empty collection) returns empty collection + */ + public void testTimedInvokeAll2() throws InterruptedException { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> r = e.invokeAll(new ArrayList>(), + MEDIUM_DELAY_MS, MILLISECONDS); + assertTrue(r.isEmpty()); + } + } + + /** + * timed invokeAll(c) throws NPE if c has null elements + */ + public void testTimedInvokeAll3() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(null); + try { + e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + } + + /** + * get of element of invokeAll(c) throws exception on failed task + */ + public void testTimedInvokeAll4() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new NPETask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(1, futures.size()); + try { + futures.get(0).get(); + shouldThrow(); + } catch (ExecutionException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + } + } + + /** + * timed invokeAll(c) returns results of all completed tasks + */ + public void testTimedInvokeAll5() throws Exception { + final ExecutorService e = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(e)) { + List> l = new ArrayList>(); + l.add(new StringTask()); + l.add(new StringTask()); + List> futures = + e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS); + assertEquals(2, futures.size()); + for (Future future : futures) + assertSame(TEST_STRING, future.get()); + } + } + + /** + * timed invokeAll(c) cancels tasks not completed by timeout + */ + public void testTimedInvokeAll6() throws Exception { + for (long timeout = timeoutMillis();;) { + final CountDownLatch done = new CountDownLatch(1); + final Callable waiter = new CheckedCallable() { + public String realCall() { + try { done.await(LONG_DELAY_MS, MILLISECONDS); } + catch (InterruptedException ok) {} + return "1"; }}; + final ExecutorService p = + new ThreadPoolExecutor(2, 2, + LONG_DELAY_MS, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p, done)) { + List> tasks = new ArrayList<>(); + tasks.add(new StringTask("0")); + tasks.add(waiter); + tasks.add(new StringTask("2")); + long startTime = System.nanoTime(); + List> futures = + p.invokeAll(tasks, timeout, MILLISECONDS); + assertEquals(tasks.size(), futures.size()); + assertTrue(millisElapsedSince(startTime) >= timeout); + for (Future future : futures) + assertTrue(future.isDone()); + assertTrue(futures.get(1).isCancelled()); + try { + assertEquals("0", futures.get(0).get()); + assertEquals("2", futures.get(2).get()); + break; + } catch (CancellationException retryWithLongerTimeout) { + timeout *= 2; + if (timeout >= LONG_DELAY_MS / 2) + fail("expected exactly one task to be cancelled"); + } + } + } + } + + /** + * Execution continues if there is at least one thread even if + * thread factory fails to create more + */ + public void testFailingThreadFactory() throws InterruptedException { + final ExecutorService e = + new ThreadPoolExecutor(100, 100, + LONG_DELAY_MS, MILLISECONDS, + new LinkedBlockingQueue(), + new FailingThreadFactory()); + try (PoolCleaner cleaner = cleaner(e)) { + final int TASKS = 100; + final CountDownLatch done = new CountDownLatch(TASKS); + for (int k = 0; k < TASKS; ++k) + e.execute(new CheckedRunnable() { + public void realRun() { + done.countDown(); + }}); + assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS)); + } + } + + /** + * allowsCoreThreadTimeOut is by default false. + */ + public void testAllowsCoreThreadTimeOut() { + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 2, + 1000, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + assertFalse(p.allowsCoreThreadTimeOut()); + } + } + + /** + * allowCoreThreadTimeOut(true) causes idle threads to time out + */ + public void testAllowCoreThreadTimeOut_true() throws Exception { + long keepAliveTime = timeoutMillis(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 10, + keepAliveTime, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + p.allowCoreThreadTimeOut(true); + p.execute(new CheckedRunnable() { + public void realRun() { + threadStarted.countDown(); + assertEquals(1, p.getPoolSize()); + }}); + await(threadStarted); + delay(keepAliveTime); + long startTime = System.nanoTime(); + while (p.getPoolSize() > 0 + && millisElapsedSince(startTime) < LONG_DELAY_MS) + Thread.yield(); + assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); + assertEquals(0, p.getPoolSize()); + } + } + + /** + * allowCoreThreadTimeOut(false) causes idle threads not to time out + */ + public void testAllowCoreThreadTimeOut_false() throws Exception { + long keepAliveTime = timeoutMillis(); + final ThreadPoolExecutor p = + new ThreadPoolExecutor(2, 10, + keepAliveTime, MILLISECONDS, + new ArrayBlockingQueue(10)); + try (PoolCleaner cleaner = cleaner(p)) { + final CountDownLatch threadStarted = new CountDownLatch(1); + p.allowCoreThreadTimeOut(false); + p.execute(new CheckedRunnable() { + public void realRun() throws InterruptedException { + threadStarted.countDown(); + assertTrue(p.getPoolSize() >= 1); + }}); + delay(2 * keepAliveTime); + assertTrue(p.getPoolSize() >= 1); + } + } + + /** + * execute allows the same task to be submitted multiple times, even + * if rejected + */ + public void testRejectedRecycledTask() throws InterruptedException { + final int nTasks = 1000; + final CountDownLatch done = new CountDownLatch(nTasks); + final Runnable recycledTask = new Runnable() { + public void run() { + done.countDown(); + }}; + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 30, + 60, SECONDS, + new ArrayBlockingQueue(30)); + try (PoolCleaner cleaner = cleaner(p)) { + for (int i = 0; i < nTasks; ++i) { + for (;;) { + try { + p.execute(recycledTask); + break; + } + catch (RejectedExecutionException ignore) {} + } + } + // enough time to run all tasks + assertTrue(done.await(nTasks * SHORT_DELAY_MS, MILLISECONDS)); + } + } + + /** + * get(cancelled task) throws CancellationException + */ + public void testGet_cancelled() throws Exception { + final CountDownLatch done = new CountDownLatch(1); + final ExecutorService e = + new ThreadPoolExecutor(1, 1, + LONG_DELAY_MS, MILLISECONDS, + new LinkedBlockingQueue()); + try (PoolCleaner cleaner = cleaner(e, done)) { + final CountDownLatch blockerStarted = new CountDownLatch(1); + final List> futures = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + Runnable r = new CheckedRunnable() { public void realRun() + throws Throwable { + blockerStarted.countDown(); + assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS)); + }}; + futures.add(e.submit(r)); + } + await(blockerStarted); + for (Future future : futures) future.cancel(false); + for (Future future : futures) { + try { + future.get(); + shouldThrow(); + } catch (CancellationException success) {} + try { + future.get(LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (CancellationException success) {} + assertTrue(future.isCancelled()); + assertTrue(future.isDone()); + } + } + } + +} diff --git a/jdk/test/java/util/concurrent/tck/ThreadTest.java b/jdk/test/java/util/concurrent/tck/ThreadTest.java new file mode 100644 index 00000000000..939d590beb9 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/ThreadTest.java @@ -0,0 +1,100 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ThreadTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(ThreadTest.class); + } + + static class MyHandler implements Thread.UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e) { + e.printStackTrace(); + } + } + + /** + * getUncaughtExceptionHandler returns ThreadGroup unless set, + * otherwise returning value of last setUncaughtExceptionHandler. + */ + public void testGetAndSetUncaughtExceptionHandler() { + // these must be done all at once to avoid state + // dependencies across tests + Thread current = Thread.currentThread(); + ThreadGroup tg = current.getThreadGroup(); + MyHandler eh = new MyHandler(); + assertSame(tg, current.getUncaughtExceptionHandler()); + current.setUncaughtExceptionHandler(eh); + try { + assertSame(eh, current.getUncaughtExceptionHandler()); + } finally { + current.setUncaughtExceptionHandler(null); + } + assertSame(tg, current.getUncaughtExceptionHandler()); + } + + /** + * getDefaultUncaughtExceptionHandler returns value of last + * setDefaultUncaughtExceptionHandler. + */ + public void testGetAndSetDefaultUncaughtExceptionHandler() { + assertEquals(null, Thread.getDefaultUncaughtExceptionHandler()); + // failure due to securityException is OK. + // Would be nice to explicitly test both ways, but cannot yet. + Thread.UncaughtExceptionHandler defaultHandler + = Thread.getDefaultUncaughtExceptionHandler(); + MyHandler eh = new MyHandler(); + try { + Thread.setDefaultUncaughtExceptionHandler(eh); + try { + assertSame(eh, Thread.getDefaultUncaughtExceptionHandler()); + } finally { + Thread.setDefaultUncaughtExceptionHandler(defaultHandler); + } + } catch (SecurityException ok) { + assertNotNull(System.getSecurityManager()); + } + assertSame(defaultHandler, Thread.getDefaultUncaughtExceptionHandler()); + } + + // How to test actually using UEH within junit? + +} diff --git a/jdk/test/java/util/concurrent/tck/TimeUnitTest.java b/jdk/test/java/util/concurrent/tck/TimeUnitTest.java new file mode 100644 index 00000000000..3b33c9a4971 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/TimeUnitTest.java @@ -0,0 +1,463 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TimeUnitTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + + public static Test suite() { + return new TestSuite(TimeUnitTest.class); + } + + // (loops to 88888 check increments at all time divisions.) + + /** + * convert correctly converts sample values across the units + */ + public void testConvert() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*60*60*24, + SECONDS.convert(t, DAYS)); + assertEquals(t*60*60, + SECONDS.convert(t, HOURS)); + assertEquals(t*60, + SECONDS.convert(t, MINUTES)); + assertEquals(t, + SECONDS.convert(t, SECONDS)); + assertEquals(t, + SECONDS.convert(1000L*t, MILLISECONDS)); + assertEquals(t, + SECONDS.convert(1000000L*t, MICROSECONDS)); + assertEquals(t, + SECONDS.convert(1000000000L*t, NANOSECONDS)); + + assertEquals(1000L*t*60*60*24, + MILLISECONDS.convert(t, DAYS)); + assertEquals(1000L*t*60*60, + MILLISECONDS.convert(t, HOURS)); + assertEquals(1000L*t*60, + MILLISECONDS.convert(t, MINUTES)); + assertEquals(1000L*t, + MILLISECONDS.convert(t, SECONDS)); + assertEquals(t, + MILLISECONDS.convert(t, MILLISECONDS)); + assertEquals(t, + MILLISECONDS.convert(1000L*t, MICROSECONDS)); + assertEquals(t, + MILLISECONDS.convert(1000000L*t, NANOSECONDS)); + + assertEquals(1000000L*t*60*60*24, + MICROSECONDS.convert(t, DAYS)); + assertEquals(1000000L*t*60*60, + MICROSECONDS.convert(t, HOURS)); + assertEquals(1000000L*t*60, + MICROSECONDS.convert(t, MINUTES)); + assertEquals(1000000L*t, + MICROSECONDS.convert(t, SECONDS)); + assertEquals(1000L*t, + MICROSECONDS.convert(t, MILLISECONDS)); + assertEquals(t, + MICROSECONDS.convert(t, MICROSECONDS)); + assertEquals(t, + MICROSECONDS.convert(1000L*t, NANOSECONDS)); + + assertEquals(1000000000L*t*60*60*24, + NANOSECONDS.convert(t, DAYS)); + assertEquals(1000000000L*t*60*60, + NANOSECONDS.convert(t, HOURS)); + assertEquals(1000000000L*t*60, + NANOSECONDS.convert(t, MINUTES)); + assertEquals(1000000000L*t, + NANOSECONDS.convert(t, SECONDS)); + assertEquals(1000000L*t, + NANOSECONDS.convert(t, MILLISECONDS)); + assertEquals(1000L*t, + NANOSECONDS.convert(t, MICROSECONDS)); + assertEquals(t, + NANOSECONDS.convert(t, NANOSECONDS)); + } + } + + /** + * toNanos correctly converts sample values in different units to + * nanoseconds + */ + public void testToNanos() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*1000000000L*60*60*24, + DAYS.toNanos(t)); + assertEquals(t*1000000000L*60*60, + HOURS.toNanos(t)); + assertEquals(t*1000000000L*60, + MINUTES.toNanos(t)); + assertEquals(1000000000L*t, + SECONDS.toNanos(t)); + assertEquals(1000000L*t, + MILLISECONDS.toNanos(t)); + assertEquals(1000L*t, + MICROSECONDS.toNanos(t)); + assertEquals(t, + NANOSECONDS.toNanos(t)); + } + } + + /** + * toMicros correctly converts sample values in different units to + * microseconds + */ + public void testToMicros() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*1000000L*60*60*24, + DAYS.toMicros(t)); + assertEquals(t*1000000L*60*60, + HOURS.toMicros(t)); + assertEquals(t*1000000L*60, + MINUTES.toMicros(t)); + assertEquals(1000000L*t, + SECONDS.toMicros(t)); + assertEquals(1000L*t, + MILLISECONDS.toMicros(t)); + assertEquals(t, + MICROSECONDS.toMicros(t)); + assertEquals(t, + NANOSECONDS.toMicros(t*1000L)); + } + } + + /** + * toMillis correctly converts sample values in different units to + * milliseconds + */ + public void testToMillis() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*1000L*60*60*24, + DAYS.toMillis(t)); + assertEquals(t*1000L*60*60, + HOURS.toMillis(t)); + assertEquals(t*1000L*60, + MINUTES.toMillis(t)); + assertEquals(1000L*t, + SECONDS.toMillis(t)); + assertEquals(t, + MILLISECONDS.toMillis(t)); + assertEquals(t, + MICROSECONDS.toMillis(t*1000L)); + assertEquals(t, + NANOSECONDS.toMillis(t*1000000L)); + } + } + + /** + * toSeconds correctly converts sample values in different units to + * seconds + */ + public void testToSeconds() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*60*60*24, + DAYS.toSeconds(t)); + assertEquals(t*60*60, + HOURS.toSeconds(t)); + assertEquals(t*60, + MINUTES.toSeconds(t)); + assertEquals(t, + SECONDS.toSeconds(t)); + assertEquals(t, + MILLISECONDS.toSeconds(t*1000L)); + assertEquals(t, + MICROSECONDS.toSeconds(t*1000000L)); + assertEquals(t, + NANOSECONDS.toSeconds(t*1000000000L)); + } + } + + /** + * toMinutes correctly converts sample values in different units to + * minutes + */ + public void testToMinutes() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*60*24, + DAYS.toMinutes(t)); + assertEquals(t*60, + HOURS.toMinutes(t)); + assertEquals(t, + MINUTES.toMinutes(t)); + assertEquals(t, + SECONDS.toMinutes(t*60)); + assertEquals(t, + MILLISECONDS.toMinutes(t*1000L*60)); + assertEquals(t, + MICROSECONDS.toMinutes(t*1000000L*60)); + assertEquals(t, + NANOSECONDS.toMinutes(t*1000000000L*60)); + } + } + + /** + * toHours correctly converts sample values in different units to + * hours + */ + public void testToHours() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t*24, + DAYS.toHours(t)); + assertEquals(t, + HOURS.toHours(t)); + assertEquals(t, + MINUTES.toHours(t*60)); + assertEquals(t, + SECONDS.toHours(t*60*60)); + assertEquals(t, + MILLISECONDS.toHours(t*1000L*60*60)); + assertEquals(t, + MICROSECONDS.toHours(t*1000000L*60*60)); + assertEquals(t, + NANOSECONDS.toHours(t*1000000000L*60*60)); + } + } + + /** + * toDays correctly converts sample values in different units to + * days + */ + public void testToDays() { + for (long t = 0; t < 88888; ++t) { + assertEquals(t, + DAYS.toDays(t)); + assertEquals(t, + HOURS.toDays(t*24)); + assertEquals(t, + MINUTES.toDays(t*60*24)); + assertEquals(t, + SECONDS.toDays(t*60*60*24)); + assertEquals(t, + MILLISECONDS.toDays(t*1000L*60*60*24)); + assertEquals(t, + MICROSECONDS.toDays(t*1000000L*60*60*24)); + assertEquals(t, + NANOSECONDS.toDays(t*1000000000L*60*60*24)); + } + } + + /** + * convert saturates positive too-large values to Long.MAX_VALUE + * and negative to LONG.MIN_VALUE + */ + public void testConvertSaturate() { + assertEquals(Long.MAX_VALUE, + NANOSECONDS.convert(Long.MAX_VALUE / 2, SECONDS)); + assertEquals(Long.MIN_VALUE, + NANOSECONDS.convert(-Long.MAX_VALUE / 4, SECONDS)); + assertEquals(Long.MAX_VALUE, + NANOSECONDS.convert(Long.MAX_VALUE / 2, MINUTES)); + assertEquals(Long.MIN_VALUE, + NANOSECONDS.convert(-Long.MAX_VALUE / 4, MINUTES)); + assertEquals(Long.MAX_VALUE, + NANOSECONDS.convert(Long.MAX_VALUE / 2, HOURS)); + assertEquals(Long.MIN_VALUE, + NANOSECONDS.convert(-Long.MAX_VALUE / 4, HOURS)); + assertEquals(Long.MAX_VALUE, + NANOSECONDS.convert(Long.MAX_VALUE / 2, DAYS)); + assertEquals(Long.MIN_VALUE, + NANOSECONDS.convert(-Long.MAX_VALUE / 4, DAYS)); + } + + /** + * toNanos saturates positive too-large values to Long.MAX_VALUE + * and negative to LONG.MIN_VALUE + */ + public void testToNanosSaturate() { + assertEquals(Long.MAX_VALUE, + MILLISECONDS.toNanos(Long.MAX_VALUE / 2)); + assertEquals(Long.MIN_VALUE, + MILLISECONDS.toNanos(-Long.MAX_VALUE / 3)); + } + + /** + * toString returns name of unit + */ + public void testToString() { + assertEquals("SECONDS", SECONDS.toString()); + } + + /** + * name returns name of unit + */ + public void testName() { + assertEquals("SECONDS", SECONDS.name()); + } + + /** + * Timed wait without holding lock throws + * IllegalMonitorStateException + */ + public void testTimedWait_IllegalMonitorException() { + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + Object o = new Object(); + TimeUnit tu = MILLISECONDS; + + try { + tu.timedWait(o, LONG_DELAY_MS); + threadShouldThrow(); + } catch (IllegalMonitorStateException success) {} + }}); + + awaitTermination(t); + } + + /** + * timedWait throws InterruptedException when interrupted + */ + public void testTimedWait_Interruptible() { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + Object o = new Object(); + TimeUnit tu = MILLISECONDS; + + Thread.currentThread().interrupt(); + try { + synchronized (o) { + tu.timedWait(o, LONG_DELAY_MS); + } + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + synchronized (o) { + tu.timedWait(o, LONG_DELAY_MS); + } + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * timedJoin throws InterruptedException when interrupted + */ + public void testTimedJoin_Interruptible() { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + final Thread s = newStartedThread(new CheckedInterruptedRunnable() { + public void realRun() throws InterruptedException { + Thread.sleep(LONG_DELAY_MS); + }}); + final Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + TimeUnit tu = MILLISECONDS; + Thread.currentThread().interrupt(); + try { + tu.timedJoin(s, LONG_DELAY_MS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + tu.timedJoin(s, LONG_DELAY_MS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + s.interrupt(); + awaitTermination(s); + } + + /** + * timedSleep throws InterruptedException when interrupted + */ + public void testTimedSleep_Interruptible() { + final CountDownLatch pleaseInterrupt = new CountDownLatch(1); + Thread t = newStartedThread(new CheckedRunnable() { + public void realRun() throws InterruptedException { + TimeUnit tu = MILLISECONDS; + Thread.currentThread().interrupt(); + try { + tu.sleep(LONG_DELAY_MS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + + pleaseInterrupt.countDown(); + try { + tu.sleep(LONG_DELAY_MS); + shouldThrow(); + } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); + }}); + + await(pleaseInterrupt); + assertThreadStaysAlive(t); + t.interrupt(); + awaitTermination(t); + } + + /** + * a deserialized serialized unit is the same instance + */ + public void testSerialization() throws Exception { + TimeUnit x = MILLISECONDS; + assertSame(x, serialClone(x)); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/TreeMapTest.java b/jdk/test/java/util/concurrent/tck/TreeMapTest.java new file mode 100644 index 00000000000..ebf1dd01b9b --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/TreeMapTest.java @@ -0,0 +1,1111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; +import java.util.TreeMap; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TreeMapTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(TreeMapTest.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static TreeMap map5() { + TreeMap map = new TreeMap(); + assertTrue(map.isEmpty()); + map.put(one, "A"); + map.put(five, "E"); + map.put(three, "C"); + map.put(two, "B"); + map.put(four, "D"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map; + } + + /** + * clear removes all pairs + */ + public void testClear() { + TreeMap map = map5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * copy constructor creates map equal to source map + */ + public void testConstructFromSorted() { + TreeMap map = map5(); + TreeMap map2 = new TreeMap(map); + assertEquals(map, map2); + } + + /** + * Maps with same contents are equal + */ + public void testEquals() { + TreeMap map1 = map5(); + TreeMap map2 = map5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testContainsKey() { + TreeMap map = map5(); + assertTrue(map.containsKey(one)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testContainsValue() { + TreeMap map = map5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testGet() { + TreeMap map = map5(); + assertEquals("A", (String)map.get(one)); + TreeMap empty = new TreeMap(); + assertNull(empty.get(one)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testIsEmpty() { + TreeMap empty = new TreeMap(); + TreeMap map = map5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testFirstKey() { + TreeMap map = map5(); + assertEquals(one, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testLastKey() { + TreeMap map = map5(); + assertEquals(five, map.lastKey()); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + TreeMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * descendingkeySet.toArray returns contains all keys + */ + public void testDescendingKeySetToArray() { + TreeMap map = map5(); + Set s = map.descendingKeySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + assertTrue(s.containsAll(Arrays.asList(ar))); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testKeySet() { + TreeMap map = map5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(one)); + assertTrue(s.contains(two)); + assertTrue(s.contains(three)); + assertTrue(s.contains(four)); + assertTrue(s.contains(five)); + } + + /** + * keySet is ordered + */ + public void testKeySetOrder() { + TreeMap map = map5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descending iterator of key set is inverse ordered + */ + public void testKeySetDescendingIteratorOrder() { + TreeMap map = map5(); + NavigableSet s = map.navigableKeySet(); + Iterator i = s.descendingIterator(); + Integer last = (Integer)i.next(); + assertEquals(last, five); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descendingKeySet is ordered + */ + public void testDescendingKeySetOrder() { + TreeMap map = map5(); + Set s = map.descendingKeySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, five); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * descending iterator of descendingKeySet is ordered + */ + public void testDescendingKeySetDescendingIteratorOrder() { + TreeMap map = map5(); + NavigableSet s = map.descendingKeySet(); + Iterator i = s.descendingIterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + int count = 1; + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + ++count; + } + assertEquals(5, count); + } + + /** + * values collection contains all values + */ + public void testValues() { + TreeMap map = map5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testEntrySet() { + TreeMap map = map5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * descendingEntrySet contains all pairs + */ + public void testDescendingEntrySet() { + TreeMap map = map5(); + Set s = map.descendingMap().entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * entrySet.toArray contains all entries + */ + public void testEntrySetToArray() { + TreeMap map = map5(); + Set s = map.entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** + * descendingEntrySet.toArray contains all entries + */ + public void testDescendingEntrySetToArray() { + TreeMap map = map5(); + Set s = map.descendingMap().entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testPutAll() { + TreeMap empty = new TreeMap(); + TreeMap map = map5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(one)); + assertTrue(empty.containsKey(two)); + assertTrue(empty.containsKey(three)); + assertTrue(empty.containsKey(four)); + assertTrue(empty.containsKey(five)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testRemove() { + TreeMap map = map5(); + map.remove(five); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testLowerEntry() { + TreeMap map = map5(); + Map.Entry e1 = map.lowerEntry(three); + assertEquals(two, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(one); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testHigherEntry() { + TreeMap map = map5(); + Map.Entry e1 = map.higherEntry(three); + assertEquals(four, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.higherEntry(five); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(six); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testFloorEntry() { + TreeMap map = map5(); + Map.Entry e1 = map.floorEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.floorEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.floorEntry(one); + assertEquals(one, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testCeilingEntry() { + TreeMap map = map5(); + Map.Entry e1 = map.ceilingEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(five); + assertEquals(five, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(six); + assertNull(e4); + } + + /** + * lowerKey returns preceding element + */ + public void testLowerKey() { + TreeMap q = map5(); + Object e1 = q.lowerKey(three); + assertEquals(two, e1); + + Object e2 = q.lowerKey(six); + assertEquals(five, e2); + + Object e3 = q.lowerKey(one); + assertNull(e3); + + Object e4 = q.lowerKey(zero); + assertNull(e4); + } + + /** + * higherKey returns next element + */ + public void testHigherKey() { + TreeMap q = map5(); + Object e1 = q.higherKey(three); + assertEquals(four, e1); + + Object e2 = q.higherKey(zero); + assertEquals(one, e2); + + Object e3 = q.higherKey(five); + assertNull(e3); + + Object e4 = q.higherKey(six); + assertNull(e4); + } + + /** + * floorKey returns preceding element + */ + public void testFloorKey() { + TreeMap q = map5(); + Object e1 = q.floorKey(three); + assertEquals(three, e1); + + Object e2 = q.floorKey(six); + assertEquals(five, e2); + + Object e3 = q.floorKey(one); + assertEquals(one, e3); + + Object e4 = q.floorKey(zero); + assertNull(e4); + } + + /** + * ceilingKey returns next element + */ + public void testCeilingKey() { + TreeMap q = map5(); + Object e1 = q.ceilingKey(three); + assertEquals(three, e1); + + Object e2 = q.ceilingKey(zero); + assertEquals(one, e2); + + Object e3 = q.ceilingKey(five); + assertEquals(five, e3); + + Object e4 = q.ceilingKey(six); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testPollFirstEntry() { + TreeMap map = map5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(two, e.getKey()); + map.put(one, "A"); + e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(three, e.getKey()); + map.remove(four); + e = map.pollFirstEntry(); + assertEquals(five, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testPollLastEntry() { + TreeMap map = map5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(four, e.getKey()); + map.put(five, "E"); + e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(three, e.getKey()); + map.remove(two); + e = map.pollLastEntry(); + assertEquals(one, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testSize() { + TreeMap map = map5(); + TreeMap empty = new TreeMap(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testToString() { + TreeMap map = map5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception tests + + /** + * get(null) of nonempty map throws NPE + */ + public void testGet_NullPointerException() { + TreeMap c = map5(); + try { + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) of nonempty map throws NPE + */ + public void testContainsKey_NullPointerException() { + TreeMap c = map5(); + try { + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE for nonempty map + */ + public void testRemove1_NullPointerException() { + TreeMap c = new TreeMap(); + c.put("sadsdf", "asdads"); + try { + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A deserialized map equals original + */ + public void testSerialization() throws Exception { + NavigableMap x = map5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testSubMapContents() { + TreeMap map = map5(); + NavigableMap sm = map.subMap(two, true, four, false); + assertEquals(two, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(three, k); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals("C", sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testSubMapContents2() { + TreeMap map = map5(); + NavigableMap sm = map.subMap(two, true, three, false); + assertEquals(1, sm.size()); + assertEquals(two, sm.firstKey()); + assertEquals(two, sm.lastKey()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertFalse(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(three), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testHeadMapContents() { + TreeMap map = map5(); + NavigableMap sm = map.headMap(four, false); + assertTrue(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(four, map.firstKey()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testTailMapContents() { + TreeMap map = map5(); + NavigableMap sm = map.tailMap(two, true); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertTrue(sm.containsKey(four)); + assertTrue(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + Iterator r = sm.descendingKeySet().iterator(); + k = (Integer)(r.next()); + assertEquals(five, k); + k = (Integer)(r.next()); + assertEquals(four, k); + k = (Integer)(r.next()); + assertEquals(three, k); + k = (Integer)(r.next()); + assertEquals(two, k); + assertFalse(r.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(two, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(three, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(four, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + NavigableMap ssm = sm.tailMap(four, true); + assertEquals(four, ssm.firstKey()); + assertEquals(five, ssm.lastKey()); + assertEquals("D", ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + + Random rnd = new Random(666); + BitSet bs; + + /** + * Submaps of submaps subdivide correctly + */ + public void testRecursiveSubMaps() throws Exception { + int mapSize = expensiveTests ? 1000 : 100; + Class cl = TreeMap.class; + NavigableMap map = newMap(cl); + bs = new BitSet(mapSize); + + populate(map, mapSize); + check(map, 0, mapSize - 1, true); + check(map.descendingMap(), 0, mapSize - 1, false); + + mutateMap(map, 0, mapSize - 1); + check(map, 0, mapSize - 1, true); + check(map.descendingMap(), 0, mapSize - 1, false); + + bashSubMap(map.subMap(0, true, mapSize, false), + 0, mapSize - 1, true); + } + + static NavigableMap newMap(Class cl) throws Exception { + NavigableMap result + = (NavigableMap) cl.newInstance(); + assertEquals(0, result.size()); + assertFalse(result.keySet().iterator().hasNext()); + return result; + } + + void populate(NavigableMap map, int limit) { + for (int i = 0, n = 2 * limit / 3; i < n; i++) { + int key = rnd.nextInt(limit); + put(map, key); + } + } + + void mutateMap(NavigableMap map, int min, int max) { + int size = map.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(map, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (map.size() < size) { + int key = min + rnd.nextInt(rangeSize); + assertTrue(key >= min && key <= max); + put(map, key); + } + } + + void mutateSubMap(NavigableMap map, int min, int max) { + int size = map.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(map, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (map.size() < size) { + int key = min - 5 + rnd.nextInt(rangeSize + 10); + if (key >= min && key <= max) { + put(map, key); + } else { + try { + map.put(key, 2 * key); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + } + + void put(NavigableMap map, int key) { + if (map.put(key, 2 * key) == null) + bs.set(key); + } + + void remove(NavigableMap map, int key) { + if (map.remove(key) != null) + bs.clear(key); + } + + void bashSubMap(NavigableMap map, + int min, int max, boolean ascending) { + check(map, min, max, ascending); + check(map.descendingMap(), min, max, !ascending); + + mutateSubMap(map, min, max); + check(map, min, max, ascending); + check(map.descendingMap(), min, max, !ascending); + + // Recurse + if (max - min < 2) + return; + int midPoint = (min + max) / 2; + + // headMap - pick direction and endpoint inclusion randomly + boolean incl = rnd.nextBoolean(); + NavigableMap hm = map.headMap(midPoint, incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true); + else + bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1), + false); + } else { + if (rnd.nextBoolean()) + bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false); + else + bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max, + true); + } + + // tailMap - pick direction and endpoint inclusion randomly + incl = rnd.nextBoolean(); + NavigableMap tm = map.tailMap(midPoint,incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true); + else + bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max, + false); + } else { + if (rnd.nextBoolean()) { + bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false); + } else { + bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1), + true); + } + } + + // subMap - pick direction and endpoint inclusion randomly + int rangeSize = max - min + 1; + int[] endpoints = new int[2]; + endpoints[0] = min + rnd.nextInt(rangeSize); + endpoints[1] = min + rnd.nextInt(rangeSize); + Arrays.sort(endpoints); + boolean lowIncl = rnd.nextBoolean(); + boolean highIncl = rnd.nextBoolean(); + if (ascending) { + NavigableMap sm = map.subMap( + endpoints[0], lowIncl, endpoints[1], highIncl); + if (rnd.nextBoolean()) + bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + else + bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + } else { + NavigableMap sm = map.subMap( + endpoints[1], highIncl, endpoints[0], lowIncl); + if (rnd.nextBoolean()) + bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + else + bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + } + } + + /** + * min and max are both inclusive. If max < min, interval is empty. + */ + void check(NavigableMap map, + final int min, final int max, final boolean ascending) { + class ReferenceSet { + int lower(int key) { + return ascending ? lowerAscending(key) : higherAscending(key); + } + int floor(int key) { + return ascending ? floorAscending(key) : ceilingAscending(key); + } + int ceiling(int key) { + return ascending ? ceilingAscending(key) : floorAscending(key); + } + int higher(int key) { + return ascending ? higherAscending(key) : lowerAscending(key); + } + int first() { + return ascending ? firstAscending() : lastAscending(); + } + int last() { + return ascending ? lastAscending() : firstAscending(); + } + int lowerAscending(int key) { + return floorAscending(key - 1); + } + int floorAscending(int key) { + if (key < min) + return -1; + else if (key > max) + key = max; + + // BitSet should support this! Test would run much faster + while (key >= min) { + if (bs.get(key)) + return key; + key--; + } + return -1; + } + int ceilingAscending(int key) { + if (key < min) + key = min; + else if (key > max) + return -1; + int result = bs.nextSetBit(key); + return result > max ? -1 : result; + } + int higherAscending(int key) { + return ceilingAscending(key + 1); + } + private int firstAscending() { + int result = ceilingAscending(min); + return result > max ? -1 : result; + } + private int lastAscending() { + int result = floorAscending(max); + return result < min ? -1 : result; + } + } + ReferenceSet rs = new ReferenceSet(); + + // Test contents using containsKey + int size = 0; + for (int i = min; i <= max; i++) { + boolean bsContainsI = bs.get(i); + assertEquals(bsContainsI, map.containsKey(i)); + if (bsContainsI) + size++; + } + assertEquals(size, map.size()); + + // Test contents using contains keySet iterator + int size2 = 0; + int previousKey = -1; + for (int key : map.keySet()) { + assertTrue(bs.get(key)); + size2++; + assertTrue(previousKey < 0 || + (ascending ? key - previousKey > 0 : key - previousKey < 0)); + previousKey = key; + } + assertEquals(size2, size); + + // Test navigation ops + for (int key = min - 1; key <= max + 1; key++) { + assertEq(map.lowerKey(key), rs.lower(key)); + assertEq(map.floorKey(key), rs.floor(key)); + assertEq(map.higherKey(key), rs.higher(key)); + assertEq(map.ceilingKey(key), rs.ceiling(key)); + } + + // Test extrema + if (map.size() != 0) { + assertEq(map.firstKey(), rs.first()); + assertEq(map.lastKey(), rs.last()); + } else { + assertEq(rs.first(), -1); + assertEq(rs.last(), -1); + try { + map.firstKey(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + map.lastKey(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + } + + static void assertEq(Integer i, int j) { + if (i == null) + assertEquals(j, -1); + else + assertEquals((int) i, j); + } + + static boolean eq(Integer i, int j) { + return i == null ? j == -1 : i == j; + } + +} diff --git a/jdk/test/java/util/concurrent/tck/TreeSetTest.java b/jdk/test/java/util/concurrent/tck/TreeSetTest.java new file mode 100644 index 00000000000..eb7b6efd9d0 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/TreeSetTest.java @@ -0,0 +1,1008 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TreeSetTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(TreeSetTest.class); + } + + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * The number of elements to place in collections, arrays, etc. + */ + static final int SIZE = 20; + + /** + * Returns a new set of given size containing consecutive + * Integers 0 ... n. + */ + private TreeSet populatedSet(int n) { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.add(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.add(new Integer(i))); + assertFalse(q.isEmpty()); + assertEquals(n, q.size()); + return q; + } + + /** + * Returns a new set of first 5 ints. + */ + private TreeSet set5() { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + q.add(one); + q.add(two); + q.add(three); + q.add(four); + q.add(five); + assertEquals(5, q.size()); + return q; + } + + /** + * A new set has unbounded capacity + */ + public void testConstructor1() { + assertEquals(0, new TreeSet().size()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor3() { + try { + new TreeSet((Collection)null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor4() { + try { + new TreeSet(Arrays.asList(new Integer[SIZE])); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection with some null elements throws NPE + */ + public void testConstructor5() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + new TreeSet(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of collection used to initialize + */ + public void testConstructor6() { + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + TreeSet q = new TreeSet(Arrays.asList(ints)); + for (int i = 0; i < SIZE; ++i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * The comparator used in constructor is used + */ + public void testConstructor7() { + MyReverseComparator cmp = new MyReverseComparator(); + TreeSet q = new TreeSet(cmp); + assertEquals(cmp, q.comparator()); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(i); + q.addAll(Arrays.asList(ints)); + for (int i = SIZE - 1; i >= 0; --i) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.add(new Integer(2)); + q.pollFirst(); + q.pollFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + TreeSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * add(null) throws NPE if nonempty + */ + public void testAddNull() { + TreeSet q = populatedSet(SIZE); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Add of comparable element succeeds + */ + public void testAdd() { + TreeSet q = new TreeSet(); + assertTrue(q.add(zero)); + assertTrue(q.add(one)); + } + + /** + * Add of duplicate element fails + */ + public void testAddDup() { + TreeSet q = new TreeSet(); + assertTrue(q.add(zero)); + assertFalse(q.add(zero)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testAddNonComparable() { + TreeSet q = new TreeSet(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + TreeSet q = new TreeSet(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + TreeSet q = new TreeSet(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + TreeSet q = new TreeSet(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + TreeSet q = new TreeSet(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.pollFirst()); + } + + /** + * pollFirst succeeds unless empty + */ + public void testPollFirst() { + TreeSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * pollLast succeeds unless empty + */ + public void testPollLast() { + TreeSet q = populatedSet(SIZE); + for (int i = SIZE - 1; i >= 0; --i) { + assertEquals(i, q.pollLast()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + TreeSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + TreeSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + TreeSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + q.add(new Integer(1)); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + TreeSet q = populatedSet(SIZE); + TreeSet p = new TreeSet(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + TreeSet q = populatedSet(SIZE); + TreeSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + TreeSet q = populatedSet(SIZE); + TreeSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testLower() { + TreeSet q = set5(); + Object e1 = q.lower(three); + assertEquals(two, e1); + + Object e2 = q.lower(six); + assertEquals(five, e2); + + Object e3 = q.lower(one); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testHigher() { + TreeSet q = set5(); + Object e1 = q.higher(three); + assertEquals(four, e1); + + Object e2 = q.higher(zero); + assertEquals(one, e2); + + Object e3 = q.higher(five); + assertNull(e3); + + Object e4 = q.higher(six); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testFloor() { + TreeSet q = set5(); + Object e1 = q.floor(three); + assertEquals(three, e1); + + Object e2 = q.floor(six); + assertEquals(five, e2); + + Object e3 = q.floor(one); + assertEquals(one, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testCeiling() { + TreeSet q = set5(); + Object e1 = q.ceiling(three); + assertEquals(three, e1); + + Object e2 = q.ceiling(zero); + assertEquals(one, e2); + + Object e3 = q.ceiling(five); + assertEquals(five, e3); + + Object e4 = q.ceiling(six); + assertNull(e4); + } + + /** + * toArray contains all elements in sorted order + */ + public void testToArray() { + TreeSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements in sorted order + */ + public void testToArray2() { + TreeSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + TreeSet q = populatedSet(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty set has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(new TreeSet().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final TreeSet q = new TreeSet(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(it.next(), new Integer(2)); + assertEquals(it.next(), new Integer(3)); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + TreeSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testSerialization() throws Exception { + NavigableSet x = populatedSet(SIZE); + NavigableSet y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testSubSetContents() { + TreeSet set = set5(); + SortedSet sm = set.subSet(two, four); + assertEquals(two, sm.first()); + assertEquals(three, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.first()); + assertEquals(three, sm.last()); + assertTrue(sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testSubSetContents2() { + TreeSet set = set5(); + SortedSet sm = set.subSet(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.first()); + assertEquals(two, sm.last()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertFalse(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(three)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testHeadSetContents() { + TreeSet set = set5(); + SortedSet sm = set.headSet(four); + assertTrue(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(four, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testTailSetContents() { + TreeSet set = set5(); + SortedSet sm = set.tailSet(two); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertTrue(sm.contains(four)); + assertTrue(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(four); + assertEquals(four, ssm.first()); + assertEquals(five, ssm.last()); + assertTrue(ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + + Random rnd = new Random(666); + BitSet bs; + + /** + * Subsets of subsets subdivide correctly + */ + public void testRecursiveSubSets() throws Exception { + int setSize = expensiveTests ? 1000 : 100; + Class cl = TreeSet.class; + + NavigableSet set = newSet(cl); + bs = new BitSet(setSize); + + populate(set, setSize); + check(set, 0, setSize - 1, true); + check(set.descendingSet(), 0, setSize - 1, false); + + mutateSet(set, 0, setSize - 1); + check(set, 0, setSize - 1, true); + check(set.descendingSet(), 0, setSize - 1, false); + + bashSubSet(set.subSet(0, true, setSize, false), + 0, setSize - 1, true); + } + + /** + * addAll is idempotent + */ + public void testAddAll_idempotent() throws Exception { + Set x = populatedSet(SIZE); + Set y = new TreeSet(x); + y.addAll(x); + assertEquals(x, y); + assertEquals(y, x); + } + + static NavigableSet newSet(Class cl) throws Exception { + NavigableSet result = (NavigableSet) cl.newInstance(); + assertEquals(0, result.size()); + assertFalse(result.iterator().hasNext()); + return result; + } + + void populate(NavigableSet set, int limit) { + for (int i = 0, n = 2 * limit / 3; i < n; i++) { + int element = rnd.nextInt(limit); + put(set, element); + } + } + + void mutateSet(NavigableSet set, int min, int max) { + int size = set.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(set, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = set.iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (set.size() < size) { + int element = min + rnd.nextInt(rangeSize); + assertTrue(element >= min && element <= max); + put(set, element); + } + } + + void mutateSubSet(NavigableSet set, int min, int max) { + int size = set.size(); + int rangeSize = max - min + 1; + + // Remove a bunch of entries directly + for (int i = 0, n = rangeSize / 2; i < n; i++) { + remove(set, min - 5 + rnd.nextInt(rangeSize + 10)); + } + + // Remove a bunch of entries with iterator + for (Iterator it = set.iterator(); it.hasNext(); ) { + if (rnd.nextBoolean()) { + bs.clear(it.next()); + it.remove(); + } + } + + // Add entries till we're back to original size + while (set.size() < size) { + int element = min - 5 + rnd.nextInt(rangeSize + 10); + if (element >= min && element <= max) { + put(set, element); + } else { + try { + set.add(element); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + } + } + + void put(NavigableSet set, int element) { + if (set.add(element)) + bs.set(element); + } + + void remove(NavigableSet set, int element) { + if (set.remove(element)) + bs.clear(element); + } + + void bashSubSet(NavigableSet set, + int min, int max, boolean ascending) { + check(set, min, max, ascending); + check(set.descendingSet(), min, max, !ascending); + + mutateSubSet(set, min, max); + check(set, min, max, ascending); + check(set.descendingSet(), min, max, !ascending); + + // Recurse + if (max - min < 2) + return; + int midPoint = (min + max) / 2; + + // headSet - pick direction and endpoint inclusion randomly + boolean incl = rnd.nextBoolean(); + NavigableSet hm = set.headSet(midPoint, incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true); + else + bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1), + false); + } else { + if (rnd.nextBoolean()) + bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false); + else + bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max, + true); + } + + // tailSet - pick direction and endpoint inclusion randomly + incl = rnd.nextBoolean(); + NavigableSet tm = set.tailSet(midPoint,incl); + if (ascending) { + if (rnd.nextBoolean()) + bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true); + else + bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max, + false); + } else { + if (rnd.nextBoolean()) { + bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false); + } else { + bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1), + true); + } + } + + // subSet - pick direction and endpoint inclusion randomly + int rangeSize = max - min + 1; + int[] endpoints = new int[2]; + endpoints[0] = min + rnd.nextInt(rangeSize); + endpoints[1] = min + rnd.nextInt(rangeSize); + Arrays.sort(endpoints); + boolean lowIncl = rnd.nextBoolean(); + boolean highIncl = rnd.nextBoolean(); + if (ascending) { + NavigableSet sm = set.subSet( + endpoints[0], lowIncl, endpoints[1], highIncl); + if (rnd.nextBoolean()) + bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + else + bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + } else { + NavigableSet sm = set.subSet( + endpoints[1], highIncl, endpoints[0], lowIncl); + if (rnd.nextBoolean()) + bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), false); + else + bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1), + endpoints[1] - (highIncl ? 0 : 1), true); + } + } + + /** + * min and max are both inclusive. If max < min, interval is empty. + */ + void check(NavigableSet set, + final int min, final int max, final boolean ascending) { + class ReferenceSet { + int lower(int element) { + return ascending ? + lowerAscending(element) : higherAscending(element); + } + int floor(int element) { + return ascending ? + floorAscending(element) : ceilingAscending(element); + } + int ceiling(int element) { + return ascending ? + ceilingAscending(element) : floorAscending(element); + } + int higher(int element) { + return ascending ? + higherAscending(element) : lowerAscending(element); + } + int first() { + return ascending ? firstAscending() : lastAscending(); + } + int last() { + return ascending ? lastAscending() : firstAscending(); + } + int lowerAscending(int element) { + return floorAscending(element - 1); + } + int floorAscending(int element) { + if (element < min) + return -1; + else if (element > max) + element = max; + + // BitSet should support this! Test would run much faster + while (element >= min) { + if (bs.get(element)) + return element; + element--; + } + return -1; + } + int ceilingAscending(int element) { + if (element < min) + element = min; + else if (element > max) + return -1; + int result = bs.nextSetBit(element); + return (result > max) ? -1 : result; + } + int higherAscending(int element) { + return ceilingAscending(element + 1); + } + private int firstAscending() { + int result = ceilingAscending(min); + return (result > max) ? -1 : result; + } + private int lastAscending() { + int result = floorAscending(max); + return (result < min) ? -1 : result; + } + } + ReferenceSet rs = new ReferenceSet(); + + // Test contents using containsElement + int size = 0; + for (int i = min; i <= max; i++) { + boolean bsContainsI = bs.get(i); + assertEquals(bsContainsI, set.contains(i)); + if (bsContainsI) + size++; + } + assertEquals(size, set.size()); + + // Test contents using contains elementSet iterator + int size2 = 0; + int previousElement = -1; + for (int element : set) { + assertTrue(bs.get(element)); + size2++; + assertTrue(previousElement < 0 || (ascending ? + element - previousElement > 0 : element - previousElement < 0)); + previousElement = element; + } + assertEquals(size2, size); + + // Test navigation ops + for (int element = min - 1; element <= max + 1; element++) { + assertEq(set.lower(element), rs.lower(element)); + assertEq(set.floor(element), rs.floor(element)); + assertEq(set.higher(element), rs.higher(element)); + assertEq(set.ceiling(element), rs.ceiling(element)); + } + + // Test extrema + if (set.size() != 0) { + assertEq(set.first(), rs.first()); + assertEq(set.last(), rs.last()); + } else { + assertEq(rs.first(), -1); + assertEq(rs.last(), -1); + try { + set.first(); + shouldThrow(); + } catch (NoSuchElementException success) {} + try { + set.last(); + shouldThrow(); + } catch (NoSuchElementException success) {} + } + } + + static void assertEq(Integer i, int j) { + if (i == null) + assertEquals(j, -1); + else + assertEquals((int) i, j); + } + + static boolean eq(Integer i, int j) { + return (i == null) ? j == -1 : i == j; + } + +} diff --git a/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java b/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java new file mode 100644 index 00000000000..1b5fa424b1c --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java @@ -0,0 +1,1138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TreeSubMapTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(TreeSubMapTest.class); + } + + /** + * Returns a new map from Integers 1-5 to Strings "A"-"E". + */ + private static NavigableMap map5() { + TreeMap map = new TreeMap(); + assertTrue(map.isEmpty()); + map.put(zero, "Z"); + map.put(one, "A"); + map.put(five, "E"); + map.put(three, "C"); + map.put(two, "B"); + map.put(four, "D"); + map.put(seven, "F"); + assertFalse(map.isEmpty()); + assertEquals(7, map.size()); + return map.subMap(one, true, seven, false); + } + + private static NavigableMap map0() { + TreeMap map = new TreeMap(); + assertTrue(map.isEmpty()); + return map.tailMap(one, true); + } + + /** + * Returns a new map from Integers -5 to -1 to Strings "A"-"E". + */ + private static NavigableMap dmap5() { + TreeMap map = new TreeMap(); + assertTrue(map.isEmpty()); + map.put(m1, "A"); + map.put(m5, "E"); + map.put(m3, "C"); + map.put(m2, "B"); + map.put(m4, "D"); + assertFalse(map.isEmpty()); + assertEquals(5, map.size()); + return map.descendingMap(); + } + + private static NavigableMap dmap0() { + TreeMap map = new TreeMap(); + assertTrue(map.isEmpty()); + return map; + } + + /** + * clear removes all pairs + */ + public void testClear() { + NavigableMap map = map5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * Maps with same contents are equal + */ + public void testEquals() { + NavigableMap map1 = map5(); + NavigableMap map2 = map5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testContainsKey() { + NavigableMap map = map5(); + assertTrue(map.containsKey(one)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testContainsValue() { + NavigableMap map = map5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testGet() { + NavigableMap map = map5(); + assertEquals("A", (String)map.get(one)); + NavigableMap empty = map0(); + assertNull(empty.get(one)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testIsEmpty() { + NavigableMap empty = map0(); + NavigableMap map = map5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testFirstKey() { + NavigableMap map = map5(); + assertEquals(one, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testLastKey() { + NavigableMap map = map5(); + assertEquals(five, map.lastKey()); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testKeySet() { + NavigableMap map = map5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(one)); + assertTrue(s.contains(two)); + assertTrue(s.contains(three)); + assertTrue(s.contains(four)); + assertTrue(s.contains(five)); + } + + /** + * keySet is ordered + */ + public void testKeySetOrder() { + NavigableMap map = map5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, one); + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) < 0); + last = k; + } + } + + /** + * values collection contains all values + */ + public void testValues() { + NavigableMap map = map5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testEntrySet() { + NavigableMap map = map5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(one) && e.getValue().equals("A")) || + (e.getKey().equals(two) && e.getValue().equals("B")) || + (e.getKey().equals(three) && e.getValue().equals("C")) || + (e.getKey().equals(four) && e.getValue().equals("D")) || + (e.getKey().equals(five) && e.getValue().equals("E"))); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testPutAll() { + NavigableMap empty = map0(); + NavigableMap map = map5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(one)); + assertTrue(empty.containsKey(two)); + assertTrue(empty.containsKey(three)); + assertTrue(empty.containsKey(four)); + assertTrue(empty.containsKey(five)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testRemove() { + NavigableMap map = map5(); + map.remove(five); + assertEquals(4, map.size()); + assertFalse(map.containsKey(five)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testLowerEntry() { + NavigableMap map = map5(); + Map.Entry e1 = map.lowerEntry(three); + assertEquals(two, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(one); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testHigherEntry() { + NavigableMap map = map5(); + Map.Entry e1 = map.higherEntry(three); + assertEquals(four, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.higherEntry(five); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(six); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testFloorEntry() { + NavigableMap map = map5(); + Map.Entry e1 = map.floorEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.floorEntry(six); + assertEquals(five, e2.getKey()); + + Map.Entry e3 = map.floorEntry(one); + assertEquals(one, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testCeilingEntry() { + NavigableMap map = map5(); + Map.Entry e1 = map.ceilingEntry(three); + assertEquals(three, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(one, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(five); + assertEquals(five, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(six); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testPollFirstEntry() { + NavigableMap map = map5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(two, e.getKey()); + map.put(one, "A"); + e = map.pollFirstEntry(); + assertEquals(one, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(three, e.getKey()); + map.remove(four); + e = map.pollFirstEntry(); + assertEquals(five, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + assertTrue(map.isEmpty()); + Map.Entry f = map.firstEntry(); + assertNull(f); + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testPollLastEntry() { + NavigableMap map = map5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(four, e.getKey()); + map.put(five, "E"); + e = map.pollLastEntry(); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(three, e.getKey()); + map.remove(two); + e = map.pollLastEntry(); + assertEquals(one, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testSize() { + NavigableMap map = map5(); + NavigableMap empty = map0(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testToString() { + NavigableMap map = map5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception tests + + /** + * get(null) of nonempty map throws NPE + */ + public void testGet_NullPointerException() { + NavigableMap c = map5(); + try { + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * containsKey(null) of nonempty map throws NPE + */ + public void testContainsKey_NullPointerException() { + NavigableMap c = map5(); + try { + c.containsKey(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testPut1_NullPointerException() { + NavigableMap c = map5(); + try { + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * remove(null) throws NPE + */ + public void testRemove1_NullPointerException() { + NavigableMap c = map5(); + try { + c.remove(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A deserialized map equals original + */ + public void testSerialization() throws Exception { + NavigableMap x = map5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testSubMapContents() { + NavigableMap map = map5(); + SortedMap sm = map.subMap(two, four); + assertEquals(two, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.firstKey()); + assertEquals(three, sm.lastKey()); + assertEquals("C", sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testSubMapContents2() { + NavigableMap map = map5(); + SortedMap sm = map.subMap(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.firstKey()); + assertEquals(two, sm.lastKey()); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertFalse(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(two)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(three), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testHeadMapContents() { + NavigableMap map = map5(); + SortedMap sm = map.headMap(four); + assertTrue(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertFalse(sm.containsKey(four)); + assertFalse(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(four, map.firstKey()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testTailMapContents() { + NavigableMap map = map5(); + SortedMap sm = map.tailMap(two); + assertFalse(sm.containsKey(one)); + assertTrue(sm.containsKey(two)); + assertTrue(sm.containsKey(three)); + assertTrue(sm.containsKey(four)); + assertTrue(sm.containsKey(five)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(two, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(three, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(four, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(five, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + SortedMap ssm = sm.tailMap(four); + assertEquals(four, ssm.firstKey()); + assertEquals(five, ssm.lastKey()); + assertEquals("D", ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + + /** + * clear removes all pairs + */ + public void testDescendingClear() { + NavigableMap map = dmap5(); + map.clear(); + assertEquals(0, map.size()); + } + + /** + * Maps with same contents are equal + */ + public void testDescendingEquals() { + NavigableMap map1 = dmap5(); + NavigableMap map2 = dmap5(); + assertEquals(map1, map2); + assertEquals(map2, map1); + map1.clear(); + assertFalse(map1.equals(map2)); + assertFalse(map2.equals(map1)); + } + + /** + * containsKey returns true for contained key + */ + public void testDescendingContainsKey() { + NavigableMap map = dmap5(); + assertTrue(map.containsKey(m1)); + assertFalse(map.containsKey(zero)); + } + + /** + * containsValue returns true for held values + */ + public void testDescendingContainsValue() { + NavigableMap map = dmap5(); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); + } + + /** + * get returns the correct element at the given key, + * or null if not present + */ + public void testDescendingGet() { + NavigableMap map = dmap5(); + assertEquals("A", (String)map.get(m1)); + NavigableMap empty = dmap0(); + assertNull(empty.get(m1)); + } + + /** + * isEmpty is true of empty map and false for non-empty + */ + public void testDescendingIsEmpty() { + NavigableMap empty = dmap0(); + NavigableMap map = dmap5(); + assertTrue(empty.isEmpty()); + assertFalse(map.isEmpty()); + } + + /** + * firstKey returns first key + */ + public void testDescendingFirstKey() { + NavigableMap map = dmap5(); + assertEquals(m1, map.firstKey()); + } + + /** + * lastKey returns last key + */ + public void testDescendingLastKey() { + NavigableMap map = dmap5(); + assertEquals(m5, map.lastKey()); + } + + /** + * keySet returns a Set containing all the keys + */ + public void testDescendingKeySet() { + NavigableMap map = dmap5(); + Set s = map.keySet(); + assertEquals(5, s.size()); + assertTrue(s.contains(m1)); + assertTrue(s.contains(m2)); + assertTrue(s.contains(m3)); + assertTrue(s.contains(m4)); + assertTrue(s.contains(m5)); + } + + /** + * keySet is ordered + */ + public void testDescendingKeySetOrder() { + NavigableMap map = dmap5(); + Set s = map.keySet(); + Iterator i = s.iterator(); + Integer last = (Integer)i.next(); + assertEquals(last, m1); + while (i.hasNext()) { + Integer k = (Integer)i.next(); + assertTrue(last.compareTo(k) > 0); + last = k; + } + } + + /** + * values collection contains all values + */ + public void testDescendingValues() { + NavigableMap map = dmap5(); + Collection s = map.values(); + assertEquals(5, s.size()); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * keySet.toArray returns contains all keys + */ + public void testDescendingAscendingKeySetToArray() { + NavigableMap map = dmap5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * descendingkeySet.toArray returns contains all keys + */ + public void testDescendingDescendingKeySetToArray() { + NavigableMap map = dmap5(); + Set s = map.descendingKeySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + assertTrue(s.containsAll(Arrays.asList(ar))); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testDescendingValuesToArray() { + NavigableMap map = dmap5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet contains all pairs + */ + public void testDescendingEntrySet() { + NavigableMap map = dmap5(); + Set s = map.entrySet(); + assertEquals(5, s.size()); + Iterator it = s.iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry) it.next(); + assertTrue( + (e.getKey().equals(m1) && e.getValue().equals("A")) || + (e.getKey().equals(m2) && e.getValue().equals("B")) || + (e.getKey().equals(m3) && e.getValue().equals("C")) || + (e.getKey().equals(m4) && e.getValue().equals("D")) || + (e.getKey().equals(m5) && e.getValue().equals("E"))); + } + } + + /** + * putAll adds all key-value pairs from the given map + */ + public void testDescendingPutAll() { + NavigableMap empty = dmap0(); + NavigableMap map = dmap5(); + empty.putAll(map); + assertEquals(5, empty.size()); + assertTrue(empty.containsKey(m1)); + assertTrue(empty.containsKey(m2)); + assertTrue(empty.containsKey(m3)); + assertTrue(empty.containsKey(m4)); + assertTrue(empty.containsKey(m5)); + } + + /** + * remove removes the correct key-value pair from the map + */ + public void testDescendingRemove() { + NavigableMap map = dmap5(); + map.remove(m5); + assertEquals(4, map.size()); + assertFalse(map.containsKey(m5)); + } + + /** + * lowerEntry returns preceding entry. + */ + public void testDescendingLowerEntry() { + NavigableMap map = dmap5(); + Map.Entry e1 = map.lowerEntry(m3); + assertEquals(m2, e1.getKey()); + + Map.Entry e2 = map.lowerEntry(m6); + assertEquals(m5, e2.getKey()); + + Map.Entry e3 = map.lowerEntry(m1); + assertNull(e3); + + Map.Entry e4 = map.lowerEntry(zero); + assertNull(e4); + } + + /** + * higherEntry returns next entry. + */ + public void testDescendingHigherEntry() { + NavigableMap map = dmap5(); + Map.Entry e1 = map.higherEntry(m3); + assertEquals(m4, e1.getKey()); + + Map.Entry e2 = map.higherEntry(zero); + assertEquals(m1, e2.getKey()); + + Map.Entry e3 = map.higherEntry(m5); + assertNull(e3); + + Map.Entry e4 = map.higherEntry(m6); + assertNull(e4); + } + + /** + * floorEntry returns preceding entry. + */ + public void testDescendingFloorEntry() { + NavigableMap map = dmap5(); + Map.Entry e1 = map.floorEntry(m3); + assertEquals(m3, e1.getKey()); + + Map.Entry e2 = map.floorEntry(m6); + assertEquals(m5, e2.getKey()); + + Map.Entry e3 = map.floorEntry(m1); + assertEquals(m1, e3.getKey()); + + Map.Entry e4 = map.floorEntry(zero); + assertNull(e4); + } + + /** + * ceilingEntry returns next entry. + */ + public void testDescendingCeilingEntry() { + NavigableMap map = dmap5(); + Map.Entry e1 = map.ceilingEntry(m3); + assertEquals(m3, e1.getKey()); + + Map.Entry e2 = map.ceilingEntry(zero); + assertEquals(m1, e2.getKey()); + + Map.Entry e3 = map.ceilingEntry(m5); + assertEquals(m5, e3.getKey()); + + Map.Entry e4 = map.ceilingEntry(m6); + assertNull(e4); + } + + /** + * pollFirstEntry returns entries in order + */ + public void testDescendingPollFirstEntry() { + NavigableMap map = dmap5(); + Map.Entry e = map.pollFirstEntry(); + assertEquals(m1, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(m2, e.getKey()); + map.put(m1, "A"); + e = map.pollFirstEntry(); + assertEquals(m1, e.getKey()); + assertEquals("A", e.getValue()); + e = map.pollFirstEntry(); + assertEquals(m3, e.getKey()); + map.remove(m4); + e = map.pollFirstEntry(); + assertEquals(m5, e.getKey()); + try { + e.setValue("A"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollFirstEntry(); + assertNull(e); + } + + /** + * pollLastEntry returns entries in order + */ + public void testDescendingPollLastEntry() { + NavigableMap map = dmap5(); + Map.Entry e = map.pollLastEntry(); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(m4, e.getKey()); + map.put(m5, "E"); + e = map.pollLastEntry(); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + e = map.pollLastEntry(); + assertEquals(m3, e.getKey()); + map.remove(m2); + e = map.pollLastEntry(); + assertEquals(m1, e.getKey()); + try { + e.setValue("E"); + shouldThrow(); + } catch (UnsupportedOperationException success) {} + e = map.pollLastEntry(); + assertNull(e); + } + + /** + * size returns the correct values + */ + public void testDescendingSize() { + NavigableMap map = dmap5(); + NavigableMap empty = dmap0(); + assertEquals(0, empty.size()); + assertEquals(5, map.size()); + } + + /** + * toString contains toString of elements + */ + public void testDescendingToString() { + NavigableMap map = dmap5(); + String s = map.toString(); + for (int i = 1; i <= 5; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + // Exception testDescendings + + /** + * get(null) of nonempty map throws NPE + */ + public void testDescendingGet_NullPointerException() { + NavigableMap c = dmap5(); + try { + c.get(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * put(null,x) throws NPE + */ + public void testDescendingPut1_NullPointerException() { + NavigableMap c = dmap5(); + try { + c.put(null, "whatever"); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * A deserialized map equals original + */ + public void testDescendingSerialization() throws Exception { + NavigableMap x = dmap5(); + NavigableMap y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + } + + /** + * subMap returns map with keys in requested range + */ + public void testDescendingSubMapContents() { + NavigableMap map = dmap5(); + SortedMap sm = map.subMap(m2, m4); + assertEquals(m2, sm.firstKey()); + assertEquals(m3, sm.lastKey()); + assertEquals(2, sm.size()); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(m2)); + assertEquals(4, map.size()); + assertEquals(1, sm.size()); + assertEquals(m3, sm.firstKey()); + assertEquals(m3, sm.lastKey()); + assertEquals("C", sm.remove(m3)); + assertTrue(sm.isEmpty()); + assertEquals(3, map.size()); + } + + public void testDescendingSubMapContents2() { + NavigableMap map = dmap5(); + SortedMap sm = map.subMap(m2, m3); + assertEquals(1, sm.size()); + assertEquals(m2, sm.firstKey()); + assertEquals(m2, sm.lastKey()); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertFalse(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + assertFalse(i.hasNext()); + Iterator j = sm.keySet().iterator(); + j.next(); + j.remove(); + assertFalse(map.containsKey(m2)); + assertEquals(4, map.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertSame(sm.remove(m3), null); + assertEquals(4, map.size()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testDescendingHeadMapContents() { + NavigableMap map = dmap5(); + SortedMap sm = map.headMap(m4); + assertTrue(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertFalse(sm.containsKey(m4)); + assertFalse(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m1, k); + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, map.size()); + assertEquals(m4, map.firstKey()); + } + + /** + * headMap returns map with keys in requested range + */ + public void testDescendingTailMapContents() { + NavigableMap map = dmap5(); + SortedMap sm = map.tailMap(m2); + assertFalse(sm.containsKey(m1)); + assertTrue(sm.containsKey(m2)); + assertTrue(sm.containsKey(m3)); + assertTrue(sm.containsKey(m4)); + assertTrue(sm.containsKey(m5)); + Iterator i = sm.keySet().iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + k = (Integer)(i.next()); + assertEquals(m4, k); + k = (Integer)(i.next()); + assertEquals(m5, k); + assertFalse(i.hasNext()); + + Iterator ei = sm.entrySet().iterator(); + Map.Entry e; + e = (Map.Entry)(ei.next()); + assertEquals(m2, e.getKey()); + assertEquals("B", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m3, e.getKey()); + assertEquals("C", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m4, e.getKey()); + assertEquals("D", e.getValue()); + e = (Map.Entry)(ei.next()); + assertEquals(m5, e.getKey()); + assertEquals("E", e.getValue()); + assertFalse(i.hasNext()); + + SortedMap ssm = sm.tailMap(m4); + assertEquals(m4, ssm.firstKey()); + assertEquals(m5, ssm.lastKey()); + assertEquals("D", ssm.remove(m4)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, map.size()); + } + +} diff --git a/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java b/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java new file mode 100644 index 00000000000..afc0604075d --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java @@ -0,0 +1,1139 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TreeSubSetTest extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(TreeSubSetTest.class); + } + + static class MyReverseComparator implements Comparator { + public int compare(Object x, Object y) { + return ((Comparable)y).compareTo(x); + } + } + + /** + * Returns a new set of given size containing consecutive + * Integers 0 ... n. + */ + private NavigableSet populatedSet(int n) { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + + for (int i = n - 1; i >= 0; i -= 2) + assertTrue(q.add(new Integer(i))); + for (int i = (n & 1); i < n; i += 2) + assertTrue(q.add(new Integer(i))); + assertTrue(q.add(new Integer(-n))); + assertTrue(q.add(new Integer(n))); + NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false); + assertFalse(s.isEmpty()); + assertEquals(n, s.size()); + return s; + } + + /** + * Returns a new set of first 5 ints. + */ + private NavigableSet set5() { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + q.add(one); + q.add(two); + q.add(three); + q.add(four); + q.add(five); + q.add(zero); + q.add(seven); + NavigableSet s = q.subSet(one, true, seven, false); + assertEquals(5, s.size()); + return s; + } + + private NavigableSet dset5() { + TreeSet q = new TreeSet(); + assertTrue(q.isEmpty()); + q.add(m1); + q.add(m2); + q.add(m3); + q.add(m4); + q.add(m5); + NavigableSet s = q.descendingSet(); + assertEquals(5, s.size()); + return s; + } + + private static NavigableSet set0() { + TreeSet set = new TreeSet(); + assertTrue(set.isEmpty()); + return set.tailSet(m1, false); + } + + private static NavigableSet dset0() { + TreeSet set = new TreeSet(); + assertTrue(set.isEmpty()); + return set; + } + + /** + * A new set has unbounded capacity + */ + public void testConstructor1() { + assertEquals(0, set0().size()); + } + + /** + * isEmpty is true before add, false after + */ + public void testEmpty() { + NavigableSet q = set0(); + assertTrue(q.isEmpty()); + assertTrue(q.add(new Integer(1))); + assertFalse(q.isEmpty()); + assertTrue(q.add(new Integer(2))); + q.pollFirst(); + q.pollFirst(); + assertTrue(q.isEmpty()); + } + + /** + * size changes when elements added and removed + */ + public void testSize() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * add(null) throws NPE + */ + public void testAddNull() { + NavigableSet q = set0(); + try { + q.add(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Add of comparable element succeeds + */ + public void testAdd() { + NavigableSet q = set0(); + assertTrue(q.add(six)); + } + + /** + * Add of duplicate element fails + */ + public void testAddDup() { + NavigableSet q = set0(); + assertTrue(q.add(six)); + assertFalse(q.add(six)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testAddNonComparable() { + NavigableSet q = set0(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testAddAll1() { + NavigableSet q = set0(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testAddAll2() { + NavigableSet q = set0(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testAddAll3() { + NavigableSet q = set0(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i + SIZE); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + NavigableSet q = set0(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.pollFirst()); + } + + /** + * poll succeeds unless empty + */ + public void testPoll() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testRemoveElement() { + NavigableSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertTrue(q.contains(i - 1)); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.contains(i)); + assertTrue(q.remove(i)); + assertFalse(q.contains(i)); + assertFalse(q.remove(i + 1)); + assertFalse(q.contains(i + 1)); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testContains() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testClear() { + NavigableSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertTrue(q.add(new Integer(1))); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testContainsAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = set0(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testRetainAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testLower() { + NavigableSet q = set5(); + Object e1 = q.lower(three); + assertEquals(two, e1); + + Object e2 = q.lower(six); + assertEquals(five, e2); + + Object e3 = q.lower(one); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testHigher() { + NavigableSet q = set5(); + Object e1 = q.higher(three); + assertEquals(four, e1); + + Object e2 = q.higher(zero); + assertEquals(one, e2); + + Object e3 = q.higher(five); + assertNull(e3); + + Object e4 = q.higher(six); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testFloor() { + NavigableSet q = set5(); + Object e1 = q.floor(three); + assertEquals(three, e1); + + Object e2 = q.floor(six); + assertEquals(five, e2); + + Object e3 = q.floor(one); + assertEquals(one, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testCeiling() { + NavigableSet q = set5(); + Object e1 = q.ceiling(three); + assertEquals(three, e1); + + Object e2 = q.ceiling(zero); + assertEquals(one, e2); + + Object e3 = q.ceiling(five); + assertEquals(five, e3); + + Object e4 = q.ceiling(six); + assertNull(e4); + } + + /** + * toArray contains all elements in sorted order + */ + public void testToArray() { + NavigableSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + for (int i = 0; i < o.length; i++) + assertSame(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements in sorted order + */ + public void testToArray2() { + NavigableSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + Integer[] array = q.toArray(ints); + assertSame(ints, array); + for (int i = 0; i < ints.length; i++) + assertSame(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testIterator() { + NavigableSet q = populatedSet(SIZE); + Iterator it = q.iterator(); + int i; + for (i = 0; it.hasNext(); i++) + assertTrue(q.contains(it.next())); + assertEquals(i, SIZE); + assertIteratorExhausted(it); + } + + /** + * iterator of empty set has no elements + */ + public void testEmptyIterator() { + assertIteratorExhausted(set0().iterator()); + } + + /** + * iterator.remove removes current element + */ + public void testIteratorRemove() { + final NavigableSet q = set0(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(2, it.next()); + assertEquals(3, it.next()); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testToString() { + NavigableSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testSerialization() throws Exception { + NavigableSet x = populatedSet(SIZE); + NavigableSet y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testSubSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.subSet(two, four); + assertEquals(two, sm.first()); + assertEquals(three, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(three, sm.first()); + assertEquals(three, sm.last()); + assertTrue(sm.remove(three)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testSubSetContents2() { + NavigableSet set = set5(); + SortedSet sm = set.subSet(two, three); + assertEquals(1, sm.size()); + assertEquals(two, sm.first()); + assertEquals(two, sm.last()); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertFalse(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(two)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(three)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testHeadSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.headSet(four); + assertTrue(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertFalse(sm.contains(four)); + assertFalse(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(one, k); + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(four, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testTailSetContents() { + NavigableSet set = set5(); + SortedSet sm = set.tailSet(two); + assertFalse(sm.contains(one)); + assertTrue(sm.contains(two)); + assertTrue(sm.contains(three)); + assertTrue(sm.contains(four)); + assertTrue(sm.contains(five)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(two, k); + k = (Integer)(i.next()); + assertEquals(three, k); + k = (Integer)(i.next()); + assertEquals(four, k); + k = (Integer)(i.next()); + assertEquals(five, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(four); + assertEquals(four, ssm.first()); + assertEquals(five, ssm.last()); + assertTrue(ssm.remove(four)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + + /** + * size changes when elements added and removed + */ + public void testDescendingSize() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(SIZE - i, q.size()); + q.pollFirst(); + } + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.size()); + q.add(new Integer(i)); + } + } + + /** + * Add of comparable element succeeds + */ + public void testDescendingAdd() { + NavigableSet q = dset0(); + assertTrue(q.add(m6)); + } + + /** + * Add of duplicate element fails + */ + public void testDescendingAddDup() { + NavigableSet q = dset0(); + assertTrue(q.add(m6)); + assertFalse(q.add(m6)); + } + + /** + * Add of non-Comparable throws CCE + */ + public void testDescendingAddNonComparable() { + NavigableSet q = dset0(); + try { + q.add(new Object()); + q.add(new Object()); + shouldThrow(); + } catch (ClassCastException success) {} + } + + /** + * addAll(null) throws NPE + */ + public void testDescendingAddAll1() { + NavigableSet q = dset0(); + try { + q.addAll(null); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with null elements throws NPE + */ + public void testDescendingAddAll2() { + NavigableSet q = dset0(); + Integer[] ints = new Integer[SIZE]; + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * addAll of a collection with any null elements throws NPE after + * possibly adding some elements + */ + public void testDescendingAddAll3() { + NavigableSet q = dset0(); + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE - 1; ++i) + ints[i] = new Integer(i + SIZE); + try { + q.addAll(Arrays.asList(ints)); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Set contains all elements of successful addAll + */ + public void testDescendingAddAll5() { + Integer[] empty = new Integer[0]; + Integer[] ints = new Integer[SIZE]; + for (int i = 0; i < SIZE; ++i) + ints[i] = new Integer(SIZE - 1 - i); + NavigableSet q = dset0(); + assertFalse(q.addAll(Arrays.asList(empty))); + assertTrue(q.addAll(Arrays.asList(ints))); + for (int i = 0; i < SIZE; ++i) + assertEquals(new Integer(i), q.pollFirst()); + } + + /** + * poll succeeds unless empty + */ + public void testDescendingPoll() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertEquals(i, q.pollFirst()); + } + assertNull(q.pollFirst()); + } + + /** + * remove(x) removes x and returns true if present + */ + public void testDescendingRemoveElement() { + NavigableSet q = populatedSet(SIZE); + for (int i = 1; i < SIZE; i += 2) { + assertTrue(q.remove(new Integer(i))); + } + for (int i = 0; i < SIZE; i += 2) { + assertTrue(q.remove(new Integer(i))); + assertFalse(q.remove(new Integer(i + 1))); + } + assertTrue(q.isEmpty()); + } + + /** + * contains(x) reports true when elements added but not yet removed + */ + public void testDescendingContains() { + NavigableSet q = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.contains(new Integer(i))); + q.pollFirst(); + assertFalse(q.contains(new Integer(i))); + } + } + + /** + * clear removes all elements + */ + public void testDescendingClear() { + NavigableSet q = populatedSet(SIZE); + q.clear(); + assertTrue(q.isEmpty()); + assertEquals(0, q.size()); + assertTrue(q.add(new Integer(1))); + assertFalse(q.isEmpty()); + q.clear(); + assertTrue(q.isEmpty()); + } + + /** + * containsAll(c) is true when c contains a subset of elements + */ + public void testDescendingContainsAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = dset0(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(q.containsAll(p)); + assertFalse(p.containsAll(q)); + p.add(new Integer(i)); + } + assertTrue(p.containsAll(q)); + } + + /** + * retainAll(c) retains only those elements of c and reports true if changed + */ + public void testDescendingRetainAll() { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(SIZE); + for (int i = 0; i < SIZE; ++i) { + boolean changed = q.retainAll(p); + if (i == 0) + assertFalse(changed); + else + assertTrue(changed); + + assertTrue(q.containsAll(p)); + assertEquals(SIZE - i, q.size()); + p.pollFirst(); + } + } + + /** + * removeAll(c) removes only those elements of c and reports true if changed + */ + public void testDescendingRemoveAll() { + for (int i = 1; i < SIZE; ++i) { + NavigableSet q = populatedSet(SIZE); + NavigableSet p = populatedSet(i); + assertTrue(q.removeAll(p)); + assertEquals(SIZE - i, q.size()); + for (int j = 0; j < i; ++j) { + Integer x = (Integer)(p.pollFirst()); + assertFalse(q.contains(x)); + } + } + } + + /** + * lower returns preceding element + */ + public void testDescendingLower() { + NavigableSet q = dset5(); + Object e1 = q.lower(m3); + assertEquals(m2, e1); + + Object e2 = q.lower(m6); + assertEquals(m5, e2); + + Object e3 = q.lower(m1); + assertNull(e3); + + Object e4 = q.lower(zero); + assertNull(e4); + } + + /** + * higher returns next element + */ + public void testDescendingHigher() { + NavigableSet q = dset5(); + Object e1 = q.higher(m3); + assertEquals(m4, e1); + + Object e2 = q.higher(zero); + assertEquals(m1, e2); + + Object e3 = q.higher(m5); + assertNull(e3); + + Object e4 = q.higher(m6); + assertNull(e4); + } + + /** + * floor returns preceding element + */ + public void testDescendingFloor() { + NavigableSet q = dset5(); + Object e1 = q.floor(m3); + assertEquals(m3, e1); + + Object e2 = q.floor(m6); + assertEquals(m5, e2); + + Object e3 = q.floor(m1); + assertEquals(m1, e3); + + Object e4 = q.floor(zero); + assertNull(e4); + } + + /** + * ceiling returns next element + */ + public void testDescendingCeiling() { + NavigableSet q = dset5(); + Object e1 = q.ceiling(m3); + assertEquals(m3, e1); + + Object e2 = q.ceiling(zero); + assertEquals(m1, e2); + + Object e3 = q.ceiling(m5); + assertEquals(m5, e3); + + Object e4 = q.ceiling(m6); + assertNull(e4); + } + + /** + * toArray contains all elements + */ + public void testDescendingToArray() { + NavigableSet q = populatedSet(SIZE); + Object[] o = q.toArray(); + Arrays.sort(o); + for (int i = 0; i < o.length; i++) + assertEquals(o[i], q.pollFirst()); + } + + /** + * toArray(a) contains all elements + */ + public void testDescendingToArray2() { + NavigableSet q = populatedSet(SIZE); + Integer[] ints = new Integer[SIZE]; + assertSame(ints, q.toArray(ints)); + Arrays.sort(ints); + for (int i = 0; i < ints.length; i++) + assertEquals(ints[i], q.pollFirst()); + } + + /** + * iterator iterates through all elements + */ + public void testDescendingIterator() { + NavigableSet q = populatedSet(SIZE); + int i = 0; + Iterator it = q.iterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(i, SIZE); + } + + /** + * iterator of empty set has no elements + */ + public void testDescendingEmptyIterator() { + NavigableSet q = dset0(); + int i = 0; + Iterator it = q.iterator(); + while (it.hasNext()) { + assertTrue(q.contains(it.next())); + ++i; + } + assertEquals(0, i); + } + + /** + * iterator.remove removes current element + */ + public void testDescendingIteratorRemove() { + final NavigableSet q = dset0(); + q.add(new Integer(2)); + q.add(new Integer(1)); + q.add(new Integer(3)); + + Iterator it = q.iterator(); + it.next(); + it.remove(); + + it = q.iterator(); + assertEquals(2, it.next()); + assertEquals(3, it.next()); + assertFalse(it.hasNext()); + } + + /** + * toString contains toStrings of elements + */ + public void testDescendingToString() { + NavigableSet q = populatedSet(SIZE); + String s = q.toString(); + for (int i = 0; i < SIZE; ++i) { + assertTrue(s.contains(String.valueOf(i))); + } + } + + /** + * A deserialized serialized set has same elements + */ + public void testDescendingSerialization() throws Exception { + NavigableSet x = dset5(); + NavigableSet y = serialClone(x); + + assertNotSame(x, y); + assertEquals(x.size(), y.size()); + assertEquals(x.toString(), y.toString()); + assertEquals(x, y); + assertEquals(y, x); + while (!x.isEmpty()) { + assertFalse(y.isEmpty()); + assertEquals(x.pollFirst(), y.pollFirst()); + } + assertTrue(y.isEmpty()); + } + + /** + * subSet returns set with keys in requested range + */ + public void testDescendingSubSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.subSet(m2, m4); + assertEquals(m2, sm.first()); + assertEquals(m3, sm.last()); + assertEquals(2, sm.size()); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(m2)); + assertEquals(4, set.size()); + assertEquals(1, sm.size()); + assertEquals(m3, sm.first()); + assertEquals(m3, sm.last()); + assertTrue(sm.remove(m3)); + assertTrue(sm.isEmpty()); + assertEquals(3, set.size()); + } + + public void testDescendingSubSetContents2() { + NavigableSet set = dset5(); + SortedSet sm = set.subSet(m2, m3); + assertEquals(1, sm.size()); + assertEquals(m2, sm.first()); + assertEquals(m2, sm.last()); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertFalse(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + assertFalse(i.hasNext()); + Iterator j = sm.iterator(); + j.next(); + j.remove(); + assertFalse(set.contains(m2)); + assertEquals(4, set.size()); + assertEquals(0, sm.size()); + assertTrue(sm.isEmpty()); + assertFalse(sm.remove(m3)); + assertEquals(4, set.size()); + } + + /** + * headSet returns set with keys in requested range + */ + public void testDescendingHeadSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.headSet(m4); + assertTrue(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertFalse(sm.contains(m4)); + assertFalse(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m1, k); + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + assertFalse(i.hasNext()); + sm.clear(); + assertTrue(sm.isEmpty()); + assertEquals(2, set.size()); + assertEquals(m4, set.first()); + } + + /** + * tailSet returns set with keys in requested range + */ + public void testDescendingTailSetContents() { + NavigableSet set = dset5(); + SortedSet sm = set.tailSet(m2); + assertFalse(sm.contains(m1)); + assertTrue(sm.contains(m2)); + assertTrue(sm.contains(m3)); + assertTrue(sm.contains(m4)); + assertTrue(sm.contains(m5)); + Iterator i = sm.iterator(); + Object k; + k = (Integer)(i.next()); + assertEquals(m2, k); + k = (Integer)(i.next()); + assertEquals(m3, k); + k = (Integer)(i.next()); + assertEquals(m4, k); + k = (Integer)(i.next()); + assertEquals(m5, k); + assertFalse(i.hasNext()); + + SortedSet ssm = sm.tailSet(m4); + assertEquals(m4, ssm.first()); + assertEquals(m5, ssm.last()); + assertTrue(ssm.remove(m4)); + assertEquals(1, ssm.size()); + assertEquals(3, sm.size()); + assertEquals(4, set.size()); + } + + /** + * addAll is idempotent + */ + public void testAddAll_idempotent() throws Exception { + Set x = populatedSet(SIZE); + Set y = new TreeSet(x); + y.addAll(x); + assertEquals(x, y); + assertEquals(y, x); + } + +} diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java index 0eb7c874249..d9a87bb8115 100644 --- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java +++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ public class DoubleStreamTestDataProvider { private static final double[] pseudoRandom; private static final Object[][] testData; + private static final Object[][] testSmallData; private static final Object[][] spliteratorTestData; static { @@ -78,11 +79,15 @@ public class DoubleStreamTestDataProvider { static { { - List list = new ArrayList<>(); + List listSmall = new ArrayList<>(); + List list1000 = new ArrayList<>(); + List list = null; for (Object[] data : arrays) { final Object name = data[0]; final double[] doubles = (double[]) data[1]; + list = doubles.length >= 1000 ? list1000 : listSmall; + list.add(new Object[]{"array:" + name, TestData.Factory.ofArray("array:" + name, doubles)}); @@ -93,7 +98,9 @@ public class DoubleStreamTestDataProvider { list.add(new Object[]{"SpinedList:" + name, TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)}); } - testData = list.toArray(new Object[0][]); + testSmallData = listSmall.toArray(new Object[0][]); + list1000.addAll(listSmall); + testData = list1000.toArray(new Object[0][]); } { @@ -136,6 +143,11 @@ public class DoubleStreamTestDataProvider { return testData; } + @DataProvider(name = "DoubleStreamTestData.small") + public static Object[][] makeSmallDoubleStreamTestData() { + return testSmallData; + } + // returns an array of (String name, Supplier>) @DataProvider(name = "DoubleSpliterator") public static Object[][] spliteratorProvider() { diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java index dded670d167..945cd9580e5 100644 --- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java +++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ public class IntStreamTestDataProvider { private static final int[] pseudoRandom; private static final Object[][] testData; + private static final Object[][] testSmallData; private static final Object[][] spliteratorTestData; static { @@ -78,11 +79,15 @@ public class IntStreamTestDataProvider { static { { - List list = new ArrayList<>(); + List listSmall = new ArrayList<>(); + List list1000 = new ArrayList<>(); + List list = null; for (Object[] data : arrays) { final Object name = data[0]; final int[] ints = (int[]) data[1]; + list = ints.length >= 1000 ? list1000 : listSmall; + list.add(new Object[]{"array:" + name, TestData.Factory.ofArray("array:" + name, ints)}); @@ -98,7 +103,9 @@ public class IntStreamTestDataProvider { list.add(streamDataDescr("IntStream.rangeClosed(0,l): " + ints.length, () -> IntStream.rangeClosed(0, ints.length))); } - testData = list.toArray(new Object[0][]); + testSmallData = listSmall.toArray(new Object[0][]); + list1000.addAll(listSmall); + testData = list1000.toArray(new Object[0][]); } { @@ -150,6 +157,11 @@ public class IntStreamTestDataProvider { return testData; } + @DataProvider(name = "IntStreamTestData.small") + public static Object[][] makeSmallIntStreamTestData() { + return testSmallData; + } + // returns an array of (String name, Supplier>) @DataProvider(name = "IntSpliterator") public static Object[][] spliteratorProvider() { diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java index 4ce7ae6d217..80b1dcc1019 100644 --- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java +++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ public class LongStreamTestDataProvider { private static final long[] pseudoRandom; private static final Object[][] testData; + private static final Object[][] testSmallData; private static final Object[][] spliteratorTestData; static { @@ -78,11 +79,15 @@ public class LongStreamTestDataProvider { static { { - List list = new ArrayList<>(); + List listSmall = new ArrayList<>(); + List list1000 = new ArrayList<>(); + List list = null; for (Object[] data : arrays) { final Object name = data[0]; final long[] longs = (long[]) data[1]; + list = longs.length >= 1000 ? list1000 : listSmall; + list.add(new Object[]{"array:" + name, TestData.Factory.ofArray("array:" + name, longs)}); @@ -98,7 +103,9 @@ public class LongStreamTestDataProvider { list.add(streamDataDescr("LongStream.longRangeClosed(0,l): " + longs.length, () -> LongStream.rangeClosed(0, longs.length))); } - testData = list.toArray(new Object[0][]); + testSmallData = listSmall.toArray(new Object[0][]); + list1000.addAll(listSmall); + testData = list1000.toArray(new Object[0][]); } { @@ -150,6 +157,11 @@ public class LongStreamTestDataProvider { return testData; } + @DataProvider(name = "LongStreamTestData.small") + public static Object[][] makeSmallLongStreamTestData() { + return testSmallData; + } + // returns an array of (String name, Supplier>) @DataProvider(name = "LongSpliterator") public static Object[][] spliteratorProvider() { diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java index 6f772f391ee..8a849015260 100644 --- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java +++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,6 +46,7 @@ public class StreamTestDataProvider { private static final Integer[] pseudoRandom; private static final Object[][] testData; + private static final Object[][] testSmallData; private static final Object[][] withNullTestData; private static final Object[][] spliteratorTestData; @@ -84,12 +85,16 @@ public class StreamTestDataProvider { static { { - List list = new ArrayList<>(); + List listSmall = new ArrayList<>(); + List list1000 = new ArrayList<>(); + List list = null; for (Object[] data : arrays) { final Object name = data[0]; final Integer[] ints = (Integer[])data[1]; final List intsAsList = Arrays.asList(ints); + list = ints.length >= 1000 ? list1000 : listSmall; + list.add(arrayDataDescr("array:" + name, ints)); list.add(collectionDataDescr("ArrayList.asList:" + name, intsAsList)); list.add(collectionDataDescr("ArrayList:" + name, new ArrayList<>(intsAsList))); @@ -114,7 +119,9 @@ public class StreamTestDataProvider { // @@@ Add more } - testData = list.toArray(new Object[0][]); + testSmallData = listSmall.toArray(new Object[0][]); + list1000.addAll(listSmall); + testData = list1000.toArray(new Object[0][]); } // Simple combination of numbers and null values, probably excessive but may catch @@ -192,6 +199,11 @@ public class StreamTestDataProvider { return testData; } + @DataProvider(name = "StreamTestData.small") + public static Object[][] makeSmallStreamTestData() { + return testSmallData; + } + @DataProvider(name = "withNull:StreamTestData") public static Object[][] makeStreamWithNullTestData() { return withNullTestData; diff --git a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java index 453ec6e952c..afbedc871da 100644 --- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java +++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @summary flat-map operations - * @bug 8044047 + * @bug 8044047 8076458 */ package org.openjdk.tests.java.util.stream; @@ -140,7 +140,10 @@ public class FlatMapOpTest extends OpTestCase { result = exerciseOps(data, s-> s.flatMap(e -> Stream.empty())); assertEquals(0, result.size()); + } + @Test(dataProvider = "StreamTestData.small", dataProviderClass = StreamTestDataProvider.class) + public void testOpsX(String name, TestData.OfRef data) { exerciseOps(data, s -> s.flatMap(mfLt)); exerciseOps(data, s -> s.flatMap(integerRangeMapper)); exerciseOps(data, s -> s.flatMap((Integer e) -> IntStream.range(0, e).boxed().limit(10))); @@ -156,7 +159,10 @@ public class FlatMapOpTest extends OpTestCase { result = exerciseOps(data, s -> s.flatMap(i -> IntStream.empty())); assertEquals(0, result.size()); + } + @Test(dataProvider = "IntStreamTestData.small", dataProviderClass = IntStreamTestDataProvider.class) + public void testIntOpsX(String name, TestData.OfInt data) { exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, e))); exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, e).limit(10))); } @@ -171,7 +177,10 @@ public class FlatMapOpTest extends OpTestCase { result = exerciseOps(data, s -> LongStream.empty()); assertEquals(0, result.size()); + } + @Test(dataProvider = "LongStreamTestData.small", dataProviderClass = LongStreamTestDataProvider.class) + public void testLongOpsX(String name, TestData.OfLong data) { exerciseOps(data, s -> s.flatMap(e -> LongStream.range(0, e))); exerciseOps(data, s -> s.flatMap(e -> LongStream.range(0, e).limit(10))); } @@ -186,7 +195,10 @@ public class FlatMapOpTest extends OpTestCase { result = exerciseOps(data, s -> DoubleStream.empty()); assertEquals(0, result.size()); + } + @Test(dataProvider = "DoubleStreamTestData.small", dataProviderClass = DoubleStreamTestDataProvider.class) + public void testDoubleOpsX(String name, TestData.OfDouble data) { exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, (int) e).asDoubleStream())); exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, (int) e).limit(10).asDoubleStream())); } diff --git a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java index 274f9d8e255..d7f0d38cdee 100644 --- a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java +++ b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ * @bug 7105780 * @summary Add SSLSocket client/SSLEngine server to templates directory. * @run main/othervm SSLSocketSSLEngineTemplate + * @key intermittent */ /** diff --git a/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java index 299a9ca65db..390c4fa0c69 100644 --- a/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java +++ b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java @@ -21,12 +21,21 @@ * questions. */ +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.CertPath; +import java.security.cert.CertificateFactory; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.zip.ZipFile; +import jdk.security.jarsigner.JarSigner; public class CreateMultiReleaseTestJars { final private String main = @@ -120,14 +129,21 @@ public class CreateMultiReleaseTestJars { String testsrc = System.getProperty("test.src","."); String testdir = findTestDir(testsrc); String keystore = testdir + "/sun/security/tools/jarsigner/JarSigning.keystore"; - String[] jsArgs = { - "-keystore", keystore, - "-storepass", "bbbbbb", - "-signedJar", "signed-multi-release.jar", - "multi-release.jar", "b" - }; - sun.security.tools.jarsigner.Main.main(jsArgs); + // jarsigner -keystore keystore -storepass "bbbbbb" + // -signedJar signed-multi-release.jar multi-release.jar b + + char[] password = "bbbbbb".toCharArray(); + KeyStore ks = KeyStore.getInstance(new File(keystore), password); + PrivateKey pkb = (PrivateKey)ks.getKey("b", password); + CertPath cp = CertificateFactory.getInstance("X.509") + .generateCertPath(Arrays.asList(ks.getCertificateChain("b"))); + JarSigner js = new JarSigner.Builder(pkb, cp).build(); + try (ZipFile in = new ZipFile("multi-release.jar"); + FileOutputStream os = new FileOutputStream("signed-multi-release.jar")) + { + js.sign(in, os); + } } String findTestDir(String dir) throws IOException { diff --git a/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java b/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java new file mode 100644 index 00000000000..8c8049bdf5c --- /dev/null +++ b/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; + +import java.lang.management.BufferPoolMXBean; +import java.lang.management.ManagementFactory; + +import java.nio.ByteBuffer; + +import java.nio.channels.FileChannel; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; + +import java.util.List; +import java.util.Random; + +/* + * @test + * @requires sun.arch.data.model == "64" + * @build TestMaxCachedBufferSize + * @run main/othervm TestMaxCachedBufferSize + * @run main/othervm -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize + * @run main/othervm -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize + * @run main/othervm -Djdk.nio.maxCachedBufferSize=100000 TestMaxCachedBufferSize + * @run main/othervm -Djdk.nio.maxCachedBufferSize=10000000 TestMaxCachedBufferSize + * @summary Test the implementation of the jdk.nio.maxCachedBufferSize property. + */ +public class TestMaxCachedBufferSize { + private static final int DEFAULT_ITERS = 10 * 1000; + private static final int DEFAULT_THREAD_NUM = 4; + + private static final int SMALL_BUFFER_MIN_SIZE = 4 * 1024; + private static final int SMALL_BUFFER_MAX_SIZE = 64 * 1024; + private static final int SMALL_BUFFER_DIFF_SIZE = + SMALL_BUFFER_MAX_SIZE - SMALL_BUFFER_MIN_SIZE; + + private static final int LARGE_BUFFER_MIN_SIZE = 512 * 1024; + private static final int LARGE_BUFFER_MAX_SIZE = 4 * 1024 * 1024; + private static final int LARGE_BUFFER_DIFF_SIZE = + LARGE_BUFFER_MAX_SIZE - LARGE_BUFFER_MIN_SIZE; + + private static final int LARGE_BUFFER_FREQUENCY = 100; + + private static final String FILE_NAME_PREFIX = "nio-out-file-"; + private static final int VERBOSE_PERIOD = 5 * 1000; + + private static int iters = DEFAULT_ITERS; + private static int threadNum = DEFAULT_THREAD_NUM; + + private static BufferPoolMXBean getDirectPool() { + final List pools = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + for (BufferPoolMXBean pool : pools) { + if (pool.getName().equals("direct")) { + return pool; + } + } + throw new Error("could not find direct pool"); + } + private static final BufferPoolMXBean directPool = getDirectPool(); + + // Each worker will do write operations on a file channel using + // buffers of various sizes. The buffer size is randomly chosen to + // be within a small or a large range. This way we can control + // which buffers can be cached (all, only the small ones, or none) + // by setting the jdk.nio.maxCachedBufferSize property. + private static class Worker implements Runnable { + private final int id; + private final Random random = new Random(); + private long smallBufferCount = 0; + private long largeBufferCount = 0; + + private int getWriteSize() { + int minSize = 0; + int diff = 0; + if (random.nextInt() % LARGE_BUFFER_FREQUENCY != 0) { + // small buffer + minSize = SMALL_BUFFER_MIN_SIZE; + diff = SMALL_BUFFER_DIFF_SIZE; + smallBufferCount += 1; + } else { + // large buffer + minSize = LARGE_BUFFER_MIN_SIZE; + diff = LARGE_BUFFER_DIFF_SIZE; + largeBufferCount += 1; + } + return minSize + random.nextInt(diff); + } + + private void loop() { + final String fileName = String.format("%s%d", FILE_NAME_PREFIX, id); + + try { + for (int i = 0; i < iters; i += 1) { + final int writeSize = getWriteSize(); + + // This will allocate a HeapByteBuffer. It should not + // be a direct buffer, otherwise the write() method on + // the channel below will not create a temporary + // direct buffer for the write. + final ByteBuffer buffer = ByteBuffer.allocate(writeSize); + + // Put some random data on it. + while (buffer.hasRemaining()) { + buffer.put((byte) random.nextInt()); + } + buffer.rewind(); + + final Path file = Paths.get(fileName); + try (FileChannel outChannel = FileChannel.open(file, CREATE, TRUNCATE_EXISTING, WRITE)) { + // The write() method will create a temporary + // direct buffer for the write and attempt to cache + // it. It's important that buffer is not a + // direct buffer, otherwise the temporary buffer + // will not be created. + long res = outChannel.write(buffer); + } + + if ((i + 1) % VERBOSE_PERIOD == 0) { + System.out.printf( + " Worker %3d | %8d Iters | Small %8d Large %8d | Direct %4d / %7dK\n", + id, i + 1, smallBufferCount, largeBufferCount, + directPool.getCount(), directPool.getTotalCapacity() / 1024); + } + } + } catch (IOException e) { + throw new Error("I/O error", e); + } + } + + @Override + public void run() { + loop(); + } + + public Worker(int id) { + this.id = id; + } + } + + public static void checkDirectBuffers(long expectedCount, long expectedMax) { + final long directCount = directPool.getCount(); + final long directTotalCapacity = directPool.getTotalCapacity(); + System.out.printf("Direct %d / %dK\n", + directCount, directTotalCapacity / 1024); + + // Note that directCount could be < expectedCount. This can + // happen if a GC occurs after one of the worker threads exits + // since its thread-local DirectByteBuffer could be cleaned up + // before we reach here. + if (directCount > expectedCount) { + throw new Error(String.format( + "inconsistent direct buffer total count, expected = %d, found = %d", + expectedCount, directCount)); + } + + if (directTotalCapacity > expectedMax) { + throw new Error(String.format( + "inconsistent direct buffer total capacity, expectex max = %d, found = %d", + expectedMax, directTotalCapacity)); + } + } + + public static void main(String[] args) { + final String maxBufferSizeStr = System.getProperty("jdk.nio.maxCachedBufferSize"); + final long maxBufferSize = + (maxBufferSizeStr != null) ? Long.valueOf(maxBufferSizeStr) : Long.MAX_VALUE; + + // We assume that the max cannot be equal to a size of a + // buffer that can be allocated (makes sanity checking at the + // end easier). + if ((SMALL_BUFFER_MIN_SIZE <= maxBufferSize && + maxBufferSize <= SMALL_BUFFER_MAX_SIZE) || + (LARGE_BUFFER_MIN_SIZE <= maxBufferSize && + maxBufferSize <= LARGE_BUFFER_MAX_SIZE)) { + throw new Error(String.format("max buffer size = %d not allowed", + maxBufferSize)); + } + + System.out.printf("Threads %d | Iterations %d | MaxBufferSize %d\n", + threadNum, iters, maxBufferSize); + System.out.println(); + + final Thread[] threads = new Thread[threadNum]; + for (int i = 0; i < threadNum; i += 1) { + threads[i] = new Thread(new Worker(i)); + threads[i].start(); + } + + try { + for (int i = 0; i < threadNum; i += 1) { + threads[i].join(); + } + } catch (InterruptedException e) { + throw new Error("join() interrupted!", e); + } + + // There is an assumption here that, at this point, only the + // cached DirectByteBuffers should be active. Given we + // haven't used any other DirectByteBuffers in this test, this + // should hold. + // + // Also note that we can only do the sanity checking at the + // end and not during the run given that, at any time, there + // could be buffers currently in use by some of the workers + // that will not be cached. + + System.out.println(); + if (maxBufferSize < SMALL_BUFFER_MAX_SIZE) { + // The max buffer size is smaller than all buffers that + // were allocated. No buffers should have been cached. + checkDirectBuffers(0, 0); + } else if (maxBufferSize < LARGE_BUFFER_MIN_SIZE) { + // The max buffer size is larger than all small buffers + // but smaller than all large buffers that were + // allocated. Only small buffers could have been cached. + checkDirectBuffers(threadNum, + (long) threadNum * (long) SMALL_BUFFER_MAX_SIZE); + } else { + // The max buffer size is larger than all buffers that + // were allocated. All buffers could have been cached. + checkDirectBuffers(threadNum, + (long) threadNum * (long) LARGE_BUFFER_MAX_SIZE); + } + } +} diff --git a/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java b/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java index 2cd68b44a9b..db7098e5fdf 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java +++ b/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,21 +28,22 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm ReinitCipher + * @run main/othervm ReinitCipher sm */ -import java.util.*; - -import java.security.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.Provider; +import java.util.Random; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; public class ReinitCipher extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new ReinitCipher()); + main(new ReinitCipher(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("Cipher", "ARCFOUR") == null) { System.out.println("Not supported by provider, skipping"); diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java b/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java index f0721daa2e5..5fa94cbfc54 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,16 +27,18 @@ * @summary Test internal PKCS5Padding impl with various error conditions. * @author Valerie Peng * @library .. + * @run main/othervm TestPKCS5PaddingError + * @run main/othervm TestPKCS5PaddingError sm */ -import java.io.*; -import java.nio.*; -import java.util.*; -import java.security.*; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; public class TestPKCS5PaddingError extends PKCS11Test { private static class CI { // class for holding Cipher Information @@ -62,10 +64,8 @@ public class TestPKCS5PaddingError extends PKCS11Test { private static StringBuffer debugBuf = new StringBuffer(); + @Override public void main(Provider p) throws Exception { - boolean status = true; - Random random = new Random(); - try { byte[] plainText = new byte[200]; @@ -127,6 +127,6 @@ public class TestPKCS5PaddingError extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestPKCS5PaddingError()); + main(new TestPKCS5PaddingError(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java index 876048f46cf..b9656a54086 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,20 +28,28 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm TestRSACipher + * @run main/othervm TestRSACipher sm */ -import java.io.*; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.util.Arrays; +import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; public class TestRSACipher extends PKCS11Test { private static final String[] RSA_ALGOS = { "RSA/ECB/PKCS1Padding", "RSA" }; + @Override public void main(Provider p) throws Exception { try { Cipher.getInstance(RSA_ALGOS[0], p); @@ -122,7 +130,7 @@ public class TestRSACipher extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestRSACipher()); + main(new TestRSACipher(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java index c00b39942cc..8637302b547 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,20 @@ * @summary basic test for RSA cipher key wrapping functionality * @author Valerie Peng * @library .. + * @run main/othervm TestRSACipherWrap + * @run main/othervm TestRSACipherWrap sm */ -import java.io.*; -import java.util.*; -import java.security.*; - -import javax.crypto.*; +import java.security.GeneralSecurityException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class TestRSACipherWrap extends PKCS11Test { @@ -41,6 +48,7 @@ public class TestRSACipherWrap extends PKCS11Test { private static final String[] RSA_ALGOS = { "RSA/ECB/PKCS1Padding", "RSA" }; + @Override public void main(Provider p) throws Exception { try { Cipher.getInstance(RSA_ALGOS[0], p); @@ -104,6 +112,6 @@ public class TestRSACipherWrap extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestRSACipherWrap()); + main(new TestRSACipherWrap(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java b/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java index 17ffe9553c2..cebe4224129 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,16 +28,21 @@ * @author Valerie Peng * @library .. * @key randomness + * @run main/othervm TestRawRSACipher + * @run main/othervm TestRawRSACipher sm */ -import javax.crypto.*; -import java.io.*; -import javax.crypto.spec.SecretKeySpec; -import java.security.*; -import java.util.*; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.util.Arrays; +import java.util.Random; +import javax.crypto.Cipher; public class TestRawRSACipher extends PKCS11Test { + @Override public void main(Provider p) throws Exception { try { Cipher.getInstance("RSA/ECB/NoPadding", p); @@ -80,6 +85,6 @@ public class TestRawRSACipher extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestRawRSACipher()); + main(new TestRawRSACipher(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java index e6b7f7d8a34..018edc7417a 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,16 +28,19 @@ * @author Valerie Peng * @library .. * @key randomness + * @run main/othervm TestSymmCiphers + * @run main/othervm TestSymmCiphers sm */ -import java.io.*; -import java.nio.*; -import java.util.*; -import java.security.*; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Random; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; public class TestSymmCiphers extends PKCS11Test { @@ -81,6 +84,7 @@ public class TestSymmCiphers extends PKCS11Test { }; private static StringBuffer debugBuf = new StringBuffer(); + @Override public void main(Provider p) throws Exception { // NSS reports CKR_DEVICE_ERROR when the data passed to // its EncryptUpdate/DecryptUpdate is not multiple of blocks @@ -272,6 +276,6 @@ public class TestSymmCiphers extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestSymmCiphers()); + main(new TestSymmCiphers(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java index 07d48649be5..2359e77bf03 100644 --- a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java +++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,17 +28,22 @@ * @author Valerie Peng * @library .. * @key randomness + * @run main/othervm TestSymmCiphersNoPad + * @run main/othervm TestSymmCiphersNoPad sm */ -import java.io.*; -import java.nio.*; -import java.util.*; - -import java.security.*; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Random; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; public class TestSymmCiphersNoPad extends PKCS11Test { @@ -67,6 +72,7 @@ public class TestSymmCiphersNoPad extends PKCS11Test { private static StringBuffer debugBuf; + @Override public void main(Provider p) throws Exception { boolean status = true; Random random = new Random(); @@ -234,6 +240,6 @@ public class TestSymmCiphersNoPad extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestSymmCiphersNoPad()); + main(new TestSymmCiphersNoPad(), args); } } diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java index 2c123a84a06..45e03897359 100644 --- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java +++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,20 @@ * @summary Verify that DH works properly * @author Andreas Sterbenz * @library .. + * @run main/othervm TestDH + * @run main/othervm TestDH sm */ -import java.io.*; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.util.Arrays; +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; public class TestDH extends PKCS11Test { + @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "DH") == null) { System.out.println("DH not supported, skipping"); @@ -91,8 +94,9 @@ public class TestDH extends PKCS11Test { testAlgorithm(ka2, kp2, ka1, kp1, "TlsPremasterSecret"); } - private static void testAlgorithm(KeyAgreement ka1, KeyPair kp1, KeyAgreement ka2, KeyPair kp2, String algorithm) throws Exception { - SecretKey key1 = null; + private static void testAlgorithm(KeyAgreement ka1, KeyPair kp1, + KeyAgreement ka2, KeyPair kp2, String algorithm) throws Exception { + SecretKey key1; ka1.init(kp1.getPrivate()); ka1.doPhase(kp2.getPublic(), true); @@ -115,7 +119,7 @@ public class TestDH extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestDH()); + main(new TestDH(), args); } } diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java index 95f1ce1c52f..15a96b9b19e 100644 --- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java +++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,14 +26,18 @@ * @bug 7146728 * @summary Interop test for DH with secret that has a leading 0x00 byte * @library .. + * @run main/othervm TestInterop + * @run main/othervm TestInterop sm */ import java.math.BigInteger; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.util.Arrays; +import javax.crypto.KeyAgreement; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHPublicKeySpec; public class TestInterop extends PKCS11Test { @@ -72,6 +76,7 @@ public class TestInterop extends PKCS11Test { + "30313414180008978013330410484011186019824874948204261839391153650949864" + "429505597086564709"); + @Override public void main(Provider prov) throws Exception { if (prov.getService("KeyAgreement", "DH") == null) { System.out.println("DH not supported, skipping"); @@ -138,6 +143,6 @@ public class TestInterop extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestInterop()); + main(new TestInterop(), args); } } diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java index ee9332764c8..aec6c0c7c16 100644 --- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java +++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,15 +27,19 @@ * @summary KAT test for DH (normal and with secret that has leading a 0x00 byte) * @author Andreas Sterbenz * @library .. + * @run main/othervm TestShort + * @run main/othervm TestShort sm */ import java.math.BigInteger; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.util.Arrays; +import javax.crypto.KeyAgreement; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHPublicKeySpec; public class TestShort extends PKCS11Test { @@ -83,6 +87,7 @@ public class TestShort extends PKCS11Test { + "1a:6a:15:d8:a4:8c:0a:ce:f0:15:03:0c:c2:56:82:a2:75:9b:49:fe:ed:60:c5:6e" + ":de:47:55:62:4f:16:20:6d:74:cc:7b:95:93:25:2c:ea"); + @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyAgreement", "DH") == null) { System.out.println("DH not supported, skipping"); @@ -142,7 +147,7 @@ public class TestShort extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestShort()); + main(new TestShort(), args); } } diff --git a/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java b/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java index 2d7f77bc9c5..3c8b3a52283 100644 --- a/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java +++ b/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,19 +28,21 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm DESParity + * @run main/othervm DESParity sm */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.spec.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.Provider; +import java.util.Random; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.SecretKeySpec; public class DESParity extends PKCS11Test { + @Override public void main(Provider p) throws Exception { if (p.getService("SecretKeyFactory", "DES") == null) { System.out.println("Not supported by provider, skipping"); @@ -73,7 +75,7 @@ public class DESParity extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new DESParity()); + main(new DESParity(), args); } } diff --git a/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java b/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java index 3bd7e96f117..e0f669f58e3 100644 --- a/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java +++ b/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,16 @@ * @summary test the KeyGenerator * @author Andreas Sterbenz * @library .. + * @run main/othervm TestKeyGenerator + * @run main/othervm TestKeyGenerator sm */ -import java.util.*; - -import java.security.*; - -import javax.crypto.*; +import java.security.InvalidParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.ProviderException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; enum TestResult { PASS, @@ -44,7 +47,7 @@ enum TestResult { public class TestKeyGenerator extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestKeyGenerator()); + main(new TestKeyGenerator(), args); } private TestResult test(String algorithm, int keyLen, Provider p, @@ -85,6 +88,7 @@ public class TestKeyGenerator extends PKCS11Test { return actual; } + @Override public void main(Provider p) throws Exception { test("DES", 0, p, TestResult.FAIL); test("DES", 56, p, TestResult.PASS); // ensure JCE-Compatibility diff --git a/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java b/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java index c11911a12fd..840e1ce3dcf 100644 --- a/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java +++ b/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,14 +27,14 @@ * @summary Ensure that 2048-bit DH key pairs can be generated * @author Valerie Peng * @library .. + * @run main/othervm TestDH2048 + * @run main/othervm TestDH2048 sm */ -import java.io.*; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; public class TestDH2048 extends PKCS11Test { @@ -47,6 +47,7 @@ public class TestDH2048 extends PKCS11Test { } } + @Override public void main(Provider p) throws Exception { if (p.getService("KeyPairGenerator", "DH") == null) { System.out.println("KPG for DH not supported, skipping"); @@ -61,6 +62,6 @@ public class TestDH2048 extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestDH2048()); + main(new TestDH2048(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Mac/MacKAT.java b/jdk/test/sun/security/pkcs11/Mac/MacKAT.java index c7da9297159..d7679f91ffd 100644 --- a/jdk/test/sun/security/pkcs11/Mac/MacKAT.java +++ b/jdk/test/sun/security/pkcs11/Mac/MacKAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,8 @@ import javax.crypto.spec.SecretKeySpec; * @summary Basic known-answer-test for Hmac algorithms * @author Andreas Sterbenz * @library .. - * @run main MacKAT + * @run main/othervm MacKAT + * @run main/othervm MacKAT sm */ public class MacKAT extends PKCS11Test { @@ -178,7 +179,7 @@ public class MacKAT extends PKCS11Test { }; public static void main(String[] args) throws Exception { - main(new MacKAT()); + main(new MacKAT(), args); } @Override diff --git a/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java b/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java index 21eae39b0c6..8d6689721fc 100644 --- a/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java +++ b/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,8 @@ import javax.crypto.spec.SecretKeySpec; * @summary Check if doFinal and update operation result in same Mac * @author Yu-Ching Valerie Peng, Bill Situ, Alexander Fomin * @library .. - * @run main MacSameTest + * @run main/othervm MacSameTest + * @run main/othervm MacSameTest sm * @key randomness */ public class MacSameTest extends PKCS11Test { @@ -57,7 +58,7 @@ public class MacSameTest extends PKCS11Test { * @param args the command line arguments */ public static void main(String[] args) throws Exception { - main(new MacSameTest()); + main(new MacSameTest(), args); } @Override diff --git a/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java b/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java index 68bef343889..39970d8df00 100644 --- a/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java +++ b/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,21 +28,22 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm ReinitMac + * @run main/othervm ReinitMac sm */ -import java.util.*; - -import java.security.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.Provider; +import java.util.Random; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; public class ReinitMac extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new ReinitMac()); + main(new ReinitMac(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("Mac", "HmacMD5") == null) { System.out.println(p + " does not support HmacMD5, skipping"); diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java b/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java index 1b502a95bcf..eca0485c8a9 100644 --- a/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java +++ b/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,19 +28,23 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm ByteBuffers + * @run main/othervm ByteBuffers sm */ -import java.util.*; -import java.nio.*; - -import java.security.*; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.Provider; +import java.util.Arrays; +import java.util.Random; public class ByteBuffers extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new ByteBuffers()); + main(new ByteBuffers(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("MessageDigest", "MD5") == null) { System.out.println("Provider does not support MD5, skipping"); diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java b/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java index e6f948d064f..0f48f28c859 100644 --- a/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java +++ b/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,19 +27,23 @@ * @summary Basic known-answer-test for all our MessageDigest algorithms * @author Andreas Sterbenz * @library .. + * @run main/othervm DigestKAT + * @run main/othervm DigestKAT sm */ -import java.io.*; -import java.util.*; - -import java.security.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.security.MessageDigest; +import java.security.Provider; +import java.util.Arrays; public class DigestKAT extends PKCS11Test { private final static char[] hexDigits = "0123456789abcdef".toCharArray(); public static String toString(byte[] b) { - StringBuffer sb = new StringBuffer(b.length * 3); + StringBuilder sb = new StringBuilder(b.length * 3); for (int i = 0; i < b.length; i++) { int k = b[i] & 0xff; if (i != 0) { @@ -106,6 +110,7 @@ public class DigestKAT extends PKCS11Test { this.data = data; this.digest = digest; } + @Override void run(Provider p) throws Exception { if (p.getService("MessageDigest", alg) == null) { System.out.println("Skipped " + alg); @@ -123,7 +128,6 @@ public class DigestKAT extends PKCS11Test { System.out.println("out: " + DigestKAT.toString(myDigest)); throw new Exception("Digest test for " + alg + " failed"); } -// System.out.println("Passed " + alg); } } @@ -221,12 +225,13 @@ public class DigestKAT extends PKCS11Test { System.out.println("Done (" + (stop - start) + " ms)."); } + @Override public void main(Provider p) throws Exception{ runTests(tests, p); } public static void main(String[] args) throws Exception { - main(new DigestKAT()); + main(new DigestKAT(), args); } } diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java b/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java index 2f8290a2a7a..9d8250c9ab0 100644 --- a/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java +++ b/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,18 +28,22 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm ReinitDigest + * @run main/othervm ReinitDigest sm */ -import java.util.*; - -import java.security.*; +import java.security.MessageDigest; +import java.security.Provider; +import java.util.Arrays; +import java.util.Random; public class ReinitDigest extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new ReinitDigest()); + main(new ReinitDigest(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("MessageDigest", "MD5") == null) { System.out.println("Provider does not support MD5, skipping"); diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java b/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java index 5ea2264109f..0aca8fbf791 100644 --- a/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java +++ b/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,11 +28,14 @@ * @author Valerie Peng * @library .. * @key randomness + * @run main/othervm TestCloning + * @run main/othervm TestCloning sm */ -import java.util.*; - -import java.security.*; +import java.security.MessageDigest; +import java.security.Provider; +import java.util.Arrays; +import java.util.Random; public class TestCloning extends PKCS11Test { @@ -41,13 +44,14 @@ public class TestCloning extends PKCS11Test { }; public static void main(String[] args) throws Exception { - main(new TestCloning()); + main(new TestCloning(), args); } private static final byte[] data1 = new byte[10]; private static final byte[] data2 = new byte[10*1024]; + @Override public void main(Provider p) throws Exception { Random r = new Random(); byte[] data1 = new byte[10]; diff --git a/jdk/test/sun/security/pkcs11/PKCS11Test.java b/jdk/test/sun/security/pkcs11/PKCS11Test.java index e0a17eaa87e..46621e1ae2e 100644 --- a/jdk/test/sun/security/pkcs11/PKCS11Test.java +++ b/jdk/test/sun/security/pkcs11/PKCS11Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,15 +24,38 @@ // common infrastructure for SunPKCS11 tests -import java.io.*; -import java.util.*; - -import java.security.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPairGenerator; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.ProviderException; +import java.security.Security; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.Set; public abstract class PKCS11Test { + private boolean enableSM = false; + + static final Properties props = System.getProperties(); + static final String PKCS11 = "PKCS11"; // directory of the test source @@ -40,7 +63,8 @@ public abstract class PKCS11Test { static final char SEP = File.separatorChar; - private final static String REL_CLOSED = "../../../../closed/sun/security/pkcs11".replace('/', SEP); + private static final String DEFAULT_POLICY = + BASE + SEP + ".." + SEP + "policy"; // directory corresponding to BASE in the /closed hierarchy static final String CLOSED_BASE; @@ -53,6 +77,9 @@ public abstract class PKCS11Test { String p1 = absBase.substring(0, k + 6); String p2 = absBase.substring(k + 5); CLOSED_BASE = p1 + "closed" + p2; + + // set it as a system property to make it available in policy file + System.setProperty("closed.base", CLOSED_BASE); } static String NSPR_PREFIX = ""; @@ -86,7 +113,7 @@ public abstract class PKCS11Test { if (p.getName().equals("SunPKCS11")) { found = true; break; - }; + } } catch (Exception e) { // ignore and move on to the next one } @@ -103,6 +130,19 @@ public abstract class PKCS11Test { pkcs11 = p; } + /* + * Use Solaris SPARC 11.2 or later to avoid an intermittent failure + * when running SunPKCS11-Solaris (8044554) + */ + static boolean isBadSolarisSparc(Provider p) { + if ("SunPKCS11-Solaris".equals(p.getName()) && badSolarisSparc) { + System.out.println("SunPKCS11-Solaris provider requires " + + "Solaris SPARC 11.2 or later, skipping"); + return true; + } + return false; + } + // Return a SunPKCS11 provider configured with the specified config file static Provider getSunPKCS11(String config) throws Exception { if (pkcs11 == null) { @@ -114,15 +154,43 @@ public abstract class PKCS11Test { public abstract void main(Provider p) throws Exception; private void premain(Provider p) throws Exception { - long start = System.currentTimeMillis(); - System.out.println("Running test with provider " + p.getName() + "..."); - main(p); - long stop = System.currentTimeMillis(); - System.out.println("Completed test with provider " + p.getName() + - " (" + (stop - start) + " ms)."); + // set a security manager and policy before a test case runs, + // and disable them after the test case finished + try { + if (enableSM) { + System.setSecurityManager(new SecurityManager()); + } + long start = System.currentTimeMillis(); + System.out.printf( + "Running test with provider %s (security manager %s) ...%n", + p.getName(), enableSM ? "enabled" : "disabled"); + main(p); + long stop = System.currentTimeMillis(); + System.out.println("Completed test with provider " + p.getName() + + " (" + (stop - start) + " ms)."); + } finally { + if (enableSM) { + System.setSecurityManager(null); + } + } } public static void main(PKCS11Test test) throws Exception { + main(test, null); + } + + public static void main(PKCS11Test test, String[] args) throws Exception { + if (args != null) { + if (args.length > 0 && "sm".equals(args[0])) { + test.enableSM = true; + } + if (test.enableSM) { + System.setProperty("java.security.policy", + (args.length > 1) ? BASE + SEP + args[1] + : DEFAULT_POLICY); + } + } + Provider[] oldProviders = Security.getProviders(); try { System.out.println("Beginning test run " + test.getClass().getName() + "..."); @@ -218,7 +286,6 @@ public abstract class PKCS11Test { } static String getNSSLibDir(String library) throws Exception { - Properties props = System.getProperties(); String osName = props.getProperty("os.name"); if (osName.startsWith("Win")) { osName = "Windows"; @@ -249,6 +316,15 @@ public abstract class PKCS11Test { return nssLibDir; } + static boolean isBadNSSVersion(Provider p) { + if (isNSS(p) && badNSSVersion) { + System.out.println("NSS 3.11 has a DER issue that recent " + + "version do not."); + return true; + } + return false; + } + protected static void safeReload(String lib) throws Exception { try { System.load(lib); @@ -317,34 +393,32 @@ public abstract class PKCS11Test { try { libfile = getNSSLibDir() + System.mapLibraryName(library); - FileInputStream is = new FileInputStream(libfile); - byte[] data = new byte[1000]; - int read = 0; + try (FileInputStream is = new FileInputStream(libfile)) { + byte[] data = new byte[1000]; + int read = 0; - while (is.available() > 0) { - if (read == 0) { - read = is.read(data, 0, 1000); - } else { - // Prepend last 100 bytes in case the header was split - // between the reads. - System.arraycopy(data, 900, data, 0, 100); - read = 100 + is.read(data, 100, 900); - } + while (is.available() > 0) { + if (read == 0) { + read = is.read(data, 0, 1000); + } else { + // Prepend last 100 bytes in case the header was split + // between the reads. + System.arraycopy(data, 900, data, 0, 100); + read = 100 + is.read(data, 100, 900); + } - s = new String(data, 0, read); - if ((i = s.indexOf(nssHeader)) > 0) { - found = true; - // If the nssHeader is before 920 we can break, otherwise - // we may not have the whole header so do another read. If - // no bytes are in the stream, that is ok, found is true. - if (i < 920) { - break; + s = new String(data, 0, read); + if ((i = s.indexOf(nssHeader)) > 0) { + found = true; + // If the nssHeader is before 920 we can break, otherwise + // we may not have the whole header so do another read. If + // no bytes are in the stream, that is ok, found is true. + if (i < 920) { + break; + } } } } - - is.close(); - } catch (Exception e) { e.printStackTrace(); } @@ -438,14 +512,13 @@ public abstract class PKCS11Test { } // Generate a vector of supported elliptic curves of a given provider - static Vector getKnownCurves(Provider p) throws Exception { + static List getKnownCurves(Provider p) throws Exception { int index; int begin; int end; String curve; - KeyPair kp = null; - Vector results = new Vector(); + List results = new ArrayList<>(); // Get Curves to test from SunEC. String kcProp = Security.getProvider("SunEC"). getProperty("AlgorithmParameters.EC SupportedCurves"); @@ -483,7 +556,7 @@ public abstract class PKCS11Test { try { KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p); kpg.initialize(e); - kp = kpg.generateKeyPair(); + kpg.generateKeyPair(); results.add(e); System.out.println("Supported"); } catch (ProviderException ex) { @@ -514,9 +587,8 @@ public abstract class PKCS11Test { } // Check support for a curve with a provided Vector of EC support - boolean checkSupport(Vector supportedEC, + boolean checkSupport(List supportedEC, ECParameterSpec curve) { - boolean found = false; for (ECParameterSpec ec: supportedEC) { if (ec.equals(curve)) { return true; @@ -529,7 +601,7 @@ public abstract class PKCS11Test { // Location of the NSS libraries on each supported platform static { - osMap = new HashMap(); + osMap = new HashMap<>(); osMap.put("SunOS-sparc-32", new String[]{"/usr/lib/mps/"}); osMap.put("SunOS-sparcv9-64", new String[]{"/usr/lib/mps/64/"}); osMap.put("SunOS-x86-32", new String[]{"/usr/lib/mps/"}); @@ -551,11 +623,20 @@ public abstract class PKCS11Test { private final static char[] hexDigits = "0123456789abcdef".toCharArray(); + static final boolean badNSSVersion = + getNSSVersion() >= 3.11 && getNSSVersion() < 3.12; + + static final boolean badSolarisSparc = + System.getProperty("os.name").equals("SunOS") && + System.getProperty("os.arch").equals("sparcv9") && + System.getProperty("os.version").compareTo("5.11") <= 0 && + getDistro().compareTo("11.2") < 0; + public static String toString(byte[] b) { if (b == null) { return "(null)"; } - StringBuffer sb = new StringBuffer(b.length * 3); + StringBuilder sb = new StringBuilder(b.length * 3); for (int i = 0; i < b.length; i++) { int k = b[i] & 0xff; if (i != 0) { @@ -637,8 +718,7 @@ public abstract class PKCS11Test { /** * Get the identifier for the operating system distribution */ - public String getDistro() { - + static String getDistro() { try (BufferedReader in = new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec("uname -v").getInputStream()))) { diff --git a/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java index 56833bd2cc6..67c691a3bc2 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java +++ b/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,14 +28,26 @@ * @author Andreas Sterbenz * @library .. * @run main/othervm AddPrivateKey + * @run main/othervm AddPrivateKey sm policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStore.PasswordProtection; +import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; // this test is currently only run for the NSS KeyStore provider, but it // is really a generic KeyStore test so it should be modified to run for @@ -63,6 +75,12 @@ public class AddPrivateKey extends SecmodTest { System.out.println(); Security.addProvider(p); + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyStore ks = KeyStore.getInstance(PKCS11, p); ks.load(null, password); for (String alias : aliases(ks)) { diff --git a/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java b/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java index 6b2b545611a..2eedf3cd506 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java +++ b/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,14 +28,21 @@ * @author Andreas Sterbenz * @library .. * @run main/othervm AddTrustedCert + * @run main/othervm AddTrustedCert sm policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStore.TrustedCertificateEntry; +import java.security.Provider; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; public class AddTrustedCert extends SecmodTest { @@ -56,6 +63,13 @@ public class AddTrustedCert extends SecmodTest { System.out.println(p); Security.addProvider(p); + + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyStore ks = KeyStore.getInstance(PKCS11, p); ks.load(null, password); Collection aliases = new TreeSet<>(Collections.list( diff --git a/jdk/test/sun/security/pkcs11/Secmod/Crypto.java b/jdk/test/sun/security/pkcs11/Secmod/Crypto.java index 735149f1c84..2e571051cbd 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/Crypto.java +++ b/jdk/test/sun/security/pkcs11/Secmod/Crypto.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,14 @@ * @author Andreas Sterbenz * @library .. * @run main/othervm Crypto + * @run main/othervm Crypto sm policy */ -import java.security.*; +import java.io.File; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Signature; public class Crypto extends SecmodTest { @@ -42,6 +47,12 @@ public class Crypto extends SecmodTest { String configName = BASE + SEP + "nsscrypto.cfg"; Provider p = getSunPKCS11(configName); + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", p); KeyPair kp = kpg.generateKeyPair(); diff --git a/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java index a2547652348..b3170597b5d 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java +++ b/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,13 +29,19 @@ * @author Andreas Sterbenz * @library .. * @run main/othervm GetPrivateKey + * @run main/othervm GetPrivateKey sm policy */ -import java.util.*; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; +import java.io.File; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; +import java.security.Signature; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; public class GetPrivateKey extends SecmodTest { @@ -49,6 +55,13 @@ public class GetPrivateKey extends SecmodTest { System.out.println(p); Security.addProvider(p); + + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyStore ks = KeyStore.getInstance(PKCS11, p); ks.load(null, password); Collection aliases = new TreeSet<>( diff --git a/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java index e3a60befff4..7613fce9ba5 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java +++ b/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,13 +28,19 @@ * @author Wang Weijun * @library .. * @run main/othervm JksSetPrivateKey + * @run main/othervm JksSetPrivateKey sm policy */ -import java.util.*; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; +import java.io.File; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; public class JksSetPrivateKey extends SecmodTest { @@ -48,9 +54,16 @@ public class JksSetPrivateKey extends SecmodTest { System.out.println(p); Security.addProvider(p); + + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyStore ks = KeyStore.getInstance("PKCS11", p); ks.load(null, password); - Collection aliases = new TreeSet(Collections.list(ks.aliases())); + Collection aliases = new TreeSet<>(Collections.list(ks.aliases())); System.out.println("entries: " + aliases.size()); System.out.println(aliases); @@ -66,14 +79,14 @@ public class JksSetPrivateKey extends SecmodTest { jks.setKeyEntry("k1", privateKey, "changeit".toCharArray(), chain); throw new Exception("No, an NSS PrivateKey shouldn't be extractable and put inside a JKS keystore"); } catch (KeyStoreException e) { - System.err.println(e);; // This is OK + System.err.println(e); // This is OK } try { jks.setKeyEntry("k2", new DummyPrivateKey(), "changeit".toCharArray(), chain); throw new Exception("No, non-PKCS#8 key shouldn't be put inside a KeyStore"); } catch (KeyStoreException e) { - System.err.println(e);; // This is OK + System.err.println(e); // This is OK } System.out.println("OK"); @@ -81,35 +94,41 @@ public class JksSetPrivateKey extends SecmodTest { jks.setKeyEntry("k3", new DummyPrivateKey2(), "changeit".toCharArray(), chain); throw new Exception("No, not-extractble key shouldn't be put inside a KeyStore"); } catch (KeyStoreException e) { - System.err.println(e);; // This is OK + System.err.println(e); // This is OK } System.out.println("OK"); } } class DummyPrivateKey implements PrivateKey { + @Override public String getAlgorithm() { return "DUMMY"; } + @Override public String getFormat() { return "DUMMY"; } + @Override public byte[] getEncoded() { return "DUMMY".getBytes(); } } class DummyPrivateKey2 implements PrivateKey { + @Override public String getAlgorithm() { return "DUMMY"; } + @Override public String getFormat() { return "PKCS#8"; } + @Override public byte[] getEncoded() { return null; } diff --git a/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java b/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java index fd1aa694ddf..c08fe8446aa 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java +++ b/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,7 @@ * questions. */ +import java.io.File; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -35,6 +36,7 @@ import java.util.Collections; * @summary Checks that PKCS#11 keystore can't be loaded with wrong password * @library ../ * @run main/othervm LoadKeystore + * @run main/othervm LoadKeystore sm policy */ public class LoadKeystore extends SecmodTest { @@ -50,6 +52,12 @@ public class LoadKeystore extends SecmodTest { System.out.println(); Security.addProvider(p); + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + try { System.out.println("Load keystore with wrong type"); KeyStore.getInstance("unknown", p); diff --git a/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java b/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java index 3d4a43bff30..277a51ec4c2 100644 --- a/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java +++ b/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,13 +28,17 @@ * @author Andreas Sterbenz * @library .. * @run main/othervm TrustAnchors + * @run main/othervm TrustAnchors sm policy */ -import java.util.*; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; +import java.io.File; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; public class TrustAnchors extends SecmodTest { @@ -57,9 +61,16 @@ public class TrustAnchors extends SecmodTest { System.out.println(p); Security.addProvider(p); + + if (args.length > 1 && "sm".equals(args[0])) { + System.setProperty("java.security.policy", + BASE + File.separator + args[1]); + System.setSecurityManager(new SecurityManager()); + } + KeyStore ks = KeyStore.getInstance("PKCS11", p); ks.load(null, null); - Collection aliases = new TreeSet(Collections.list(ks.aliases())); + Collection aliases = new TreeSet<>(Collections.list(ks.aliases())); System.out.println("entries: " + aliases.size()); System.out.println(aliases); diff --git a/jdk/test/sun/security/pkcs11/Secmod/policy b/jdk/test/sun/security/pkcs11/Secmod/policy new file mode 100644 index 00000000000..e4c95ca6dd5 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/Secmod/policy @@ -0,0 +1,6 @@ +grant { + permission java.security.SecurityPermission "authProvider.*"; + permission java.io.FilePermission "${test.src}/-", "read"; + permission java.io.FilePermission "${pkcs11test.nss.db}/-", "read"; + permission java.io.FilePermission "${pkcs11test.nss.libdir}/-", "read"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java b/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java index f9bfb1759ef..8c2c1686f73 100644 --- a/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java +++ b/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,17 +28,17 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm Basic + * @run main/othervm Basic sm */ -import java.io.*; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.SecureRandom; public class Basic extends PKCS11Test { + @Override public void main(Provider p) throws Exception { SecureRandom random; try { @@ -58,7 +58,7 @@ public class Basic extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new Basic()); + main(new Basic(), args); } } diff --git a/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java b/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java index d45063a8eda..39e4424af35 100644 --- a/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java +++ b/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,19 +28,24 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm ByteBuffers + * @run main/othervm ByteBuffers sm */ -import java.util.*; -import java.nio.*; - -import java.security.*; +import java.nio.ByteBuffer; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Signature; +import java.util.Random; public class ByteBuffers extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new ByteBuffers()); + main(new ByteBuffers(), args); } + @Override public void main(Provider p) throws Exception { /* @@ -48,9 +53,9 @@ public class ByteBuffers extends PKCS11Test { * when running SunPKCS11-Solaris provider (8044554) */ if (p.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + diff --git a/jdk/test/sun/security/pkcs11/Signature/TestDSA.java b/jdk/test/sun/security/pkcs11/Signature/TestDSA.java index 2b81bb3a333..c4fcf5d5956 100644 --- a/jdk/test/sun/security/pkcs11/Signature/TestDSA.java +++ b/jdk/test/sun/security/pkcs11/Signature/TestDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,14 +28,24 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm TestDSA + * @run main/othervm TestDSA sm */ -import java.io.*; -import java.util.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; import java.math.BigInteger; - -import java.security.*; -import java.security.spec.*; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.util.Random; public class TestDSA extends PKCS11Test { @@ -102,9 +112,10 @@ public class TestDSA extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestDSA()); + main(new TestDSA(), args); } + @Override public void main(Provider provider) throws Exception { long start = System.currentTimeMillis(); @@ -115,9 +126,9 @@ public class TestDSA extends PKCS11Test { * when running SunPKCS11-Solaris (8044554) */ if (provider.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + diff --git a/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java b/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java index efe97077628..f671bba3bd7 100644 --- a/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java +++ b/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + /* * @test * @bug 7200306 8029158 @@ -27,19 +28,24 @@ * with unsupported key sizes * @library .. * @key randomness + * @run main/othervm TestDSAKeyLength + * @run main/othervm TestDSAKeyLength sm */ - -import java.security.*; -import java.security.spec.*; -import java.security.interfaces.*; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Signature; public class TestDSAKeyLength extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestDSAKeyLength()); + main(new TestDSAKeyLength(), args); } + @Override public void main(Provider provider) throws Exception { if (isNSS(provider) && getNSSVersion() >= 3.14) { System.out.println("Skip testing NSS " + getNSSVersion()); @@ -51,9 +57,9 @@ public class TestDSAKeyLength extends PKCS11Test { * when running SunPKCS11-Solaris (8044554) */ if (provider.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + diff --git a/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java b/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java index 0b79e68d20c..43341472e99 100644 --- a/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java +++ b/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,14 +27,26 @@ * @summary Make sure initSign/initVerify() check RSA key lengths * @author Yu-Ching Valerie Peng * @library .. + * @run main/othervm TestRSAKeyLength + * @run main/othervm TestRSAKeyLength sm */ -import java.security.*; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignedObject; public class TestRSAKeyLength extends PKCS11Test { + public static void main(String[] args) throws Exception { - main(new TestRSAKeyLength()); + main(new TestRSAKeyLength(), args); } + + @Override public void main(Provider p) throws Exception { /* @@ -42,9 +54,9 @@ public class TestRSAKeyLength extends PKCS11Test { * when running SunPKCS11-Solaris (8044554) */ if (p.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + diff --git a/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java b/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java index 82b7f4abc26..4c0bbfba8f6 100644 --- a/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java +++ b/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,16 +29,31 @@ * @author Andreas Sterbenz * @library .. * @library ../../../../java/security/testlibrary + * @run main/othervm ReadCertificates + * @run main/othervm ReadCertificates sm policy */ -import java.io.*; -import java.util.*; - -import java.security.cert.*; -import java.security.*; -import java.security.interfaces.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; import java.security.spec.ECParameterSpec; - +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import javax.security.auth.x500.X500Principal; public class ReadCertificates extends PKCS11Test { @@ -49,16 +64,18 @@ public class ReadCertificates extends PKCS11Test { private static Collection readCertificates(File file) throws Exception { System.out.println("Loading " + file.getName() + "..."); - InputStream in = new FileInputStream(file); - Collection certs = (Collection)factory.generateCertificates(in); - in.close(); + Collection certs; + try (InputStream in = new FileInputStream(file)) { + certs = (Collection)factory.generateCertificates(in); + } return certs; } public static void main(String[] args) throws Exception { - main(new ReadCertificates()); + main(new ReadCertificates(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("Signature", "SHA1withECDSA") == null) { System.out.println("Provider does not support ECDSA, skipping..."); @@ -79,7 +96,7 @@ public class ReadCertificates extends PKCS11Test { } catch (CertificateException e) { // ignore } - Map certs = new LinkedHashMap(); + Map certs = new LinkedHashMap<>(); File dir = new File(BASE, "certs"); File closedDir = new File(CLOSED_BASE, "certs"); @@ -103,7 +120,7 @@ public class ReadCertificates extends PKCS11Test { System.out.println("OK: " + certs.size() + " certificates."); // Get supported curves - Vector supportedEC = getKnownCurves(p); + List supportedEC = getKnownCurves(p); System.out.println("Test Certs:\n"); for (X509Certificate cert : certs.values()) { @@ -127,7 +144,8 @@ public class ReadCertificates extends PKCS11Test { System.out.println("Warning: " + e.getMessage() + ". Trying another provider..."); cert.verify(key); - } catch (Exception e) { + } catch (CertificateException | InvalidKeyException | + NoSuchProviderException | SignatureException e) { System.out.println(e.getMessage()); if (key instanceof ECPublicKey) { System.out.println("Failed.\n\tCurve: " + @@ -145,7 +163,7 @@ public class ReadCertificates extends PKCS11Test { // try some random invalid signatures to make sure we get the correct // error System.out.println("Checking incorrect signatures..."); - List certList = new ArrayList(certs.values()); + List certList = new ArrayList<>(certs.values()); for (int i = 0; i < 20; i++) { X509Certificate cert, signer; do { @@ -161,9 +179,7 @@ public class ReadCertificates extends PKCS11Test { } else { throw new Exception("Verified invalid signature"); } - } catch (SignatureException e) { - System.out.println("OK: " + e); - } catch (InvalidKeyException e) { + } catch (SignatureException | InvalidKeyException e) { System.out.println("OK: " + e); } } diff --git a/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java b/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java index 112470509fe..3252b08d195 100644 --- a/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java +++ b/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,26 +29,41 @@ * @library .. * @library ../../../../java/security/testlibrary * @key randomness + * @run main/othervm ReadPKCS12 + * @run main/othervm ReadPKCS12 sm policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.interfaces.*; -import java.security.cert.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; import java.security.cert.Certificate; - -import javax.security.auth.x500.X500Principal; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; public class ReadPKCS12 extends PKCS11Test { private final static boolean COPY = false; public static void main(String[] args) throws Exception { - main(new ReadPKCS12()); + main(new ReadPKCS12(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("Signature", "SHA1withECDSA") == null) { System.out.println("Provider does not support ECDSA, skipping..."); @@ -71,29 +86,30 @@ public class ReadPKCS12 extends PKCS11Test { KeyStore ks2; if (COPY) { ks2 = KeyStore.getInstance("JKS"); - InputStream in = new FileInputStream("keystore.old"); - ks2.load(in, "passphrase".toCharArray()); - in.close(); + try (InputStream in = new FileInputStream("keystore.old")) { + ks2.load(in, "passphrase".toCharArray()); + } } File dir = new File(BASE, "pkcs12"); File closedDir = new File(CLOSED_BASE, "pkcs12"); - Map passwords = new HashMap(); - BufferedReader reader = new BufferedReader(new FileReader((new File(BASE, "p12passwords.txt")))); - while (true) { - String line = reader.readLine(); - if (line == null) { - break; + Map passwords = new HashMap<>(); + try (BufferedReader reader = new BufferedReader( + new FileReader(new File(BASE, "p12passwords.txt")))) { + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if ((line.length() == 0) || line.startsWith("#")) { + continue; + } + String[] s = line.split(" "); + passwords.put(s[0], s[1].toCharArray()); } - line = line.trim(); - if ((line.length() == 0) || line.startsWith("#")) { - continue; - } - String[] s = line.split(" "); - passwords.put(s[0], s[1].toCharArray()); } - reader.close(); for (File file : concat(dir.listFiles(), closedDir.listFiles())) { String name = file.getName(); @@ -108,10 +124,11 @@ public class ReadPKCS12 extends PKCS11Test { password = passwords.get("*"); } - InputStream in = new FileInputStream(file); - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(in, password); - in.close(); + KeyStore ks; + try (InputStream in = new FileInputStream(file)) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(in, password); + } List aliases = Collections.list(ks.aliases()); System.out.println("Aliases: " + aliases); @@ -147,9 +164,9 @@ public class ReadPKCS12 extends PKCS11Test { } if (COPY) { - OutputStream out = new FileOutputStream("keystore.new"); - ks2.store(out, "passphrase".toCharArray()); - out.close(); + try (OutputStream out = new FileOutputStream("keystore.new")) { + ks2.store(out, "passphrase".toCharArray()); + } } System.out.println("OK"); diff --git a/jdk/test/sun/security/pkcs11/ec/TestCurves.java b/jdk/test/sun/security/pkcs11/ec/TestCurves.java index 3ada7788cf9..d8e52a9aa69 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestCurves.java +++ b/jdk/test/sun/security/pkcs11/ec/TestCurves.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,53 +29,46 @@ * @library .. * @modules jdk.crypto.pkcs11/sun.security.pkcs11.wrapper * @compile -XDignore.symbol.file TestCurves.java - * @run main TestCurves + * @run main/othervm TestCurves + * @run main/othervm TestCurves sm * @key randomness */ -import java.util.*; - -import java.security.*; -import java.security.spec.*; - -import javax.crypto.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.ProviderException; +import java.security.Signature; +import java.security.spec.ECParameterSpec; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import javax.crypto.KeyAgreement; public class TestCurves extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestCurves()); + main(new TestCurves(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "ECDH") == null) { System.out.println("Not supported by provider, skipping"); return; } - if (isNSS(p) && getNSSVersion() >= 3.11 && getNSSVersion() < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not."); + if (isBadNSSVersion(p)) { return; } - /* - * Use Solaris SPARC 11.2 or later to avoid an intermittent failure - * when running SunPKCS11-Solaris (8044554) - */ - if (p.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && - getDistro().compareTo("11.2") < 0) { - - System.out.println("SunPKCS11-Solaris provider requires " + - "Solaris SPARC 11.2 or later, skipping"); + if (isBadSolarisSparc(p)) { return; } // Check if this is sparc for later failure avoidance. boolean sparc = false; - if (System.getProperty("os.arch").equals("sparcv9")) { + if (props.getProperty("os.arch").equals("sparcv9")) { sparc = true; System.out.println("This is a sparcv9"); } @@ -84,7 +77,7 @@ public class TestCurves extends PKCS11Test { byte[] data = new byte[2048]; random.nextBytes(data); - Vector curves = getKnownCurves(p); + List curves = getKnownCurves(p); for (ECParameterSpec params : curves) { System.out.println("Testing " + params + "..."); KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p); diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDH.java b/jdk/test/sun/security/pkcs11/ec/TestECDH.java index 8d950818560..421f46ccfc2 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestECDH.java +++ b/jdk/test/sun/security/pkcs11/ec/TestECDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,16 +28,21 @@ * @author Andreas Sterbenz * @library .. * @library ../../../../java/security/testlibrary + * @run main/othervm TestECDH + * @run main/othervm TestECDH sm policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.spec.*; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; import java.security.interfaces.ECPublicKey; - -import javax.crypto.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import javax.crypto.KeyAgreement; public class TestECDH extends PKCS11Test { @@ -55,6 +60,7 @@ public class TestECDH extends PKCS11Test { private final static String secret163 = "04:ae:71:c1:c6:4d:f4:34:4d:72:70:a4:64:65:7f:2d:88:2d:3f:50:be"; + @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "ECDH") == null) { System.out.println("Provider does not support ECDH, skipping"); @@ -89,10 +95,12 @@ public class TestECDH extends PKCS11Test { System.out.println("OK"); } - private final static void test(Provider p, String pub1s, String priv1s, String pub2s, String priv2s, String secrets) throws Exception { + private final static void test(Provider p, String pub1s, String priv1s, + String pub2s, String priv2s, String secrets) throws Exception { KeyFactory kf = KeyFactory.getInstance("EC", p); PublicKey pub1 = kf.generatePublic(new X509EncodedKeySpec(parse(pub1s))); - System.out.println("Testing using parameters " + ((ECPublicKey)pub1).getParams() + "..."); + System.out.println("Testing using parameters " + + ((ECPublicKey)pub1).getParams() + "..."); PrivateKey priv1 = kf.generatePrivate(new PKCS8EncodedKeySpec(parse(priv1s))); PublicKey pub2 = kf.generatePublic(new X509EncodedKeySpec(parse(pub2s))); @@ -121,7 +129,7 @@ public class TestECDH extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestECDH()); + main(new TestECDH(), args); } } diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDH2.java b/jdk/test/sun/security/pkcs11/ec/TestECDH2.java index d5b03ae32aa..dee7a706ce7 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestECDH2.java +++ b/jdk/test/sun/security/pkcs11/ec/TestECDH2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,19 +30,25 @@ * @library ../../../../java/security/testlibrary * @modules java.base/sun.security.util * @compile -XDignore.symbol.file TestECDH2.java - * @run main TestECDH2 + * @run main/othervm TestECDH2 + * @run main/othervm TestECDH2 sm */ -import java.io.*; -import java.util.*; import java.math.BigInteger; - -import java.security.*; -import java.security.spec.*; -import java.security.interfaces.*; -import javax.crypto.*; - -import sun.security.util.ECUtil; +import java.security.AlgorithmParameters; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.util.Arrays; +import javax.crypto.KeyAgreement; public class TestECDH2 extends PKCS11Test { @@ -81,7 +87,9 @@ public class TestECDH2 extends PKCS11Test { private KeyPair genECKeyPair(String curvName, String privD, String pubX, String pubY, Provider p) throws Exception { - ECParameterSpec ecParams = ECUtil.getECParameterSpec(p, curvName); + AlgorithmParameters params = AlgorithmParameters.getInstance("EC", p); + params.init(new ECGenParameterSpec(curvName)); + ECParameterSpec ecParams = params.getParameterSpec(ECParameterSpec.class); ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(privD, 16), ecParams); ECPublicKeySpec pubKeySpec = @@ -98,19 +106,17 @@ public class TestECDH2 extends PKCS11Test { return kpg.generateKeyPair(); } public static void main(String[] args) throws Exception { - main(new TestECDH2()); + main(new TestECDH2(), args); } + @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyAgreement", "ECDH") == null) { System.out.println("ECDH not supported, skipping"); return; } - if (isNSS(provider) && getNSSVersion() >= 3.11 && - getNSSVersion() < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not."); + if (isBadNSSVersion(provider)) { return; } diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDSA.java b/jdk/test/sun/security/pkcs11/ec/TestECDSA.java index b1bd35f4783..9e0b8401aa9 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestECDSA.java +++ b/jdk/test/sun/security/pkcs11/ec/TestECDSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,13 +29,22 @@ * @library .. * @library ../../../../java/security/testlibrary * @key randomness + * @run main/othervm TestECDSA + * @run main/othervm TestECDSA sm policy */ -import java.util.*; - -import java.security.*; -import java.security.spec.*; -import java.security.interfaces.*; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.interfaces.ECPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Random; public class TestECDSA extends PKCS11Test { @@ -79,7 +88,8 @@ public class TestECDSA extends PKCS11Test { private final static byte[] data2Raw = {}; private final static byte[] data2SHA = b("da:39:a3:ee:5e:6b:4b:0d:32:55:bf:ef:95:60:18:90:af:d8:07:09"); - private static void verify(Provider provider, String alg, PublicKey key, byte[] data, byte[] sig, boolean result) throws Exception { + private static void verify(Provider provider, String alg, PublicKey key, + byte[] data, byte[] sig, boolean result) throws Exception { Signature s = Signature.getInstance(alg, provider); s.initVerify(key); boolean r; @@ -105,9 +115,10 @@ public class TestECDSA extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestECDSA()); + main(new TestECDSA(), args); } + @Override public void main(Provider provider) throws Exception { long start = System.currentTimeMillis(); @@ -116,25 +127,11 @@ public class TestECDSA extends PKCS11Test { return; } - if (isNSS(provider) && getNSSVersion() >= 3.11 && - getNSSVersion() < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not."); + if (isBadNSSVersion(provider)) { return; } - /* - * Use Solaris SPARC 11.2 or later to avoid an intermittent failure - * when running SunPKCS11-Solaris (8044554) - */ - if (provider.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && - getDistro().compareTo("11.2") < 0) { - - System.out.println("SunPKCS11-Solaris provider requires " + - "Solaris SPARC 11.2 or later, skipping"); + if (isBadSolarisSparc(provider)) { return; } diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java b/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java index b3f234a5b0f..c2cd7188a03 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java +++ b/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,18 +30,23 @@ * @library ../../../../java/security/testlibrary * @modules java.base/sun.security.util * @compile -XDignore.symbol.file TestECDSA2.java - * @run main TestECDSA2 + * @run main/othervm TestECDSA2 + * @run main/othervm TestECDSA2 sm */ -import java.io.*; -import java.util.*; import java.math.BigInteger; - -import java.security.*; -import java.security.spec.*; -import java.security.interfaces.*; - -import sun.security.util.ECUtil; +import java.security.AlgorithmParameters; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; public class TestECDSA2 extends PKCS11Test { @@ -78,7 +83,9 @@ public class TestECDSA2 extends PKCS11Test { private KeyPair genECKeyPair(String curvName, String privD, String pubX, String pubY, Provider p) throws Exception { - ECParameterSpec ecParams = ECUtil.getECParameterSpec(p, curvName); + AlgorithmParameters params = AlgorithmParameters.getInstance("EC", p); + params.init(new ECGenParameterSpec(curvName)); + ECParameterSpec ecParams = params.getParameterSpec(ECParameterSpec.class); ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(privD, 16), ecParams); ECPublicKeySpec pubKeySpec = @@ -90,9 +97,10 @@ public class TestECDSA2 extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestECDSA2()); + main(new TestECDSA2(), args); } + @Override public void main(Provider provider) throws Exception { boolean testP256 = (provider.getService("Signature", "SHA256withECDSA") != null); @@ -105,10 +113,7 @@ public class TestECDSA2 extends PKCS11Test { return; } - if (isNSS(provider) && getNSSVersion() >= 3.11 && - getNSSVersion() < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not."); + if (isBadNSSVersion(provider)) { return; } diff --git a/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java b/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java index 1dd2c326048..829b44ee7d9 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java +++ b/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,29 +27,32 @@ * @summary Verify that we can use ECGenParameterSpec * @author Andreas Sterbenz * @library .. + * @run main/othervm TestECGenSpec + * @run main/othervm TestECGenSpec sm */ -import java.util.*; - -import java.security.*; -import java.security.spec.*; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; public class TestECGenSpec extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestECGenSpec()); + main(new TestECGenSpec(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("Signature", "SHA1withECDSA") == null) { System.out.println("Provider does not support ECDSA, skipping..."); return; } - if (isNSS(p) && getNSSVersion() >= 3.11 && getNSSVersion() < 3.12) { - System.out.println("NSS 3.11 has a DER issue that recent " + - "version do not."); + if (isBadNSSVersion(p)) { return; } diff --git a/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java b/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java index 6df2fbb1320..519c861ce3d 100644 --- a/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java +++ b/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,14 +27,23 @@ * @summary Test the P11ECKeyFactory * @author Andreas Sterbenz * @library .. + * @run main/othervm TestKeyFactory + * @run main/othervm TestKeyFactory sm */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; public class TestKeyFactory extends PKCS11Test { @@ -111,9 +120,10 @@ public class TestKeyFactory extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestKeyFactory()); + main(new TestKeyFactory(), args); } + @Override public void main(Provider p) throws Exception { if (p.getService("KeyFactory", "EC") == null) { System.out.println("Provider does not support EC, skipping"); diff --git a/jdk/test/sun/security/pkcs11/ec/policy b/jdk/test/sun/security/pkcs11/ec/policy new file mode 100644 index 00000000000..c850c16bc58 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/ec/policy @@ -0,0 +1,7 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.security.SecurityPermission "insertProvider.*"; + permission java.security.SecurityPermission "removeProvider.*"; + permission java.io.FilePermission "${test.src}/-", "read"; + permission java.io.FilePermission "${closed.base}/-", "read"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java index 743b4562b87..619e8db530d 100644 --- a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java +++ b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,15 +29,21 @@ * @library .. * @modules java.base/com.sun.net.ssl.internal.ssl * @run main/othervm TrustManagerTest + * @run main/othervm TrustManagerTest sm TrustManagerTest.policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.cert.*; - -import javax.net.ssl.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.Policy; +import java.security.Provider; +import java.security.Security; +import java.security.URIParameter; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; // This test belongs more in JSSE than here, but the JSSE workspace does not // have the NSS test infrastructure. It will live here for the time being. @@ -73,6 +79,12 @@ public class TrustManagerTest extends SecmodTest { X509Certificate ca = loadCertificate("certs/ca.cer"); X509Certificate anchor = loadCertificate("certs/anchor.cer"); + if (args.length > 1 && "sm".equals(args[0])) { + Policy.setPolicy(Policy.getInstance("JavaPolicy", + new URIParameter(new File(BASE, args[1]).toURI()))); + System.setSecurityManager(new SecurityManager()); + } + KeyStore trustStore = KeyStore.getInstance("JKS"); trustStore.load(null, null); trustStore.setCertificateEntry("anchor", anchor); @@ -90,11 +102,10 @@ public class TrustManagerTest extends SecmodTest { } private static X509Certificate loadCertificate(String name) throws Exception { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - InputStream in = new FileInputStream(BASE + SEP + name); - X509Certificate cert = (X509Certificate)cf.generateCertificate(in); - in.close(); - return cert; + try (InputStream in = new FileInputStream(BASE + SEP + name)) { + return (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(in); + } } } diff --git a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy new file mode 100644 index 00000000000..16bb57d4e1b --- /dev/null +++ b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy @@ -0,0 +1,3 @@ +grant { + +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/policy b/jdk/test/sun/security/pkcs11/policy new file mode 100644 index 00000000000..54281a78179 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/policy @@ -0,0 +1,3 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java b/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java index 7f08481d6f7..e0bad7cd2d4 100644 --- a/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java +++ b/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,18 +28,28 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm KeyWrap + * @run main/othervm KeyWrap sm */ -import java.io.*; -import java.util.*; - -import java.security.*; - -import javax.crypto.*; -import javax.crypto.spec.*; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.util.Random; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; public class KeyWrap extends PKCS11Test { + @Override public void main(Provider p) throws Exception { try { Cipher.getInstance("RSA/ECB/PKCS1Padding", p); @@ -62,7 +72,7 @@ public class KeyWrap extends PKCS11Test { PublicKey pub = (PublicKey)kf.translateKey(kp.getPublic()); PrivateKey priv = (PrivateKey)kf.translateKey(kp.getPrivate()); kp = new KeyPair(pub, priv); - } catch (Exception ee) { + } catch (NoSuchAlgorithmException | InvalidKeyException ee) { ee.printStackTrace(); System.out.println("Provider does not support RSA, skipping"); return; @@ -93,7 +103,7 @@ public class KeyWrap extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new KeyWrap()); + main(new KeyWrap(), args); } } diff --git a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java index cd845c68201..24c16243585 100644 --- a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java +++ b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,24 +28,28 @@ * @author Andreas Sterbenz * @library .. * @library ../../../../java/security/testlibrary + * @run main/othervm TestCACerts + * @run main/othervm TestCACerts sm TestCACerts.policy */ // this test serves as our known answer test -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.cert.*; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Enumeration; public class TestCACerts extends PKCS11Test { - private final static char SEP = File.separatorChar; - public static void main(String[] args) throws Exception { - main(new TestCACerts()); + main(new TestCACerts(), args); } + @Override public void main(Provider p) throws Exception { /* @@ -53,9 +57,9 @@ public class TestCACerts extends PKCS11Test { * when running SunPKCS11-Solaris (8044554) */ if (p.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + @@ -67,12 +71,13 @@ public class TestCACerts extends PKCS11Test { Providers.setAt(p, 1); try { String PROVIDER = p.getName(); - String javaHome = System.getProperty("java.home"); + String javaHome = props.getProperty("java.home"); String caCerts = javaHome + SEP + "lib" + SEP + "security" + SEP + "cacerts"; - InputStream in = new FileInputStream(caCerts); - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(in, null); - in.close(); + KeyStore ks; + try (InputStream in = new FileInputStream(caCerts)) { + ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(in, null); + } for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { String alias = (String)e.nextElement(); if (ks.isCertificateEntry(alias)) { diff --git a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy new file mode 100644 index 00000000000..37f028361bc --- /dev/null +++ b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy @@ -0,0 +1,7 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.security.SecurityPermission "insertProvider.*"; + permission java.security.SecurityPermission "removeProvider.*"; + permission java.util.PropertyPermission "java.home", "read"; + permission java.io.FilePermission "${java.home}/lib/security/cacerts", "read"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java b/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java index 049be1046f9..802774285c3 100644 --- a/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java +++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,26 +27,26 @@ * @summary Test KeyFactory of the new RSA provider * @author Andreas Sterbenz * @library .. + * @run main/othervm TestKeyFactory + * @run main/othervm TestKeyFactory sm rsakeys.ks.policy */ import java.io.*; import java.util.*; import java.security.*; -import java.security.interfaces.*; import java.security.spec.*; public class TestKeyFactory extends PKCS11Test { - private final static String BASE = System.getProperty("test.src", "."); - private static final char[] password = "test12".toCharArray(); static KeyStore getKeyStore() throws Exception { - InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks")); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(in, password); - in.close(); + KeyStore ks; + try (InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"))) { + ks = KeyStore.getInstance("JKS"); + ks.load(in, password); + } return ks; } @@ -128,9 +128,10 @@ public class TestKeyFactory extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestKeyFactory()); + main(new TestKeyFactory(), args); } + @Override public void main(Provider p) throws Exception { long start = System.currentTimeMillis(); KeyStore ks = getKeyStore(); diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java index 655edd3b5a2..687a7a87bc1 100644 --- a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java +++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,16 +30,20 @@ * @library /lib/testlibrary * @build jdk.testlibrary.* * @run main/othervm TestKeyPairGenerator + * @run main/othervm TestKeyPairGenerator sm TestKeyPairGenerator.policy * @key intermittent randomness */ -import java.io.*; -import java.util.*; import java.math.BigInteger; - -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAKeyGenParameterSpec; import jdk.testlibrary.RandomFactory; public class TestKeyPairGenerator extends PKCS11Test { @@ -48,7 +52,8 @@ public class TestKeyPairGenerator extends PKCS11Test { private static byte[] data; - private static void testSignature(String algorithm, PrivateKey privateKey, PublicKey publicKey) throws Exception { + private static void testSignature(String algorithm, PrivateKey privateKey, + PublicKey publicKey) throws Exception { System.out.println("Testing " + algorithm + "..."); Signature s = Signature.getInstance(algorithm, provider); s.initSign(privateKey); @@ -98,9 +103,10 @@ public class TestKeyPairGenerator extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestKeyPairGenerator()); + main(new TestKeyPairGenerator(), args); } + @Override public void main(Provider p) throws Exception { long start = System.currentTimeMillis(); provider = p; diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy new file mode 100644 index 00000000000..3f076e14679 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy @@ -0,0 +1,4 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.util.PropertyPermission "seed", "read"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java b/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java index a6070ea675d..ae4718f4f56 100644 --- a/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java +++ b/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,18 +28,25 @@ * @author Andreas Sterbenz * @library .. * @key randomness + * @run main/othervm TestSignatures + * @run main/othervm TestSignatures sm rsakeys.ks.policy */ -import java.io.*; -import java.util.*; - -import java.security.*; -import java.security.interfaces.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.interfaces.RSAPublicKey; +import java.util.Enumeration; +import java.util.Random; public class TestSignatures extends PKCS11Test { - private final static String BASE = System.getProperty("test.src", "."); - private static final char[] password = "test12".toCharArray(); private static Provider provider; @@ -47,14 +54,16 @@ public class TestSignatures extends PKCS11Test { private static byte[] data; static KeyStore getKeyStore() throws Exception { - InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks")); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(in, password); - in.close(); + KeyStore ks; + try (InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"))) { + ks = KeyStore.getInstance("JKS"); + ks.load(in, password); + } return ks; } - private static void testSignature(String algorithm, PrivateKey privateKey, PublicKey publicKey) throws Exception { + private static void testSignature(String algorithm, PrivateKey privateKey, + PublicKey publicKey) throws Exception { System.out.println("Testing " + algorithm + "..."); Signature s = Signature.getInstance(algorithm, provider); s.initSign(privateKey); @@ -78,7 +87,8 @@ public class TestSignatures extends PKCS11Test { } } - private static void test(PrivateKey privateKey, PublicKey publicKey) throws Exception { + private static void test(PrivateKey privateKey, PublicKey publicKey) + throws Exception { testSignature("MD2withRSA", privateKey, publicKey); testSignature("MD5withRSA", privateKey, publicKey); testSignature("SHA1withRSA", privateKey, publicKey); @@ -93,9 +103,10 @@ public class TestSignatures extends PKCS11Test { } public static void main(String[] args) throws Exception { - main(new TestSignatures()); + main(new TestSignatures(), args); } + @Override public void main(Provider p) throws Exception { /* @@ -103,9 +114,9 @@ public class TestSignatures extends PKCS11Test { * when running SunPKCS11-Solaris (8044554) */ if (p.getName().equals("SunPKCS11-Solaris") && - System.getProperty("os.name").equals("SunOS") && - System.getProperty("os.arch").equals("sparcv9") && - System.getProperty("os.version").compareTo("5.11") <= 0 && + props.getProperty("os.name").equals("SunOS") && + props.getProperty("os.arch").equals("sparcv9") && + props.getProperty("os.version").compareTo("5.11") <= 0 && getDistro().compareTo("11.2") < 0) { System.out.println("SunPKCS11-Solaris provider requires " + diff --git a/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy b/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy new file mode 100644 index 00000000000..4a0b0d2c46d --- /dev/null +++ b/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy @@ -0,0 +1,4 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.io.FilePermission "${test.src}/rsakeys.ks", "read"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java b/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java index 4ec23743b2a..f118d76d5c5 100644 --- a/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java +++ b/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,8 @@ public class CipherTest { // use any available port for the server socket static volatile int serverPort = 0; - final int THREADS; + static final int THREADS = Integer.getInteger("numThreads", 4); + static final String TEST_SRC = System.getProperty("test.src", "."); // assume that if we do not read anything for 20 seconds, something // has gone wrong @@ -68,6 +69,7 @@ public class CipherTest { this.cipherTest = cipherTest; } + @Override public abstract void run(); void handleRequest(InputStream in, OutputStream out) throws IOException { @@ -117,6 +119,7 @@ public class CipherTest { return TLSCipherStatus.isEnabled(cipherSuite, protocol); } + @Override public String toString() { String s = cipherSuite + " in " + protocol + " mode"; if (clientAuth != null) { @@ -260,7 +263,6 @@ public class CipherTest { private boolean failed; private CipherTest(PeerFactory peerFactory) throws IOException { - THREADS = Integer.parseInt(System.getProperty("numThreads", "4")); factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket socket = (SSLSocket)factory.createSocket(); String[] cipherSuites = socket.getSupportedCipherSuites(); @@ -350,6 +352,7 @@ public class CipherTest { this.cipherTest = cipherTest; } + @Override public final void run() { while (true) { TestParameters params = cipherTest.getTest(); @@ -405,10 +408,11 @@ public class CipherTest { private static KeyStore readKeyStore(String name) throws Exception { File file = new File(PATH, name); - InputStream in = new FileInputStream(file); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(in, passwd); - in.close(); + KeyStore ks; + try (InputStream in = new FileInputStream(file)) { + ks = KeyStore.getInstance("JKS"); + ks.load(in, passwd); + } return ks; } @@ -421,7 +425,7 @@ public class CipherTest { } else { relPath = pathToStores; } - PATH = new File(System.getProperty("test.src", "."), relPath); + PATH = new File(TEST_SRC, relPath); CipherTest.peerFactory = peerFactory; System.out.print( "Initializing test '" + peerFactory.getName() + "'..."); @@ -494,16 +498,19 @@ class AlwaysTrustManager implements X509TrustManager { } + @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { // empty } + @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // empty } + @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @@ -522,6 +529,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager { this.authType = "ECDSA".equals(authType) ? "EC" : authType; } + @Override public String[] getClientAliases(String keyType, Principal[] issuers) { if (authType == null) { return null; @@ -529,6 +537,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager { return keyManager.getClientAliases(authType, issuers); } + @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { if (authType == null) { @@ -538,6 +547,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager { issuers, socket); } + @Override public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { if (authType == null) { @@ -547,24 +557,29 @@ class MyX509KeyManager extends X509ExtendedKeyManager { issuers, engine); } + @Override public String[] getServerAliases(String keyType, Principal[] issuers) { throw new UnsupportedOperationException("Servers not supported"); } + @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { throw new UnsupportedOperationException("Servers not supported"); } + @Override public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { throw new UnsupportedOperationException("Servers not supported"); } + @Override public X509Certificate[] getCertificateChain(String alias) { return keyManager.getCertificateChain(alias); } + @Override public PrivateKey getPrivateKey(String alias) { return keyManager.getPrivateKey(alias); } @@ -577,6 +592,7 @@ class DaemonThreadFactory implements ThreadFactory { private final static ThreadFactory DEFAULT = Executors.defaultThreadFactory(); + @Override public Thread newThread(Runnable r) { Thread t = DEFAULT.newThread(r); t.setDaemon(true); diff --git a/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java b/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java index dea0495cd13..4a31b67c296 100644 --- a/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java +++ b/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,25 +34,28 @@ * @library .. * @library ../../../../java/security/testlibrary * @run main/othervm ClientJSSEServerJSSE + * @run main/othervm ClientJSSEServerJSSE sm policy */ -import java.security.*; +import java.security.Provider; +import java.security.Security; public class ClientJSSEServerJSSE extends PKCS11Test { private static String[] cmdArgs; public static void main(String[] args) throws Exception { - cmdArgs = args; - main(new ClientJSSEServerJSSE()); - } - - public void main(Provider p) throws Exception { // reset security properties to make sure that the algorithms // and keys used in this test are not disabled. Security.setProperty("jdk.tls.disabledAlgorithms", ""); Security.setProperty("jdk.certpath.disabledAlgorithms", ""); + cmdArgs = args; + main(new ClientJSSEServerJSSE(), args); + } + + @Override + public void main(Provider p) throws Exception { if (p.getService("KeyFactory", "EC") == null) { System.out.println("Provider does not support EC, skipping"); return; @@ -64,14 +67,17 @@ public class ClientJSSEServerJSSE extends PKCS11Test { private static class JSSEFactory extends CipherTest.PeerFactory { + @Override String getName() { return "Client JSSE - Server JSSE"; } + @Override CipherTest.Client newClient(CipherTest cipherTest) throws Exception { return new JSSEClient(cipherTest); } + @Override CipherTest.Server newServer(CipherTest cipherTest) throws Exception { return new JSSEServer(cipherTest); } diff --git a/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java b/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java index af8d4b5a088..90c55e68e4a 100644 --- a/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java +++ b/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,16 +21,17 @@ * questions. */ -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; - -import java.security.*; -import java.security.cert.*; -import java.security.cert.Certificate; - -import javax.net.ssl.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; class JSSEServer extends CipherTest.Server { @@ -48,15 +49,17 @@ class JSSEServer extends CipherTest.Server { serverSocket.setWantClientAuth(true); } + @Override public void run() { System.out.println("JSSE Server listening on port " + cipherTest.serverPort); Executor exec = Executors.newFixedThreadPool - (cipherTest.THREADS, DaemonThreadFactory.INSTANCE); + (CipherTest.THREADS, DaemonThreadFactory.INSTANCE); try { while (true) { final SSLSocket socket = (SSLSocket)serverSocket.accept(); socket.setSoTimeout(cipherTest.TIMEOUT); Runnable r = new Runnable() { + @Override public void run() { try { InputStream in = socket.getInputStream(); diff --git a/jdk/test/sun/security/pkcs11/sslecc/policy b/jdk/test/sun/security/pkcs11/sslecc/policy new file mode 100644 index 00000000000..f95da0407a7 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/sslecc/policy @@ -0,0 +1,9 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.security.SecurityPermission "insertProvider.*"; + permission java.security.SecurityPermission "removeProvider.*"; + permission java.util.PropertyPermission "test.src", "read"; + permission java.util.PropertyPermission "numThreads", "read"; + permission java.io.FilePermission "${test.src}/*", "read"; + permission java.net.SocketPermission "127.0.0.1:*", "listen,resolve,accept,connect"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java b/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java index 99636d0e2aa..1511dff4d4b 100644 --- a/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java +++ b/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,136 +28,138 @@ * @author Andreas Sterbenz * @library .. * @modules java.base/sun.security.internal.spec + * @run main/othervm TestKeyMaterial + * @run main/othervm TestKeyMaterial sm policy */ -import java.io.*; -import java.util.*; - -import java.security.Security; +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Provider; - +import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; - -import javax.crypto.spec.*; - -import sun.security.internal.spec.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import sun.security.internal.spec.TlsKeyMaterialParameterSpec; +import sun.security.internal.spec.TlsKeyMaterialSpec; public class TestKeyMaterial extends PKCS11Test { - private static int PREFIX_LENGTH = "km-master: ".length(); + private static final int PREFIX_LENGTH = "km-master: ".length(); public static void main(String[] args) throws Exception { - main(new TestKeyMaterial()); + main(new TestKeyMaterial(), args); } + @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyGenerator", "SunTlsKeyMaterial") == null) { System.out.println("Provider does not support algorithm, skipping"); return; } - InputStream in = new FileInputStream(new File(BASE, "keymatdata.txt")); - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try (BufferedReader reader = Files.newBufferedReader( + Paths.get(BASE, "keymatdata.txt"))) { - int n = 0; - int lineNumber = 0; + int n = 0; + int lineNumber = 0; - byte[] master = null; - int major = 0; - int minor = 0; - byte[] clientRandom = null; - byte[] serverRandom = null; - String cipherAlgorithm = null; - int keyLength = 0; - int expandedKeyLength = 0; - int ivLength = 0; - int macLength = 0; - byte[] clientCipherBytes = null; - byte[] serverCipherBytes = null; - byte[] clientIv = null; - byte[] serverIv = null; - byte[] clientMacBytes = null; - byte[] serverMacBytes = null; + byte[] master = null; + int major = 0; + int minor = 0; + byte[] clientRandom = null; + byte[] serverRandom = null; + String cipherAlgorithm = null; + int keyLength = 0; + int expandedKeyLength = 0; + int ivLength = 0; + int macLength = 0; + byte[] clientCipherBytes = null; + byte[] serverCipherBytes = null; + byte[] clientIv = null; + byte[] serverIv = null; + byte[] clientMacBytes = null; + byte[] serverMacBytes = null; - while (true) { - String line = reader.readLine(); - lineNumber++; - if (line == null) { - break; + while (true) { + String line = reader.readLine(); + lineNumber++; + if (line == null) { + break; + } + if (line.startsWith("km-") == false) { + continue; + } + String data = line.substring(PREFIX_LENGTH); + if (line.startsWith("km-master:")) { + master = parse(data); + } else if (line.startsWith("km-major:")) { + major = Integer.parseInt(data); + } else if (line.startsWith("km-minor:")) { + minor = Integer.parseInt(data); + } else if (line.startsWith("km-crandom:")) { + clientRandom = parse(data); + } else if (line.startsWith("km-srandom:")) { + serverRandom = parse(data); + } else if (line.startsWith("km-cipalg:")) { + cipherAlgorithm = data; + } else if (line.startsWith("km-keylen:")) { + keyLength = Integer.parseInt(data); + } else if (line.startsWith("km-explen:")) { + expandedKeyLength = Integer.parseInt(data); + } else if (line.startsWith("km-ivlen:")) { + ivLength = Integer.parseInt(data); + } else if (line.startsWith("km-maclen:")) { + macLength = Integer.parseInt(data); + } else if (line.startsWith("km-ccipkey:")) { + clientCipherBytes = parse(data); + } else if (line.startsWith("km-scipkey:")) { + serverCipherBytes = parse(data); + } else if (line.startsWith("km-civ:")) { + clientIv = parse(data); + } else if (line.startsWith("km-siv:")) { + serverIv = parse(data); + } else if (line.startsWith("km-cmackey:")) { + clientMacBytes = parse(data); + } else if (line.startsWith("km-smackey:")) { + serverMacBytes = parse(data); + + System.out.print("."); + n++; + + KeyGenerator kg = + KeyGenerator.getInstance("SunTlsKeyMaterial", provider); + SecretKey masterKey = + new SecretKeySpec(master, "TlsMasterSecret"); + TlsKeyMaterialParameterSpec spec = + new TlsKeyMaterialParameterSpec(masterKey, major, minor, + clientRandom, serverRandom, cipherAlgorithm, + keyLength, expandedKeyLength, ivLength, macLength, + null, -1, -1); + + kg.init(spec); + TlsKeyMaterialSpec result = + (TlsKeyMaterialSpec)kg.generateKey(); + match(lineNumber, clientCipherBytes, + result.getClientCipherKey(), cipherAlgorithm); + match(lineNumber, serverCipherBytes, + result.getServerCipherKey(), cipherAlgorithm); + match(lineNumber, clientIv, result.getClientIv(), ""); + match(lineNumber, serverIv, result.getServerIv(), ""); + match(lineNumber, clientMacBytes, result.getClientMacKey(), ""); + match(lineNumber, serverMacBytes, result.getServerMacKey(), ""); + + } else { + throw new Exception("Unknown line: " + line); + } } - if (line.startsWith("km-") == false) { - continue; - } - String data = line.substring(PREFIX_LENGTH); - if (line.startsWith("km-master:")) { - master = parse(data); - } else if (line.startsWith("km-major:")) { - major = Integer.parseInt(data); - } else if (line.startsWith("km-minor:")) { - minor = Integer.parseInt(data); - } else if (line.startsWith("km-crandom:")) { - clientRandom = parse(data); - } else if (line.startsWith("km-srandom:")) { - serverRandom = parse(data); - } else if (line.startsWith("km-cipalg:")) { - cipherAlgorithm = data; - } else if (line.startsWith("km-keylen:")) { - keyLength = Integer.parseInt(data); - } else if (line.startsWith("km-explen:")) { - expandedKeyLength = Integer.parseInt(data); - } else if (line.startsWith("km-ivlen:")) { - ivLength = Integer.parseInt(data); - } else if (line.startsWith("km-maclen:")) { - macLength = Integer.parseInt(data); - } else if (line.startsWith("km-ccipkey:")) { - clientCipherBytes = parse(data); - } else if (line.startsWith("km-scipkey:")) { - serverCipherBytes = parse(data); - } else if (line.startsWith("km-civ:")) { - clientIv = parse(data); - } else if (line.startsWith("km-siv:")) { - serverIv = parse(data); - } else if (line.startsWith("km-cmackey:")) { - clientMacBytes = parse(data); - } else if (line.startsWith("km-smackey:")) { - serverMacBytes = parse(data); - - System.out.print("."); - n++; - - KeyGenerator kg = - KeyGenerator.getInstance("SunTlsKeyMaterial", provider); - SecretKey masterKey = - new SecretKeySpec(master, "TlsMasterSecret"); - TlsKeyMaterialParameterSpec spec = - new TlsKeyMaterialParameterSpec(masterKey, major, minor, - clientRandom, serverRandom, cipherAlgorithm, - keyLength, expandedKeyLength, ivLength, macLength, - null, -1, -1); - - kg.init(spec); - TlsKeyMaterialSpec result = - (TlsKeyMaterialSpec)kg.generateKey(); - match(lineNumber, clientCipherBytes, - result.getClientCipherKey(), cipherAlgorithm); - match(lineNumber, serverCipherBytes, - result.getServerCipherKey(), cipherAlgorithm); - match(lineNumber, clientIv, result.getClientIv(), ""); - match(lineNumber, serverIv, result.getServerIv(), ""); - match(lineNumber, clientMacBytes, result.getClientMacKey(), ""); - match(lineNumber, serverMacBytes, result.getServerMacKey(), ""); - - } else { - throw new Exception("Unknown line: " + line); + if (n == 0) { + throw new Exception("no tests"); } + System.out.println(); + System.out.println("OK: " + n + " tests"); } - if (n == 0) { - throw new Exception("no tests"); - } - in.close(); - System.out.println(); - System.out.println("OK: " + n + " tests"); } private static void stripParity(byte[] b) { diff --git a/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java b/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java index ffaac041d33..a8d8f72a299 100644 --- a/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java +++ b/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,15 +27,18 @@ * @summary Need to strip leading zeros in TlsPremasterSecret of DHKeyAgreement * @library .. * @author Pasi Eronen + * @run main/othervm TestLeadingZeroesP11 + * @run main/othervm TestLeadingZeroesP11 sm */ -import java.io.*; -import java.security.*; -import java.security.spec.*; -import java.security.interfaces.*; -import javax.crypto.*; -import javax.crypto.spec.*; -import javax.crypto.interfaces.*; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.KeyAgreement; /** * Test that leading zeroes are stripped in TlsPremasterSecret case, @@ -48,9 +51,10 @@ import javax.crypto.interfaces.*; public class TestLeadingZeroesP11 extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestLeadingZeroesP11()); + main(new TestLeadingZeroesP11(), args); } + @Override public void main(Provider p) throws Exception { // decode pre-generated keypairs diff --git a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java index fb00bd7fbf0..855b8c21f13 100644 --- a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java +++ b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,111 +29,112 @@ * @library .. * @modules java.base/sun.security.internal.interfaces * java.base/sun.security.internal.spec + * @run main/othervm TestMasterSecret + * @run main/othervm TestMasterSecret sm TestMasterSecret.policy */ -import java.io.*; -import java.util.*; - -import java.security.Security; +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Provider; - +import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; - -import javax.crypto.spec.*; - -import sun.security.internal.spec.*; +import javax.crypto.spec.SecretKeySpec; import sun.security.internal.interfaces.TlsMasterSecret; +import sun.security.internal.spec.TlsMasterSecretParameterSpec; public class TestMasterSecret extends PKCS11Test { - private static int PREFIX_LENGTH = "m-premaster: ".length(); + private static final int PREFIX_LENGTH = "m-premaster: ".length(); public static void main(String[] args) throws Exception { - main(new TestMasterSecret()); + main(new TestMasterSecret(), args); } + @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyGenerator", "SunTlsMasterSecret") == null) { System.out.println("Not supported by provider, skipping"); return; } - InputStream in = new FileInputStream(new File(BASE, "masterdata.txt")); - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - int n = 0; - int lineNumber = 0; + try (BufferedReader reader = Files.newBufferedReader( + Paths.get(BASE, "masterdata.txt"))) { - String algorithm = null; - byte[] premaster = null; - byte[] clientRandom = null; - byte[] serverRandom = null; - int protoMajor = 0; - int protoMinor = 0; - int preMajor = 0; - int preMinor = 0; - byte[] master = null; + int n = 0; + int lineNumber = 0; - while (true) { - String line = reader.readLine(); - lineNumber++; - if (line == null) { - break; - } - if (line.startsWith("m-") == false) { - continue; - } - String data = line.substring(PREFIX_LENGTH); - if (line.startsWith("m-algorithm:")) { - algorithm = data; - } else if (line.startsWith("m-premaster:")) { - premaster = parse(data); - } else if (line.startsWith("m-crandom:")) { - clientRandom = parse(data); - } else if (line.startsWith("m-srandom:")) { - serverRandom = parse(data); - } else if (line.startsWith("m-protomajor:")) { - protoMajor = Integer.parseInt(data); - } else if (line.startsWith("m-protominor:")) { - protoMinor = Integer.parseInt(data); - } else if (line.startsWith("m-premajor:")) { - preMajor = Integer.parseInt(data); - } else if (line.startsWith("m-preminor:")) { - preMinor = Integer.parseInt(data); - } else if (line.startsWith("m-master:")) { - master = parse(data); + String algorithm = null; + byte[] premaster = null; + byte[] clientRandom = null; + byte[] serverRandom = null; + int protoMajor = 0; + int protoMinor = 0; + int preMajor = 0; + int preMinor = 0; + byte[] master = null; - System.out.print("."); - n++; - - KeyGenerator kg = - KeyGenerator.getInstance("SunTlsMasterSecret", provider); - SecretKey premasterKey = - new SecretKeySpec(premaster, algorithm); - TlsMasterSecretParameterSpec spec = - new TlsMasterSecretParameterSpec(premasterKey, - protoMajor, protoMinor, clientRandom, serverRandom, - null, -1, -1); - kg.init(spec); - TlsMasterSecret key = (TlsMasterSecret)kg.generateKey(); - byte[] enc = key.getEncoded(); - if (Arrays.equals(master, enc) == false) { - throw new Exception("mismatch line: " + lineNumber); + while (true) { + String line = reader.readLine(); + lineNumber++; + if (line == null) { + break; } - if ((preMajor != key.getMajorVersion()) || - (preMinor != key.getMinorVersion())) { - throw new Exception("version mismatch line: " + lineNumber); + if (line.startsWith("m-") == false) { + continue; + } + String data = line.substring(PREFIX_LENGTH); + if (line.startsWith("m-algorithm:")) { + algorithm = data; + } else if (line.startsWith("m-premaster:")) { + premaster = parse(data); + } else if (line.startsWith("m-crandom:")) { + clientRandom = parse(data); + } else if (line.startsWith("m-srandom:")) { + serverRandom = parse(data); + } else if (line.startsWith("m-protomajor:")) { + protoMajor = Integer.parseInt(data); + } else if (line.startsWith("m-protominor:")) { + protoMinor = Integer.parseInt(data); + } else if (line.startsWith("m-premajor:")) { + preMajor = Integer.parseInt(data); + } else if (line.startsWith("m-preminor:")) { + preMinor = Integer.parseInt(data); + } else if (line.startsWith("m-master:")) { + master = parse(data); + + System.out.print("."); + n++; + + KeyGenerator kg = + KeyGenerator.getInstance("SunTlsMasterSecret", provider); + SecretKey premasterKey = + new SecretKeySpec(premaster, algorithm); + TlsMasterSecretParameterSpec spec = + new TlsMasterSecretParameterSpec(premasterKey, + protoMajor, protoMinor, clientRandom, serverRandom, + null, -1, -1); + kg.init(spec); + TlsMasterSecret key = (TlsMasterSecret)kg.generateKey(); + byte[] enc = key.getEncoded(); + if (Arrays.equals(master, enc) == false) { + throw new Exception("mismatch line: " + lineNumber); + } + if ((preMajor != key.getMajorVersion()) || + (preMinor != key.getMinorVersion())) { + throw new Exception("version mismatch line: " + lineNumber); + } + } else { + throw new Exception("Unknown line: " + line); } - } else { - throw new Exception("Unknown line: " + line); } + if (n == 0) { + throw new Exception("no tests"); + } + System.out.println(); + System.out.println("OK: " + n + " tests"); } - if (n == 0) { - throw new Exception("no tests"); - } - in.close(); - System.out.println(); - System.out.println("OK: " + n + " tests"); } } diff --git a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy new file mode 100644 index 00000000000..4b98541ad7a --- /dev/null +++ b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy @@ -0,0 +1,8 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.io.FilePermission "${test.src}/*", "read"; + permission java.lang.RuntimePermission + "accessClassInPackage.sun.security.internal.spec"; + permission java.lang.RuntimePermission + "accessClassInPackage.sun.security.internal.interfaces"; +}; \ No newline at end of file diff --git a/jdk/test/sun/security/pkcs11/tls/TestPRF.java b/jdk/test/sun/security/pkcs11/tls/TestPRF.java index eefffe70820..6e05ea64f90 100644 --- a/jdk/test/sun/security/pkcs11/tls/TestPRF.java +++ b/jdk/test/sun/security/pkcs11/tls/TestPRF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,116 +28,116 @@ * @author Andreas Sterbenz * @library .. * @modules java.base/sun.security.internal.spec + * @run main/othervm TestPRF + * @run main/othervm TestPRF sm policy */ -import java.io.*; -import java.util.*; - -import java.security.Security; +import java.io.BufferedReader; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Provider; - +import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; - -import javax.crypto.spec.*; - -import sun.security.internal.spec.*; +import javax.crypto.spec.SecretKeySpec; +import sun.security.internal.spec.TlsPrfParameterSpec; public class TestPRF extends PKCS11Test { - private static int PREFIX_LENGTH = "prf-output: ".length(); + private static final int PREFIX_LENGTH = "prf-output: ".length(); public static void main(String[] args) throws Exception { - main(new TestPRF()); + main(new TestPRF(), args); } + @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyGenerator", "SunTlsPrf") == null) { System.out.println("Provider does not support algorithm, skipping"); return; } - InputStream in = new FileInputStream(new File(BASE, "prfdata.txt")); - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try (BufferedReader reader = Files.newBufferedReader( + Paths.get(BASE, "prfdata.txt"))) { - int n = 0; - int lineNumber = 0; + int n = 0; + int lineNumber = 0; - byte[] secret = null; - String label = null; - byte[] seed = null; - int length = 0; - byte[] output = null; + byte[] secret = null; + String label = null; + byte[] seed = null; + int length = 0; + byte[] output = null; - while (true) { - String line = reader.readLine(); - lineNumber++; - if (line == null) { - break; - } - if (line.startsWith("prf-") == false) { - continue; - } - - String data = line.substring(PREFIX_LENGTH); - if (line.startsWith("prf-secret:")) { - secret = parse(data); - } else if (line.startsWith("prf-label:")) { - label = data; - } else if (line.startsWith("prf-seed:")) { - seed = parse(data); - } else if (line.startsWith("prf-length:")) { - length = Integer.parseInt(data); - } else if (line.startsWith("prf-output:")) { - output = parse(data); - - System.out.print("."); - n++; - - KeyGenerator kg = - KeyGenerator.getInstance("SunTlsPrf", provider); - SecretKey inKey; - if (secret == null) { - inKey = null; - } else { - inKey = new SecretKeySpec(secret, "Generic"); + while (true) { + String line = reader.readLine(); + lineNumber++; + if (line == null) { + break; } - TlsPrfParameterSpec spec = - new TlsPrfParameterSpec(inKey, label, seed, length, - null, -1, -1); - SecretKey key; - try { - kg.init(spec); - key = kg.generateKey(); - } catch (Exception e) { + if (line.startsWith("prf-") == false) { + continue; + } + + String data = line.substring(PREFIX_LENGTH); + if (line.startsWith("prf-secret:")) { + secret = parse(data); + } else if (line.startsWith("prf-label:")) { + label = data; + } else if (line.startsWith("prf-seed:")) { + seed = parse(data); + } else if (line.startsWith("prf-length:")) { + length = Integer.parseInt(data); + } else if (line.startsWith("prf-output:")) { + output = parse(data); + + System.out.print("."); + n++; + + KeyGenerator kg = + KeyGenerator.getInstance("SunTlsPrf", provider); + SecretKey inKey; if (secret == null) { - // This fails on Solaris, but since we never call this - // API for this case in JSSE, ignore the failure. - // (SunJSSE uses the CKM_TLS_KEY_AND_MAC_DERIVE - // mechanism) - System.out.print("X"); - continue; + inKey = null; + } else { + inKey = new SecretKeySpec(secret, "Generic"); } - System.out.println(); - throw new Exception("Error on line: " + lineNumber, e); + TlsPrfParameterSpec spec = + new TlsPrfParameterSpec(inKey, label, seed, length, + null, -1, -1); + SecretKey key; + try { + kg.init(spec); + key = kg.generateKey(); + } catch (Exception e) { + if (secret == null) { + // This fails on Solaris, but since we never call this + // API for this case in JSSE, ignore the failure. + // (SunJSSE uses the CKM_TLS_KEY_AND_MAC_DERIVE + // mechanism) + System.out.print("X"); + continue; + } + System.out.println(); + throw new Exception("Error on line: " + lineNumber, e); + } + byte[] enc = key.getEncoded(); + if (Arrays.equals(output, enc) == false) { + System.out.println(); + System.out.println("expected: " + toString(output)); + System.out.println("actual: " + toString(enc)); + throw new Exception("mismatch line: " + lineNumber); + } + } else { + throw new Exception("Unknown line: " + line); } - byte[] enc = key.getEncoded(); - if (Arrays.equals(output, enc) == false) { - System.out.println(); - System.out.println("expected: " + toString(output)); - System.out.println("actual: " + toString(enc)); - throw new Exception("mismatch line: " + lineNumber); - } - } else { - throw new Exception("Unknown line: " + line); } + if (n == 0) { + throw new Exception("no tests"); + } + System.out.println(); + System.out.println("OK: " + n + " tests"); } - if (n == 0) { - throw new Exception("no tests"); - } - in.close(); - System.out.println(); - System.out.println("OK: " + n + " tests"); } } diff --git a/jdk/test/sun/security/pkcs11/tls/TestPremaster.java b/jdk/test/sun/security/pkcs11/tls/TestPremaster.java index 15b13ac8507..bbbbf2c376d 100644 --- a/jdk/test/sun/security/pkcs11/tls/TestPremaster.java +++ b/jdk/test/sun/security/pkcs11/tls/TestPremaster.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,23 +28,22 @@ * @author Andreas Sterbenz * @library .. * @modules java.base/sun.security.internal.spec + * @run main/othervm TestPremaster + * @run main/othervm TestPremaster sm policy */ -import java.security.Security; import java.security.Provider; - import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import java.util.Formatter; - import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; public class TestPremaster extends PKCS11Test { public static void main(String[] args) throws Exception { - main(new TestPremaster()); + main(new TestPremaster(), args); } + @Override public void main(Provider provider) throws Exception { if (provider.getService( "KeyGenerator", "SunTlsRsaPremasterSecret") == null) { diff --git a/jdk/test/sun/security/pkcs11/tls/policy b/jdk/test/sun/security/pkcs11/tls/policy new file mode 100644 index 00000000000..6d161b9b2e5 --- /dev/null +++ b/jdk/test/sun/security/pkcs11/tls/policy @@ -0,0 +1,5 @@ +grant { + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.io.FilePermission "${test.src}/*", "read"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec"; +}; \ No newline at end of file diff --git a/langtools/.hgtags b/langtools/.hgtags index 3786fbae996..90a13fb7426 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -345,3 +345,4 @@ cb73b474703e2de266542b505cffd658bcc052da jdk-9+99 51136404ee5e6cd5868b60d66ebd55a02170b508 jdk-9+100 3b3bea483542bc08278af529fb25f2e5930da945 jdk-9+101 6149fc30cd710eb3484dc9863d8837ecaedb96b6 jdk-9+102 +94cfc50c1b8a74fd7b0ed2e9e4f4a9dab4f2c6a1 jdk-9+103 diff --git a/langtools/make/CompileInterim.gmk b/langtools/make/CompileInterim.gmk index 018343bb4ae..2ccd58feaa2 100644 --- a/langtools/make/CompileInterim.gmk +++ b/langtools/make/CompileInterim.gmk @@ -46,7 +46,7 @@ $(eval $(call SetupJavaCompilation,BUILD_INTERIM_LANGTOOLS, \ $(SUPPORT_OUTPUTDIR)/gensrc/jdk.compiler \ $(SUPPORT_OUTPUTDIR)/gensrc/jdk.javadoc \ $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jdeps, \ - EXCLUDES := sun jdk, \ + EXCLUDES := sun, \ COPY := .gif .png .xml .css .js javax.tools.JavaCompilerTool, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/langtools_interim_classes, \ JAR := $(INTERIM_LANGTOOLS_JAR))) diff --git a/langtools/make/gensrc/Gensrc-jdk.javadoc.gmk b/langtools/make/gensrc/Gensrc-jdk.javadoc.gmk index 1d9ab44b32b..3720b1be5ae 100644 --- a/langtools/make/gensrc/Gensrc-jdk.javadoc.gmk +++ b/langtools/make/gensrc/Gensrc-jdk.javadoc.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,12 @@ include GensrcCommon.gmk -$(eval $(call SetupVersionProperties,JAVADOC_VERSION,\ +$(eval $(call SetupVersionProperties,OLD_JAVADOC_VERSION,\ com/sun/tools/javadoc/resources/version.properties)) +$(eval $(call SetupVersionProperties,JAVADOC_VERSION,\ + jdk/javadoc/internal/tool/resources/version.properties)) + $(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, $(JAVADOC_VERSION))) all: $(COMPILE_PROPERTIES) diff --git a/langtools/make/netbeans/langtools/build.xml b/langtools/make/netbeans/langtools/build.xml index 9e6c4cfddc8..0013b62228e 100644 --- a/langtools/make/netbeans/langtools/build.xml +++ b/langtools/make/netbeans/langtools/build.xml @@ -93,7 +93,11 @@ - + + + diff --git a/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java b/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java index 85da537519f..950daf73328 100644 --- a/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java +++ b/langtools/src/java.compiler/share/classes/javax/tools/ToolProvider.java @@ -104,7 +104,7 @@ public class ToolProvider { } private static final String systemDocumentationToolName - = "com.sun.tools.javadoc.api.JavadocTool"; + = "jdk.javadoc.internal.api.JavadocTool"; /** * Returns the Java™ programming language documentation tool provided diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 9456af32f9c..68e36be3f44 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -26,9 +26,7 @@ package com.sun.tools.javac.code; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import javax.lang.model.element.ElementVisitor; import javax.tools.JavaFileObject; @@ -39,7 +37,6 @@ import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.Completer; import com.sun.tools.javac.code.Symbol.CompletionFailure; import com.sun.tools.javac.code.Symbol.MethodSymbol; -import com.sun.tools.javac.code.Symbol.OperatorSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; @@ -50,7 +47,6 @@ import com.sun.tools.javac.code.Type.JCPrimitiveType; import com.sun.tools.javac.code.Type.JCVoidType; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Type.UnknownType; -import com.sun.tools.javac.jvm.ByteCodes; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; @@ -65,7 +61,6 @@ import com.sun.tools.javac.util.Names; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; -import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.code.TypeTag.*; /** A class that defines all predefined constants and operators @@ -193,6 +188,7 @@ public class Symtab { public final Type autoCloseableType; public final Type trustMeType; public final Type lambdaMetafactory; + public final Type stringConcatFactory; public final Type repeatableType; public final Type documentedType; public final Type elementTypeType; @@ -472,6 +468,7 @@ public class Symtab { trustMeType = enterClass("java.lang.SafeVarargs"); nativeHeaderType = enterClass("java.lang.annotation.Native"); lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory"); + stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory"); functionalInterfaceType = enterClass("java.lang.FunctionalInterface"); synthesizeEmptyInterfaceIfMissing(autoCloseableType); @@ -479,6 +476,7 @@ public class Symtab { synthesizeEmptyInterfaceIfMissing(serializableType); synthesizeEmptyInterfaceIfMissing(lambdaMetafactory); synthesizeEmptyInterfaceIfMissing(serializedLambdaType); + synthesizeEmptyInterfaceIfMissing(stringConcatFactory); synthesizeBoxTypeIfMissing(doubleType); synthesizeBoxTypeIfMissing(floatType); synthesizeBoxTypeIfMissing(voidType); diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java index 35185e73afc..7ce6ccd17ec 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ArgumentAttr.java @@ -68,6 +68,7 @@ import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; +import static com.sun.tools.javac.code.TypeTag.ARRAY; import static com.sun.tools.javac.code.TypeTag.DEFERRED; import static com.sun.tools.javac.code.TypeTag.FORALL; import static com.sun.tools.javac.code.TypeTag.METHOD; @@ -275,7 +276,7 @@ public class ArgumentAttr extends JCTree.Visitor { res.type != null && res.type.hasTag(FORALL) || (res.flags() & Flags.VARARGS) != 0 || (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && - exprTree.type.isRaw())) { + exprTree.type.isRaw() && !exprTree.type.hasTag(ARRAY))) { tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED; } else { tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED; diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index d5bc1cdedef..f62d866bacb 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -239,7 +239,7 @@ public class Attr extends JCTree.Visitor { //this means we are dealing with a partially inferred poly expression owntype = shouldCheck ? resultInfo.pt : found; if (resultInfo.checkMode.installPostInferenceHook()) { - inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), + inferenceContext.addFreeTypeListener(List.of(found), instantiatedContext -> { ResultInfo pendingResult = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt)); diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 583a34bf8df..73533e7ab43 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1812,7 +1812,7 @@ public class LambdaToMethod extends TreeTranslator { TranslationContext(T tree) { this.tree = tree; - this.owner = owner(); + this.owner = owner(true); this.depth = frameStack.size() - 1; this.prev = context(); ClassSymbol csym = diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index f7dd6da221d..83ed8adbef5 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -25,8 +25,6 @@ package com.sun.tools.javac.jvm; -import java.util.*; - import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; @@ -45,7 +43,6 @@ import com.sun.tools.javac.tree.JCTree.*; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; -import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.jvm.CRTFlags.*; @@ -69,12 +66,12 @@ public class Gen extends JCTree.Visitor { private final TreeMaker make; private final Names names; private final Target target; - private final Map stringBufferAppend; private Name accessDollar; private final Types types; private final Lower lower; private final Flow flow; private final Annotate annotate; + private final StringConcat concat; /** Format of stackmap tables to be generated. */ private final Code.StackMapFormat stackMap; @@ -105,8 +102,9 @@ public class Gen extends JCTree.Visitor { make = TreeMaker.instance(context); target = Target.instance(context); types = Types.instance(context); + concat = StringConcat.instance(context); + methodType = new MethodType(null, null, null, syms.methodClass); - stringBufferAppend = new HashMap<>(); accessDollar = names. fromString("access" + target.syntheticNameChar()); flow = Flow.instance(context); @@ -753,6 +751,18 @@ public class Gen extends JCTree.Visitor { } } + public Code getCode() { + return code; + } + + public Items getItems() { + return items; + } + + public Env getAttrEnv() { + return attrEnv; + } + /** Visitor class for expressions which might be constant expressions. * This class is a subset of TreeScanner. Intended to visit trees pruned by * Lower as long as constant expressions looking for references to any @@ -1895,25 +1905,7 @@ public class Gen extends JCTree.Visitor { OperatorSymbol operator = (OperatorSymbol) tree.operator; Item l; if (operator.opcode == string_add) { - // Generate code to make a string buffer - makeStringBuffer(tree.pos()); - - // Generate code for first string, possibly save one - // copy under buffer - l = genExpr(tree.lhs, tree.lhs.type); - if (l.width() > 0) { - code.emitop0(dup_x1 + 3 * (l.width() - 1)); - } - - // Load first string and append to buffer. - l.load(); - appendString(tree.lhs); - - // Append all other strings to buffer. - appendStrings(tree.rhs); - - // Convert buffer to string. - bufferToString(tree.pos()); + l = concat.makeConcat(tree); } else { // Generate code for first expression l = genExpr(tree.lhs, tree.lhs.type); @@ -2026,13 +2018,7 @@ public class Gen extends JCTree.Visitor { public void visitBinary(JCBinary tree) { OperatorSymbol operator = (OperatorSymbol)tree.operator; if (operator.opcode == string_add) { - // Create a string buffer. - makeStringBuffer(tree.pos()); - // Append all strings to buffer. - appendStrings(tree); - // Convert buffer to string. - bufferToString(tree.pos()); - result = items.makeStackItem(syms.stringType); + result = concat.makeConcat(tree); } else if (tree.hasTag(AND)) { CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); if (!lcond.isFalse()) { @@ -2066,67 +2052,7 @@ public class Gen extends JCTree.Visitor { result = completeBinop(tree.lhs, tree.rhs, operator); } } -//where - /** Make a new string buffer. - */ - void makeStringBuffer(DiagnosticPosition pos) { - code.emitop2(new_, makeRef(pos, syms.stringBuilderType)); - code.emitop0(dup); - callMethod( - pos, syms.stringBuilderType, names.init, List.nil(), false); - } - /** Append value (on tos) to string buffer (on tos - 1). - */ - void appendString(JCTree tree) { - Type t = tree.type.baseType(); - if (!t.isPrimitive() && t.tsym != syms.stringType.tsym) { - t = syms.objectType; - } - items.makeMemberItem(getStringBufferAppend(tree, t), false).invoke(); - } - Symbol getStringBufferAppend(JCTree tree, Type t) { - Assert.checkNull(t.constValue()); - Symbol method = stringBufferAppend.get(t); - if (method == null) { - method = rs.resolveInternalMethod(tree.pos(), - attrEnv, - syms.stringBuilderType, - names.append, - List.of(t), - null); - stringBufferAppend.put(t, method); - } - return method; - } - - /** Add all strings in tree to string buffer. - */ - void appendStrings(JCTree tree) { - tree = TreeInfo.skipParens(tree); - if (tree.hasTag(PLUS) && tree.type.constValue() == null) { - JCBinary op = (JCBinary) tree; - if (op.operator.kind == MTH && - ((OperatorSymbol) op.operator).opcode == string_add) { - appendStrings(op.lhs); - appendStrings(op.rhs); - return; - } - } - genExpr(tree, tree.type).load(); - appendString(tree); - } - - /** Convert string buffer on tos to string. - */ - void bufferToString(DiagnosticPosition pos) { - callMethod( - pos, - syms.stringBuilderType, - names.toString, - List.nil(), - false); - } /** Complete generating code for operation, with left operand * already on stack. @@ -2173,8 +2099,8 @@ public class Gen extends JCTree.Visitor { } public void visitTypeCast(JCTypeCast tree) { - setTypeAnnotationPositions(tree.pos); result = genExpr(tree.expr, tree.clazz.type).load(); + setTypeAnnotationPositions(tree.pos); // Additional code is only needed if we cast to a reference type // which is not statically a supertype of the expression's type. // For basic types, the coerce(...) in genExpr(...) will do @@ -2191,8 +2117,8 @@ public class Gen extends JCTree.Visitor { } public void visitTypeTest(JCInstanceOf tree) { - setTypeAnnotationPositions(tree.pos); genExpr(tree.expr, tree.expr.type).load(); + setTypeAnnotationPositions(tree.pos); code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); result = items.makeStackItem(syms.booleanType); } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java new file mode 100644 index 00000000000..bb5436bd96c --- /dev/null +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.jvm; + +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.comp.Resolve; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.*; + +import static com.sun.tools.javac.code.Kinds.Kind.MTH; +import static com.sun.tools.javac.code.TypeTag.DOUBLE; +import static com.sun.tools.javac.code.TypeTag.LONG; +import static com.sun.tools.javac.jvm.ByteCodes.*; +import static com.sun.tools.javac.tree.JCTree.Tag.PLUS; +import com.sun.tools.javac.jvm.Items.*; + +import java.util.HashMap; +import java.util.Map; + +/** This lowers the String concatenation to something that JVM can understand. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public abstract class StringConcat { + + /** + * Maximum number of slots for String Concat call. + * JDK's StringConcatFactory does not support more than that. + */ + private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200; + private static final char TAG_ARG = '\u0001'; + private static final char TAG_CONST = '\u0002'; + + protected final Gen gen; + protected final Symtab syms; + protected final Names names; + protected final TreeMaker make; + protected final Types types; + protected final Map sbAppends; + protected final Resolve rs; + + protected static final Context.Key concatKey = new Context.Key<>(); + + public static StringConcat instance(Context context) { + StringConcat instance = context.get(concatKey); + if (instance == null) { + instance = makeConcat(context); + } + return instance; + } + + private static StringConcat makeConcat(Context context) { + Target target = Target.instance(context); + String opt = Options.instance(context).get("stringConcat"); + if (target.hasStringConcatFactory()) { + if (opt == null) { + opt = "indyWithConstants"; + } + } else { + if (opt != null && !"inline".equals(opt)) { + Assert.error("StringConcatFactory-based string concat is requested on a platform that does not support it."); + } + opt = "inline"; + } + + switch (opt) { + case "inline": + return new Inline(context); + case "indy": + return new IndyPlain(context); + case "indyWithConstants": + return new IndyConstants(context); + default: + Assert.error("Unknown stringConcat: " + opt); + throw new IllegalStateException("Unknown stringConcat: " + opt); + } + } + + protected StringConcat(Context context) { + context.put(concatKey, this); + gen = Gen.instance(context); + syms = Symtab.instance(context); + types = Types.instance(context); + names = Names.instance(context); + make = TreeMaker.instance(context); + rs = Resolve.instance(context); + sbAppends = new HashMap<>(); + } + + public abstract Item makeConcat(JCTree.JCAssignOp tree); + public abstract Item makeConcat(JCTree.JCBinary tree); + + protected List collectAll(JCTree tree) { + return collect(tree, List.nil()); + } + + protected List collectAll(JCTree.JCExpression lhs, JCTree.JCExpression rhs) { + return List.nil() + .appendList(collectAll(lhs)) + .appendList(collectAll(rhs)); + } + + private List collect(JCTree tree, List res) { + tree = TreeInfo.skipParens(tree); + if (tree.hasTag(PLUS) && tree.type.constValue() == null) { + JCTree.JCBinary op = (JCTree.JCBinary) tree; + if (op.operator.kind == MTH && + ((Symbol.OperatorSymbol) op.operator).opcode == string_add) { + return res + .appendList(collect(op.lhs, res)) + .appendList(collect(op.rhs, res)); + } + } + return res.append(tree); + } + + /** + * "Legacy" bytecode flavor: emit the StringBuilder.append chains for string + * concatenation. + */ + private static class Inline extends StringConcat { + public Inline(Context context) { + super(context); + } + + @Override + public Item makeConcat(JCTree.JCAssignOp tree) { + // Generate code to make a string builder + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + + // Create a string builder. + newStringBuilder(tree); + + // Generate code for first string, possibly save one + // copy under builder + Item l = gen.genExpr(tree.lhs, tree.lhs.type); + if (l.width() > 0) { + gen.getCode().emitop0(dup_x1 + 3 * (l.width() - 1)); + } + + // Load first string and append to builder. + l.load(); + appendString(tree.lhs); + + // Append all other strings to builder. + List args = collectAll(tree.rhs); + for (JCTree t : args) { + gen.genExpr(t, t.type).load(); + appendString(t); + } + + // Convert builder to string. + builderToString(pos); + + return l; + } + + @Override + public Item makeConcat(JCTree.JCBinary tree) { + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + + // Create a string builder. + newStringBuilder(tree); + + // Append all strings to builder. + List args = collectAll(tree); + for (JCTree t : args) { + gen.genExpr(t, t.type).load(); + appendString(t); + } + + // Convert builder to string. + builderToString(pos); + + return gen.getItems().makeStackItem(syms.stringType); + } + + private JCDiagnostic.DiagnosticPosition newStringBuilder(JCTree tree) { + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + gen.getCode().emitop2(new_, gen.makeRef(pos, syms.stringBuilderType)); + gen.getCode().emitop0(dup); + gen.callMethod(pos, syms.stringBuilderType, names.init, List.nil(), false); + return pos; + } + + private void appendString(JCTree tree) { + Type t = tree.type.baseType(); + if (!t.isPrimitive() && t.tsym != syms.stringType.tsym) { + t = syms.objectType; + } + + Assert.checkNull(t.constValue()); + Symbol method = sbAppends.get(t); + if (method == null) { + method = rs.resolveInternalMethod(tree.pos(), gen.getAttrEnv(), syms.stringBuilderType, names.append, List.of(t), null); + sbAppends.put(t, method); + } + + gen.getItems().makeMemberItem(method, false).invoke(); + } + + private void builderToString(JCDiagnostic.DiagnosticPosition pos) { + gen.callMethod(pos, syms.stringBuilderType, names.toString, List.nil(), false); + } + } + + /** + * Base class for indified concatenation bytecode flavors. + */ + private static abstract class Indy extends StringConcat { + public Indy(Context context) { + super(context); + } + + @Override + public Item makeConcat(JCTree.JCAssignOp tree) { + List args = collectAll(tree.lhs, tree.rhs); + Item l = gen.genExpr(tree.lhs, tree.lhs.type); + emit(args, tree.type, tree.pos()); + return l; + } + + @Override + public Item makeConcat(JCTree.JCBinary tree) { + List args = collectAll(tree.lhs, tree.rhs); + emit(args, tree.type, tree.pos()); + return gen.getItems().makeStackItem(syms.stringType); + } + + protected abstract void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos); + + /** Peel the argument list into smaller chunks. */ + protected List> split(List args) { + ListBuffer> splits = new ListBuffer<>(); + + int slots = 0; + + // Need to peel, so that neither call has more than acceptable number + // of slots for the arguments. + ListBuffer cArgs = new ListBuffer<>(); + for (JCTree t : args) { + int needSlots = (t.type.getTag() == LONG || t.type.getTag() == DOUBLE) ? 2 : 1; + if (slots + needSlots >= MAX_INDY_CONCAT_ARG_SLOTS) { + splits.add(cArgs.toList()); + cArgs.clear(); + slots = 0; + } + cArgs.add(t); + slots += needSlots; + } + + // Flush the tail slice + if (!cArgs.isEmpty()) { + splits.add(cArgs.toList()); + } + + return splits.toList(); + } + } + + /** + * Emits the invokedynamic call to JDK java.lang.invoke.StringConcatFactory, + * without handling constants specially. + * + * We bypass empty strings, because they have no meaning at this level. This + * captures the Java language trick to force String concat with e.g. ("" + int)-like + * expression. Down here, we already know we are in String concat business, and do + * not require these markers. + */ + private static class IndyPlain extends Indy { + public IndyPlain(Context context) { + super(context); + } + + /** Emit the indy concat for all these arguments, possibly peeling along the way */ + protected void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos) { + List> split = split(args); + + for (List t : split) { + Assert.check(!t.isEmpty(), "Arguments list is empty"); + + ListBuffer dynamicArgs = new ListBuffer<>(); + for (JCTree arg : t) { + Object constVal = arg.type.constValue(); + if ("".equals(constVal)) continue; + if (arg.type == syms.botType) { + dynamicArgs.add(types.boxedClass(syms.voidType).type); + } else { + dynamicArgs.add(arg.type); + } + gen.genExpr(arg, arg.type).load(); + } + + doCall(type, pos, dynamicArgs.toList()); + } + + // More that one peel slice produced: concatenate the results + if (split.size() > 1) { + ListBuffer argTypes = new ListBuffer<>(); + for (int c = 0; c < split.size(); c++) { + argTypes.append(syms.stringType); + } + doCall(type, pos, argTypes.toList()); + } + } + + /** Produce the actual invokedynamic call to StringConcatFactory */ + private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, List dynamicArgTypes) { + Type.MethodType indyType = new Type.MethodType(dynamicArgTypes, + type, + List.nil(), + syms.methodClass); + + int prevPos = make.pos; + try { + make.at(pos); + + List bsm_staticArgs = List.of(syms.methodHandleLookupType, + syms.stringType, + syms.methodTypeType); + + Symbol bsm = rs.resolveInternalMethod(pos, + gen.getAttrEnv(), + syms.stringConcatFactory, + names.makeConcat, + bsm_staticArgs, + null); + + Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcat, + syms.noSymbol, + ClassFile.REF_invokeStatic, + (Symbol.MethodSymbol)bsm, + indyType, + List.nil().toArray()); + + Items.Item item = gen.getItems().makeDynamicItem(dynSym); + item.invoke(); + } finally { + make.at(prevPos); + } + } + } + + /** + * Emits the invokedynamic call to JDK java.lang.invoke.StringConcatFactory. + * This code concatenates all known constants into the recipe, possibly escaping + * some constants separately. + * + * We also bypass empty strings, because they have no meaning at this level. This + * captures the Java language trick to force String concat with e.g. ("" + int)-like + * expression. Down here, we already know we are in String concat business, and do + * not require these markers. + */ + private static final class IndyConstants extends Indy { + public IndyConstants(Context context) { + super(context); + } + + @Override + protected void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos) { + List> split = split(args); + + for (List t : split) { + Assert.check(!t.isEmpty(), "Arguments list is empty"); + + StringBuilder recipe = new StringBuilder(t.size()); + ListBuffer dynamicArgs = new ListBuffer<>(); + ListBuffer staticArgs = new ListBuffer<>(); + + for (JCTree arg : t) { + Object constVal = arg.type.constValue(); + if ("".equals(constVal)) continue; + if (arg.type == syms.botType) { + // Concat the null into the recipe right away + recipe.append((String) null); + } else if (constVal != null) { + // Concat the String representation of the constant, except + // for the case it contains special tags, which requires us + // to expose it as detached constant. + String a = arg.type.stringValue(); + if (a.indexOf(TAG_CONST) != -1 || a.indexOf(TAG_ARG) != -1) { + recipe.append(TAG_CONST); + staticArgs.add(a); + } else { + recipe.append(a); + } + } else { + // Ordinary arguments come through the dynamic arguments. + recipe.append(TAG_ARG); + dynamicArgs.add(arg.type); + gen.genExpr(arg, arg.type).load(); + } + } + + doCall(type, pos, recipe.toString(), staticArgs.toList(), dynamicArgs.toList()); + } + + // More that one peel slice produced: concatenate the results + // All arguments are assumed to be non-constant Strings. + if (split.size() > 1) { + ListBuffer argTypes = new ListBuffer<>(); + StringBuilder recipe = new StringBuilder(); + for (int c = 0; c < split.size(); c++) { + argTypes.append(syms.stringType); + recipe.append(TAG_ARG); + } + doCall(type, pos, recipe.toString(), List.nil(), argTypes.toList()); + } + } + + /** Produce the actual invokedynamic call to StringConcatFactory */ + private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, String recipe, List staticArgs, List dynamicArgTypes) { + Type.MethodType indyType = new Type.MethodType(dynamicArgTypes, + type, + List.nil(), + syms.methodClass); + + int prevPos = make.pos; + try { + make.at(pos); + + ListBuffer constTypes = new ListBuffer<>(); + ListBuffer constants = new ListBuffer<>(); + for (Object t : staticArgs) { + constants.add(t); + constTypes.add(syms.stringType); + } + + List bsm_staticArgs = List.of(syms.methodHandleLookupType, + syms.stringType, + syms.methodTypeType) + .append(syms.stringType) + .appendList(constTypes); + + Symbol bsm = rs.resolveInternalMethod(pos, + gen.getAttrEnv(), + syms.stringConcatFactory, + names.makeConcatWithConstants, + bsm_staticArgs, + null); + + Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcatWithConstants, + syms.noSymbol, + ClassFile.REF_invokeStatic, + (Symbol.MethodSymbol)bsm, + indyType, + List.of(recipe).appendList(constants).toArray()); + + Items.Item item = gen.getItems().makeDynamicItem(dynSym); + item.invoke(); + } finally { + make.at(prevPos); + } + } + } + +} diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java index aeb2b11adc2..df70c50b8ec 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java @@ -135,4 +135,10 @@ public enum Target { return hasInvokedynamic(); } + /** Does the target JDK contain StringConcatFactory class? + */ + public boolean hasStringConcatFactory() { + return compareTo(JDK1_9) >= 0; + } + } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java index 4f5b11f07a5..213277432c0 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java @@ -179,6 +179,10 @@ public class Names { public final Name altMetafactory; public final Name dollarThis; + // string concat + public final Name makeConcat; + public final Name makeConcatWithConstants; + public final Name.Table table; public Names(Context context) { @@ -316,6 +320,10 @@ public class Names { lambda = fromString("lambda$"); metafactory = fromString("metafactory"); altMetafactory = fromString("altMetafactory"); + + // string concat + makeConcat = fromString("makeConcat"); + makeConcatWithConstants = fromString("makeConcatWithConstants"); } protected Name.Table createTable(Options options) { diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/javadoc/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/javadoc/package-info.java index ace69c00622..36a9f5c224c 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/javadoc/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/javadoc/package-info.java @@ -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 @@ -24,6 +24,11 @@ */ /** +

+Note: The declarations in this package have been superseded by those +in the package {@code jdk.javadoc.doclet}. +

+ The Doclet API (also called the Javadoc API) provides a mechanism for clients to inspect the source-level structure of programs and libraries, including javadoc comments embedded in the source. diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/Taglet.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/Taglet.java index 5a41b1d654e..16578ea9ada 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/Taglet.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/Taglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -29,7 +29,14 @@ import com.sun.javadoc.*; /** * The interface for a custom tag used by Doclets. A custom - * tag must implement this interface. To be loaded and used by + * tag must implement this interface. + * + *

+ * Note: This interface has been superseded by one + * in the new package {@code jdk.javadoc.doclet.taglet}. + *

+ * + * To be loaded and used by * doclets at run-time, the taglet must have a static method called * register that accepts a {@link java.util.Map} as an * argument with the following signature: diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/package-info.java index 7c8766c4f69..a9eb28cbea5 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/package-info.java @@ -31,4 +31,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.formats.html.markup; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/package-info.java index dadc9e1500d..d2f2618e64d 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,7 @@ */ /** - This is the default doclet provided with JDK that produces Javadoc's - default HTML-formatted API output. For more documentation + This produces Javadoc's HTML-formatted API output. For more documentation on this doclet, please refer to the link below. @see @@ -36,4 +35,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.formats.html; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java index 27154a72162..07936a8d1c9 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.tools.JavaFileManager; import com.sun.javadoc.*; @@ -37,6 +38,7 @@ import com.sun.tools.javac.jvm.Profile; import com.sun.tools.doclets.internal.toolkit.builders.BuilderFactory; import com.sun.tools.doclets.internal.toolkit.taglets.*; import com.sun.tools.doclets.internal.toolkit.util.*; +import com.sun.tools.doclets.internal.toolkit.util.VisibleMemberMap.GetterSetter; import com.sun.tools.javac.util.StringUtils; /** @@ -304,6 +306,13 @@ public abstract class Configuration { */ public SortedSet packages; + // The following three fields provide caches for use by all instances of VisibleMemberMap. + public final Map propertiesCache = new HashMap<>(); + public final Map classPropertiesMap = new HashMap<>(); + public final Map getterSetterMap = new HashMap<>(); + + public DocFileFactory docFileFactory; + /** * Constructor. Constructs the message retriever with resource file. */ diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/builders/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/builders/package-info.java index ef8b4acf1c1..c11193fc36a 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/builders/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/builders/package-info.java @@ -37,4 +37,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.internal.toolkit.builders; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/package-info.java index e8bcae2fed3..30bc952e01b 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/package-info.java @@ -53,4 +53,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.internal.toolkit; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/package-info.java index 43750f1d774..cad2426bf1f 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/package-info.java @@ -48,4 +48,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.internal.toolkit.taglets; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFileFactory.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFileFactory.java index db34c5cc5c9..7c0b88db18c 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFileFactory.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFileFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,6 @@ package com.sun.tools.doclets.internal.toolkit.util; -import java.util.Map; -import java.util.WeakHashMap; - import javax.tools.JavaFileManager; import javax.tools.JavaFileManager.Location; import javax.tools.StandardJavaFileManager; @@ -45,15 +42,13 @@ import com.sun.tools.doclets.internal.toolkit.Configuration; * * @since 1.8 */ -abstract class DocFileFactory { - private static final Map factories = new WeakHashMap<>(); - +public abstract class DocFileFactory { /** * Get the appropriate factory, based on the file manager given in the * configuration. */ static synchronized DocFileFactory getFactory(Configuration configuration) { - DocFileFactory f = factories.get(configuration); + DocFileFactory f = configuration.docFileFactory; if (f == null) { JavaFileManager fm = configuration.getFileManager(); if (fm instanceof StandardJavaFileManager) { @@ -61,7 +56,7 @@ abstract class DocFileFactory { } else { throw new IllegalStateException(); } - factories.put(configuration, f); + configuration.docFileFactory = f; } return f; } diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java index 487215ca4a0..7a1abfb1eec 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java @@ -101,9 +101,9 @@ public class VisibleMemberMap { private final Configuration configuration; private final Utils utils; - private static final Map propertiesCache = new HashMap<>(); - private static final Map classPropertiesMap = new HashMap<>(); - private static final Map getterSetterMap = new HashMap<>(); + private final Map propertiesCache; + private final Map classPropertiesMap; + private final Map getterSetterMap; /** * Construct a VisibleMemberMap of the given type for the given @@ -123,6 +123,9 @@ public class VisibleMemberMap { this.kind = kind; this.configuration = configuration; this.utils = configuration.utils; + propertiesCache = configuration.propertiesCache; + classPropertiesMap = configuration.classPropertiesMap; + getterSetterMap = configuration.getterSetterMap; new ClassMembers(classdoc, STARTLEVEL).build(); } @@ -713,7 +716,7 @@ public class VisibleMemberMap { } } - private class GetterSetter { + public class GetterSetter { private final ProgramElementDoc getter; private final ProgramElementDoc setter; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/links/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/links/package-info.java index 1319ccd0877..da4d9455716 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/links/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/links/package-info.java @@ -31,4 +31,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.internal.toolkit.util.links; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/package-info.java index 2e898013ffb..ef49b0ab1e2 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/util/package-info.java @@ -32,4 +32,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets.internal.toolkit.util; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/package-info.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/package-info.java index 0ced35a2c62..735bfa5c5cc 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/package-info.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/package-info.java @@ -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 @@ -24,6 +24,11 @@ */ /** +

+Note: The declarations in this package have been superseded by those +in the new package {@code jdk.javadoc.doclet}. +

+ As of JDK version 1.5, replaced by {@code com.sun.tools.doclets.internal.toolkit.util}. @@ -32,4 +37,5 @@ This code and its internal interfaces are subject to change or deletion without notice. */ + package com.sun.tools.doclets; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/Start.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/Start.java index a1c31b6e722..9e3419f5449 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/Start.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/Start.java @@ -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 @@ -103,7 +103,11 @@ public class Start extends ToolOption.Helper { this(programName, errWriter, warnWriter, noticeWriter, defaultDocletClassName, null); } - Start(String programName, + public Start(PrintWriter pw) { + this(javadocName, pw, pw, pw, standardDocletClassName); + } + + public Start(String programName, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter, @@ -139,7 +143,7 @@ public class Start extends ToolOption.Helper { this(javadocName, docletParentClassLoader); } - Start() { + public Start() { this(javadocName); } @@ -212,7 +216,7 @@ public class Start extends ToolOption.Helper { /** * Main program - external wrapper */ - int begin(String... argv) { + public int begin(String... argv) { boolean ok = begin(null, argv, Collections. emptySet()); return ok ? 0 : 1; } diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Doclet.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Doclet.java new file mode 100644 index 00000000000..9e7885675a6 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/Doclet.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.doclet; + +import java.util.ListIterator; +import java.util.Locale; +import java.util.Set; + +import javax.lang.model.SourceVersion; + +/** + * The user doclet must implement this interface, as described in the + *
package description. + * Each implementation of a Doclet must provide a public no-argument constructor + * to be used by tools to instantiate the doclet. The tool infrastructure will + * interact with classes implementing this interface as follows: + *
    + *
  1. The tool will create an instance of a doclet using the no-arg constructor + * of the doclet class. + *
  2. Next, the tool calls the {@link #init init} method with an appropriate locale + * and reporter. + *
  3. Afterwards, the tool calls {@link #getSupportedOptions getSupportedOptions}, + * and {@link #getSupportedSourceVersion getSupportedSourceVersion}. + * These methods are only called once. + *
  4. As appropriate, the tool calls the {@link #run run} method on the doclet + * object, giving it a DocletEnvironment object, from which the doclet can determine + * the elements to be included in the documentation. + *
+ *

+ * If a doclet object is created and used without the above protocol being followed, + * then the doclet's behavior is not defined by this interface specification. + *

To start the doclet, pass {@code -doclet} followed by the fully-qualified + * name of the entry point class (i.e. the implementation of this interface) on + * the javadoc tool command line. + * + * @since 9 + */ +public interface Doclet { + + /** + * Initializes this doclet with the given locale and error reporter. + * This locale will be used by the reporter and the doclet components. + * It is recommended to call this as early as possible, for a + * uniform localized user experience, + * @param locale the locale to be used + * @param reporter the reporter to be used + */ + public void init(Locale locale, Reporter reporter); + + /** + * Returns a name identifying the doclet. A name is a simple identifier + * without white spaces, as defined in The Java™ Language Specification, + * section 6.2 "Names and Identifiers". + * @return name of the Doclet + */ + public abstract String getName(); + + /** + * Returns all the supported options. + * + * @return a Set containing all the supported options, an empty set if none. + */ + public Set

+ * Note: The declarations in this package supersede those + * in the older package {@code com.sun.javadoc}. For details on the + * mapping of old types to new types, see the + * Migration Guide. + *

+ * + *

+ * Doclets are invoked by javadoc and this API can be used to write out + * program information to files. For example, the standard doclet is + * invoked by default, to generate HTML documentation. + *

+ + * The invocation is defined by the interface {@link jdk.javadoc.doclet.Doclet} + * -- the {@link jdk.javadoc.doclet.Doclet#run(DocletEnvironment) run} interface + * method, defines the entry point. + *

+ *    public boolean run(DocletEnvironment environment)
+ * 
+ * The {@link jdk.javadoc.doclet.DocletEnvironment} instance holds the environment that the + * doclet will be initialized with. From this environment all other information can be + * extracted, in the form of {@link javax.lang.model} elements. One can further use the + * APIs and utilities described by {@link javax.lang.model} to query Elements and Types. + *

+ * + * + *

Terminology

+ * + * + * When calling javadoc, one can pass in package names and source file names -- + * these are called the specified PackageElements and TypeElements. + * Javadoc options are also passed in; the access control Javadoc options + * ({@code -public}, {@code -protected}, {@code -package}, + * and {@code -private}) are used to filter program elements, producing a + * result set, called the included set, or "selected" set. + *

+ + * + * A qualified element name is one that has its package + * name prepended to it, such as {@code java.lang.String}. A non-qualified + * name has no package name, such as {@code String}. + *

+ * + * + *

Example

+ * + * The following is an example doclet that displays information of a class + * and its members, supporting an option "someoption". + *
+ * import com.sun.source.doctree.DocCommentTree;
+ * import com.sun.source.util.DocTrees;
+ * import java.io.IOException;
+ * import java.util.Collections;
+ * import java.util.Set;
+ * import javax.lang.model.SourceVersion;
+ * import javax.lang.model.element.Element;
+ * import javax.lang.model.element.TypeElement;
+ * import jdk.javadoc.doclet.*;
+ *
+ * public class Example implements Doclet {
+ *
+ *     @Override
+ *     public void init(Locale locale, Reporter reporter) {
+ *        return;
+ *     }
+ *
+ *     @Override
+ *     public boolean run(RootDoc root) {
+ *         // cache the DocTrees utility class to access DocComments
+ *         DocTrees docTrees = root.getDocTrees();
+ *
+ *         // location of an element in the same directory as overview.html
+ *         try {
+ *             Element barElement = null;
+ *             for (Element e : root.getIncludedClasses()) {
+ *                 if (e.getSimpleName().toString().equals("FooBar")) {
+ *                     barElement = e;
+ *                 }
+ *             }
+ *             DocCommentTree docCommentTree =
+ *                     docTrees.getDocCommentTree(barElement, "overview.html");
+ *             if (docCommentTree != null) {
+ *                 System.out.println("Overview html: " +
+ *                         docCommentTree.getFullBody());
+ *             }
+ *         } catch (IOException missing) {
+ *             System.err.println("No overview.html found.");
+ *         }
+ *
+ *         for (TypeElement t : root.getIncludedClasses()) {
+ *             System.out.println(t.getKind() + ":" + t);
+ *             for (Element e : t.getEnclosedElements()) {
+ *                 DocCommentTree docCommentTree = docTrees.getDocCommentTree(e);
+ *                 if (docCommentTree != null) {
+ *                     System.out.println("Element (" + e.getKind() + ": " +
+ *                             e + ") has the following comments:");
+ *                     System.out.println("Entire body: " + docCommentTree.getFullBody());
+ *                     System.out.println("Block tags: " + docCommentTree.getBlockTags());
+ *                 } else {
+ *                     System.out.println("no comment.");
+ *                 }
+ *             }
+ *         }
+ *         return true;
+ *     }
+ *
+ *     @Override
+ *     public String getName() {
+ *         return "Example";
+ *     }
+ *
+ *   private String someOption;
+ *
+ *   @Override
+ *   public Set<Option> getSupportedOptions() {
+ *       Option[] options = {
+ *           new Option() {
+ *               public int getArgumentCount() {
+ *                   return 1;
+ *               }
+ *               public String getDescription() {
+ *                   return "someoption";
+ *               }
+ *               public Option.Kind getKind() {
+ *                   return Option.Kind.STANDARD;
+ *               }
+ *               public String getName() {
+ *                   return "someoption";
+ *               }
+ *               public String getParameters() {
+ *                   return "url";
+ *               }
+ *               public boolean matches(String option) {
+ *                  String opt = option.startsWith("-") ? option.substring(1) : option;
+ *                  return getName().equals(opt);
+ *               }
+ *               public boolean process(String option, ListIterator<String> arguments) {
+ *                  overviewpath = arguments.next();
+ *                  return true;
+ *               }
+ *          }
+ *      };
+ *      return new HashSet<Option>(Arrays.asList(options));
+ *     }
+ *
+ *     @Override
+ *     public SourceVersion getSupportedSourceVersion() {
+ *         // support the latest release
+ *         return SourceVersion.latest();
+ *     }
+ * }
+ * 
+ *

+ * This doclet when invoked with a command line, such as: + *

+ *     javadoc -doclet Example -sourcepath <source-location>
+ * 
+ * will produce an output, such as: + *
+ *  Overview.html: overview comments
+ *  ...
+ *  ...
+ *  CLASS: SomeKlass
+ *  .....
+ *  Element (METHOD: main(java.lang.String...)) has the following comments:
+ *  Entire body: The main entry point.
+ *  Block tags: @param an array of Strings
+ *  ...
+ * 
+ * + *

Migration Guide

+ * + *

Many of the types in the old {@code com.sun.javadoc} API do not have equivalents in this + * package. Instead, types in the {@code javax.lang.model} and {@code com.sun.source} APIs + * are used instead. + * + *

The following table gives a guide to the mapping from old types to their replacements. + * In some cases, there is no direct equivalent. + * + * + +
Guide for mapping old types to new types
Old TypeNew Type +
AnnotatedTypejavax.lang.model.type.Type +
AnnotationDescjavax.lang.model.element.AnnotationMirror +
AnnotationDesc.ElementValuePairjavax.lang.model.element.AnnotationValue +
AnnotationTypeDocjavax.lang.model.element.TypeElement +
AnnotationTypeElementDocjavax.lang.model.element.ExecutableElement +
AnnotationValuejavax.lang.model.element.AnnotationValue +
ClassDocjavax.lang.model.element.TypeElement +
ConstructorDocjavax.lang.model.element.ExecutableElement +
Docjavax.lang.model.element.Element +
DocErrorReporterjdk.javadoc.doclet.Reporter +
Docletjdk.javadoc.doclet.Doclet +
ExecutableMemberDocjavax.lang.model.element.ExecutableElement +
FieldDocjavax.lang.model.element.VariableElement +
LanguageVersionjavax.lang.model.SourceVersion +
MemberDocjavax.lang.model.element.Element +
MethodDocjavax.lang.model.element.ExecutableElement +
PackageDocjavax.lang.model.element.PackageElement +
Parameterjavax.lang.model.element.VariableElement +
ParameterizedTypejavax.lang.model.type.DeclaredType +
ParamTagcom.sun.source.doctree.ParamTree +
ProgramElementDocjavax.lang.model.element.Element +
RootDocjdk.javadoc.doclet.DocletEnvironment +
SeeTagcom.sun.source.doctree.LinkTree
com.sun.source.doctree.SeeTree +
SerialFieldTagcom.sun.source.doctree.SerialFieldTree +
SourcePositioncom.sun.source.util.SourcePositions +
Tagcom.sun.source.doctree.DocTree +
ThrowsTagcom.sun.source.doctree.ThrowsTree +
Typejavax.lang.model.type.Type +
TypeVariablejavax.lang.model.type.TypeVariable +
WildcardTypejavax.lang.model.type.WildcardType + *
+ * + * @see jdk.javadoc.doclet.Doclet + * @see jdk.javadoc.doclet.DocletEnvironment + * @since 9 +*/ + +package jdk.javadoc.doclet; diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/Taglet.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/Taglet.java new file mode 100644 index 00000000000..f102bd55314 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/Taglet.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.doclet.taglet; + +import java.util.List; +import java.util.Set; + +import com.sun.source.doctree.DocTree; + +/** + * The interface for a custom tag used by Doclets. A custom + * tag must implement this interface, and must have a public + * default constructor (i.e. a public constructor with no + * parameters), by which, the doclet will instantiate and + * register the custom tag. + * + * @since 9 + */ + +public interface Taglet { + + /** + * Returns the set of locations in which a taglet may be used. + * @return the set of locations in which a taglet may be used + * allowed in or an empty set. + */ + Set getAllowedLocations(); + + /** + * Indicates the tag is an inline or a body tag. + * @return true if this Taglet + * is an inline tag, false otherwise. + */ + public abstract boolean isInlineTag(); + + /** + * Returns the name of the tag. + * @return the name of this custom tag. + */ + public abstract String getName(); + + /** + * Given the {@link DocTree DocTree} representation of this custom + * tag, return its string representation, which is output + * to the generated page. + * @param tag the Tag representation of this custom tag. + * @return the string representation of this Tag. + */ + public abstract String toString(DocTree tag); + + /** + * Given a List of {@link DocTree DocTrees} representing this custom + * tag, return its string representation, which is output + * to the generated page. This method should + * return null if this taglet represents an inline or body tag. + * @param tags the list of DocTrees representing this custom tag. + * @return the string representation of this Tag. + */ + public abstract String toString(List tags); + + /** + * The kind of location. + */ + public static enum Location { + /** In an Overview document. */ + OVERVIEW, + /** In the documentation for a package. */ + PACKAGE, + /** In the documentation for a class, interface or enum. */ + TYPE, + /** In the documentation for a constructor. */ + CONSTRUCTOR, + /** In the documentation for a method. */ + METHOD, + /** In the documentation for a field. */ + FIELD + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/package-info.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/package-info.java new file mode 100644 index 00000000000..066f5e61472 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/doclet/taglet/package-info.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + */ + +/** + * The Taglet API provides a way to declare custom tags that can be + * used by the standard doclet. + * + *

+ * Note: The declarations in this package supersede those + * in the older package {@code com.sun.tools.doclets}. + *

+ * + * @since 9 + */ +package jdk.javadoc.doclet.taglet; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTaskImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTaskImpl.java similarity index 95% rename from langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTaskImpl.java rename to langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTaskImpl.java index 18d24b1e6b5..97ec41633c9 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTaskImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTaskImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,21 +23,20 @@ * questions. */ -package com.sun.tools.javadoc.api; +package jdk.javadoc.internal.api; -import com.sun.tools.javac.util.ClientCodeException; +import java.util.Collections; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import javax.tools.DocumentationTool.DocumentationTask; import javax.tools.JavaFileObject; +import com.sun.tools.javac.util.ClientCodeException; import com.sun.tools.javac.util.Context; -import com.sun.tools.javadoc.Start; -import java.util.Collections; - import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; +import jdk.javadoc.internal.tool.Start; /** * Provides access to functionality specific to the JDK documentation tool, diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTool.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTool.java similarity index 94% rename from langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTool.java rename to langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTool.java index 2aa5c1691dd..8b91c15df2e 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTool.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/api/JavadocTool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ * questions. */ -package com.sun.tools.javadoc.api; +package jdk.javadoc.internal.api; import java.io.InputStream; import java.io.OutputStream; @@ -52,7 +52,7 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.Log; -import com.sun.tools.javadoc.ToolOption; +import jdk.javadoc.internal.tool.ToolOption; /** * Provides access to functionality specific to the JDK documentation tool, @@ -150,10 +150,7 @@ public class JavadocTool implements DocumentationTool { PrintWriter err_pw = new PrintWriter(err == null ? System.err : err, true); PrintWriter out_pw = new PrintWriter(out == null ? System.out : out); try { - String standardDocletName = "com.sun.tools.doclets.standard.Standard"; - ClassLoader cl = getClass().getClassLoader(); - return com.sun.tools.javadoc.Main.execute( - "javadoc", err_pw, err_pw, out_pw, standardDocletName, cl, arguments); + return jdk.javadoc.internal.tool.Main.execute(arguments, err_pw); } finally { err_pw.flush(); out_pw.flush(); diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java new file mode 100644 index 00000000000..d82da4653cd --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java @@ -0,0 +1,352 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.util.List; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.SimpleTypeVisitor9; + +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +import static jdk.javadoc.internal.doclets.formats.html.LinkInfoImpl.Kind.*; + +/** + * Print method and constructor info. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public abstract class AbstractExecutableMemberWriter extends AbstractMemberWriter { + + public AbstractExecutableMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + public AbstractExecutableMemberWriter(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * Add the type parameters for the executable member. + * + * @param member the member to write type parameters for. + * @param htmltree the content tree to which the parameters will be added. + */ + protected void addTypeParameters(ExecutableElement member, Content htmltree) { + Content typeParameters = getTypeParameters(member); + if (!typeParameters.isEmpty()) { + htmltree.addContent(typeParameters); + htmltree.addContent(writer.getSpace()); + } + } + + /** + * Get the type parameters for the executable member. + * + * @param member the member for which to get the type parameters. + * @return the type parameters. + */ + protected Content getTypeParameters(ExecutableElement member) { + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, MEMBER_TYPE_PARAMS, member); + return writer.getTypeParameterLinks(linkInfo); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getDeprecatedLink(Element member) { + StringBuilder sb = new StringBuilder(); + sb.append(utils.getFullyQualifiedName(member)); + if (!utils.isConstructor(member)) { + sb.append("."); + sb.append(member.getSimpleName().toString()); + } + sb.append(utils.flatSignature((ExecutableElement) member)); + + return writer.getDocLink(MEMBER, member, sb.toString()); + } + + /** + * Add the summary link for the member. + * + * @param context the id of the context where the link will be printed + * @param te the classDoc that we should link to + * @param member the member being linked to + * @param tdSummary the content tree to which the link will be added + */ + @Override + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement te, Element member, + Content tdSummary) { + ExecutableElement ee = (ExecutableElement)member; + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, te, ee, + name(ee), false)); + Content code = HtmlTree.CODE(memberLink); + addParameters(ee, false, code, name(ee).length() - 1); + tdSummary.addContent(code); + } + + /** + * Add the inherited summary link for the member. + * + * @param te the type element that we should link to + * @param member the member being linked to + * @param linksTree the content tree to which the link will be added + */ + @Override + protected void addInheritedSummaryLink(TypeElement te, Element member, Content linksTree) { + linksTree.addContent(writer.getDocLink(MEMBER, te, member, name(member), false)); + } + + /** + * Add the parameter for the executable member. + * + * @param member the member to write parameter for. + * @param param the parameter that needs to be written. + * @param isVarArg true if this is a link to var arg. + * @param tree the content tree to which the parameter information will be added. + */ + protected void addParam(ExecutableElement member, VariableElement param, + boolean isVarArg, Content tree) { + Content link = writer.getLink(new LinkInfoImpl(configuration, EXECUTABLE_MEMBER_PARAM, + param.asType()).varargs(isVarArg)); + tree.addContent(link); + if(name(param).length() > 0) { + tree.addContent(writer.getSpace()); + tree.addContent(name(param)); + } + } + + /** + * Add the receiver annotations information. + * + * @param member the member to write receiver annotations for. + * @param rcvrType the receiver type. + * @param descList list of annotation description. + * @param tree the content tree to which the information will be added. + */ + protected void addReceiverAnnotations(ExecutableElement member, TypeMirror rcvrType, + List annotationMirrors, Content tree) { + writer.addReceiverAnnotationInfo(member, rcvrType, annotationMirrors, tree); + tree.addContent(writer.getSpace()); + tree.addContent(utils.getTypeName(rcvrType, false)); + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, RECEIVER_TYPE, rcvrType); + tree.addContent(writer.getTypeParameterLinks(linkInfo)); + tree.addContent(writer.getSpace()); + tree.addContent("this"); + } + + + /** + * Add all the parameters for the executable member. + * + * @param member the member to write parameters for. + * @param htmltree the content tree to which the parameters information will be added. + */ + protected void addParameters(ExecutableElement member, Content htmltree, int indentSize) { + addParameters(member, true, htmltree, indentSize); + } + + /** + * Add all the parameters for the executable member. + * + * @param member the member to write parameters for. + * @param includeAnnotations true if annotation information needs to be added. + * @param htmltree the content tree to which the parameters information will be added. + */ + protected void addParameters(ExecutableElement member, + boolean includeAnnotations, Content htmltree, int indentSize) { + htmltree.addContent("("); + String sep = ""; + List parameters = member.getParameters(); + String indent = makeSpace(indentSize + 1); + TypeMirror rcvrType = member.getReceiverType(); + if (includeAnnotations && rcvrType != null && utils.isAnnotated(rcvrType)) { + List annotationMirrors = rcvrType.getAnnotationMirrors(); + addReceiverAnnotations(member, rcvrType, annotationMirrors, htmltree); + sep = "," + DocletConstants.NL + indent; + } + int paramstart; + for (paramstart = 0; paramstart < parameters.size(); paramstart++) { + htmltree.addContent(sep); + VariableElement param = parameters.get(paramstart); + + if (param.getKind() != ElementKind.INSTANCE_INIT) { + if (includeAnnotations) { + boolean foundAnnotations = + writer.addAnnotationInfo(indent.length(), + member, param, htmltree); + if (foundAnnotations) { + htmltree.addContent(DocletConstants.NL); + htmltree.addContent(indent); + } + } + addParam(member, param, + (paramstart == parameters.size() - 1) && member.isVarArgs(), htmltree); + break; + } + } + + for (int i = paramstart + 1; i < parameters.size(); i++) { + htmltree.addContent(","); + htmltree.addContent(DocletConstants.NL); + htmltree.addContent(indent); + if (includeAnnotations) { + boolean foundAnnotations = + writer.addAnnotationInfo(indent.length(), member, parameters.get(i), + htmltree); + if (foundAnnotations) { + htmltree.addContent(DocletConstants.NL); + htmltree.addContent(indent); + } + } + addParam(member, parameters.get(i), (i == parameters.size() - 1) && member.isVarArgs(), + htmltree); + } + htmltree.addContent(")"); + } + + /** + * Add exceptions for the executable member. + * + * @param member the member to write exceptions for. + * @param htmltree the content tree to which the exceptions information will be added. + */ + protected void addExceptions(ExecutableElement member, Content htmltree, int indentSize) { + List exceptions = member.getThrownTypes(); + if (!exceptions.isEmpty()) { + String indent = makeSpace(indentSize + 1 - 7); + htmltree.addContent(DocletConstants.NL); + htmltree.addContent(indent); + htmltree.addContent("throws "); + indent = makeSpace(indentSize + 1); + Content link = writer.getLink(new LinkInfoImpl(configuration, MEMBER, exceptions.get(0))); + htmltree.addContent(link); + for(int i = 1; i < exceptions.size(); i++) { + htmltree.addContent(","); + htmltree.addContent(DocletConstants.NL); + htmltree.addContent(indent); + Content exceptionLink = writer.getLink(new LinkInfoImpl(configuration, MEMBER, + exceptions.get(i))); + htmltree.addContent(exceptionLink); + } + } + } + + protected TypeElement implementsMethodInIntfac(ExecutableElement method, + List intfacs) { + for (TypeElement intf : intfacs) { + List methods = utils.getMethods(intf); + if (!methods.isEmpty()) { + for (ExecutableElement md : methods) { + if (name(md).equals(name(method)) && + md.toString().equals(method.toString())) { + return intf; + } + } + } + } + return null; + } + + /** + * For backward compatibility, include an anchor using the erasures of the + * parameters. NOTE: We won't need this method anymore after we fix + * see tags so that they use the type instead of the erasure. + * + * @param executableElement the ExecutableElement to anchor to. + * @return the 1.4.x style anchor for the executable element. + */ + protected String getErasureAnchor(ExecutableElement executableElement) { + final StringBuilder buf = new StringBuilder(name(executableElement) + "("); + List parameters = executableElement.getParameters(); + boolean foundTypeVariable = false; + for (int i = 0; i < parameters.size(); i++) { + if (i > 0) { + buf.append(","); + } + TypeMirror t = parameters.get(i).asType(); + SimpleTypeVisitor9 stv = new SimpleTypeVisitor9() { + boolean foundTypeVariable = false; + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitArray(ArrayType t, Void p) { + visit(t.getComponentType()); + buf.append(utils.getDimension(t)); + return foundTypeVariable; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitTypeVariable(TypeVariable t, Void p) { + buf.append(utils.asTypeElement(t).getQualifiedName()); + foundTypeVariable = true; + return foundTypeVariable; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitDeclared(DeclaredType t, Void p) { + buf.append(utils.getQualifiedTypeName(t)); + return foundTypeVariable; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Boolean defaultAction(TypeMirror e, Void p) { + buf.append(e.toString()); + return foundTypeVariable; + } + }; + + boolean isTypeVariable = stv.visit(t); + if (!foundTypeVariable) { + foundTypeVariable = isTypeVariable; + } + } + buf.append(")"); + return foundTypeVariable ? writer.getName(buf.toString()) : null; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractIndexWriter.java new file mode 100644 index 00000000000..b9d4d36c2a5 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractIndexWriter.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.zip.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.SimpleElementVisitor9; + +import com.sun.source.doctree.DocTree; +import com.sun.tools.javac.util.DefinedBy; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; + +/** + * Generate Index for all the Member Names with Indexing in + * Unicode Order. This class is a base class for {@link SingleIndexWriter} and + * {@link SplitIndexWriter}. It uses the functionality from + * {@link HtmlDocletWriter} to generate the Index Contents. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see IndexBuilder + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class AbstractIndexWriter extends HtmlDocletWriter { + + /** + * The index of all the members with unicode character. + */ + protected IndexBuilder indexbuilder; + + /** + * This constructor will be used by {@link SplitIndexWriter}. Initializes + * path to this file and relative path from this file. + * + * @param configuration The current configuration + * @param path Path to the file which is getting generated. + * @param indexbuilder Unicode based Index from {@link IndexBuilder} + */ + protected AbstractIndexWriter(ConfigurationImpl configuration, + DocPath path, + IndexBuilder indexbuilder) + throws IOException { + super(configuration, path); + this.indexbuilder = indexbuilder; + } + + /** + * Get the index label for navigation bar. + * + * @return a content tree for the tree label + */ + protected Content getNavLinkIndex() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, indexLabel); + return li; + } + + /** + * Add the member information for the unicode character along with the + * list of the members. + * + * @param uc Unicode for which member list information to be generated + * @param memberlist List of members for the unicode character + * @param contentTree the content tree to which the information will be added + */ + protected void addContents(Character uc, Collection memberlist, + Content contentTree) { + addHeading(uc, contentTree); + // Display the list only if there are elements to be displayed. + if (!memberlist.isEmpty()) { + Content dl = new HtmlTree(HtmlTag.DL); + for (Element element : memberlist) { + addDescription(dl, element); + } + contentTree.addContent(dl); + } + } + + protected void addSearchContents(Character uc, List searchList, + Content contentTree) { + addHeading(uc, contentTree); + // Display the list only if there are elements to be displayed. + if (!searchList.isEmpty()) { + Content dl = new HtmlTree(HtmlTag.DL); + for (SearchIndexItem sii : searchList) { + addDescription(sii, dl); + } + contentTree.addContent(dl); + } + } + + protected void addContents(Character uc, List memberlist, + List searchList, Content contentTree) { + addHeading(uc, contentTree); + int memberListSize = memberlist.size(); + int searchListSize = searchList.size(); + int i = 0; + int j = 0; + Content dl = new HtmlTree(HtmlTag.DL); + while (i < memberListSize && j < searchListSize) { + String name = utils.getSimpleName(memberlist.get(i)); + if (name.compareTo(searchList.get(j).getLabel()) < 0) { + addDescription(dl, memberlist.get(i)); + i++; + } else if (name.compareTo(searchList.get(j).getLabel()) > 0) { + addDescription(searchList.get(j), dl); + j++; + } else { + addDescription(dl, memberlist.get(i)); + addDescription(searchList.get(j), dl); + j++; + i++; + } + } + if (i >= memberListSize) { + while (j < searchListSize) { + addDescription(searchList.get(j), dl); + j++; + } + } + if (j >= searchListSize) { + while (i < memberListSize) { + addDescription(dl, memberlist.get(i)); + i++; + } + } + contentTree.addContent(dl); + } + + protected void addHeading(Character uc, Content contentTree) { + String unicode = uc.toString(); + contentTree.addContent(getMarkerAnchorForIndex(unicode)); + Content headContent = new StringContent(unicode); + Content heading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, false, + HtmlStyle.title, headContent); + contentTree.addContent(heading); + } + + protected void addDescription(Content dl, Element element) { + SearchIndexItem si = new SearchIndexItem(); + new SimpleElementVisitor9() { + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + public Void visitPackage(PackageElement e, Void p) { + addDescription(e, dl, si); + configuration.packageSearchIndex.add(si); + return null; + } + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + public Void visitType(TypeElement e, Void p) { + addDescription(e, dl, si); + configuration.typeSearchIndex.add(si); + return null; + } + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + protected Void defaultAction(Element e, Void p) { + addDescription(e, dl, si); + configuration.memberSearchIndex.add(si); + return null; + } + + }.visit(element); + } + + /** + * Add one line summary comment for the package. + * + * @param pkg the package to be documented + * @param dlTree the content tree to which the description will be added + */ + protected void addDescription(PackageElement pkg, Content dlTree, SearchIndexItem si) { + Content link = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg))); + si.setLabel(utils.getPackageName(pkg)); + si.setCategory(getResource("doclet.Packages").toString()); + Content dt = HtmlTree.DT(link); + dt.addContent(" - "); + dt.addContent(getResource("doclet.package")); + dt.addContent(" " + utils.getPackageName(pkg)); + dlTree.addContent(dt); + Content dd = new HtmlTree(HtmlTag.DD); + addSummaryComment(pkg, dd); + dlTree.addContent(dd); + } + + /** + * Add one line summary comment for the class. + * + * @param typeElement the class being documented + * @param dlTree the content tree to which the description will be added + */ + protected void addDescription(TypeElement typeElement, Content dlTree, SearchIndexItem si) { + Content link = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.INDEX, typeElement).strong(true)); + si.setContainingPackage(utils.getPackageName(utils.containingPackage(typeElement))); + si.setLabel(utils.getSimpleName(typeElement)); + si.setCategory(getResource("doclet.Types").toString()); + Content dt = HtmlTree.DT(link); + dt.addContent(" - "); + addClassInfo(typeElement, dt); + dlTree.addContent(dt); + Content dd = new HtmlTree(HtmlTag.DD); + addComment(typeElement, dd); + dlTree.addContent(dd); + } + + /** + * Add the classkind (class, interface, exception), error of the class + * passed. + * + * @param te the class being documented + * @param contentTree the content tree to which the class info will be added + */ + protected void addClassInfo(TypeElement te, Content contentTree) { + contentTree.addContent(getResource("doclet.in", + utils.getTypeElementName(te, false), + getPackageLink(utils.containingPackage(te), + utils.getPackageName(utils.containingPackage(te))) + )); + } + + /** + * Add description for Class, Field, Method or Constructor. + * + * @param member the member of the Class Kind + * @param dlTree the content tree to which the description will be added + * @param si search index item + */ + protected void addDescription(Element member, Content dlTree, SearchIndexItem si) { + + si.setContainingPackage(utils.getPackageName(utils.containingPackage(member))); + si.setContainingClass(utils.getSimpleName(utils.getEnclosingTypeElement(member))); + String name = utils.getSimpleName(member); + if (utils.isExecutableElement(member)) { + ExecutableElement ee = (ExecutableElement)member; + name = name + utils.flatSignature(ee); + si.setLabel(name); + if (!((utils.signature(ee)).equals(utils.flatSignature(ee)))) { + si.setUrl(getName(getAnchor(ee))); + } + + } else { + si.setLabel(name); + } + si.setCategory(getResource("doclet.Members").toString()); + Content span = HtmlTree.SPAN(HtmlStyle.memberNameLink, + getDocLink(LinkInfoImpl.Kind.INDEX, member, name)); + Content dt = HtmlTree.DT(span); + dt.addContent(" - "); + addMemberDesc(member, dt); + dlTree.addContent(dt); + Content dd = new HtmlTree(HtmlTag.DD); + addComment(member, dd); + dlTree.addContent(dd); + } + + protected void addDescription(SearchIndexItem sii, Content dlTree) { + String path = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/"; + path += sii.getUrl(); + HtmlTree labelLink = HtmlTree.A(path, new StringContent(sii.getLabel())); + Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink)); + dt.addContent(" - "); + dt.addContent(getResource("doclet.Search_tag_in", sii.getHolder())); + dlTree.addContent(dt); + Content dd = new HtmlTree(HtmlTag.DD); + if (sii.getDescription().isEmpty()) { + dd.addContent(getSpace()); + } else { + dd.addContent(sii.getDescription()); + } + dlTree.addContent(dd); + } + + /** + * Add comment for each element in the index. If the element is deprecated + * and it has a @deprecated tag, use that comment. Else if the containing + * class for this element is deprecated, then add the word "Deprecated." at + * the start and then print the normal comment. + * + * @param element Index element + * @param contentTree the content tree to which the comment will be added + */ + protected void addComment(Element element, Content contentTree) { + List tags; + Content span = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.block); + if (utils.isDeprecated(element)) { + div.addContent(span); + tags = utils.getBlockTags(element, DocTree.Kind.DEPRECATED); + if (!tags.isEmpty()) + addInlineDeprecatedComment(element, tags.get(0), div); + contentTree.addContent(div); + } else { + TypeElement encl = utils.getEnclosingTypeElement(element); + while (encl != null) { + if (utils.isDeprecated(encl)) { + div.addContent(span); + contentTree.addContent(div); + break; + } + encl = utils.getEnclosingTypeElement(encl); + } + addSummaryComment(element, contentTree); + } + } + + /** + * Add description about the Static Variable/Method/Constructor for a + * member. + * + * @param member MemberDoc for the member within the Class Kind + * @param contentTree the content tree to which the member description will be added + */ + protected void addMemberDesc(Element member, Content contentTree) { + TypeElement containing = utils.getEnclosingTypeElement(member); + String classdesc = utils.getTypeElementName(containing, true) + " "; + if (utils.isField(member)) { + Content resource = getResource(utils.isStatic(member) + ? "doclet.Static_variable_in" + : "doclet.Variable_in", classdesc); + contentTree.addContent(resource); + } else if (utils.isConstructor(member)) { + contentTree.addContent( + getResource("doclet.Constructor_for", classdesc)); + } else if (utils.isMethod(member)) { + Content resource = getResource(utils.isStatic(member) + ? "doclet.Static_method_in" + : "doclet.Method_in", classdesc); + contentTree.addContent(resource); + } + addPreQualifiedClassLink(LinkInfoImpl.Kind.INDEX, containing, + false, contentTree); + } + + /** + * Get the marker anchor which will be added to the index documentation tree. + * + * @param anchorNameForIndex the anchor name attribute for index page + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchorForIndex(String anchorNameForIndex) { + return getMarkerAnchor(getNameForIndex(anchorNameForIndex), null); + } + + /** + * Generate a valid HTML name for member index page. + * + * @param unicode the string that needs to be converted to valid HTML name. + * @return a valid HTML name string. + */ + public String getNameForIndex(String unicode) { + return "I:" + getName(unicode); + } + + protected void createSearchIndexFiles() { + createSearchIndexFile(DocPaths.PACKAGE_SEARCH_INDEX_JSON, DocPaths.PACKAGE_SEARCH_INDEX_ZIP, + configuration.packageSearchIndex); + createSearchIndexFile(DocPaths.TYPE_SEARCH_INDEX_JSON, DocPaths.TYPE_SEARCH_INDEX_ZIP, + configuration.typeSearchIndex); + createSearchIndexFile(DocPaths.MEMBER_SEARCH_INDEX_JSON, DocPaths.MEMBER_SEARCH_INDEX_ZIP, + configuration.memberSearchIndex); + createSearchIndexFile(DocPaths.TAG_SEARCH_INDEX_JSON, DocPaths.TAG_SEARCH_INDEX_ZIP, + configuration.tagSearchIndex); + } + + protected void createSearchIndexFile(DocPath searchIndexFile, DocPath searchIndexZip, + List searchIndex) { + if (!searchIndex.isEmpty()) { + try { + StringBuilder searchVar = new StringBuilder("["); + boolean first = true; + DocFile searchFile = DocFile.createFileForOutput(configuration, searchIndexFile); + Path p = Paths.get(searchFile.getPath()); + for (SearchIndexItem item : searchIndex) { + if (first) { + searchVar.append(item.toString()); + first = false; + } else { + searchVar.append(",").append(item.toString()); + } + } + searchVar.append("]"); + Files.write(p, searchVar.toString().getBytes()); + DocFile zipFile = DocFile.createFileForOutput(configuration, searchIndexZip); + try (FileOutputStream fos = new FileOutputStream(zipFile.getPath()); + ZipOutputStream zos = new ZipOutputStream(fos)) { + zipFile(searchFile.getPath(), searchIndexFile, zos); + } + Files.delete(p); + } catch (IOException ie) { + throw new DocletAbortException(ie); + } + } + } + + protected void zipFile(String inputFile, DocPath file, ZipOutputStream zos) { + try { + try { + ZipEntry ze = new ZipEntry(file.getPath()); + zos.putNextEntry(ze); + try (FileInputStream fis = new FileInputStream(new File(inputFile))) { + byte[] buf = new byte[2048]; + int len = fis.read(buf); + while (len > 0) { + zos.write(buf, 0, len); + len = fis.read(buf); + } + } + } finally { + zos.closeEntry(); + } + } catch (IOException e) { + throw new DocletAbortException(e); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java new file mode 100644 index 00000000000..c963b909a52 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java @@ -0,0 +1,708 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.util.*; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleElementVisitor9; + +import com.sun.source.doctree.DocTree; +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.taglets.DeprecatedTaglet; +import jdk.javadoc.internal.doclets.toolkit.util.MethodTypes; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; + +import static javax.lang.model.element.Modifier.*; + +/** + * The base class for member writers. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Jamie Ho (Re-write) + * @author Bhavesh Patel (Modified) + */ +public abstract class AbstractMemberWriter { + + protected final ConfigurationImpl configuration; + protected final Utils utils; + protected final SubWriterHolderWriter writer; + protected final TypeElement typeElement; + protected Map typeMap = new LinkedHashMap<>(); + protected Set methodTypes = EnumSet.noneOf(MethodTypes.class); + private int methodTypesOr = 0; + public final boolean nodepr; + + protected boolean printedSummaryHeader = false; + + public AbstractMemberWriter(SubWriterHolderWriter writer, TypeElement typeElement) { + this.configuration = writer.configuration; + this.writer = writer; + this.nodepr = configuration.nodeprecated; + this.typeElement = typeElement; + this.utils = writer.configuration.utils; + } + + public AbstractMemberWriter(SubWriterHolderWriter writer) { + this(writer, null); + } + + /*** abstracts ***/ + + /** + * Add the summary label for the member. + * + * @param memberTree the content tree to which the label will be added + */ + public abstract void addSummaryLabel(Content memberTree); + + /** + * Get the summary for the member summary table. + * + * @return a string for the table summary + */ + public abstract String getTableSummary(); + + /** + * Get the caption for the member summary table. + * + * @return a string for the table caption + */ + public abstract Content getCaption(); + + /** + * Get the summary table header for the member. + * + * @param member the member to be documented + * @return the summary table header + */ + public abstract List getSummaryTableHeader(Element member); + + /** + * Add inherited summary label for the member. + * + * @param typeElement the TypeElement to which to link to + * @param inheritedTree the content tree to which the inherited summary label will be added + */ + public abstract void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree); + + /** + * Add the anchor for the summary section of the member. + * + * @param typeElement the TypeElement to be documented + * @param memberTree the content tree to which the summary anchor will be added + */ + public abstract void addSummaryAnchor(TypeElement typeElement, Content memberTree); + + /** + * Add the anchor for the inherited summary section of the member. + * + * @param typeElement the TypeElement to be documented + * @param inheritedTree the content tree to which the inherited summary anchor will be added + */ + public abstract void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree); + + /** + * Add the summary type for the member. + * + * @param member the member to be documented + * @param tdSummaryType the content tree to which the type will be added + */ + protected abstract void addSummaryType(Element member, Content tdSummaryType); + + /** + * Add the summary link for the member. + * + * @param typeElement the TypeElement to be documented + * @param member the member to be documented + * @param tdSummary the content tree to which the link will be added + */ + protected void addSummaryLink(TypeElement typeElement, Element member, Content tdSummary) { + addSummaryLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, tdSummary); + } + + /** + * Add the summary link for the member. + * + * @param context the id of the context where the link will be printed + * @param typeElement the TypeElement to be documented + * @param member the member to be documented + * @param tdSummary the content tree to which the summary link will be added + */ + protected abstract void addSummaryLink(LinkInfoImpl.Kind context, + TypeElement typeElement, Element member, Content tdSummary); + + /** + * Add the inherited summary link for the member. + * + * @param typeElement the TypeElement to be documented + * @param member the member to be documented + * @param linksTree the content tree to which the inherited summary link will be added + */ + protected abstract void addInheritedSummaryLink(TypeElement typeElement, + Element member, Content linksTree); + + /** + * Get the deprecated link. + * + * @param member the member being linked to + * @return a content tree representing the link + */ + protected abstract Content getDeprecatedLink(Element member); + + /** + * Get the navigation summary link. + * + * @param typeElement the TypeElement to be documented + * @param link true if its a link else the label to be printed + * @return a content tree for the navigation summary link. + */ + protected abstract Content getNavSummaryLink(TypeElement typeElement, boolean link); + + /** + * Add the navigation detail link. + * + * @param link true if its a link else the label to be printed + * @param liNav the content tree to which the navigation detail link will be added + */ + protected abstract void addNavDetailLink(boolean link, Content liNav); + + /** + * Add the member name to the content tree. + * + * @param name the member name to be added to the content tree. + * @param htmltree the content tree to which the name will be added. + */ + protected void addName(String name, Content htmltree) { + htmltree.addContent(name); + } + + protected String typeString(Element member) { + return new SimpleElementVisitor9() { + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public String visitExecutable(ExecutableElement e, Void p) { + return utils.isMethod(e) ? e.getReturnType().toString() : ""; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public String visitVariable(VariableElement e, Void p) { + return e.toString(); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected String defaultAction(Element e, Void p) { + return ""; + } + }.visit(member); + } + + /** + * Add the modifier for the member. The modifiers are ordered as specified + * by The Java Language Specification. + * + * @param member the member for which teh modifier will be added. + * @param htmltree the content tree to which the modifier information will be added. + */ + protected void addModifiers(Element member, Content htmltree) { + Set set = new TreeSet<>(member.getModifiers()); + + // remove the ones we really don't need + set.remove(NATIVE); + set.remove(SYNCHRONIZED); + set.remove(STRICTFP); + + // According to JLS, we should not be showing public modifier for + // interface methods. + if ((utils.isField(member) || utils.isMethod(member)) + && writer instanceof ClassWriterImpl + && utils.isInterface(((ClassWriterImpl) writer).getTypeElement())) { + // Remove the implicit abstract and public modifiers + if (utils.isMethod(member) && utils.isInterface(member.getEnclosingElement())) { + set.remove(ABSTRACT); + set.remove(PUBLIC); + } + if (!utils.isMethod(member)) { + set.remove(PUBLIC); + } + } + if (!set.isEmpty()) { + String mods = set.stream().map(m -> m.toString()).collect(Collectors.joining(" ")); + htmltree.addContent(mods); + htmltree.addContent(writer.getSpace()); + } + } + + protected String makeSpace(int len) { + if (len <= 0) { + return ""; + } + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + sb.append(' '); + } + return sb.toString(); + } + + /** + * Add the modifier and type for the member in the member summary. + * + * @param member the member to add the type for + * @param type the type to add + * @param tdSummaryType the content tree to which the modified and type will be added + */ + protected void addModifierAndType(Element member, TypeMirror type, + Content tdSummaryType) { + HtmlTree code = new HtmlTree(HtmlTag.CODE); + addModifier(member, code); + if (type == null) { + code.addContent(utils.isClass(member) ? "class" : "interface"); + code.addContent(writer.getSpace()); + } else { + List list = utils.isExecutableElement(member) + ? ((ExecutableElement)member).getTypeParameters() + : null; + if (list != null && !list.isEmpty()) { + Content typeParameters = ((AbstractExecutableMemberWriter) this) + .getTypeParameters((ExecutableElement)member); + code.addContent(typeParameters); + //Code to avoid ugly wrapping in member summary table. + if (typeParameters.charCount() > 10) { + code.addContent(new HtmlTree(HtmlTag.BR)); + } else { + code.addContent(writer.getSpace()); + } + code.addContent( + writer.getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type))); + } else { + code.addContent( + writer.getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.SUMMARY_RETURN_TYPE, type))); + } + + } + tdSummaryType.addContent(code); + } + + /** + * Add the modifier for the member. + * + * @param member the member to add the type for + * @param code the content tree to which the modified will be added + */ + private void addModifier(Element member, Content code) { + if (utils.isProtected(member)) { + code.addContent("protected "); + } else if (utils.isPrivate(member)) { + code.addContent("private "); + } else if (!utils.isPublic(member)) { // Package private + code.addContent(configuration.getText("doclet.Package_private")); + code.addContent(" "); + } + boolean isAnnotatedTypeElement = utils.isAnnotationType(member.getEnclosingElement()); + if (!isAnnotatedTypeElement && utils.isMethod(member)) { + if (!utils.isInterface(member.getEnclosingElement()) && utils.isAbstract(member)) { + code.addContent("abstract "); + } + if (utils.isDefault(member)) { + code.addContent("default "); + } + } + if (utils.isStatic(member)) { + code.addContent("static "); + } + } + + /** + * Add the deprecated information for the given member. + * + * @param member the member being documented. + * @param contentTree the content tree to which the deprecated information will be added. + */ + protected void addDeprecatedInfo(Element member, Content contentTree) { + Content output = (new DeprecatedTaglet()).getTagletOutput(member, + writer.getTagletWriterInstance(false)); + if (!output.isEmpty()) { + Content deprecatedContent = output; + Content div = HtmlTree.DIV(HtmlStyle.block, deprecatedContent); + contentTree.addContent(div); + } + } + + /** + * Add the comment for the given member. + * + * @param member the member being documented. + * @param htmltree the content tree to which the comment will be added. + */ + protected void addComment(Element member, Content htmltree) { + if (!utils.getBody(member).isEmpty()) { + writer.addInlineComment(member, htmltree); + } + } + + protected String name(Element member) { + return utils.getSimpleName(member); + } + + /** + * Get the header for the section. + * + * @param member the member being documented. + * @return a header content for the section. + */ + protected Content getHead(Element member) { + Content memberContent = new StringContent(name(member)); + Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, memberContent); + return heading; + } + + /** + * Return true if the given ProgramElement is inherited + * by the class that is being documented. + * + * @param ped The ProgramElement being checked. + * return true if the ProgramElement is being inherited and + * false otherwise. + */ + protected boolean isInherited(Element ped){ + return (!utils.isPrivate(ped) && + (!utils.isPackagePrivate(ped) || + ped.getEnclosingElement().equals(ped.getEnclosingElement()))); + } + + /** + * Add deprecated information to the documentation tree + * + * @param deprmembers list of deprecated members + * @param headingKey the caption for the deprecated members table + * @param tableSummary the summary for the deprecated members table + * @param tableHeader table headers for the deprecated members table + * @param contentTree the content tree to which the deprecated members table will be added + */ + protected void addDeprecatedAPI(Collection deprmembers, String headingKey, + String tableSummary, List tableHeader, Content contentTree) { + if (deprmembers.size() > 0) { + Content caption = writer.getTableCaption(configuration.getResource(headingKey)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) + : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); + table.addContent(writer.getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (Element member : deprmembers) { + HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, getDeprecatedLink(member)); + List deprTrees = utils.getBlockTags(member, DocTree.Kind.DEPRECATED); + if (!deprTrees.isEmpty()) { + writer.addInlineDeprecatedComment(member, deprTrees.get(0), td); + } + HtmlTree tr = HtmlTree.TR(td); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + Content ul = HtmlTree.UL(HtmlStyle.blockList, li); + contentTree.addContent(ul); + } + } + + /** + * Add use information to the documentation tree. + * + * @param mems list of program elements for which the use information will be added + * @param heading the section heading + * @param tableSummary the summary for the use table + * @param contentTree the content tree to which the use information will be added + */ + protected void addUseInfo(List mems, + Content heading, String tableSummary, Content contentTree) { + if (mems == null || mems.isEmpty()) { + return; + } + List members = mems; + boolean printedUseTableHeader = false; + if (members.size() > 0) { + Content caption = writer.getTableCaption(heading); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.useSummary, caption) + : HtmlTree.TABLE(HtmlStyle.useSummary, tableSummary, caption); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (Element element : members) { + TypeElement te = utils.getEnclosingTypeElement(element); + if (!printedUseTableHeader) { + table.addContent(writer.getSummaryTableHeader( + this.getSummaryTableHeader(element), "col")); + printedUseTableHeader = true; + } + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + HtmlTree tdFirst = new HtmlTree(HtmlTag.TD); + tdFirst.addStyle(HtmlStyle.colFirst); + writer.addSummaryType(this, element, tdFirst); + tr.addContent(tdFirst); + HtmlTree tdLast = new HtmlTree(HtmlTag.TD); + tdLast.addStyle(HtmlStyle.colLast); + if (te != null + && !utils.isConstructor(element) + && !utils.isClass(element) + && !utils.isInterface(element) + && !utils.isAnnotationType(element)) { + HtmlTree name = new HtmlTree(HtmlTag.SPAN); + name.addStyle(HtmlStyle.typeNameLabel); + name.addContent(name(te) + "."); + tdLast.addContent(name); + } + addSummaryLink(utils.isClass(element) || utils.isInterface(element) + ? LinkInfoImpl.Kind.CLASS_USE + : LinkInfoImpl.Kind.MEMBER, + te, element, tdLast); + writer.addSummaryLinkComment(this, element, tdLast); + tr.addContent(tdLast); + tbody.addContent(tr); + } + table.addContent(tbody); + contentTree.addContent(table); + } + } + + /** + * Add the navigation detail link. + * + * @param members the members to be linked + * @param liNav the content tree to which the navigation detail link will be added + */ + protected void addNavDetailLink(SortedSet members, Content liNav) { + addNavDetailLink(!members.isEmpty(), liNav); + } + + /** + * Add the navigation summary link. + * + * @param members members to be linked + * @param visibleMemberMap the visible inherited members map + * @param liNav the content tree to which the navigation summary link will be added + */ + protected void addNavSummaryLink(SortedSet members, + VisibleMemberMap visibleMemberMap, Content liNav) { + if (!members.isEmpty()) { + liNav.addContent(getNavSummaryLink(null, true)); + return; + } + + TypeElement superClass = utils.getSuperClass(typeElement); + while (superClass != null) { + if (visibleMemberMap.hasMembersFor(superClass)) { + liNav.addContent(getNavSummaryLink(superClass, true)); + return; + } + superClass = utils.getSuperClass(superClass); + } + liNav.addContent(getNavSummaryLink(null, false)); + } + + protected void serialWarning(Element e, String key, String a1, String a2) { + if (configuration.serialwarn) { + configuration.getDocletSpecificMsg().warning(e, key, a1, a2); + } + } + + /** + * Add the member summary for the given class. + * + * @param tElement the class that is being documented + * @param member the member being documented + * @param firstSentenceTags the first sentence tags to be added to the summary + * @param tableContents the list of contents to which the documentation will be added + * @param counter the counter for determining id and style for the table row + */ + public void addMemberSummary(TypeElement tElement, Element member, + List firstSentenceTags, List tableContents, int counter) { + HtmlTree tdSummaryType = new HtmlTree(HtmlTag.TD); + tdSummaryType.addStyle(HtmlStyle.colFirst); + writer.addSummaryType(this, member, tdSummaryType); + HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); + setSummaryColumnStyle(tdSummary); + addSummaryLink(tElement, member, tdSummary); + writer.addSummaryLinkComment(this, member, firstSentenceTags, tdSummary); + HtmlTree tr = HtmlTree.TR(tdSummaryType); + tr.addContent(tdSummary); + if (utils.isMethod(member) && !utils.isAnnotationType(member)) { + int methodType = utils.isStatic(member) ? MethodTypes.STATIC.value() : + MethodTypes.INSTANCE.value(); + if (utils.isInterface(member.getEnclosingElement())) { + methodType = utils.isAbstract(member) + ? methodType | MethodTypes.ABSTRACT.value() + : methodType | MethodTypes.DEFAULT.value(); + } else { + methodType = utils.isAbstract(member) + ? methodType | MethodTypes.ABSTRACT.value() + : methodType | MethodTypes.CONCRETE.value(); + } + if (utils.isDeprecated(member) || utils.isDeprecated(typeElement)) { + methodType = methodType | MethodTypes.DEPRECATED.value(); + } + methodTypesOr = methodTypesOr | methodType; + String tableId = "i" + counter; + typeMap.put(tableId, methodType); + tr.addAttr(HtmlAttr.ID, tableId); + } + if (counter%2 == 0) + tr.addStyle(HtmlStyle.altColor); + else + tr.addStyle(HtmlStyle.rowColor); + tableContents.add(tr); + } + + /** + * Generate the method types set and return true if the method summary table + * needs to show tabs. + * + * @return true if the table should show tabs + */ + public boolean showTabs() { + int value; + for (MethodTypes type : EnumSet.allOf(MethodTypes.class)) { + value = type.value(); + if ((value & methodTypesOr) == value) { + methodTypes.add(type); + } + } + boolean showTabs = methodTypes.size() > 1; + if (showTabs) { + methodTypes.add(MethodTypes.ALL); + } + return showTabs; + } + + /** + * Set the style for the summary column. + * + * @param tdTree the column for which the style will be set + */ + public void setSummaryColumnStyle(HtmlTree tdTree) { + tdTree.addStyle(HtmlStyle.colLast); + } + + /** + * Add inherited member summary for the given class and member. + * + * @param tElement the class the inherited member belongs to + * @param nestedClass the inherited member that is summarized + * @param isFirst true if this is the first member in the list + * @param isLast true if this is the last member in the list + * @param linksTree the content tree to which the summary will be added + */ + public void addInheritedMemberSummary(TypeElement tElement, + Element nestedClass, boolean isFirst, boolean isLast, + Content linksTree) { + writer.addInheritedMemberSummary(this, tElement, nestedClass, isFirst, + linksTree); + } + + /** + * Get the inherited summary header for the given class. + * + * @param tElement the class the inherited member belongs to + * @return a content tree for the inherited summary header + */ + public Content getInheritedSummaryHeader(TypeElement tElement) { + Content inheritedTree = writer.getMemberTreeHeader(); + writer.addInheritedSummaryHeader(this, tElement, inheritedTree); + return inheritedTree; + } + + /** + * Get the inherited summary links tree. + * + * @return a content tree for the inherited summary links + */ + public Content getInheritedSummaryLinksTree() { + return new HtmlTree(HtmlTag.CODE); + } + + /** + * Get the summary table tree for the given class. + * + * @param tElement the class for which the summary table is generated + * @param tableContents list of contents to be displayed in the summary table + * @return a content tree for the summary table + */ + public Content getSummaryTableTree(TypeElement tElement, List tableContents) { + return writer.getSummaryTableTree(this, tElement, tableContents, showTabs()); + } + + /** + * Get the member tree to be documented. + * + * @param memberTree the content tree of member to be documented + * @return a content tree that will be added to the class documentation + */ + public Content getMemberTree(Content memberTree) { + return writer.getMemberTree(memberTree); + } + + /** + * Get the member tree to be documented. + * + * @param memberTree the content tree of member to be documented + * @param isLastContent true if the content to be added is the last content + * @return a content tree that will be added to the class documentation + */ + public Content getMemberTree(Content memberTree, boolean isLastContent) { + if (isLastContent) + return HtmlTree.UL(HtmlStyle.blockListLast, memberTree); + else + return HtmlTree.UL(HtmlStyle.blockList, memberTree); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractPackageIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractPackageIndexWriter.java new file mode 100644 index 00000000000..26061a83b8d --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractPackageIndexWriter.java @@ -0,0 +1,202 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; + +/** + * Abstract class to generate the overview files in + * Frame and Non-Frame format. This will be sub-classed by to + * generate overview-frame.html as well as overview-summary.html. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public abstract class AbstractPackageIndexWriter extends HtmlDocletWriter { + + /** + * A Set of Packages to be documented. + */ + protected SortedSet packages; + + /** + * Constructor. Also initializes the packages variable. + * + * @param configuration The current configuration + * @param filename Name of the package index file to be generated. + */ + public AbstractPackageIndexWriter(ConfigurationImpl configuration, + DocPath filename) throws IOException { + super(configuration, filename); + packages = configuration.packages; + } + + /** + * Adds the navigation bar header to the documentation tree. + * + * @param body the document tree to which the navigation bar header will be added + */ + protected abstract void addNavigationBarHeader(Content body); + + /** + * Adds the navigation bar footer to the documentation tree. + * + * @param body the document tree to which the navigation bar footer will be added + */ + protected abstract void addNavigationBarFooter(Content body); + + /** + * Adds the overview header to the documentation tree. + * + * @param body the document tree to which the overview header will be added + */ + protected abstract void addOverviewHeader(Content body); + + /** + * Adds the packages list to the documentation tree. + * + * @param packages a collection of packagedoc objects + * @param text caption for the table + * @param tableSummary summary for the table + * @param body the document tree to which the packages list will be added + */ + protected abstract void addPackagesList(Collection packages, String text, + String tableSummary, Content body); + + /** + * Generate and prints the contents in the package index file. Call appropriate + * methods from the sub-class in order to generate Frame or Non + * Frame format. + * + * @param title the title of the window. + * @param includeScript boolean set true if windowtitle script is to be included + */ + protected void buildPackageIndexFile(String title, boolean includeScript) throws IOException { + String windowOverview = configuration.getText(title); + Content body = getBody(includeScript, getWindowTitle(windowOverview)); + addNavigationBarHeader(body); + addOverviewHeader(body); + addIndex(body); + addOverview(body); + addNavigationBarFooter(body); + printHtmlDocument(configuration.metakeywords.getOverviewMetaKeywords(title, + configuration.doctitle), includeScript, body); + } + + /** + * Default to no overview, override to add overview. + * + * @param body the document tree to which the overview will be added + */ + protected void addOverview(Content body) throws IOException { + } + + /** + * Adds the frame or non-frame package index to the documentation tree. + * + * @param body the document tree to which the index will be added + */ + protected void addIndex(Content body) { + addIndexContents(packages, "doclet.Package_Summary", + configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Package_Summary"), + configuration.getText("doclet.packages")), body); + } + + /** + * Adds package index contents. Call appropriate methods from + * the sub-classes. Adds it to the body HtmlTree + * + * @param packages a collection of packages to be documented + * @param text string which will be used as the heading + * @param tableSummary summary for the table + * @param body the document tree to which the index contents will be added + */ + protected void addIndexContents(Collection packages, String text, + String tableSummary, Content body) { + if (!packages.isEmpty()) { + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.NAV)) + ? HtmlTree.NAV() + : new HtmlTree(HtmlTag.DIV); + htmlTree.addStyle(HtmlStyle.indexNav); + HtmlTree ul = new HtmlTree(HtmlTag.UL); + addAllClassesLink(ul); + htmlTree.addContent(ul); + body.addContent(htmlTree); + addPackagesList(packages, text, tableSummary, body); + } + } + + /** + * Adds the doctitle to the documentation tree, if it is specified on the command line. + * + * @param body the document tree to which the title will be added + */ + protected void addConfigurationTitle(Content body) { + if (configuration.doctitle.length() > 0) { + Content title = new RawHtml(configuration.doctitle); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, + HtmlStyle.title, title); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + body.addContent(div); + } + } + + /** + * Returns highlighted "Overview", in the navigation bar as this is the + * overview page. + * + * @return a Content object to be added to the documentation tree + */ + protected Content getNavLinkContents() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, overviewLabel); + return li; + } + + /** + * Do nothing. This will be overridden. + * + * @param div the document tree to which the all classes link will be added + */ + protected void addAllClassesLink(Content div) { + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java new file mode 100644 index 00000000000..68ede24c0cf --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractTreeWriter.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Abstract class to print the class hierarchy page for all the Classes. This + * is sub-classed by {@link PackageTreeWriter} and {@link TreeWriter} to + * generate the Package Tree and global Tree(for all the classes and packages) + * pages. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + */ +public abstract class AbstractTreeWriter extends HtmlDocletWriter { + + /** + * The class and interface tree built by using {@link ClassTree} + */ + protected final ClassTree classtree; + + /** + * Constructor initializes classtree variable. This constructor will be used + * while generating global tree file "overview-tree.html". + * + * @param configuration The current configuration + * @param filename File to be generated. + * @param classtree Tree built by {@link ClassTree}. + * @throws IOException + * @throws DocletAbortException + */ + protected AbstractTreeWriter(ConfigurationImpl configuration, + DocPath filename, ClassTree classtree) + throws IOException { + super(configuration, filename); + this.classtree = classtree; + } + + /** + * Add each level of the class tree. For each sub-class or + * sub-interface indents the next level information. + * Recurses itself to add sub-classes info. + * + * @param parent the superclass or superinterface of the sset + * @param collection a collection of the sub-classes at this level + * @param isEnum true if we are generating a tree for enums + * @param contentTree the content tree to which the level information will be added + */ + protected void addLevelInfo(TypeElement parent, Collection collection, + boolean isEnum, Content contentTree) { + if (!collection.isEmpty()) { + Content ul = new HtmlTree(HtmlTag.UL); + for (TypeElement local : collection) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.circle); + addPartialInfo(local, li); + addExtendsImplements(parent, local, li); + addLevelInfo(local, classtree.directSubClasses(local, isEnum), + isEnum, li); // Recurse + ul.addContent(li); + } + contentTree.addContent(ul); + } + } + + /** + * Add the heading for the tree depending upon tree type if it's a + * Class Tree or Interface tree. + * + * @param sset classes which are at the most base level, all the + * other classes in this run will derive from these classes + * @param heading heading for the tree + * @param div the content tree to which the tree will be added + */ + protected void addTree(SortedSet sset, String heading, HtmlTree div) { + addTree(sset, heading, div, false); + } + + protected void addTree(SortedSet sset, String heading, + HtmlTree div, boolean isEnums) { + if (!sset.isEmpty()) { + TypeElement firstTypeElement = sset.first(); + Content headingContent = getResource(heading); + Content sectionHeading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, true, + headingContent); + HtmlTree htmlTree; + if (configuration.allowTag(HtmlTag.SECTION)) { + htmlTree = HtmlTree.SECTION(sectionHeading); + } else { + div.addContent(sectionHeading); + htmlTree = div; + } + addLevelInfo(!utils.isInterface(firstTypeElement) ? firstTypeElement : null, + sset, isEnums, htmlTree); + if (configuration.allowTag(HtmlTag.SECTION)) { + div.addContent(htmlTree); + } + } + } + + /** + * Add information regarding the classes which this class extends or + * implements. + * + * @param parent the parent class of the class being documented + * @param typeElement the TypeElement under consideration + * @param contentTree the content tree to which the information will be added + */ + protected void addExtendsImplements(TypeElement parent, TypeElement typeElement, + Content contentTree) { + SortedSet interfaces = new TreeSet<>(utils.makeGeneralPurposeComparator()); + typeElement.getInterfaces().stream().forEach((t) -> { + interfaces.add(utils.asTypeElement(t)); + }); + if (interfaces.size() > (utils.isInterface(typeElement) ? 1 : 0)) { + boolean isFirst = true; + for (TypeElement intf : interfaces) { + if (parent != intf) { + if (utils.isPublic(intf) || utils.isLinkable(intf)) { + if (isFirst) { + isFirst = false; + if (utils.isInterface(typeElement)) { + contentTree.addContent(" ("); + contentTree.addContent(getResource("doclet.also")); + contentTree.addContent(" extends "); + } else { + contentTree.addContent(" (implements "); + } + } else { + contentTree.addContent(", "); + } + addPreQualifiedClassLink(LinkInfoImpl.Kind.TREE, intf, contentTree); + } + } + } + if (!isFirst) { + contentTree.addContent(")"); + } + } + } + + /** + * Add information about the class kind, if it's a "class" or "interface". + * + * @param typeElement the class being documented + * @param contentTree the content tree to which the information will be added + */ + protected void addPartialInfo(TypeElement typeElement, Content contentTree) { + addPreQualifiedStrongClassLink(LinkInfoImpl.Kind.TREE, typeElement, contentTree); + } + + /** + * Get the tree label for the navigation bar. + * + * @return a content tree for the tree label + */ + protected Content getNavLinkTree() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, treeLabel); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java new file mode 100644 index 00000000000..6ba6f0afc4c --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; + + +/** + * Generate the file with list of all the classes in this run. This page will be + * used in the left-hand bottom frame, when "All Classes" link is clicked in + * the left-hand top frame. The name of the generated file is + * "allclasses-frame.html". + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Doug Kramer + * @author Bhavesh Patel (Modified) + */ +public class AllClassesFrameWriter extends HtmlDocletWriter { + + /** + * Index of all the classes. + */ + protected IndexBuilder indexbuilder; + + /** + * BR tag to be used within a document tree. + */ + final HtmlTree BR = new HtmlTree(HtmlTag.BR); + + /** + * Construct AllClassesFrameWriter object. Also initializes the indexbuilder + * variable in this class. + * @param configuration The current configuration + * @param filename Path to the file which is getting generated. + * @param indexbuilder Unicode based Index from {@link IndexBuilder} + * @throws IOException + * @throws DocletAbortException + */ + public AllClassesFrameWriter(ConfigurationImpl configuration, + DocPath filename, IndexBuilder indexbuilder) + throws IOException { + super(configuration, filename); + this.indexbuilder = indexbuilder; + } + + /** + * Create AllClassesFrameWriter object. Then use it to generate the + * "allclasses-frame.html" file. Generate the file in the current or the + * destination directory. + * + * @param indexbuilder IndexBuilder object for all classes index. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, + IndexBuilder indexbuilder) { + AllClassesFrameWriter allclassgen; + DocPath filename = DocPaths.ALLCLASSES_FRAME; + try { + allclassgen = new AllClassesFrameWriter(configuration, + filename, indexbuilder); + allclassgen.buildAllClassesFile(true); + allclassgen.close(); + filename = DocPaths.ALLCLASSES_NOFRAME; + allclassgen = new AllClassesFrameWriter(configuration, + filename, indexbuilder); + allclassgen.buildAllClassesFile(false); + allclassgen.close(); + } catch (IOException exc) { + configuration.standardmessage. + error("doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Print all the classes in the file. + * @param wantFrames True if we want frames. + */ + protected void buildAllClassesFile(boolean wantFrames) throws IOException { + String label = configuration.getText("doclet.All_Classes"); + Content body = getBody(false, getWindowTitle(label)); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, + HtmlStyle.bar, allclassesLabel); + body.addContent(heading); + Content ul = new HtmlTree(HtmlTag.UL); + // Generate the class links and add it to the tdFont tree. + addAllClasses(ul, wantFrames); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN(HtmlStyle.indexContainer, ul) + : HtmlTree.DIV(HtmlStyle.indexContainer, ul); + body.addContent(htmlTree); + printHtmlDocument(null, false, body); + } + + /** + * Use the sorted index of all the classes and add all the classes to the + * content list. + * + * @param content HtmlTree content to which all classes information will be added + * @param wantFrames True if we want frames. + */ + protected void addAllClasses(Content content, boolean wantFrames) { + for (Character unicode : indexbuilder.index()) { + addContents(indexbuilder.getMemberList(unicode), wantFrames, content); + } + } + + /** + * Given a list of classes, generate links for each class or interface. + * If the class kind is interface, print it in the italics font. Also all + * links should target the right-hand frame. If clicked on any class name + * in this page, appropriate class page should get opened in the right-hand + * frame. + * + * @param classlist Sorted list of classes. + * @param wantFrames True if we want frames. + * @param content HtmlTree content to which the links will be added + */ + protected void addContents(Iterable classlist, boolean wantFrames, + Content content) { + for (Element element : classlist) { + TypeElement typeElement = (TypeElement)element; + if (!utils.isCoreClass(typeElement)) { + continue; + } + Content label = interfaceName(typeElement, false); + Content linkContent; + if (wantFrames) { + linkContent = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.ALL_CLASSES_FRAME, typeElement).label(label).target("classFrame")); + } else { + linkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, typeElement).label(label)); + } + Content li = HtmlTree.LI(linkContent); + content.addContent(li); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java new file mode 100644 index 00000000000..0bb63a68f93 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeFieldWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + + +/** + * Writes annotation type field documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class AnnotationTypeFieldWriterImpl extends AbstractMemberWriter + implements AnnotationTypeFieldWriter, MemberSummaryWriter { + + /** + * Construct a new AnnotationTypeFieldWriterImpl. + * + * @param writer the writer that will write the output. + * @param annotationType the AnnotationType that holds this member. + */ + public AnnotationTypeFieldWriterImpl(SubWriterHolderWriter writer, + TypeElement annotationType) { + super(writer, annotationType); + } + + /** + * {@inheritDoc} + */ + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent( + HtmlConstants.START_OF_ANNOTATION_TYPE_FIELD_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public Content getMemberTreeHeader() { + return writer.getMemberTreeHeader(); + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + public void addAnnotationFieldDetailsMarker(Content memberDetails) { + memberDetails.addContent(HtmlConstants.START_OF_ANNOTATION_TYPE_FIELD_DETAILS); + } + + /** + * {@inheritDoc} + */ + public void addAnnotationDetailsTreeHeader(TypeElement typeElement, + Content memberDetailsTree) { + if (!writer.printedAnnotationFieldHeading) { + memberDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.ANNOTATION_TYPE_FIELD_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.fieldDetailsLabel); + memberDetailsTree.addContent(heading); + writer.printedAnnotationFieldHeading = true; + } + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDocTreeHeader(Element member, + Content annotationDetailsTree) { + annotationDetailsTree.addContent( + writer.getMarkerAnchor(name(member))); + Content annotationDocTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(name(member)); + annotationDocTree.addContent(heading); + return annotationDocTree; + } + + /** + * {@inheritDoc} + */ + public Content getSignature(Element member) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(member, pre); + addModifiers(member, pre); + Content link = + writer.getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.MEMBER, getType(member))); + pre.addContent(link); + pre.addContent(writer.getSpace()); + if (configuration.linksource) { + Content memberName = new StringContent(name(member)); + writer.addSrcLink(member, memberName, pre); + } else { + addName(name(member), pre); + } + return pre; + } + + /** + * {@inheritDoc} + */ + public void addDeprecated(Element member, Content annotationDocTree) { + addDeprecatedInfo(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public void addComments(Element member, Content annotationDocTree) { + addComment(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public void addTags(Element member, Content annotationDocTree) { + writer.addTagsInfo(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDetails(Content annotationDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(annotationDetailsTree)); + return htmlTree; + } + return getMemberTree(annotationDetailsTree); + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDoc(Content annotationDocTree, + boolean isLastContent) { + return getMemberTree(annotationDocTree, isLastContent); + } + + /** + * Close the writer. + */ + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Field_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Field_Summary"), + configuration.getText("doclet.fields")); + } + + /** + * {@inheritDoc} + */ + public Content getCaption() { + return configuration.getResource("doclet.Fields"); + } + + /** + * {@inheritDoc} + */ + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Fields"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.ANNOTATION_TYPE_FIELD_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, member, name(member), false)); + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + protected void addInheritedSummaryLink(TypeElement typeElement, + Element member, Content linksTree) { + //Not applicable. + } + + /** + * {@inheritDoc} + */ + protected void addSummaryType(Element member, Content tdSummaryType) { + addModifierAndType(member, getType(member), tdSummaryType); + } + + /** + * {@inheritDoc} + */ + protected Content getDeprecatedLink(Element member) { + return writer.getDocLink(LinkInfoImpl.Kind.MEMBER, + member, utils.getFullyQualifiedName(member)); + } + + /** + * {@inheritDoc} + */ + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + return writer.getHyperLink( + SectionName.ANNOTATION_TYPE_FIELD_SUMMARY, + writer.getResource("doclet.navField")); + } else { + return writer.getResource("doclet.navField"); + } + } + + /** + * {@inheritDoc} + */ + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.ANNOTATION_TYPE_FIELD_DETAIL, + writer.getResource("doclet.navField"))); + } else { + liNav.addContent(writer.getResource("doclet.navField")); + } + } + private TypeMirror getType(Element member) { + if (utils.isConstructor(member)) + return null; + if (utils.isExecutableElement(member)) + return utils.getReturnType((ExecutableElement)member); + return member.asType(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeOptionalMemberWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeOptionalMemberWriterImpl.java new file mode 100644 index 00000000000..221e467101b --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeOptionalMemberWriterImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeOptionalMemberWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + + +/** + * Writes annotation type optional member documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class AnnotationTypeOptionalMemberWriterImpl extends + AnnotationTypeRequiredMemberWriterImpl + implements AnnotationTypeOptionalMemberWriter, MemberSummaryWriter { + + /** + * Construct a new AnnotationTypeOptionalMemberWriterImpl. + * + * @param writer the writer that will write the output. + * @param annotationType the AnnotationType that holds this member. + */ + public AnnotationTypeOptionalMemberWriterImpl(SubWriterHolderWriter writer, + TypeElement annotationType) { + super(writer, annotationType); + } + + /** + * {@inheritDoc} + */ + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent( + HtmlConstants.START_OF_ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + public void addDefaultValueInfo(Element member, Content annotationDocTree) { + if (utils.isAnnotationType(member)) { + ExecutableElement ee = (ExecutableElement)member; + AnnotationValue value = ee.getDefaultValue(); + if (value != null) { + Content dt = HtmlTree.DT(writer.getResource("doclet.Default")); + Content dl = HtmlTree.DL(dt); + Content dd = HtmlTree.DD(new StringContent(value.toString())); + dl.addContent(dd); + annotationDocTree.addContent(dl); + } + } + } + + /** + * {@inheritDoc} + */ + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Annotation_Type_Optional_Member_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Annotation_Type_Optional_Member_Summary"), + configuration.getText("doclet.annotation_type_optional_members")); + } + + /** + * {@inheritDoc} + */ + public Content getCaption() { + return configuration.getResource("doclet.Annotation_Type_Optional_Members"); + } + + /** + * {@inheritDoc} + */ + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Annotation_Type_Optional_Member"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.ANNOTATION_TYPE_OPTIONAL_ELEMENT_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + return writer.getHyperLink( + SectionName.ANNOTATION_TYPE_OPTIONAL_ELEMENT_SUMMARY, + writer.getResource("doclet.navAnnotationTypeOptionalMember")); + } else { + return writer.getResource("doclet.navAnnotationTypeOptionalMember"); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java new file mode 100644 index 00000000000..531d0a033f5 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeRequiredMemberWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + + +/** + * Writes annotation type required member documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class AnnotationTypeRequiredMemberWriterImpl extends AbstractMemberWriter + implements AnnotationTypeRequiredMemberWriter, MemberSummaryWriter { + + /** + * Construct a new AnnotationTypeRequiredMemberWriterImpl. + * + * @param writer the writer that will write the output. + * @param annotationType the AnnotationType that holds this member. + */ + public AnnotationTypeRequiredMemberWriterImpl(SubWriterHolderWriter writer, + TypeElement annotationType) { + super(writer, annotationType); + } + + /** + * {@inheritDoc} + */ + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent( + HtmlConstants.START_OF_ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public Content getMemberTreeHeader() { + return writer.getMemberTreeHeader(); + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + public void addAnnotationDetailsMarker(Content memberDetails) { + memberDetails.addContent(HtmlConstants.START_OF_ANNOTATION_TYPE_DETAILS); + } + + /** + * {@inheritDoc} + */ + public void addAnnotationDetailsTreeHeader(TypeElement classDoc, + Content memberDetailsTree) { + if (!writer.printedAnnotationHeading) { + memberDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.ANNOTATION_TYPE_ELEMENT_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.annotationTypeDetailsLabel); + memberDetailsTree.addContent(heading); + writer.printedAnnotationHeading = true; + } + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDocTreeHeader(Element member, + Content annotationDetailsTree) { + String simpleName = name(member); + annotationDetailsTree.addContent(writer.getMarkerAnchor(simpleName + + utils.signature((ExecutableElement) member))); + Content annotationDocTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(simpleName); + annotationDocTree.addContent(heading); + return annotationDocTree; + } + + /** + * {@inheritDoc} + */ + public Content getSignature(Element member) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(member, pre); + addModifiers(member, pre); + Content link = + writer.getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.MEMBER, getType(member))); + pre.addContent(link); + pre.addContent(writer.getSpace()); + if (configuration.linksource) { + Content memberName = new StringContent(name(member)); + writer.addSrcLink(member, memberName, pre); + } else { + addName(name(member), pre); + } + return pre; + } + + /** + * {@inheritDoc} + */ + public void addDeprecated(Element member, Content annotationDocTree) { + addDeprecatedInfo(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public void addComments(Element member, Content annotationDocTree) { + addComment(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public void addTags(Element member, Content annotationDocTree) { + writer.addTagsInfo(member, annotationDocTree); + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDetails(Content annotationDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(annotationDetailsTree)); + return htmlTree; + } + return getMemberTree(annotationDetailsTree); + } + + /** + * {@inheritDoc} + */ + public Content getAnnotationDoc(Content annotationDocTree, + boolean isLastContent) { + return getMemberTree(annotationDocTree, isLastContent); + } + + /** + * Close the writer. + */ + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Annotation_Type_Required_Member_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Annotation_Type_Required_Member_Summary"), + configuration.getText("doclet.annotation_type_required_members")); + } + + /** + * {@inheritDoc} + */ + public Content getCaption() { + return configuration.getResource("doclet.Annotation_Type_Required_Members"); + } + + /** + * {@inheritDoc} + */ + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Annotation_Type_Required_Member"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.ANNOTATION_TYPE_REQUIRED_ELEMENT_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, member, name(member), false)); + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + protected void addInheritedSummaryLink(TypeElement typeElement, + Element member, Content linksTree) { + //Not applicable. + } + + /** + * {@inheritDoc} + */ + protected void addSummaryType(Element member, Content tdSummaryType) { + addModifierAndType(member, getType(member), tdSummaryType); + } + + /** + * {@inheritDoc} + */ + protected Content getDeprecatedLink(Element member) { + String name = utils.getFullyQualifiedName(member) + "." + member.getSimpleName(); + return writer.getDocLink(LinkInfoImpl.Kind.MEMBER, member, name); + } + + /** + * {@inheritDoc} + */ + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + return writer.getHyperLink( + SectionName.ANNOTATION_TYPE_REQUIRED_ELEMENT_SUMMARY, + writer.getResource("doclet.navAnnotationTypeRequiredMember")); + } else { + return writer.getResource("doclet.navAnnotationTypeRequiredMember"); + } + } + + /** + * {@inheritDoc} + */ + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.ANNOTATION_TYPE_ELEMENT_DETAIL, + writer.getResource("doclet.navAnnotationTypeMember"))); + } else { + liNav.addContent(writer.getResource("doclet.navAnnotationTypeMember")); + } + } + + private TypeMirror getType(Element member) { + return utils.isExecutableElement(member) + ? utils.getReturnType((ExecutableElement) member) + : member.asType(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java new file mode 100644 index 00000000000..3d6a248da57 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.IOException; +import java.util.List; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.builders.MemberSummaryBuilder; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; + +/** + * Generate the Class Information Page. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see java.util.Collections + * @see java.util.List + * @see java.util.ArrayList + * @see java.util.HashMap + * + * @author Atul M Dambalkar + * @author Robert Field + * @author Bhavesh Patel (Modified) + */ +public class AnnotationTypeWriterImpl extends SubWriterHolderWriter + implements AnnotationTypeWriter { + + protected TypeElement annotationType; + + protected TypeMirror prev; + + protected TypeMirror next; + + /** + * @param configuration the configuration + * @param annotationType the annotation type being documented. + * @param prevType the previous class that was documented. + * @param nextType the next class being documented. + * @throws java.lang.Exception + */ + public AnnotationTypeWriterImpl(ConfigurationImpl configuration, + TypeElement annotationType, TypeMirror prevType, TypeMirror nextType) + throws Exception { + super(configuration, DocPath.forClass(configuration.utils, annotationType)); + this.annotationType = annotationType; + configuration.currentTypeElement = annotationType; + this.prev = prevType; + this.next = nextType; + } + + /** + * Get this package link. + * + * @return a content tree for the package link + */ + @Override + protected Content getNavLinkPackage() { + Content linkContent = getHyperLink(DocPaths.PACKAGE_SUMMARY, + packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the class link. + * + * @return a content tree for the class link + */ + @Override + protected Content getNavLinkClass() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, classLabel); + return li; + } + + /** + * Get the class use link. + * + * @return a content tree for the class use link + */ + @Override + protected Content getNavLinkClassUse() { + Content linkContent = getHyperLink(DocPaths.CLASS_USE.resolve(filename), useLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link to previous class. + * + * @return a content tree for the previous class link + */ + @Override + public Content getNavLinkPrevious() { + Content li; + if (prev != null) { + Content prevLink = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS, utils.asTypeElement(prev)) + .label(prevclassLabel).strong(true)); + li = HtmlTree.LI(prevLink); + } + else + li = HtmlTree.LI(prevclassLabel); + return li; + } + + /** + * Get link to next class. + * + * @return a content tree for the next class link + */ + @Override + public Content getNavLinkNext() { + Content li; + if (next != null) { + Content nextLink = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS, utils.asTypeElement(next)) + .label(nextclassLabel).strong(true)); + li = HtmlTree.LI(nextLink); + } + else + li = HtmlTree.LI(nextclassLabel); + return li; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getHeader(String header) { + HtmlTree bodyTree = getBody(true, getWindowTitle(utils.getSimpleName(annotationType))); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + bodyTree.addContent(HtmlConstants.START_OF_CLASS_DATA); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.header); + PackageElement pkg = utils.containingPackage(annotationType); + if (!pkg.isUnnamed()) { + Content pkgNameContent = new StringContent(utils.getPackageName(pkg)); + Content pkgNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, pkgNameContent); + div.addContent(pkgNameDiv); + } + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_HEADER, annotationType); + Content headerContent = new StringContent(header); + Content heading = HtmlTree.HEADING(HtmlConstants.CLASS_PAGE_HEADING, true, + HtmlStyle.title, headerContent); + heading.addContent(getTypeParameterLinks(linkInfo)); + div.addContent(heading); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getAnnotationContentHeader() { + return getContentHeader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addFooter(Content contentTree) { + contentTree.addContent(HtmlConstants.END_OF_CLASS_DATA); + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : contentTree; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + contentTree.addContent(htmlTree); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void printDocument(Content contentTree) throws IOException { + printHtmlDocument(configuration.metakeywords.getMetaKeywords(annotationType), + true, contentTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getAnnotationInfoTreeHeader() { + return getMemberTreeHeader(); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getAnnotationInfo(Content annotationInfoTree) { + return getMemberTree(HtmlStyle.description, annotationInfoTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addAnnotationTypeSignature(String modifiers, Content annotationInfoTree) { + annotationInfoTree.addContent(new HtmlTree(HtmlTag.BR)); + Content pre = new HtmlTree(HtmlTag.PRE); + addAnnotationInfo(annotationType, pre); + pre.addContent(modifiers); + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_SIGNATURE, annotationType); + Content annotationName = new StringContent(utils.getSimpleName(annotationType)); + Content parameterLinks = getTypeParameterLinks(linkInfo); + if (configuration.linksource) { + addSrcLink(annotationType, annotationName, pre); + pre.addContent(parameterLinks); + } else { + Content span = HtmlTree.SPAN(HtmlStyle.memberNameLabel, annotationName); + span.addContent(parameterLinks); + pre.addContent(span); + } + annotationInfoTree.addContent(pre); + } + + /** + * {@inheritDoc} + */ + @Override + public void addAnnotationTypeDescription(Content annotationInfoTree) { + if(!configuration.nocomment) { + if (!utils.getBody(annotationType).isEmpty()) { + addInlineComment(annotationType, annotationInfoTree); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addAnnotationTypeTagInfo(Content annotationInfoTree) { + if(!configuration.nocomment) { + addTagsInfo(annotationType, annotationInfoTree); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addAnnotationTypeDeprecationInfo(Content annotationInfoTree) { + Content hr = new HtmlTree(HtmlTag.HR); + annotationInfoTree.addContent(hr); + List deprs = utils.getBlockTags(annotationType, DocTree.Kind.DEPRECATED); + if (utils.isDeprecated(annotationType)) { + CommentHelper ch = utils.getCommentHelper(annotationType); + Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + Content div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + if (!deprs.isEmpty()) { + + List commentTags = ch.getDescription(configuration, deprs.get(0)); + if (!commentTags.isEmpty()) { + div.addContent(getSpace()); + addInlineDeprecatedComment(annotationType, deprs.get(0), div); + } + } + annotationInfoTree.addContent(div); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavLinkTree() { + Content treeLinkContent = getHyperLink(DocPaths.PACKAGE_TREE, + treeLabel, "", ""); + Content li = HtmlTree.LI(treeLinkContent); + return li; + } + + /** + * Add summary details to the navigation bar. + * + * @param subDiv the content tree to which the summary detail links will be added + */ + @Override + protected void addSummaryDetailLinks(Content subDiv) { + try { + Content div = HtmlTree.DIV(getNavSummaryLinks()); + div.addContent(getNavDetailLinks()); + subDiv.addContent(div); + } catch (Exception e) { + throw new DocletAbortException(e); + } + } + + /** + * Get summary links for navigation bar. + * + * @return the content tree for the navigation summary links + * @throws java.lang.Exception + */ + protected Content getNavSummaryLinks() throws Exception { + Content li = HtmlTree.LI(summaryLabel); + li.addContent(getSpace()); + Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); + MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder) + configuration.getBuilderFactory().getMemberSummaryBuilder(this); + Content liNavField = new HtmlTree(HtmlTag.LI); + addNavSummaryLink(memberSummaryBuilder, + "doclet.navField", + VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS, liNavField); + addNavGap(liNavField); + ulNav.addContent(liNavField); + Content liNavReq = new HtmlTree(HtmlTag.LI); + addNavSummaryLink(memberSummaryBuilder, + "doclet.navAnnotationTypeRequiredMember", + VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED, liNavReq); + addNavGap(liNavReq); + ulNav.addContent(liNavReq); + Content liNavOpt = new HtmlTree(HtmlTag.LI); + addNavSummaryLink(memberSummaryBuilder, + "doclet.navAnnotationTypeOptionalMember", + VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL, liNavOpt); + ulNav.addContent(liNavOpt); + return ulNav; + } + + /** + * Add the navigation summary link. + * + * @param builder builder for the member to be documented + * @param label the label for the navigation + * @param type type to be documented + * @param liNav the content tree to which the navigation summary link will be added + */ + protected void addNavSummaryLink(MemberSummaryBuilder builder, + String label, VisibleMemberMap.Kind type, Content liNav) { + AbstractMemberWriter writer = ((AbstractMemberWriter) builder. + getMemberSummaryWriter(type)); + if (writer == null) { + liNav.addContent(getResource(label)); + } else { + liNav.addContent(writer.getNavSummaryLink(null, + ! builder.getVisibleMemberMap(type).noVisibleMembers())); + } + } + + /** + * Get detail links for the navigation bar. + * + * @return the content tree for the detail links + * @throws java.lang.Exception + */ + protected Content getNavDetailLinks() throws Exception { + Content li = HtmlTree.LI(detailLabel); + li.addContent(getSpace()); + Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); + MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder) + configuration.getBuilderFactory().getMemberSummaryBuilder(this); + AbstractMemberWriter writerField = + ((AbstractMemberWriter) memberSummaryBuilder. + getMemberSummaryWriter(VisibleMemberMap.Kind.ANNOTATION_TYPE_FIELDS)); + AbstractMemberWriter writerOptional = + ((AbstractMemberWriter) memberSummaryBuilder. + getMemberSummaryWriter(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_OPTIONAL)); + AbstractMemberWriter writerRequired = + ((AbstractMemberWriter) memberSummaryBuilder. + getMemberSummaryWriter(VisibleMemberMap.Kind.ANNOTATION_TYPE_MEMBER_REQUIRED)); + Content liNavField = new HtmlTree(HtmlTag.LI); + if (writerField != null) { + writerField.addNavDetailLink(!utils.getAnnotationFields(annotationType).isEmpty(), liNavField); + } else { + liNavField.addContent(getResource("doclet.navField")); + } + addNavGap(liNavField); + ulNav.addContent(liNavField); + if (writerOptional != null){ + Content liNavOpt = new HtmlTree(HtmlTag.LI); + writerOptional.addNavDetailLink(!annotationType.getAnnotationMirrors().isEmpty(), liNavOpt); + ulNav.addContent(liNavOpt); + } else if (writerRequired != null){ + Content liNavReq = new HtmlTree(HtmlTag.LI); + writerRequired.addNavDetailLink(!annotationType.getAnnotationMirrors().isEmpty(), liNavReq); + ulNav.addContent(liNavReq); + } else { + Content liNav = HtmlTree.LI(getResource("doclet.navAnnotationTypeMember")); + ulNav.addContent(liNav); + } + return ulNav; + } + + /** + * Add gap between navigation bar elements. + * + * @param liNav the content tree to which the gap will be added + */ + protected void addNavGap(Content liNav) { + liNav.addContent(getSpace()); + liNav.addContent("|"); + liNav.addContent(getSpace()); + } + + /** + * {@inheritDoc} + */ + @Override + public TypeElement getAnnotationTypeElement() { + return annotationType; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java new file mode 100644 index 00000000000..9e3834dffba --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java @@ -0,0 +1,554 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +/** + * Generate class usage information. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert G. Field + * @author Bhavesh Patel (Modified) + */ +public class ClassUseWriter extends SubWriterHolderWriter { + + final TypeElement typeElement; + Set pkgToPackageAnnotations = null; + final Map> pkgToClassTypeParameter; + final Map> pkgToClassAnnotations; + final Map> pkgToMethodTypeParameter; + final Map> pkgToMethodArgTypeParameter; + final Map> pkgToMethodReturnTypeParameter; + final Map> pkgToMethodAnnotations; + final Map> pkgToMethodParameterAnnotations; + final Map> pkgToFieldTypeParameter; + final Map> pkgToFieldAnnotations; + final Map> pkgToSubclass; + final Map> pkgToSubinterface; + final Map> pkgToImplementingClass; + final Map> pkgToField; + final Map> pkgToMethodReturn; + final Map> pkgToMethodArgs; + final Map> pkgToMethodThrows; + final Map> pkgToConstructorAnnotations; + final Map> pkgToConstructorParameterAnnotations; + final Map> pkgToConstructorArgs; + final Map> pkgToConstructorArgTypeParameter; + final Map> pkgToConstructorThrows; + final SortedSet pkgSet; + final MethodWriterImpl methodSubWriter; + final ConstructorWriterImpl constrSubWriter; + final FieldWriterImpl fieldSubWriter; + final NestedClassWriterImpl classSubWriter; + // Summary for various use tables. + final String classUseTableSummary; + final String subclassUseTableSummary; + final String subinterfaceUseTableSummary; + final String fieldUseTableSummary; + final String methodUseTableSummary; + final String constructorUseTableSummary; + + /** + * The HTML tree for main tag. + */ + protected HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * Constructor. + * + * @param filename the file to be generated. + * @throws IOException + * @throws DocletAbortException + */ + public ClassUseWriter(ConfigurationImpl configuration, + ClassUseMapper mapper, DocPath filename, + TypeElement typeElement) throws IOException { + super(configuration, filename); + this.typeElement = typeElement; + if (mapper.classToPackageAnnotations.containsKey(typeElement)) { + pkgToPackageAnnotations = new TreeSet<>(utils.makeClassUseComparator()); + pkgToPackageAnnotations.addAll(mapper.classToPackageAnnotations.get(typeElement)); + } + configuration.currentTypeElement = typeElement; + this.pkgSet = new TreeSet<>(utils.makePackageComparator()); + this.pkgToClassTypeParameter = pkgDivide(mapper.classToClassTypeParam); + this.pkgToClassAnnotations = pkgDivide(mapper.classToClassAnnotations); + this.pkgToMethodTypeParameter = pkgDivide(mapper.classToMethodTypeParam); + this.pkgToMethodArgTypeParameter = pkgDivide(mapper.classToMethodArgTypeParam); + this.pkgToFieldTypeParameter = pkgDivide(mapper.classToFieldTypeParam); + this.pkgToFieldAnnotations = pkgDivide(mapper.annotationToField); + this.pkgToMethodReturnTypeParameter = pkgDivide(mapper.classToMethodReturnTypeParam); + this.pkgToMethodAnnotations = pkgDivide(mapper.classToMethodAnnotations); + this.pkgToMethodParameterAnnotations = pkgDivide(mapper.classToMethodParamAnnotation); + this.pkgToSubclass = pkgDivide(mapper.classToSubclass); + this.pkgToSubinterface = pkgDivide(mapper.classToSubinterface); + this.pkgToImplementingClass = pkgDivide(mapper.classToImplementingClass); + this.pkgToField = pkgDivide(mapper.classToField); + this.pkgToMethodReturn = pkgDivide(mapper.classToMethodReturn); + this.pkgToMethodArgs = pkgDivide(mapper.classToMethodArgs); + this.pkgToMethodThrows = pkgDivide(mapper.classToMethodThrows); + this.pkgToConstructorAnnotations = pkgDivide(mapper.classToConstructorAnnotations); + this.pkgToConstructorParameterAnnotations = pkgDivide(mapper.classToConstructorParamAnnotation); + this.pkgToConstructorArgs = pkgDivide(mapper.classToConstructorArgs); + this.pkgToConstructorArgTypeParameter = pkgDivide(mapper.classToConstructorArgTypeParam); + this.pkgToConstructorThrows = pkgDivide(mapper.classToConstructorThrows); + //tmp test + if (pkgSet.size() > 0 && + mapper.classToPackage.containsKey(this.typeElement) && + !pkgSet.equals(mapper.classToPackage.get(this.typeElement))) { + configuration.reporter.print(Diagnostic.Kind.WARNING, + "Internal error: package sets don't match: " + + pkgSet + " with: " + mapper.classToPackage.get(this.typeElement)); + } + methodSubWriter = new MethodWriterImpl(this); + constrSubWriter = new ConstructorWriterImpl(this); + fieldSubWriter = new FieldWriterImpl(this); + classSubWriter = new NestedClassWriterImpl(this); + classUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.classes")); + subclassUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.subclasses")); + subinterfaceUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.subinterfaces")); + fieldUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.fields")); + methodUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.methods")); + constructorUseTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.constructors")); + } + + /** + * Write out class use pages. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, ClassTree classtree) { + ClassUseMapper mapper = new ClassUseMapper(configuration, classtree); + for (TypeElement aClass : configuration.root.getIncludedClasses()) { + // If -nodeprecated option is set and the containing package is marked + // as deprecated, do not generate the class-use page. We will still generate + // the class-use page if the class is marked as deprecated but the containing + // package is not since it could still be linked from that package-use page. + if (!(configuration.nodeprecated && + configuration.utils.isDeprecated(configuration.utils.containingPackage(aClass)))) + ClassUseWriter.generate(configuration, mapper, aClass); + } + for (PackageElement pkg : configuration.packages) { + // If -nodeprecated option is set and the package is marked + // as deprecated, do not generate the package-use page. + if (!(configuration.nodeprecated && configuration.utils.isDeprecated(pkg))) + PackageUseWriter.generate(configuration, mapper, pkg); + } + } + + private Map> pkgDivide(Map> classMap) { + Map> map = new HashMap<>(); + List elements = (List) classMap.get(typeElement); + if (elements != null) { + Collections.sort(elements, utils.makeClassUseComparator()); + for (Element e : elements) { + PackageElement pkg = utils.containingPackage(e); + pkgSet.add(pkg); + List inPkg = map.get(pkg); + if (inPkg == null) { + inPkg = new ArrayList<>(); + map.put(pkg, inPkg); + } + inPkg.add(e); + } + } + return map; + } + + /** + * Generate a class page. + */ + public static void generate(ConfigurationImpl configuration, ClassUseMapper mapper, + TypeElement typeElement) { + ClassUseWriter clsgen; + DocPath path = DocPath.forPackage(configuration.utils, typeElement) + .resolve(DocPaths.CLASS_USE) + .resolve(DocPath.forName(configuration.utils, typeElement)); + try { + clsgen = new ClassUseWriter(configuration, mapper, path, typeElement); + clsgen.generateClassUseFile(); + clsgen.close(); + } catch (IOException exc) { + configuration.standardmessage. + error("doclet.exception_encountered", + exc.toString(), path.getPath()); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the class use elements. + */ + protected void generateClassUseFile() throws IOException { + HtmlTree body = getClassUseHeader(); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.classUseContainer); + if (pkgSet.size() > 0) { + addClassUse(div); + } else { + div.addContent(getResource("doclet.ClassUse_No.usage.of.0", + utils.getFullyQualifiedName(typeElement))); + } + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + body.addContent(mainTree); + } else { + body.addContent(div); + } + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : body; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add the class use documentation. + * + * @param contentTree the content tree to which the class use information will be added + */ + protected void addClassUse(Content contentTree) throws IOException { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + if (configuration.packages.size() > 1) { + addPackageList(ul); + addPackageAnnotationList(ul); + } + addClassList(ul); + contentTree.addContent(ul); + } + + /** + * Add the packages elements that use the given class. + * + * @param contentTree the content tree to which the packages elements will be added + */ + protected void addPackageList(Content contentTree) throws IOException { + Content caption = getTableCaption(configuration.getResource( + "doclet.ClassUse_Packages.that.use.0", + getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement)))); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.useSummary, caption) + : HtmlTree.TABLE(HtmlStyle.useSummary, useTableSummary, caption); + table.addContent(getSummaryTableHeader(packageTableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (PackageElement pkg : pkgSet) { + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + addPackageUse(pkg, tr); + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + contentTree.addContent(li); + } + + /** + * Add the package annotation elements. + * + * @param contentTree the content tree to which the package annotation elements will be added + */ + protected void addPackageAnnotationList(Content contentTree) throws IOException { + if (!utils.isAnnotationType(typeElement) || + pkgToPackageAnnotations == null || + pkgToPackageAnnotations.isEmpty()) { + return; + } + Content caption = getTableCaption(configuration.getResource( + "doclet.ClassUse_PackageAnnotation", + getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement)))); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.useSummary, caption) + : HtmlTree.TABLE(HtmlStyle.useSummary, useTableSummary, caption); + table.addContent(getSummaryTableHeader(packageTableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (PackageElement pkg : pkgToPackageAnnotations) { + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + Content tdFirst = HtmlTree.TD(HtmlStyle.colFirst, getPackageLink(pkg)); + tr.addContent(tdFirst); + HtmlTree tdLast = new HtmlTree(HtmlTag.TD); + tdLast.addStyle(HtmlStyle.colLast); + addSummaryComment(pkg, tdLast); + tr.addContent(tdLast); + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + contentTree.addContent(li); + } + + /** + * Add the class elements that use the given class. + * + * @param contentTree the content tree to which the class elements will be added + */ + protected void addClassList(Content contentTree) throws IOException { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + for (PackageElement pkg : pkgSet) { + Content markerAnchor = getMarkerAnchor(getPackageAnchorName(pkg)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(markerAnchor) + : HtmlTree.LI(HtmlStyle.blockList, markerAnchor); + Content link = getResource("doclet.ClassUse_Uses.of.0.in.1", + getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.CLASS_USE_HEADER, + typeElement)), + getPackageLink(pkg, utils.getPackageName(pkg))); + Content heading = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, link); + htmlTree.addContent(heading); + addClassUse(pkg, htmlTree); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + Content li = HtmlTree.LI(HtmlStyle.blockList, ul); + contentTree.addContent(li); + } + + /** + * Add the package use information. + * + * @param pkg the package that uses the given class + * @param contentTree the content tree to which the package use information will be added + */ + protected void addPackageUse(PackageElement pkg, Content contentTree) throws IOException { + Content tdFirst = HtmlTree.TD(HtmlStyle.colFirst, + getHyperLink(getPackageAnchorName(pkg), new StringContent(utils.getPackageName(pkg)))); + contentTree.addContent(tdFirst); + HtmlTree tdLast = new HtmlTree(HtmlTag.TD); + tdLast.addStyle(HtmlStyle.colLast); + addSummaryComment(pkg, tdLast); + contentTree.addContent(tdLast); + } + + /** + * Add the class use information. + * + * @param pkg the package that uses the given class + * @param contentTree the content tree to which the class use information will be added + */ + protected void addClassUse(PackageElement pkg, Content contentTree) throws IOException { + Content classLink = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement)); + Content pkgLink = getPackageLink(pkg, utils.getPackageName(pkg)); + classSubWriter.addUseInfo(pkgToClassAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_Annotation", classLink, + pkgLink), classUseTableSummary, contentTree); + classSubWriter.addUseInfo(pkgToClassTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_TypeParameter", classLink, + pkgLink), classUseTableSummary, contentTree); + classSubWriter.addUseInfo(pkgToSubclass.get(pkg), + configuration.getResource("doclet.ClassUse_Subclass", classLink, + pkgLink), subclassUseTableSummary, contentTree); + classSubWriter.addUseInfo(pkgToSubinterface.get(pkg), + configuration.getResource("doclet.ClassUse_Subinterface", classLink, + pkgLink), subinterfaceUseTableSummary, contentTree); + classSubWriter.addUseInfo(pkgToImplementingClass.get(pkg), + configuration.getResource("doclet.ClassUse_ImplementingClass", classLink, + pkgLink), classUseTableSummary, contentTree); + fieldSubWriter.addUseInfo(pkgToField.get(pkg), + configuration.getResource("doclet.ClassUse_Field", classLink, + pkgLink), fieldUseTableSummary, contentTree); + fieldSubWriter.addUseInfo(pkgToFieldAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_FieldAnnotations", classLink, + pkgLink), fieldUseTableSummary, contentTree); + fieldSubWriter.addUseInfo(pkgToFieldTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_FieldTypeParameter", classLink, + pkgLink), fieldUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_MethodAnnotations", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodParameterAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_MethodParameterAnnotations", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_MethodTypeParameter", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodReturn.get(pkg), + configuration.getResource("doclet.ClassUse_MethodReturn", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodReturnTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_MethodReturnTypeParameter", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodArgs.get(pkg), + configuration.getResource("doclet.ClassUse_MethodArgs", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodArgTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_MethodArgsTypeParameters", classLink, + pkgLink), methodUseTableSummary, contentTree); + methodSubWriter.addUseInfo(pkgToMethodThrows.get(pkg), + configuration.getResource("doclet.ClassUse_MethodThrows", classLink, + pkgLink), methodUseTableSummary, contentTree); + constrSubWriter.addUseInfo(pkgToConstructorAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_ConstructorAnnotations", classLink, + pkgLink), constructorUseTableSummary, contentTree); + constrSubWriter.addUseInfo(pkgToConstructorParameterAnnotations.get(pkg), + configuration.getResource("doclet.ClassUse_ConstructorParameterAnnotations", classLink, + pkgLink), constructorUseTableSummary, contentTree); + constrSubWriter.addUseInfo(pkgToConstructorArgs.get(pkg), + configuration.getResource("doclet.ClassUse_ConstructorArgs", classLink, + pkgLink), constructorUseTableSummary, contentTree); + constrSubWriter.addUseInfo(pkgToConstructorArgTypeParameter.get(pkg), + configuration.getResource("doclet.ClassUse_ConstructorArgsTypeParameters", classLink, + pkgLink), constructorUseTableSummary, contentTree); + constrSubWriter.addUseInfo(pkgToConstructorThrows.get(pkg), + configuration.getResource("doclet.ClassUse_ConstructorThrows", classLink, + pkgLink), constructorUseTableSummary, contentTree); + } + + /** + * Get the header for the class use Listing. + * + * @return a content tree representing the class use header + */ + protected HtmlTree getClassUseHeader() { + String cltype = configuration.getText(utils.isInterface(typeElement) + ? "doclet.Interface" + : "doclet.Class"); + String clname = utils.getFullyQualifiedName(typeElement); + String title = configuration.getText("doclet.Window_ClassUse_Header", + cltype, clname); + HtmlTree bodyTree = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + ContentBuilder headContent = new ContentBuilder(); + headContent.addContent(getResource("doclet.ClassUse_Title", cltype)); + headContent.addContent(new HtmlTree(HtmlTag.BR)); + headContent.addContent(clname); + Content heading = HtmlTree.HEADING(HtmlConstants.CLASS_PAGE_HEADING, + true, HtmlStyle.title, headContent); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * Get this package link. + * + * @return a content tree for the package link + */ + protected Content getNavLinkPackage() { + Content linkContent = + getHyperLink(DocPath.parent.resolve(DocPaths.PACKAGE_SUMMARY), packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get class page link. + * + * @return a content tree for the class page link + */ + protected Content getNavLinkClass() { + Content linkContent = getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement) + .label(configuration.getText("doclet.Class"))); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the use link. + * + * @return a content tree for the use link + */ + protected Content getNavLinkClassUse() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, useLabel); + return li; + } + + /** + * Get the tree link. + * + * @return a content tree for the tree link + */ + protected Content getNavLinkTree() { + Content linkContent = utils.isEnclosingPackageIncluded(typeElement) + ? getHyperLink(DocPath.parent.resolve(DocPaths.PACKAGE_TREE), treeLabel) + : getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), treeLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java new file mode 100644 index 00000000000..37bc63c57e6 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java @@ -0,0 +1,759 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.IOException; +import java.util.*; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleElementVisitor8; + +import com.sun.source.doctree.DocTree; +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.ClassWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.builders.MemberSummaryBuilder; +import jdk.javadoc.internal.doclets.toolkit.taglets.ParamTaglet; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap.Kind; + +/** + * Generate the Class Information Page. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see javax.lang.model.element.TypeElement + * @see java.util.Collections + * @see java.util.List + * @see java.util.ArrayList + * @see java.util.HashMap + * + * @author Atul M Dambalkar + * @author Robert Field + * @author Bhavesh Patel (Modified) + */ +public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWriter { + + protected final TypeElement typeElement; + + protected final ClassTree classtree; + + protected final TypeElement prev; + + protected final TypeElement next; + + /** + * @param configuration the configuration data for the doclet + * @param typeElement the class being documented. + * @param prevClass the previous class that was documented. + * @param nextClass the next class being documented. + * @param classTree the class tree for the given class. + * @throws java.io.IOException + */ + public ClassWriterImpl(ConfigurationImpl configuration, TypeElement typeElement, + TypeElement prevClass, TypeElement nextClass, ClassTree classTree) + throws IOException { + super(configuration, DocPath.forClass(configuration.utils, typeElement)); + this.typeElement = typeElement; + configuration.currentTypeElement = typeElement; + this.classtree = classTree; + this.prev = prevClass; + this.next = nextClass; + } + + /** + * Get this package link. + * + * @return a content tree for the package link + */ + @Override + protected Content getNavLinkPackage() { + Content linkContent = getHyperLink(DocPaths.PACKAGE_SUMMARY, + packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the class link. + * + * @return a content tree for the class link + */ + @Override + protected Content getNavLinkClass() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, classLabel); + return li; + } + + /** + * Get the class use link. + * + * @return a content tree for the class use link + */ + @Override + protected Content getNavLinkClassUse() { + Content linkContent = getHyperLink(DocPaths.CLASS_USE.resolve(filename), useLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link to previous class. + * + * @return a content tree for the previous class link + */ + @Override + public Content getNavLinkPrevious() { + Content li; + if (prev != null) { + Content prevLink = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS, prev) + .label(prevclassLabel).strong(true)); + li = HtmlTree.LI(prevLink); + } + else + li = HtmlTree.LI(prevclassLabel); + return li; + } + + /** + * Get link to next class. + * + * @return a content tree for the next class link + */ + @Override + public Content getNavLinkNext() { + Content li; + if (next != null) { + Content nextLink = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS, next) + .label(nextclassLabel).strong(true)); + li = HtmlTree.LI(nextLink); + } + else + li = HtmlTree.LI(nextclassLabel); + return li; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getHeader(String header) { + HtmlTree bodyTree = getBody(true, getWindowTitle(utils.getSimpleName(typeElement))); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + bodyTree.addContent(HtmlConstants.START_OF_CLASS_DATA); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.header); + PackageElement pkg = utils.containingPackage(typeElement); + if (!pkg.isUnnamed()) { + Content pkgNameContent = new StringContent(utils.getPackageName(pkg)); + Content pkgNameDiv = HtmlTree.DIV(HtmlStyle.subTitle, pkgNameContent); + div.addContent(pkgNameDiv); + } + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_HEADER, typeElement); + //Let's not link to ourselves in the header. + linkInfo.linkToSelf = false; + Content headerContent = new StringContent(header); + Content heading = HtmlTree.HEADING(HtmlConstants.CLASS_PAGE_HEADING, true, + HtmlStyle.title, headerContent); + heading.addContent(getTypeParameterLinks(linkInfo)); + div.addContent(heading); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getClassContentHeader() { + return getContentHeader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addFooter(Content contentTree) { + contentTree.addContent(HtmlConstants.END_OF_CLASS_DATA); + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : contentTree; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + contentTree.addContent(htmlTree); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void printDocument(Content contentTree) throws IOException { + printHtmlDocument(configuration.metakeywords.getMetaKeywords(typeElement), + true, contentTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getClassInfoTreeHeader() { + return getMemberTreeHeader(); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getClassInfo(Content classInfoTree) { + return getMemberTree(HtmlStyle.description, classInfoTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addClassSignature(String modifiers, Content classInfoTree) { + classInfoTree.addContent(new HtmlTree(HtmlTag.BR)); + Content pre = new HtmlTree(HtmlTag.PRE); + addAnnotationInfo(typeElement, pre); + pre.addContent(modifiers); + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_SIGNATURE, typeElement); + //Let's not link to ourselves in the signature. + linkInfo.linkToSelf = false; + Content className = new StringContent(utils.getSimpleName(typeElement)); + Content parameterLinks = getTypeParameterLinks(linkInfo); + if (configuration.linksource) { + addSrcLink(typeElement, className, pre); + pre.addContent(parameterLinks); + } else { + Content span = HtmlTree.SPAN(HtmlStyle.typeNameLabel, className); + span.addContent(parameterLinks); + pre.addContent(span); + } + if (!utils.isInterface(typeElement)) { + TypeMirror superclass = utils.getFirstVisibleSuperClass(typeElement); + if (superclass != null) { + pre.addContent(DocletConstants.NL); + pre.addContent("extends "); + Content link = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_SIGNATURE_PARENT_NAME, + superclass)); + pre.addContent(link); + } + } + List interfaces = typeElement.getInterfaces(); + if (!interfaces.isEmpty()) { + boolean isFirst = true; + for (TypeMirror type : interfaces) { + TypeElement tDoc = utils.asTypeElement(type); + if (!(utils.isPublic(tDoc) || utils.isLinkable(tDoc))) { + continue; + } + if (isFirst) { + pre.addContent(DocletConstants.NL); + pre.addContent(utils.isInterface(typeElement) ? "extends " : "implements "); + isFirst = false; + } else { + pre.addContent(", "); + } + Content link = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_SIGNATURE_PARENT_NAME, + type)); + pre.addContent(link); + } + } + classInfoTree.addContent(pre); + } + + /** + * {@inheritDoc} + */ + @Override + public void addClassDescription(Content classInfoTree) { + if(!configuration.nocomment) { + // generate documentation for the class. + if (!utils.getBody(typeElement).isEmpty()) { + addInlineComment(typeElement, classInfoTree); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addClassTagInfo(Content classInfoTree) { + if(!configuration.nocomment) { + // Print Information about all the tags here + addTagsInfo(typeElement, classInfoTree); + } + } + + /** + * Get the class hierarchy tree for the given class. + * + * @param type the class to print the hierarchy for + * @return a content tree for class inheritence + */ + private Content getClassInheritenceTree(TypeMirror type) { + TypeMirror sup; + HtmlTree classTreeUl = new HtmlTree(HtmlTag.UL); + classTreeUl.addStyle(HtmlStyle.inheritance); + Content liTree = null; + do { + sup = utils.getFirstVisibleSuperClass(type); + if (sup != null) { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.inheritance); + ul.addContent(getTreeForClassHelper(type)); + if (liTree != null) + ul.addContent(liTree); + Content li = HtmlTree.LI(ul); + liTree = li; + type = sup; + } else + classTreeUl.addContent(getTreeForClassHelper(type)); + } while (sup != null); + if (liTree != null) + classTreeUl.addContent(liTree); + return classTreeUl; + } + + /** + * Get the class helper tree for the given class. + * + * @param type the class to print the helper for + * @return a content tree for class helper + */ + private Content getTreeForClassHelper(TypeMirror type) { + Content li = new HtmlTree(HtmlTag.LI); + if (type.equals(typeElement.asType())) { + Content typeParameters = getTypeParameterLinks( + new LinkInfoImpl(configuration, LinkInfoImpl.Kind.TREE, + typeElement)); + if (configuration.shouldExcludeQualifier(utils.containingPackage(typeElement).toString())) { + li.addContent(utils.asTypeElement(type).getSimpleName().toString()); + li.addContent(typeParameters); + } else { + li.addContent(utils.asTypeElement(type).getQualifiedName().toString()); + li.addContent(typeParameters); + } + } else { + Content link = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS_TREE_PARENT, type) + .label(configuration.getClassName(utils.asTypeElement(type)))); + li.addContent(link); + } + return li; + } + + /** + * {@inheritDoc} + */ + @Override + public void addClassTree(Content classContentTree) { + if (!utils.isClass(typeElement)) { + return; + } + classContentTree.addContent(getClassInheritenceTree(typeElement.asType())); + } + + /** + * {@inheritDoc} + */ + @Override + public void addTypeParamInfo(Content classInfoTree) { + if (!utils.getTypeParamTrees(typeElement).isEmpty()) { + Content typeParam = (new ParamTaglet()).getTagletOutput(typeElement, + getTagletWriterInstance(false)); + Content dl = HtmlTree.DL(typeParam); + classInfoTree.addContent(dl); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSubClassInfo(Content classInfoTree) { + if (utils.isClass(typeElement)) { + if (typeElement.getQualifiedName().toString().equals("java.lang.Object") || + typeElement.getQualifiedName().toString().equals("org.omg.CORBA.Object")) { + return; // Don't generate the list, too huge + } + Set subclasses = classtree.directSubClasses(typeElement, false); + if (!subclasses.isEmpty()) { + Content label = getResource( + "doclet.Subclasses"); + Content dt = HtmlTree.DT(label); + Content dl = HtmlTree.DL(dt); + dl.addContent(getClassLinks(LinkInfoImpl.Kind.SUBCLASSES, + subclasses)); + classInfoTree.addContent(dl); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSubInterfacesInfo(Content classInfoTree) { + if (utils.isInterface(typeElement)) { + Set subInterfaces = classtree.allSubClasses(typeElement, false); + if (!subInterfaces.isEmpty()) { + Content label = getResource( + "doclet.Subinterfaces"); + Content dt = HtmlTree.DT(label); + Content dl = HtmlTree.DL(dt); + dl.addContent(getClassLinks(LinkInfoImpl.Kind.SUBINTERFACES, + subInterfaces)); + classInfoTree.addContent(dl); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addInterfaceUsageInfo (Content classInfoTree) { + if (!utils.isInterface(typeElement)) { + return; + } + if (typeElement.getQualifiedName().toString().equals("java.lang.Cloneable") || + typeElement.getQualifiedName().toString().equals("java.io.Serializable")) { + return; // Don't generate the list, too big + } + Set implcl = classtree.implementingClasses(typeElement); + if (!implcl.isEmpty()) { + Content label = getResource( + "doclet.Implementing_Classes"); + Content dt = HtmlTree.DT(label); + Content dl = HtmlTree.DL(dt); + dl.addContent(getClassLinks(LinkInfoImpl.Kind.IMPLEMENTED_CLASSES, + implcl)); + classInfoTree.addContent(dl); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addImplementedInterfacesInfo(Content classInfoTree) { + SortedSet interfaces = new TreeSet<>(utils.makeTypeMirrorClassUseComparator()); + interfaces.addAll(utils.getAllInterfaces(typeElement)); + if (utils.isClass(typeElement) && !interfaces.isEmpty()) { + Content label = getResource( + "doclet.All_Implemented_Interfaces"); + Content dt = HtmlTree.DT(label); + Content dl = HtmlTree.DL(dt); + dl.addContent(getClassLinks(LinkInfoImpl.Kind.IMPLEMENTED_INTERFACES, interfaces)); + classInfoTree.addContent(dl); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSuperInterfacesInfo(Content classInfoTree) { + SortedSet interfaces = + new TreeSet<>(utils.makeTypeMirrorIndexUseComparator()); + interfaces.addAll(utils.getAllInterfaces(typeElement)); + + if (utils.isInterface(typeElement) && !interfaces.isEmpty()) { + Content label = getResource("doclet.All_Superinterfaces"); + Content dt = HtmlTree.DT(label); + Content dl = HtmlTree.DL(dt); + dl.addContent(getClassLinks(LinkInfoImpl.Kind.SUPER_INTERFACES, interfaces)); + classInfoTree.addContent(dl); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addNestedClassInfo(final Content classInfoTree) { + Element outerClass = typeElement.getEnclosingElement(); + if (outerClass == null) + return; + new SimpleElementVisitor8() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitType(TypeElement e, Void p) { + String label = utils.isInterface(e) + ? "doclet.Enclosing_Interface" + : "doclet.Enclosing_Class"; + Content dt = HtmlTree.DT(getResource(label)); + Content dl = HtmlTree.DL(dt); + Content dd = new HtmlTree(HtmlTag.DD); + dd.addContent(getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CLASS, e))); + dl.addContent(dd); + classInfoTree.addContent(dl); + return null; + } + }.visit(outerClass); + } + + /** + * {@inheritDoc} + */ + @Override + public void addFunctionalInterfaceInfo (Content classInfoTree) { + if (isFunctionalInterface()) { + Content dt = HtmlTree.DT(getResource("doclet.Functional_Interface")); + Content dl = HtmlTree.DL(dt); + Content dd = new HtmlTree(HtmlTag.DD); + dd.addContent(getResource("doclet.Functional_Interface_Message")); + dl.addContent(dd); + classInfoTree.addContent(dl); + } + } + + public boolean isFunctionalInterface() { + List annotationMirrors = ((Element) typeElement).getAnnotationMirrors(); + for (AnnotationMirror anno : annotationMirrors) { + if (utils.isFunctionalInterface(anno)) { + return true; + } + } + return false; + } + + + /** + * {@inheritDoc} + */ + @Override + public void addClassDeprecationInfo(Content classInfoTree) { + Content hr = new HtmlTree(HtmlTag.HR); + classInfoTree.addContent(hr); + List deprs = utils.getBlockTags(typeElement, DocTree.Kind.DEPRECATED); + if (utils.isDeprecated(typeElement)) { + Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + Content div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + if (!deprs.isEmpty()) { + CommentHelper ch = utils.getCommentHelper(typeElement); + DocTree dt = deprs.get(0); + List commentTags = ch.getBody(configuration, dt); + if (!commentTags.isEmpty()) { + div.addContent(getSpace()); + addInlineDeprecatedComment(typeElement, deprs.get(0), div); + } + } + classInfoTree.addContent(div); + } + } + + /** + * Get links to the given classes. + * + * @param context the id of the context where the link will be printed + * @param list the list of classes + * @return a content tree for the class list + */ + private Content getClassLinks(LinkInfoImpl.Kind context, Collection list) { + Content dd = new HtmlTree(HtmlTag.DD); + boolean isFirst = true; + for (Object type : list) { + if (!isFirst) { + Content separator = new StringContent(", "); + dd.addContent(separator); + } else { + isFirst = false; + } + // TODO: should we simply split this method up to avoid instanceof ? + if (type instanceof TypeElement) { + Content link = getLink( + new LinkInfoImpl(configuration, context, (TypeElement)(type))); + dd.addContent(link); + } else { + Content link = getLink( + new LinkInfoImpl(configuration, context, ((TypeMirror)type))); + dd.addContent(link); + } + } + return dd; + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavLinkTree() { + Content treeLinkContent = getHyperLink(DocPaths.PACKAGE_TREE, + treeLabel, "", ""); + Content li = HtmlTree.LI(treeLinkContent); + return li; + } + + /** + * Add summary details to the navigation bar. + * + * @param subDiv the content tree to which the summary detail links will be added + */ + protected void addSummaryDetailLinks(Content subDiv) { + try { + Content div = HtmlTree.DIV(getNavSummaryLinks()); + div.addContent(getNavDetailLinks()); + subDiv.addContent(div); + } catch (Exception e) { + throw new DocletAbortException(e); + } + } + + /** + * Get summary links for navigation bar. + * + * @return the content tree for the navigation summary links + */ + protected Content getNavSummaryLinks() throws Exception { + Content li = HtmlTree.LI(summaryLabel); + li.addContent(getSpace()); + Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); + MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder) + configuration.getBuilderFactory().getMemberSummaryBuilder(this); + for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.summarySet) { + Content liNav = new HtmlTree(HtmlTag.LI); + if (kind == VisibleMemberMap.Kind.ENUM_CONSTANTS && !utils.isEnum(typeElement)) { + continue; + } + if (kind == VisibleMemberMap.Kind.CONSTRUCTORS && utils.isEnum(typeElement)) { + continue; + } + AbstractMemberWriter writer = + ((AbstractMemberWriter) memberSummaryBuilder.getMemberSummaryWriter(kind)); + if (writer == null) { + liNav.addContent(getResource(VisibleMemberMap.Kind.getNavLinkLabels(kind))); + } else { + writer.addNavSummaryLink( + memberSummaryBuilder.members(kind), + memberSummaryBuilder.getVisibleMemberMap(kind), liNav); + } + if (kind != Kind.METHODS) { + addNavGap(liNav); + } + ulNav.addContent(liNav); + } + return ulNav; + } + + /** + * Get detail links for the navigation bar. + * + * @return the content tree for the detail links + * @throws java.lang.Exception + */ + protected Content getNavDetailLinks() throws Exception { + Content li = HtmlTree.LI(detailLabel); + li.addContent(getSpace()); + Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); + MemberSummaryBuilder memberSummaryBuilder = (MemberSummaryBuilder) + configuration.getBuilderFactory().getMemberSummaryBuilder(this); + for (VisibleMemberMap.Kind kind : VisibleMemberMap.Kind.detailSet) { + Content liNav = new HtmlTree(HtmlTag.LI); + AbstractMemberWriter writer = + ((AbstractMemberWriter) memberSummaryBuilder. + getMemberSummaryWriter(kind)); + if (kind == VisibleMemberMap.Kind.ENUM_CONSTANTS && !utils.isEnum(typeElement)) { + continue; + } + if (kind == VisibleMemberMap.Kind.CONSTRUCTORS && utils.isEnum(typeElement)) { + continue; + } + if (writer == null) { + liNav.addContent(getResource(VisibleMemberMap.Kind.getNavLinkLabels(kind))); + } else { + writer.addNavDetailLink(memberSummaryBuilder.members(kind), liNav); + } + if (kind != Kind.METHODS) { + addNavGap(liNav); + } + ulNav.addContent(liNav); + } + return ulNav; + } + + /** + * Add gap between navigation bar elements. + * + * @param liNav the content tree to which the gap will be added + */ + protected void addNavGap(Content liNav) { + liNav.addContent(getSpace()); + liNav.addContent("|"); + liNav.addContent(getSpace()); + } + + /** + * Return the TypeElement being documented. + * + * @return the TypeElement being documented. + */ + @Override + public TypeElement getTypeElement() { + return typeElement; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java new file mode 100644 index 00000000000..0b7aa5ec023 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java @@ -0,0 +1,747 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.net.*; +import java.util.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; + +import com.sun.source.util.DocTreePath; +import com.sun.tools.doclint.DocLint; + +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlVersion; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.WriterFactory; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.MessageRetriever; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +import static javax.tools.Diagnostic.Kind.*; + +/** + * Configure the output based on the command line options. + *

+ * Also determine the length of the command line option. For example, + * for a option "-header" there will be a string argument associated, then the + * the length of option "-header" is two. But for option "-nohelp" no argument + * is needed so it's length is 1. + *

+ *

+ * Also do the error checking on the options used. For example it is illegal to + * use "-helpfile" option when already "-nohelp" option is used. + *

+ * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field. + * @author Atul Dambalkar. + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class ConfigurationImpl extends Configuration { + + /** + * The build date. Note: For now, we will use + * a version number instead of a date. + */ + public static final String BUILD_DATE = System.getProperty("java.version"); + + /** + * Argument for command line option "-header". + */ + public String header = ""; + + /** + * Argument for command line option "-packagesheader". + */ + public String packagesheader = ""; + + /** + * Argument for command line option "-footer". + */ + public String footer = ""; + + /** + * Argument for command line option "-doctitle". + */ + public String doctitle = ""; + + /** + * Argument for command line option "-windowtitle". + */ + public String windowtitle = ""; + + /** + * Argument for command line option "-top". + */ + public String top = ""; + + /** + * Argument for command line option "-bottom". + */ + public String bottom = ""; + + /** + * Argument for command line option "-helpfile". + */ + public String helpfile = ""; + + /** + * Argument for command line option "-stylesheetfile". + */ + public String stylesheetfile = ""; + + /** + * Argument for command line option "-Xdocrootparent". + */ + public String docrootparent = ""; + + public boolean sortedMethodDetails = false; + + /** + * True if command line option "-nohelp" is used. Default value is false. + */ + public boolean nohelp = false; + + /** + * True if command line option "-splitindex" is used. Default value is + * false. + */ + public boolean splitindex = false; + + /** + * False if command line option "-noindex" is used. Default value is true. + */ + public boolean createindex = true; + + /** + * True if command line option "-use" is used. Default value is false. + */ + public boolean classuse = false; + + /** + * False if command line option "-notree" is used. Default value is true. + */ + public boolean createtree = true; + + /** + * True if command line option "-nodeprecated" is used. Default value is + * false. + */ + public boolean nodeprecatedlist = false; + + /** + * True if command line option "-nonavbar" is used. Default value is false. + */ + public boolean nonavbar = false; + + /** + * True if command line option "-nooverview" is used. Default value is + * false + */ + private boolean nooverview = false; + + /** + * The overview path specified with "-overview" flag. + */ + public String overviewpath = null; + + /** + * This is true if option "-overview" is used or option "-overview" is not + * used and number of packages is more than one. + */ + public boolean createoverview = false; + + /** + * This is the HTML version of the generated pages. HTML 4.01 is the default output version. + */ + public HtmlVersion htmlVersion = HtmlVersion.HTML4; + + /** + * Collected set of doclint options + */ + public Map doclintOpts = new LinkedHashMap<>(); + + /** + * Unique Resource Handler for this package. + */ + public final MessageRetriever standardmessage; + + /** + * First file to appear in the right-hand frame in the generated + * documentation. + */ + public DocPath topFile = DocPath.empty; + + /** + * The TypeElement for the class file getting generated. + */ + public TypeElement currentTypeElement = null; // Set this TypeElement in the ClassWriter. + + protected List memberSearchIndex = new ArrayList<>(); + + protected List packageSearchIndex = new ArrayList<>(); + + protected List tagSearchIndex = new ArrayList<>(); + + protected List typeSearchIndex = new ArrayList<>(); + + protected Map> tagSearchIndexMap = new HashMap<>(); + + protected Set tagSearchIndexKeys; + + /** + * Constructor. Initializes resource for the + * {@link com.sun.tools.doclets.internal.toolkit.util.MessageRetriever MessageRetriever}. + */ + public ConfigurationImpl() { + standardmessage = new MessageRetriever(this, + "jdk.javadoc.internal.doclets.formats.html.resources.standard"); + } + + private final String versionRBName = "jdk.javadoc.internal.tool.resources.version"; + private ResourceBundle versionRB; + + /** + * Return the build date for the doclet. + * @return the build date + */ + @Override + public String getDocletSpecificBuildDate() { + if (versionRB == null) { + try { + versionRB = ResourceBundle.getBundle(versionRBName, getLocale()); + } catch (MissingResourceException e) { + return BUILD_DATE; + } + } + + try { + return versionRB.getString("release"); + } catch (MissingResourceException e) { + return BUILD_DATE; + } + } + + protected boolean validateOptions() { + // check shared options + if (!generalValidOptions()) { + return false; + } + boolean helpfileSeen = false; + // otherwise look at our options + for (Doclet.Option opt : optionsProcessed) { + if (opt.matches("-helpfile")) { + if (nohelp == true) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-helpfile", "-nohelp")); + return false; + } + if (helpfileSeen) { + reporter.print(ERROR, getText("doclet.Option_reuse", + "-helpfile")); + return false; + } + helpfileSeen = true; + DocFile help = DocFile.createFileForInput(this, helpfile); + if (!help.exists()) { + reporter.print(ERROR, getText("doclet.File_not_found", helpfile)); + return false; + } + } else if (opt.matches("-nohelp")) { + if (helpfileSeen) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-nohelp", "-helpfile")); + return false; + } + } else if (opt.matches("-xdocrootparent")) { + try { + URL ignored = new URL(docrootparent); + } catch (MalformedURLException e) { + reporter.print(ERROR, getText("doclet.MalformedURL", docrootparent)); + return false; + } + } else if (opt.matches("-overview")) { + if (nooverview == true) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-overview", "-nooverview")); + return false; + } + } else if (opt.matches("-nooverview")) { + if (overviewpath != null) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-nooverview", "-overview")); + return false; + } + } else if (opt.matches("-splitindex")) { + if (createindex == false) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-splitindex", "-noindex")); + return false; + } + } else if (opt.matches("-noindex")) { + if (splitindex == true) { + reporter.print(ERROR, getText("doclet.Option_conflict", + "-noindex", "-splitindex")); + return false; + } + } else if (opt.matches("-xdoclint:")) { + String dopt = doclintOpts.get(opt); + if (dopt == null) { + continue; + } + if (dopt.contains("/")) { + reporter.print(ERROR, getText("doclet.Option_doclint_no_qualifiers")); + return false; + } + if (!DocLint.isValidOption(dopt)) { + reporter.print(ERROR, getText("doclet.Option_doclint_invalid_arg")); + return false; + } + } else if (opt.matches("-xdoclint/package:")) { + String dopt = doclintOpts.get(opt); + if (!DocLint.isValidOption(dopt)) { + reporter.print(ERROR, getText("doclet.Option_doclint_package_invalid_arg")); + return false; + } + } + } + return true; + } + + @Override + public boolean finishOptionSettings() { + if (!validateOptions()) { + return false; + } + if (!root.getSpecifiedElements().isEmpty()) { + Map map = new HashMap<>(); + PackageElement pkg; + List classes = new ArrayList<>(root.getIncludedClasses()); + for (TypeElement aClass : classes) { + pkg = utils.containingPackage(aClass); + if (!map.containsKey(utils.getPackageName(pkg))) { + map.put(utils.getPackageName(pkg), pkg); + } + } + } + setCreateOverview(); + setTopFile(root); + workArounds.initDocLint(doclintOpts.values(), tagletManager.getCustomTagNames(), + Utils.toLowerCase(htmlVersion.name())); + return true; + } + + /** + * Return true if the generated output is HTML5. + */ + public boolean isOutputHtml5() { + return htmlVersion == HtmlVersion.HTML5; + } + + /** + * Return true if the tag is allowed for this specific version of HTML. + */ + public boolean allowTag(HtmlTag htmlTag) { + return htmlTag.allowTag(this.htmlVersion); + } + + /** + * {@inheritDoc} + */ + @Override + public MessageRetriever getDocletSpecificMsg() { + return standardmessage; + } + + /** + * Decide the page which will appear first in the right-hand frame. It will + * be "overview-summary.html" if "-overview" option is used or no + * "-overview" but the number of packages is more than one. It will be + * "package-summary.html" of the respective package if there is only one + * package to document. It will be a class page(first in the sorted order), + * if only classes are provided on the command line. + * + * @param root Root of the program structure. + */ + protected void setTopFile(DocletEnvironment root) { + if (!checkForDeprecation(root)) { + return; + } + if (createoverview) { + topFile = DocPaths.OVERVIEW_SUMMARY; + } else { + if (packages.size() == 1 && packages.first().isUnnamed()) { + if (!root.getIncludedClasses().isEmpty()) { + List classes = new ArrayList<>(root.getIncludedClasses()); + TypeElement te = getValidClass(classes); + topFile = DocPath.forClass(utils, te); + } + } else if (!packages.isEmpty()) { + topFile = DocPath.forPackage(packages.first()).resolve(DocPaths.PACKAGE_SUMMARY); + } + } + } + + protected TypeElement getValidClass(List classes) { + if (!nodeprecated) { + return classes.get(0); + } + for (TypeElement te : classes) { + if (!utils.isDeprecated(te)) { + return te; + } + } + return null; + } + + protected boolean checkForDeprecation(DocletEnvironment root) { + for (TypeElement te : root.getIncludedClasses()) { + if (isGeneratedDoc(te)) { + return true; + } + } + return false; + } + + /** + * Generate "overview.html" page if option "-overview" is used or number of + * packages is more than one. Sets {@link #createoverview} field to true. + */ + protected void setCreateOverview() { + if ((overviewpath != null || packages.size() > 1) && !nooverview) { + createoverview = true; + } + } + + /** + * {@inheritDoc} + */ + @Override + public WriterFactory getWriterFactory() { + return new WriterFactoryImpl(this); + } + + /** + * {@inheritDoc} + */ + @Override + public Locale getLocale() { + if (locale == null) + return Locale.getDefault(); + return locale; + } + + /** + * Return the path of the overview file and null if it does not exist. + * + * @return the path of the overview file and null if it does not exist. + */ + public JavaFileObject getOverviewPath() { + if (overviewpath != null && getFileManager() instanceof StandardJavaFileManager) { + StandardJavaFileManager fm = (StandardJavaFileManager) getFileManager(); + return fm.getJavaFileObjects(overviewpath).iterator().next(); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public JavaFileManager getFileManager() { + return root.getJavaFileManager(); + } + + @Override + public boolean showMessage(DocTreePath path, String key) { + return (path == null || workArounds.haveDocLint()); + } + + @Override + public boolean showMessage(Element e, String key) { + return (e == null || workArounds.haveDocLint()); + } + + @Override + public Content newContent() { + return new ContentBuilder(); + } + + protected void buildSearchTagIndex() { + for (SearchIndexItem sii : tagSearchIndex) { + String tagLabel = sii.getLabel(); + Character unicode = (tagLabel.length() == 0) + ? '*' + : Character.toUpperCase(tagLabel.charAt(0)); + List list = tagSearchIndexMap.get(unicode); + if (list == null) { + list = new ArrayList<>(); + tagSearchIndexMap.put(unicode, list); + } + list.add(sii); + } + tagSearchIndexKeys = tagSearchIndexMap.keySet(); + } + + @Override + public Set getSupportedOptions() { + Doclet.Option[] options = { + new Option(this, "bottom", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + bottom = args.next(); + return true; + } + }, + new Option(this, "charset", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + charset = args.next(); + return true; + } + }, + new Option(this, "doctitle", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + doctitle = args.next(); + return true; + } + }, + new Option(this, "footer", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + footer = args.next(); + return true; + } + }, + new Option(this, "header", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + header = args.next(); + return true; + } + }, + new Option(this, "helpfile", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + helpfile = args.next(); + return true; + } + }, + new Option(this, "html4") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + htmlVersion = HtmlVersion.HTML4; + return true; + } + }, + new Option(this, "html5") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + htmlVersion = HtmlVersion.HTML5; + return true; + } + }, + new Option(this, "nohelp") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + nohelp = true; + return true; + } + }, + new Option(this, "nodeprecatedlist") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + nodeprecatedlist = true; + return true; + } + }, + new Option(this, "noindex") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + createindex = false; + return true; + } + }, + new Option(this, "nonavbar") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + nonavbar = true; + return true; + } + }, + new Hidden(this, "nooverview") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + nooverview = true; + return true; + } + }, + new Option(this, "notree") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + createtree = false; + return true; + } + }, + new Hidden(this, "overview", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + overviewpath = args.next(); + return true; + } + }, + new Hidden(this, "packagesheader", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + packagesheader = args.next(); + return true; + } + }, + new Option(this, "splitindex") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + splitindex = true; + return true; + } + }, + new Option(this, "stylesheetfile", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + stylesheetfile = args.next(); + return true; + } + }, + new Option(this, "top", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + top = args.next(); + return true; + } + }, + new Option(this, "use") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + classuse = true; + return true; + } + }, + new Option(this, "windowtitle", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + windowtitle = args.next().replaceAll("\\<.*?>", ""); + return true; + } + }, + new XOption(this, "xdoclint") { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + doclintOpts.put(this, DocLint.XMSGS_OPTION); + return true; + } + }, + new XOption(this, "Xdocrootparent", 1) { + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + docrootparent = args.next(); + return true; + } + }, + new XOption(this, "doclet.xusage.xdoclint-extended.", "Xdoclint:", 0) { + @Override + public boolean matches(String option) { + String opt = option.startsWith("-") ? option.substring(1) : option; + return opt.toLowerCase().startsWith(getName().toLowerCase()); + } + + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + doclintOpts.put(this, opt.replace("-Xdoclint:", DocLint.XMSGS_CUSTOM_PREFIX)); + return true; + } + }, + new XOption(this, "doclet.xusage.xdoclint-package.", "Xdoclint/package:", 0) { + @Override + public boolean matches(String option) { + String opt = option.startsWith("-") ? option.substring(1) : option; + return opt.toLowerCase().startsWith(getName().toLowerCase()); + } + + @Override + public boolean process(String opt, ListIterator args) { + optionsProcessed.add(this); + doclintOpts.put(this, opt.replace("-Xdoclint/package:", DocLint.XCHECK_PACKAGE)); + return true; + } + } + }; + Set oset = new TreeSet<>(); + oset.addAll(Arrays.asList(options)); + oset.addAll(super.getSupportedOptions()); + return oset; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriterImpl.java new file mode 100644 index 00000000000..2b993595998 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstantsSummaryWriterImpl.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.ConstantsSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocLink; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; + + +/** + * Write the Constants Summary Page in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class ConstantsSummaryWriterImpl extends HtmlDocletWriter implements ConstantsSummaryWriter { + + /** + * The configuration used in this run of the standard doclet. + */ + ConfigurationImpl configuration; + + /** + * The current class being documented. + */ + private TypeElement currentTypeElement; + + private final String constantsTableSummary; + + private final List constantsTableHeader; + + /** + * The HTML tree for main tag. + */ + private HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * The HTML tree for constant values summary. + */ + private HtmlTree summaryTree; + + /** + * Construct a ConstantsSummaryWriter. + * @param configuration the configuration used in this run + * of the standard doclet. + */ + public ConstantsSummaryWriterImpl(ConfigurationImpl configuration) + throws IOException { + super(configuration, DocPaths.CONSTANT_VALUES); + this.configuration = configuration; + constantsTableSummary = configuration.getText("doclet.Constants_Table_Summary", + configuration.getText("doclet.Constants_Summary")); + constantsTableHeader = new ArrayList<>(); + constantsTableHeader.add(getModifierTypeHeader()); + constantsTableHeader.add(configuration.getText("doclet.ConstantField")); + constantsTableHeader.add(configuration.getText("doclet.Value")); + } + + /** + * {@inheritDoc} + */ + public Content getHeader() { + String label = configuration.getText("doclet.Constants_Summary"); + HtmlTree bodyTree = getBody(true, getWindowTitle(label)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + return bodyTree; + } + + /** + * {@inheritDoc} + */ + public Content getContentsHeader() { + return new HtmlTree(HtmlTag.UL); + } + + /** + * {@inheritDoc} + */ + public void addLinkToPackageContent(PackageElement pkg, + Set printedPackageHeaders, Content contentListTree) { + //add link to summary + Content link; + if (pkg.isUnnamed()) { + link = getHyperLink(getDocLink( + SectionName.UNNAMED_PACKAGE_ANCHOR), + defaultPackageLabel, "", ""); + } else { + String parsedPackageName = utils.parsePackageName(pkg); + Content packageNameContent = getPackageLabel(parsedPackageName); + packageNameContent.addContent(".*"); + link = getHyperLink(DocLink.fragment(parsedPackageName), + packageNameContent, "", ""); + PackageElement abbrevPkg = utils.elementUtils.getPackageElement(parsedPackageName); + printedPackageHeaders.add(abbrevPkg); + } + contentListTree.addContent(HtmlTree.LI(link)); + } + + /** + * {@inheritDoc} + */ + public void addContentsList(Content contentTree, Content contentListTree) { + Content titleContent = getResource( + "doclet.Constants_Summary"); + Content pHeading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.title, titleContent); + Content div = HtmlTree.DIV(HtmlStyle.header, pHeading); + Content headingContent = getResource( + "doclet.Contents"); + Content heading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, true, + headingContent); + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree section = HtmlTree.SECTION(heading); + section.addContent(contentListTree); + div.addContent(section); + mainTree.addContent(div); + } else { + div.addContent(heading); + div.addContent(contentListTree); + contentTree.addContent(div); + } + } + + /** + * {@inheritDoc} + */ + public Content getConstantSummaries() { + HtmlTree summariesDiv = new HtmlTree(HtmlTag.DIV); + summariesDiv.addStyle(HtmlStyle.constantValuesContainer); + return summariesDiv; + } + + /** + * {@inheritDoc} + */ + public void addPackageName(PackageElement pkg, Content summariesTree, boolean first) { + Content pkgNameContent; + if (!first && configuration.allowTag(HtmlTag.SECTION)) { + summariesTree.addContent(summaryTree); + } + if (pkg.isUnnamed()) { + summariesTree.addContent(getMarkerAnchor( + SectionName.UNNAMED_PACKAGE_ANCHOR)); + pkgNameContent = defaultPackageLabel; + } else { + String parsedPackageName = utils.parsePackageName(pkg); + summariesTree.addContent(getMarkerAnchor(parsedPackageName)); + pkgNameContent = getPackageLabel(parsedPackageName); + } + Content headingContent = new StringContent(".*"); + Content heading = HtmlTree.HEADING(HtmlConstants.PACKAGE_HEADING, true, + pkgNameContent); + heading.addContent(headingContent); + if (configuration.allowTag(HtmlTag.SECTION)) { + summaryTree = HtmlTree.SECTION(heading); + } else { + summariesTree.addContent(heading); + } + } + + /** + * {@inheritDoc} + */ + public Content getClassConstantHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * {@inheritDoc} + */ + public void addClassConstant(Content summariesTree, Content classConstantTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + summaryTree.addContent(classConstantTree); + } else { + summariesTree.addContent(classConstantTree); + } + } + + /** + * Get the table caption and header for the constant summary table + * + * @param typeElement the TypeElement to be documented + * @return constant members header content + */ + public Content getConstantMembersHeader(TypeElement typeElement) { + //generate links backward only to public classes. + Content classlink = (utils.isPublic(typeElement) || utils.isProtected(typeElement)) ? + getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CONSTANT_SUMMARY, typeElement)) : + new StringContent(utils.getFullyQualifiedName(typeElement)); + + PackageElement enclosingPackage = utils.containingPackage(typeElement); + if (!enclosingPackage.isUnnamed()) { + Content cb = new ContentBuilder(); + cb.addContent(enclosingPackage.getQualifiedName().toString()); + cb.addContent("."); + cb.addContent(classlink); + return getClassName(cb); + } else { + return getClassName(classlink); + } + } + + /** + * Get the class name in the table caption and the table header. + * + * @param classStr the class name to print. + * @return the table caption and header + */ + protected Content getClassName(Content classStr) { + Content caption = getTableCaption(classStr); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.constantsSummary, caption) + : HtmlTree.TABLE(HtmlStyle.constantsSummary, constantsTableSummary, caption); + table.addContent(getSummaryTableHeader(constantsTableHeader, "col")); + return table; + } + + /** + * {@inheritDoc} + */ + public void addConstantMembers(TypeElement typeElement, Collection fields, + Content classConstantTree) { + currentTypeElement = typeElement; + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (VariableElement field : fields) { + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + addConstantMember(field, tr); + tbody.addContent(tr); + altColor = !altColor; + } + Content table = getConstantMembersHeader(typeElement); + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + classConstantTree.addContent(li); + } + + /** + * Add the row for the constant summary table. + * + * @param member the field to be documented. + * @param trTree an htmltree object for the table row + */ + private void addConstantMember(VariableElement member, HtmlTree trTree) { + trTree.addContent(getTypeColumn(member)); + trTree.addContent(getNameColumn(member)); + trTree.addContent(getValue(member)); + } + + /** + * Get the type column for the constant summary table row. + * + * @param member the field to be documented. + * @return the type column of the constant table row + */ + private Content getTypeColumn(VariableElement member) { + Content anchor = getMarkerAnchor(currentTypeElement.getQualifiedName() + + "." + member.getSimpleName()); + Content tdType = HtmlTree.TD(HtmlStyle.colFirst, anchor); + Content code = new HtmlTree(HtmlTag.CODE); + for (Modifier mod : member.getModifiers()) { + Content modifier = new StringContent(mod.toString()); + code.addContent(modifier); + code.addContent(getSpace()); + } + Content type = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.CONSTANT_SUMMARY, member.asType())); + code.addContent(type); + tdType.addContent(code); + return tdType; + } + + /** + * Get the name column for the constant summary table row. + * + * @param member the field to be documented. + * @return the name column of the constant table row + */ + private Content getNameColumn(VariableElement member) { + Content nameContent = getDocLink(LinkInfoImpl.Kind.CONSTANT_SUMMARY, + member, member.getSimpleName().toString(), false); + Content code = HtmlTree.CODE(nameContent); + return HtmlTree.TD(code); + } + + /** + * Get the value column for the constant summary table row. + * + * @param member the field to be documented. + * @return the value column of the constant table row + */ + private Content getValue(VariableElement member) { + String value = utils.constantValueExpresion(member); + Content valueContent = new StringContent(value); + Content code = HtmlTree.CODE(valueContent); + return HtmlTree.TD(HtmlStyle.colLast, code); + } + + /** + * {@inheritDoc} + */ + public void addConstantSummaries(Content contentTree, Content summariesTree) { + if (configuration.allowTag(HtmlTag.SECTION) && summaryTree != null) { + summariesTree.addContent(summaryTree); + } + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(summariesTree); + contentTree.addContent(mainTree); + } else { + contentTree.addContent(summariesTree); + } + } + + /** + * {@inheritDoc} + */ + public void addFooter(Content contentTree) { + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : contentTree; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + contentTree.addContent(htmlTree); + } + } + + /** + * {@inheritDoc} + */ + public void printDocument(Content contentTree) throws IOException { + printHtmlDocument(null, true, contentTree); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriterImpl.java new file mode 100644 index 00000000000..c3f3a41e163 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriterImpl.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.ConstructorWriter; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; + + +/** + * Writes constructor documentation. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class ConstructorWriterImpl extends AbstractExecutableMemberWriter + implements ConstructorWriter, MemberSummaryWriter { + + private boolean foundNonPubConstructor = false; + + /** + * Construct a new ConstructorWriterImpl. + * + * @param writer The writer for the class that the constructors belong to. + * @param typeElement the class being documented. + */ + public ConstructorWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + + VisibleMemberMap visibleMemberMap = new VisibleMemberMap( + typeElement, + VisibleMemberMap.Kind.CONSTRUCTORS, configuration); + SortedSet constructors = visibleMemberMap.getMembersFor(typeElement); + for (Element constructor : constructors) { + if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { + setFoundNonPubConstructor(true); + } + } + } + + /** + * Construct a new ConstructorWriterImpl. + * + * @param writer The writer for the class that the constructors belong to. + */ + public ConstructorWriterImpl(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_CONSTRUCTOR_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getConstructorDetailsTreeHeader(TypeElement typeElement, + Content memberDetailsTree) { + memberDetailsTree.addContent(HtmlConstants.START_OF_CONSTRUCTOR_DETAILS); + Content constructorDetailsTree = writer.getMemberTreeHeader(); + constructorDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.CONSTRUCTOR_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.constructorDetailsLabel); + constructorDetailsTree.addContent(heading); + return constructorDetailsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getConstructorDocTreeHeader(ExecutableElement constructor, + Content constructorDetailsTree) { + String erasureAnchor; + if ((erasureAnchor = getErasureAnchor(constructor)) != null) { + constructorDetailsTree.addContent(writer.getMarkerAnchor((erasureAnchor))); + } + constructorDetailsTree.addContent( + writer.getMarkerAnchor(writer.getAnchor(constructor))); + Content constructorDocTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(name(constructor)); + constructorDocTree.addContent(heading); + return constructorDocTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getSignature(ExecutableElement constructor) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(constructor, pre); + int annotationLength = pre.charCount(); + addModifiers(constructor, pre); + if (configuration.linksource) { + Content constructorName = new StringContent(name(constructor)); + writer.addSrcLink(constructor, constructorName, pre); + } else { + addName(name(constructor), pre); + } + int indent = pre.charCount() - annotationLength; + addParameters(constructor, pre, indent); + addExceptions(constructor, pre, indent); + return pre; + } + + /** + * {@inheritDoc} + */ + @Override + public void setSummaryColumnStyle(HtmlTree tdTree) { + if (foundNonPubConstructor) + tdTree.addStyle(HtmlStyle.colLast); + else + tdTree.addStyle(HtmlStyle.colOne); + } + + /** + * {@inheritDoc} + */ + @Override + public void addDeprecated(ExecutableElement constructor, Content constructorDocTree) { + addDeprecatedInfo(constructor, constructorDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addComments(ExecutableElement constructor, Content constructorDocTree) { + addComment(constructor, constructorDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addTags(ExecutableElement constructor, Content constructorDocTree) { + writer.addTagsInfo(constructor, constructorDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getConstructorDetails(Content constructorDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(constructorDetailsTree)); + return htmlTree; + } + return getMemberTree(constructorDetailsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getConstructorDoc(Content constructorDocTree, + boolean isLastContent) { + return getMemberTree(constructorDocTree, isLastContent); + } + + /** + * Close the writer. + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * Let the writer know whether a non public constructor was found. + * + * @param foundNonPubConstructor true if we found a non public constructor. + */ + @Override + public void setFoundNonPubConstructor(boolean foundNonPubConstructor) { + this.foundNonPubConstructor = foundNonPubConstructor; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Constructor_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Constructor_Summary"), + configuration.getText("doclet.constructors")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Constructors"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + List header = new ArrayList<>(); + if (foundNonPubConstructor) { + header.add(configuration.getText("doclet.Modifier")); + } + header.add(configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Constructor"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.CONSTRUCTOR_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + return writer.getHyperLink(SectionName.CONSTRUCTOR_SUMMARY, + writer.getResource("doclet.navConstructor")); + } else { + return writer.getResource("doclet.navConstructor"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.CONSTRUCTOR_DETAIL, + writer.getResource("doclet.navConstructor"))); + } else { + liNav.addContent(writer.getResource("doclet.navConstructor")); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + if (foundNonPubConstructor) { + Content code = new HtmlTree(HtmlTag.CODE); + if (utils.isProtected(member)) { + code.addContent("protected "); + } else if (utils.isPrivate(member)) { + code.addContent("private "); + } else if (utils.isPublic(member)) { + code.addContent(writer.getSpace()); + } else { + code.addContent( + configuration.getText("doclet.Package_private")); + } + tdSummaryType.addContent(code); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java new file mode 100644 index 00000000000..6defcd7614d --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DeprecatedAPIListBuilder; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +import static jdk.javadoc.internal.doclets.toolkit.util.DeprecatedAPIListBuilder.*; + +/** + * Generate File to list all the deprecated classes and class members with the + * appropriate links. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see java.util.List + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class DeprecatedListWriter extends SubWriterHolderWriter { + + private String getAnchorName(DeprElementKind kind) { + switch (kind) { + case PACKAGE: + return "package"; + case INTERFACE: + return "interface"; + case CLASS: + return "class"; + case ENUM: + return "enum"; + case EXCEPTION: + return "exception"; + case ERROR: + return "error"; + case ANNOTATION_TYPE: + return "annotation.type"; + case FIELD: + return "field"; + case METHOD: + return "method"; + case CONSTRUCTOR: + return "constructor"; + case ENUM_CONSTANT: + return "enum.constant"; + case ANNOTATION_TYPE_MEMBER: + return "annotation.type.member"; + default: + throw new AssertionError("unknown kind: " + kind); + } + } + + private String getHeadingKey(DeprElementKind kind) { + switch (kind) { + case PACKAGE: + return "doclet.Deprecated_Packages"; + case INTERFACE: + return "doclet.Deprecated_Interfaces"; + case CLASS: + return "doclet.Deprecated_Classes"; + case ENUM: + return "doclet.Deprecated_Enums"; + case EXCEPTION: + return "doclet.Deprecated_Exceptions"; + case ERROR: + return "doclet.Deprecated_Errors"; + case ANNOTATION_TYPE: + return "doclet.Deprecated_Annotation_Types"; + case FIELD: + return "doclet.Deprecated_Fields"; + case METHOD: + return "doclet.Deprecated_Methods"; + case CONSTRUCTOR: + return "doclet.Deprecated_Constructors"; + case ENUM_CONSTANT: + return "doclet.Deprecated_Enum_Constants"; + case ANNOTATION_TYPE_MEMBER: + return "doclet.Deprecated_Annotation_Type_Members"; + default: + throw new AssertionError("unknown kind: " + kind); + } + } + + private String getSummaryKey(DeprElementKind kind) { + switch (kind) { + case PACKAGE: + return "doclet.deprecated_packages"; + case INTERFACE: + return "doclet.deprecated_interfaces"; + case CLASS: + return "doclet.deprecated_classes"; + case ENUM: + return "doclet.deprecated_enums"; + case EXCEPTION: + return "doclet.deprecated_exceptions"; + case ERROR: + return "doclet.deprecated_errors"; + case ANNOTATION_TYPE: + return "doclet.deprecated_annotation_types"; + case FIELD: + return "doclet.deprecated_fields"; + case METHOD: + return "doclet.deprecated_methods"; + case CONSTRUCTOR: + return "doclet.deprecated_constructors"; + case ENUM_CONSTANT: + return "doclet.deprecated_enum_constants"; + case ANNOTATION_TYPE_MEMBER: + return "doclet.deprecated_annotation_type_members"; + default: + throw new AssertionError("unknown kind: " + kind); + } + } + + private String getHeaderKey(DeprElementKind kind) { + switch (kind) { + case PACKAGE: + return "doclet.Package"; + case INTERFACE: + return "doclet.Interface"; + case CLASS: + return "doclet.Class"; + case ENUM: + return "doclet.Enum"; + case EXCEPTION: + return "doclet.Exceptions"; + case ERROR: + return "doclet.Errors"; + case ANNOTATION_TYPE: + return "doclet.AnnotationType"; + case FIELD: + return "doclet.Field"; + case METHOD: + return "doclet.Method"; + case CONSTRUCTOR: + return "doclet.Constructor"; + case ENUM_CONSTANT: + return "doclet.Enum_Constant"; + case ANNOTATION_TYPE_MEMBER: + return "doclet.Annotation_Type_Member"; + default: + throw new AssertionError("unknown kind: " + kind); + } + } + + private EnumMap writerMap; + + private ConfigurationImpl configuration; + + /** + * Constructor. + * + * @param filename the file to be generated. + */ + + public DeprecatedListWriter(ConfigurationImpl configuration, + DocPath filename) throws IOException { + super(configuration, filename); + this.configuration = configuration; + NestedClassWriterImpl classW = new NestedClassWriterImpl(this); + writerMap = new EnumMap<>(DeprElementKind.class); + for (DeprElementKind kind : DeprElementKind.values()) { + switch (kind) { + case PACKAGE: + case INTERFACE: + case CLASS: + case ENUM: + case EXCEPTION: + case ERROR: + case ANNOTATION_TYPE: + writerMap.put(kind, classW); + break; + case FIELD: + writerMap.put(kind, new FieldWriterImpl(this)); + break; + case METHOD: + writerMap.put(kind, new MethodWriterImpl(this)); + break; + case CONSTRUCTOR: + writerMap.put(kind, new ConstructorWriterImpl(this)); + break; + case ENUM_CONSTANT: + writerMap.put(kind, new EnumConstantWriterImpl(this)); + break; + case ANNOTATION_TYPE_MEMBER: + writerMap.put(kind, new AnnotationTypeOptionalMemberWriterImpl(this, null)); + break; + default: + throw new AssertionError("unknown kind: " + kind); + } + } + } + + /** + * Get list of all the deprecated classes and members in all the Packages + * specified on the Command Line. + * Then instantiate DeprecatedListWriter and generate File. + * + * @param configuration the current configuration of the doclet. + */ + public static void generate(ConfigurationImpl configuration) { + DocPath filename = DocPaths.DEPRECATED_LIST; + try { + DeprecatedListWriter depr = + new DeprecatedListWriter(configuration, filename); + depr.generateDeprecatedListFile( + new DeprecatedAPIListBuilder(configuration)); + depr.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the deprecated API list. + * + * @param deprapi list of deprecated API built already. + */ + protected void generateDeprecatedListFile(DeprecatedAPIListBuilder deprapi) + throws IOException { + HtmlTree body = getHeader(); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN() + : body; + htmlTree.addContent(getContentsList(deprapi)); + String memberTableSummary; + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.contentContainer); + for (DeprElementKind kind : DeprElementKind.values()) { + if (deprapi.hasDocumentation(kind)) { + addAnchor(deprapi, kind, div); + memberTableSummary = + configuration.getText("doclet.Member_Table_Summary", + configuration.getText(getHeadingKey(kind)), + configuration.getText(getSummaryKey(kind))); + List memberTableHeader = new ArrayList<>(); + memberTableHeader.add(configuration.getText("doclet.0_and_1", + configuration.getText(getHeaderKey(kind)), + configuration.getText("doclet.Description"))); + if (kind == DeprElementKind.PACKAGE) + addPackageDeprecatedAPI(deprapi.getSet(kind), + getHeadingKey(kind), memberTableSummary, memberTableHeader, div); + else + writerMap.get(kind).addDeprecatedAPI(deprapi.getSet(kind), + getHeadingKey(kind), memberTableSummary, memberTableHeader, div); + } + } + if (configuration.allowTag(HtmlTag.MAIN)) { + htmlTree.addContent(div); + body.addContent(htmlTree); + } else { + body.addContent(div); + } + htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : body; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add the index link. + * + * @param builder the deprecated list builder + * @param type the type of list being documented + * @param contentTree the content tree to which the index link will be added + */ + private void addIndexLink(DeprecatedAPIListBuilder builder, + DeprElementKind kind, Content contentTree) { + if (builder.hasDocumentation(kind)) { + Content li = HtmlTree.LI(getHyperLink(getAnchorName(kind), + getResource(getHeadingKey(kind)))); + contentTree.addContent(li); + } + } + + /** + * Get the contents list. + * + * @param deprapi the deprecated list builder + * @return a content tree for the contents list + */ + public Content getContentsList(DeprecatedAPIListBuilder deprapi) { + Content headContent = getResource("doclet.Deprecated_API"); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.title, headContent); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + Content headingContent = getResource("doclet.Contents"); + div.addContent(HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, true, + headingContent)); + Content ul = new HtmlTree(HtmlTag.UL); + for (DeprElementKind kind : DeprElementKind.values()) { + addIndexLink(deprapi, kind, ul); + } + div.addContent(ul); + return div; + } + + /** + * Add the anchor. + * + * @param builder the deprecated list builder + * @param type the type of list being documented + * @param htmlTree the content tree to which the anchor will be added + */ + private void addAnchor(DeprecatedAPIListBuilder builder, DeprElementKind kind, Content htmlTree) { + if (builder.hasDocumentation(kind)) { + htmlTree.addContent(getMarkerAnchor(getAnchorName(kind))); + } + } + + /** + * Get the header for the deprecated API Listing. + * + * @return a content tree for the header + */ + public HtmlTree getHeader() { + String title = configuration.getText("doclet.Window_Deprecated_List"); + HtmlTree bodyTree = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + return bodyTree; + } + + /** + * Get the deprecated label. + * + * @return a content tree for the deprecated label + */ + protected Content getNavLinkDeprecated() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, deprecatedLabel); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriterImpl.java new file mode 100644 index 00000000000..fc8bf385bb6 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/EnumConstantWriterImpl.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.EnumConstantWriter; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + +/** + * Writes enum constant documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class EnumConstantWriterImpl extends AbstractMemberWriter + implements EnumConstantWriter, MemberSummaryWriter { + + public EnumConstantWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + public EnumConstantWriterImpl(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_ENUM_CONSTANT_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getEnumConstantsDetailsTreeHeader(TypeElement typeElement, + Content memberDetailsTree) { + memberDetailsTree.addContent(HtmlConstants.START_OF_ENUM_CONSTANT_DETAILS); + Content enumConstantsDetailsTree = writer.getMemberTreeHeader(); + enumConstantsDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.ENUM_CONSTANT_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.enumConstantsDetailsLabel); + enumConstantsDetailsTree.addContent(heading); + return enumConstantsDetailsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getEnumConstantsTreeHeader(VariableElement enumConstant, + Content enumConstantsDetailsTree) { + enumConstantsDetailsTree.addContent( + writer.getMarkerAnchor(name(enumConstant))); + Content enumConstantsTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(name(enumConstant)); + enumConstantsTree.addContent(heading); + return enumConstantsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getSignature(VariableElement enumConstant) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(enumConstant, pre); + addModifiers(enumConstant, pre); + Content enumConstantLink = writer.getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.MEMBER, enumConstant.asType())); + pre.addContent(enumConstantLink); + pre.addContent(" "); + if (configuration.linksource) { + Content enumConstantName = new StringContent(name(enumConstant)); + writer.addSrcLink(enumConstant, enumConstantName, pre); + } else { + addName(name(enumConstant), pre); + } + return pre; + } + + /** + * {@inheritDoc} + */ + @Override + public void addDeprecated(VariableElement enumConstant, Content enumConstantsTree) { + addDeprecatedInfo(enumConstant, enumConstantsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addComments(VariableElement enumConstant, Content enumConstantsTree) { + addComment(enumConstant, enumConstantsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addTags(VariableElement enumConstant, Content enumConstantsTree) { + writer.addTagsInfo(enumConstant, enumConstantsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getEnumConstantsDetails(Content enumConstantsDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(enumConstantsDetailsTree)); + return htmlTree; + } + return getMemberTree(enumConstantsDetailsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getEnumConstants(Content enumConstantsTree, + boolean isLastContent) { + return getMemberTree(enumConstantsTree, isLastContent); + } + + /** + * {@inheritDoc} + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Enum_Constant_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Enum_Constant_Summary"), + configuration.getText("doclet.enum_constants")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Enum_Constants"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Enum_Constant"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.ENUM_CONSTANT_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, member, name(member), false)); + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + @Override + public void setSummaryColumnStyle(HtmlTree tdTree) { + tdTree.addStyle(HtmlStyle.colOne); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addInheritedSummaryLink(TypeElement typeElement, Element member, Content linksTree) { + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + //Not applicable. + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getDeprecatedLink(Element member) { + String name = utils.getFullyQualifiedName(member) + "." + member.getSimpleName(); + return writer.getDocLink(LinkInfoImpl.Kind.MEMBER, member, name); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + if (typeElement == null) { + return writer.getHyperLink(SectionName.ENUM_CONSTANT_SUMMARY, + writer.getResource("doclet.navEnum")); + } else { + return writer.getHyperLink( + SectionName.ENUM_CONSTANTS_INHERITANCE, + configuration.getClassName(typeElement), writer.getResource("doclet.navEnum")); + } + } else { + return writer.getResource("doclet.navEnum"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.ENUM_CONSTANT_DETAIL, + writer.getResource("doclet.navEnum"))); + } else { + liNav.addContent(writer.getResource("doclet.navEnum")); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java new file mode 100644 index 00000000000..b5e6c03b5f1 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.FieldWriter; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + +/** + * Writes field documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Jamie Ho (rewrite) + * @author Bhavesh Patel (Modified) + */ +public class FieldWriterImpl extends AbstractMemberWriter + implements FieldWriter, MemberSummaryWriter { + + public FieldWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + public FieldWriterImpl(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_FIELD_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getFieldDetailsTreeHeader(TypeElement typeElement, Content memberDetailsTree) { + memberDetailsTree.addContent(HtmlConstants.START_OF_FIELD_DETAILS); + Content fieldDetailsTree = writer.getMemberTreeHeader(); + fieldDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.FIELD_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.fieldDetailsLabel); + fieldDetailsTree.addContent(heading); + return fieldDetailsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getFieldDocTreeHeader(VariableElement field, Content fieldDetailsTree) { + fieldDetailsTree.addContent(writer.getMarkerAnchor(name(field))); + Content fieldTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(name(field)); + fieldTree.addContent(heading); + return fieldTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getSignature(VariableElement field) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(field, pre); + addModifiers(field, pre); + Content fieldlink = writer.getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.MEMBER, field.asType())); + pre.addContent(fieldlink); + pre.addContent(" "); + if (configuration.linksource) { + Content fieldName = new StringContent(name(field)); + writer.addSrcLink(field, fieldName, pre); + } else { + addName(name(field), pre); + } + return pre; + } + + /** + * {@inheritDoc} + */ + @Override + public void addDeprecated(VariableElement field, Content fieldTree) { + addDeprecatedInfo(field, fieldTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addComments(VariableElement field, Content fieldTree) { + if (!utils.getBody(field).isEmpty()) { + writer.addInlineComment(field, fieldTree); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addTags(VariableElement field, Content fieldTree) { + writer.addTagsInfo(field, fieldTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getFieldDetails(Content fieldDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(fieldDetailsTree)); + return htmlTree; + } + return getMemberTree(fieldDetailsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getFieldDoc(Content fieldTree, + boolean isLastContent) { + return getMemberTree(fieldTree, isLastContent); + } + + /** + * Close the writer. + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Field_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Field_Summary"), + configuration.getText("doclet.fields")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Fields"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Field"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.FIELD_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + inheritedTree.addContent(writer.getMarkerAnchor( + SectionName.FIELDS_INHERITANCE, configuration.getClassName(typeElement))); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + Content classLink = writer.getPreQualifiedClassLink( + LinkInfoImpl.Kind.MEMBER, typeElement, false); + Content label = new StringContent(utils.isClass(typeElement) + ? configuration.getText("doclet.Fields_Inherited_From_Class") + : configuration.getText("doclet.Fields_Inherited_From_Interface")); + Content labelHeading = HtmlTree.HEADING(HtmlConstants.INHERITED_SUMMARY_HEADING, + label); + labelHeading.addContent(writer.getSpace()); + labelHeading.addContent(classLink); + inheritedTree.addContent(labelHeading); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, typeElement , member, name(member), false)); + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addInheritedSummaryLink(TypeElement typeElement, Element member, Content linksTree) { + linksTree.addContent( + writer.getDocLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, + name(member), false)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + addModifierAndType(member, member.asType(), tdSummaryType); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getDeprecatedLink(Element member) { + String name = utils.getFullyQualifiedName(member) + "." + member.getSimpleName(); + return writer.getDocLink(LinkInfoImpl.Kind.MEMBER, member, name); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + if (typeElement == null) { + return writer.getHyperLink( + SectionName.FIELD_SUMMARY, + writer.getResource("doclet.navField")); + } else { + return writer.getHyperLink( + SectionName.FIELDS_INHERITANCE, + configuration.getClassName(typeElement), writer.getResource("doclet.navField")); + } + } else { + return writer.getResource("doclet.navField"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.FIELD_DETAIL, + writer.getResource("doclet.navField"))); + } else { + liNav.addContent(writer.getResource("doclet.navField")); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java new file mode 100644 index 00000000000..69be1be292e --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Generate the documentation in the Html "frame" format in the browser. The + * generated documentation will have two or three frames depending upon the + * number of packages on the command line. In general there will be three frames + * in the output, a left-hand top frame will have a list of all packages with + * links to target left-hand bottom frame. The left-hand bottom frame will have + * the particular package contents or the all-classes list, where as the single + * right-hand frame will have overview or package summary or class file. Also + * take care of browsers which do not support Html frames. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + */ +public class FrameOutputWriter extends HtmlDocletWriter { + + /** + * Number of packages specified on the command line. + */ + int noOfPackages; + + /** + * Constructor to construct FrameOutputWriter object. + * + * @param configuration for this run + * @param filename File to be generated. + * @throws java.io.IOException + */ + public FrameOutputWriter(ConfigurationImpl configuration, DocPath filename) throws IOException { + super(configuration, filename); + noOfPackages = configuration.packages.size(); + } + + /** + * Construct FrameOutputWriter object and then use it to generate the Html + * file which will have the description of all the frames in the + * documentation. The name of the generated file is "index.html" which is + * the default first file for Html documents. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration) { + FrameOutputWriter framegen; + DocPath filename = DocPath.empty; + try { + filename = DocPaths.INDEX; + framegen = new FrameOutputWriter(configuration, filename); + framegen.generateFrameFile(); + framegen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the constants in the "index.html" file. Print the frame details + * as well as warning if browser is not supporting the Html frames. + */ + protected void generateFrameFile() throws IOException { + Content frame = getFrameDetails(); + HtmlTree body = new HtmlTree(HtmlTag.BODY); + if (configuration.allowTag(HtmlTag.MAIN)) { + HtmlTree main = HtmlTree.MAIN(frame); + body.addContent(main); + } else { + body.addContent(frame); + } + if (configuration.windowtitle.length() > 0) { + printFramesDocument(configuration.windowtitle, configuration, + body); + } else { + printFramesDocument(configuration.getText("doclet.Generated_Docs_Untitled"), + configuration, body); + } + } + + /** + * Get the frame sizes and their contents. + * + * @return a content tree for the frame details + */ + protected Content getFrameDetails() { + HtmlTree leftContainerDiv = new HtmlTree(HtmlTag.DIV); + HtmlTree rightContainerDiv = new HtmlTree(HtmlTag.DIV); + leftContainerDiv.addStyle(HtmlStyle.leftContainer); + rightContainerDiv.addStyle(HtmlStyle.rightContainer); + if (noOfPackages <= 1) { + addAllClassesFrameTag(leftContainerDiv); + } else if (noOfPackages > 1) { + addAllPackagesFrameTag(leftContainerDiv); + addAllClassesFrameTag(leftContainerDiv); + } + addClassFrameTag(rightContainerDiv); + HtmlTree mainContainer = HtmlTree.DIV(HtmlStyle.mainContainer, leftContainerDiv); + mainContainer.addContent(rightContainerDiv); + return mainContainer; + } + + /** + * Add the IFRAME tag for the frame that lists all packages. + * + * @param contentTree the content tree to which the information will be added + */ + private void addAllPackagesFrameTag(Content contentTree) { + HtmlTree frame = HtmlTree.IFRAME(DocPaths.OVERVIEW_FRAME.getPath(), + "packageListFrame", configuration.getText("doclet.All_Packages")); + HtmlTree leftTop = HtmlTree.DIV(HtmlStyle.leftTop, frame); + contentTree.addContent(leftTop); + } + + /** + * Add the IFRAME tag for the frame that lists all classes. + * + * @param contentTree the content tree to which the information will be added + */ + private void addAllClassesFrameTag(Content contentTree) { + HtmlTree frame = HtmlTree.IFRAME(DocPaths.ALLCLASSES_FRAME.getPath(), + "packageFrame", configuration.getText("doclet.All_classes_and_interfaces")); + HtmlTree leftBottom = HtmlTree.DIV(HtmlStyle.leftBottom, frame); + contentTree.addContent(leftBottom); + } + + /** + * Add the IFRAME tag for the frame that describes the class in detail. + * + * @param contentTree the content tree to which the information will be added + */ + private void addClassFrameTag(Content contentTree) { + HtmlTree frame = HtmlTree.IFRAME(configuration.topFile.getPath(), "classFrame", + configuration.getText("doclet.Package_class_and_interface_descriptions")); + frame.addStyle(HtmlStyle.rightIframe); + contentTree.addContent(frame); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java new file mode 100644 index 00000000000..784cabd3bc9 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java @@ -0,0 +1,449 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Generate the Help File for the generated API documentation. The help file + * contents are helpful for browsing the generated documentation. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + */ +public class HelpWriter extends HtmlDocletWriter { + + HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * Constructor to construct HelpWriter object. + * @param filename File to be generated. + */ + public HelpWriter(ConfigurationImpl configuration, + DocPath filename) throws IOException { + super(configuration, filename); + } + + /** + * Construct the HelpWriter object and then use it to generate the help + * file. The name of the generated file is "help-doc.html". The help file + * will get generated if and only if "-helpfile" and "-nohelp" is not used + * on the command line. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration) { + HelpWriter helpgen; + DocPath filename = DocPath.empty; + try { + filename = DocPaths.HELP_DOC; + helpgen = new HelpWriter(configuration, filename); + helpgen.generateHelpFile(); + helpgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the help file contents. + */ + protected void generateHelpFile() throws IOException { + String title = configuration.getText("doclet.Window_Help_title"); + HtmlTree body = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : body; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + body.addContent(htmlTree); + } + addHelpFileContents(body); + if (configuration.allowTag(HtmlTag.FOOTER)) { + htmlTree = HtmlTree.FOOTER(); + } + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add the help file contents from the resource file to the content tree. While adding the + * help file contents it also keeps track of user options. If "-notree" + * is used, then the "overview-tree.html" will not get added and hence + * help information also will not get added. + * + * @param contentTree the content tree to which the help file contents will be added + */ + protected void addHelpFileContents(Content contentTree) { + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, false, HtmlStyle.title, + getResource("doclet.Help_line_1")); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + Content line2 = HtmlTree.DIV(HtmlStyle.subTitle, + getResource("doclet.Help_line_2")); + div.addContent(line2); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + contentTree.addContent(div); + } + HtmlTree htmlTree; + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + if (configuration.createoverview) { + Content overviewHeading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Overview")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(overviewHeading) + : HtmlTree.LI(HtmlStyle.blockList, overviewHeading); + Content line3 = getResource("doclet.Help_line_3", + getHyperLink(DocPaths.OVERVIEW_SUMMARY, + configuration.getText("doclet.Overview"))); + Content overviewPara = HtmlTree.P(line3); + htmlTree.addContent(overviewPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + Content packageHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Package")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(packageHead) + : HtmlTree.LI(HtmlStyle.blockList, packageHead); + Content line4 = getResource("doclet.Help_line_4"); + Content packagePara = HtmlTree.P(line4); + htmlTree.addContent(packagePara); + HtmlTree ulPackage = new HtmlTree(HtmlTag.UL); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.Interfaces_Italic"))); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.Classes"))); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.Enums"))); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.Exceptions"))); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.Errors"))); + ulPackage.addContent(HtmlTree.LI( + getResource("doclet.AnnotationTypes"))); + htmlTree.addContent(ulPackage); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content classHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_5")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(classHead) + : HtmlTree.LI(HtmlStyle.blockList, classHead); + Content line6 = getResource("doclet.Help_line_6"); + Content classPara = HtmlTree.P(line6); + htmlTree.addContent(classPara); + HtmlTree ul1 = new HtmlTree(HtmlTag.UL); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_7"))); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_8"))); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_9"))); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_10"))); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_11"))); + ul1.addContent(HtmlTree.LI( + getResource("doclet.Help_line_12"))); + htmlTree.addContent(ul1); + HtmlTree ul2 = new HtmlTree(HtmlTag.UL); + ul2.addContent(HtmlTree.LI( + getResource("doclet.Nested_Class_Summary"))); + ul2.addContent(HtmlTree.LI( + getResource("doclet.Field_Summary"))); + ul2.addContent(HtmlTree.LI( + getResource("doclet.Constructor_Summary"))); + ul2.addContent(HtmlTree.LI( + getResource("doclet.Method_Summary"))); + htmlTree.addContent(ul2); + HtmlTree ul3 = new HtmlTree(HtmlTag.UL); + ul3.addContent(HtmlTree.LI( + getResource("doclet.Field_Detail"))); + ul3.addContent(HtmlTree.LI( + getResource("doclet.Constructor_Detail"))); + ul3.addContent(HtmlTree.LI( + getResource("doclet.Method_Detail"))); + htmlTree.addContent(ul3); + Content line13 = getResource("doclet.Help_line_13"); + Content para = HtmlTree.P(line13); + htmlTree.addContent(para); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + //Annotation Types + Content aHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.AnnotationType")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(aHead) + : HtmlTree.LI(HtmlStyle.blockList, aHead); + Content aline1 = getResource("doclet.Help_annotation_type_line_1"); + Content aPara = HtmlTree.P(aline1); + htmlTree.addContent(aPara); + HtmlTree aul = new HtmlTree(HtmlTag.UL); + aul.addContent(HtmlTree.LI( + getResource("doclet.Help_annotation_type_line_2"))); + aul.addContent(HtmlTree.LI( + getResource("doclet.Help_annotation_type_line_3"))); + aul.addContent(HtmlTree.LI( + getResource("doclet.Annotation_Type_Required_Member_Summary"))); + aul.addContent(HtmlTree.LI( + getResource("doclet.Annotation_Type_Optional_Member_Summary"))); + aul.addContent(HtmlTree.LI( + getResource("doclet.Annotation_Type_Member_Detail"))); + htmlTree.addContent(aul); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + //Enums + Content enumHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Enum")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(enumHead) + : HtmlTree.LI(HtmlStyle.blockList, enumHead); + Content eline1 = getResource("doclet.Help_enum_line_1"); + Content enumPara = HtmlTree.P(eline1); + htmlTree.addContent(enumPara); + HtmlTree eul = new HtmlTree(HtmlTag.UL); + eul.addContent(HtmlTree.LI( + getResource("doclet.Help_enum_line_2"))); + eul.addContent(HtmlTree.LI( + getResource("doclet.Help_enum_line_3"))); + eul.addContent(HtmlTree.LI( + getResource("doclet.Enum_Constant_Summary"))); + eul.addContent(HtmlTree.LI( + getResource("doclet.Enum_Constant_Detail"))); + htmlTree.addContent(eul); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + if (configuration.classuse) { + Content useHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_14")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(useHead) + : HtmlTree.LI(HtmlStyle.blockList, useHead); + Content line15 = getResource("doclet.Help_line_15"); + Content usePara = HtmlTree.P(line15); + htmlTree.addContent(usePara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + if (configuration.createtree) { + Content treeHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_16")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(treeHead) + : HtmlTree.LI(HtmlStyle.blockList, treeHead); + Content line17 = getResource("doclet.Help_line_17_with_tree_link", + getHyperLink(DocPaths.OVERVIEW_TREE, + configuration.getText("doclet.Class_Hierarchy")), + HtmlTree.CODE(new StringContent("java.lang.Object"))); + Content treePara = HtmlTree.P(line17); + htmlTree.addContent(treePara); + HtmlTree tul = new HtmlTree(HtmlTag.UL); + tul.addContent(HtmlTree.LI( + getResource("doclet.Help_line_18"))); + tul.addContent(HtmlTree.LI( + getResource("doclet.Help_line_19"))); + htmlTree.addContent(tul); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + if (!(configuration.nodeprecatedlist || + configuration.nodeprecated)) { + Content dHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Deprecated_API")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(dHead) + : HtmlTree.LI(HtmlStyle.blockList, dHead); + Content line20 = getResource("doclet.Help_line_20_with_deprecated_api_link", + getHyperLink(DocPaths.DEPRECATED_LIST, + configuration.getText("doclet.Deprecated_API"))); + Content dPara = HtmlTree.P(line20); + htmlTree.addContent(dPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + if (configuration.createindex) { + Content indexlink; + if (configuration.splitindex) { + indexlink = getHyperLink(DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)), + configuration.getText("doclet.Index")); + } else { + indexlink = getHyperLink(DocPaths.INDEX_ALL, + configuration.getText("doclet.Index")); + } + Content indexHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_21")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(indexHead) + : HtmlTree.LI(HtmlStyle.blockList, indexHead); + Content line22 = getResource("doclet.Help_line_22", indexlink); + Content indexPara = HtmlTree.P(line22); + htmlTree.addContent(indexPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + } + Content prevHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_23")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(prevHead) + : HtmlTree.LI(HtmlStyle.blockList, prevHead); + Content line24 = getResource("doclet.Help_line_24"); + Content prevPara = HtmlTree.P(line24); + htmlTree.addContent(prevPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content frameHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Help_line_25")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(frameHead) + : HtmlTree.LI(HtmlStyle.blockList, frameHead); + Content line26 = getResource("doclet.Help_line_26"); + Content framePara = HtmlTree.P(line26); + htmlTree.addContent(framePara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content allclassesHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.All_Classes")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(allclassesHead) + : HtmlTree.LI(HtmlStyle.blockList, allclassesHead); + Content line27 = getResource("doclet.Help_line_27", + getHyperLink(DocPaths.ALLCLASSES_NOFRAME, + configuration.getText("doclet.All_Classes"))); + Content allclassesPara = HtmlTree.P(line27); + htmlTree.addContent(allclassesPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content sHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Serialized_Form")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(sHead) + : HtmlTree.LI(HtmlStyle.blockList, sHead); + Content line28 = getResource("doclet.Help_line_28"); + Content serialPara = HtmlTree.P(line28); + htmlTree.addContent(serialPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content constHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + getResource("doclet.Constants_Summary")); + htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION(constHead) + : HtmlTree.LI(HtmlStyle.blockList, constHead); + Content line29 = getResource("doclet.Help_line_29", + getHyperLink(DocPaths.CONSTANT_VALUES, + configuration.getText("doclet.Constants_Summary"))); + Content constPara = HtmlTree.P(line29); + htmlTree.addContent(constPara); + if (configuration.allowTag(HtmlTag.SECTION)) { + ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree)); + } else { + ul.addContent(htmlTree); + } + Content divContent = HtmlTree.DIV(HtmlStyle.contentContainer, ul); + Content line30 = HtmlTree.SPAN(HtmlStyle.emphasizedPhrase, getResource("doclet.Help_line_30")); + divContent.addContent(line30); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(divContent); + contentTree.addContent(mainTree); + } else { + contentTree.addContent(divContent); + } + } + + /** + * Get the help label. + * + * @return a content tree for the help label + */ + @Override + protected Content getNavLinkHelp() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, helpLabel); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java new file mode 100644 index 00000000000..b0f8ad9b418 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.doclet.Doclet.Option; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; +import jdk.javadoc.internal.doclets.toolkit.AbstractDoclet; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.builders.AbstractBuilder; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; + +/** + * The class with "start" method, calls individual Writers. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Robert Field + * @author Jamie Ho + * + */ +public class HtmlDoclet extends AbstractDoclet { + + public HtmlDoclet() { + configuration = new ConfigurationImpl(); + } + + /** + * The global configuration information for this run. + */ + public final ConfigurationImpl configuration; + + + private static final DocPath DOCLET_RESOURCES = DocPath + .create("/jdk/javadoc/internal/doclets/formats/html/resources"); + + public void init(Locale locale, Reporter reporter) { + configuration.reporter = reporter; + configuration.locale = locale; + } + + /** + * The "start" method as required by Javadoc. + * + * @param root the root of the documentation tree. + * @see jdk.doclet.DocletEnvironment + * @return true if the doclet ran without encountering any errors. + */ + public boolean run(DocletEnvironment root) { + return startDoclet(root); + } + + /** + * Create the configuration instance. + * Override this method to use a different + * configuration. + */ + public Configuration configuration() { + return configuration; + } + + /** + * Start the generation of files. Call generate methods in the individual + * writers, which will in turn genrate the documentation files. Call the + * TreeWriter generation first to ensure the Class Hierarchy is built + * first and then can be used in the later generation. + * + * For new format. + * + * @see jdk.doclet.RootDoc + */ + protected void generateOtherFiles(DocletEnvironment root, ClassTree classtree) + throws Exception { + super.generateOtherFiles(root, classtree); + if (configuration.linksource) { + SourceToHTMLConverter.convertRoot(configuration, + root, DocPaths.SOURCE_OUTPUT); + } + + if (configuration.topFile.isEmpty()) { + configuration.standardmessage. + error("doclet.No_Non_Deprecated_Classes_To_Document"); + return; + } + boolean nodeprecated = configuration.nodeprecated; + performCopy(configuration.helpfile); + performCopy(configuration.stylesheetfile); + // do early to reduce memory footprint + if (configuration.classuse) { + ClassUseWriter.generate(configuration, classtree); + } + IndexBuilder indexbuilder = new IndexBuilder(configuration, nodeprecated); + + if (configuration.createtree) { + TreeWriter.generate(configuration, classtree); + } + if (configuration.createindex) { + configuration.buildSearchTagIndex(); + if (configuration.splitindex) { + SplitIndexWriter.generate(configuration, indexbuilder); + } else { + SingleIndexWriter.generate(configuration, indexbuilder); + } + } + + if (!(configuration.nodeprecatedlist || nodeprecated)) { + DeprecatedListWriter.generate(configuration); + } + + AllClassesFrameWriter.generate(configuration, + new IndexBuilder(configuration, nodeprecated, true)); + + FrameOutputWriter.generate(configuration); + + if (configuration.createoverview) { + PackageIndexWriter.generate(configuration); + } + if (configuration.helpfile.length() == 0 && + !configuration.nohelp) { + HelpWriter.generate(configuration); + } + // If a stylesheet file is not specified, copy the default stylesheet + // and replace newline with platform-specific newline. + DocFile f; + if (configuration.stylesheetfile.length() == 0) { + f = DocFile.createFileForOutput(configuration, DocPaths.STYLESHEET); + f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.STYLESHEET), false, true); + } + f = DocFile.createFileForOutput(configuration, DocPaths.JAVASCRIPT); + f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.JAVASCRIPT), true, true); + if (configuration.createindex) { + f = DocFile.createFileForOutput(configuration, DocPaths.SEARCH_JS); + f.copyResource(DOCLET_RESOURCES.resolve(DocPaths.SEARCH_JS), true, true); + + f = DocFile.createFileForOutput(configuration, DocPaths.RESOURCES.resolve(DocPaths.GLASS_IMG)); + f.copyResource(DOCLET_RESOURCES.resolve(DocPaths.GLASS_IMG), true, false); + + f = DocFile.createFileForOutput(configuration, DocPaths.RESOURCES.resolve(DocPaths.X_IMG)); + f.copyResource(DOCLET_RESOURCES.resolve(DocPaths.X_IMG), true, false); + copyJqueryFiles(); + } + } + + protected void copyJqueryFiles() { + List files = Arrays.asList( + "jquery-1.10.2.js", + "jquery-ui.js", + "jquery-ui.css", + "jquery-ui.min.js", + "jquery-ui.min.css", + "jquery-ui.structure.min.css", + "jquery-ui.structure.css", + "external/jquery/jquery.js", + "jszip/dist/jszip.js", + "jszip/dist/jszip.min.js", + "jszip-utils/dist/jszip-utils.js", + "jszip-utils/dist/jszip-utils.min.js", + "jszip-utils/dist/jszip-utils-ie.js", + "jszip-utils/dist/jszip-utils-ie.min.js", + "images/ui-bg_flat_0_aaaaaa_40x100.png", + "images/ui-icons_454545_256x240.png", + "images/ui-bg_glass_95_fef1ec_1x400.png", + "images/ui-bg_glass_75_dadada_1x400.png", + "images/ui-bg_highlight-soft_75_cccccc_1x100.png", + "images/ui-icons_888888_256x240.png", + "images/ui-icons_2e83ff_256x240.png", + "images/ui-bg_glass_65_ffffff_1x400.png", + "images/ui-icons_cd0a0a_256x240.png", + "images/ui-bg_glass_55_fbf9ee_1x400.png", + "images/ui-icons_222222_256x240.png", + "images/ui-bg_glass_75_e6e6e6_1x400.png", + "images/ui-bg_flat_75_ffffff_40x100.png"); + DocFile f; + for (String file : files) { + DocPath filePath = DocPaths.JQUERY_FILES.resolve(file); + f = DocFile.createFileForOutput(configuration, filePath); + f.copyResource(DOCLET_RESOURCES.resolve(filePath), true, false); + } + } + + /** + * {@inheritDoc} + */ + protected void generateClassFiles(SortedSet arr, ClassTree classtree) { + List list = new ArrayList<>(arr); + ListIterator iterator = list.listIterator(); + TypeElement klass = null; + while (iterator.hasNext()) { + TypeElement prev = iterator.hasPrevious() ? klass : null; + klass = iterator.next(); + TypeElement next = iterator.nextIndex() == list.size() + ? null : list.get(iterator.nextIndex()); + if (!(configuration.isGeneratedDoc(klass) && utils.isIncluded(klass))) { + continue; + } + try { + if (utils.isAnnotationType(klass)) { + AbstractBuilder annotationTypeBuilder = + configuration.getBuilderFactory() + .getAnnotationTypeBuilder(klass, + prev == null ? null : prev.asType(), + next == null ? null : next.asType()); + annotationTypeBuilder.build(); + } else { + AbstractBuilder classBuilder = + configuration.getBuilderFactory().getClassBuilder(klass, + prev, next, classtree); + classBuilder.build(); + } + } catch (IOException e) { + throw new DocletAbortException(e); + } catch (DocletAbortException de) { + throw de; + } catch (Exception e) { + e.printStackTrace(); + throw new DocletAbortException(e); + } + } + } + + + /** + * {@inheritDoc} + */ + protected void generatePackageFiles(ClassTree classtree) throws Exception { + Set packages = configuration.packages; + if (packages.size() > 1) { + PackageIndexFrameWriter.generate(configuration); + } + List pList = new ArrayList<>(packages); + PackageElement prev = null; + for (int i = 0 ; i < pList.size() ; i++) { + // if -nodeprecated option is set and the package is marked as + // deprecated, do not generate the package-summary.html, package-frame.html + // and package-tree.html pages for that package. + PackageElement pkg = pList.get(i); + if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) { + PackageFrameWriter.generate(configuration, pkg); + int nexti = i + 1; + PackageElement next = null; + if (nexti < pList.size()) { + next = pList.get(nexti); + // If the next package is unnamed package, skip 2 ahead if possible + if (next.isUnnamed() && ++nexti < pList.size()) { + next = pList.get(nexti); + } + } + AbstractBuilder packageSummaryBuilder = + configuration.getBuilderFactory().getPackageSummaryBuilder( + pkg, prev, next); + packageSummaryBuilder.build(); + if (configuration.createtree) { + PackageTreeWriter.generate(configuration, pkg, prev, next, + configuration.nodeprecated); + } + prev = pkg; + } + } + } + + public Set

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Robert Field + * @author Bhavesh Patel (Modified) + */ +public class HtmlDocletWriter extends HtmlDocWriter { + + /** + * Relative path from the file getting generated to the destination + * directory. For example, if the file getting generated is + * "java/lang/Object.html", then the path to the root is "../..". + * This string can be empty if the file getting generated is in + * the destination directory. + */ + public final DocPath pathToRoot; + + /** + * Platform-independent path from the current or the + * destination directory to the file getting generated. + * Used when creating the file. + */ + public final DocPath path; + + /** + * Name of the file getting generated. If the file getting generated is + * "java/lang/Object.html", then the filename is "Object.html". + */ + public final DocPath filename; + + /** + * The global configuration information for this run. + */ + public final ConfigurationImpl configuration; + + protected final Utils utils; + + /** + * To check whether annotation heading is printed or not. + */ + protected boolean printedAnnotationHeading = false; + + /** + * To check whether annotation field heading is printed or not. + */ + protected boolean printedAnnotationFieldHeading = false; + + /** + * To check whether the repeated annotations is documented or not. + */ + private boolean isAnnotationDocumented = false; + + /** + * To check whether the container annotations is documented or not. + */ + private boolean isContainerDocumented = false; + + HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV); + + /** + * Constructor to construct the HtmlStandardWriter object. + * + * @param path File to be generated. + */ + public HtmlDocletWriter(ConfigurationImpl configuration, DocPath path) + throws IOException { + super(configuration, path); + this.configuration = configuration; + this.utils = configuration.utils; + this.path = path; + this.pathToRoot = path.parent().invert(); + this.filename = path.basename(); + } + + /** + * Replace {@docRoot} tag used in options that accept HTML text, such + * as -header, -footer, -top and -bottom, and when converting a relative + * HREF where commentTagsToString inserts a {@docRoot} where one was + * missing. (Also see DocRootTaglet for {@docRoot} tags in doc + * comments.) + *

+ * Replace {@docRoot} tag in htmlstr with the relative path to the + * destination directory from the directory where the file is being + * written, looping to handle all such tags in htmlstr. + *

+ * For example, for "-d docs" and -header containing {@docRoot}, when + * the HTML page for source file p/C1.java is being generated, the + * {@docRoot} tag would be inserted into the header as "../", + * the relative path from docs/p/ to docs/ (the document root). + *

+ * Note: This doc comment was written with '&#064;' representing '@' + * to prevent the inline tag from being interpreted. + */ + public String replaceDocRootDir(String htmlstr) { + // Return if no inline tags exist + int index = htmlstr.indexOf("{@"); + if (index < 0) { + return htmlstr; + } + Matcher docrootMatcher = docrootPattern.matcher(htmlstr); + if (!docrootMatcher.find()) { + return htmlstr; + } + StringBuilder buf = new StringBuilder(); + int prevEnd = 0; + do { + int match = docrootMatcher.start(); + // append htmlstr up to start of next {@docroot} + buf.append(htmlstr.substring(prevEnd, match)); + prevEnd = docrootMatcher.end(); + if (configuration.docrootparent.length() > 0 && htmlstr.startsWith("/..", prevEnd)) { + // Insert the absolute link if {@docRoot} is followed by "/..". + buf.append(configuration.docrootparent); + prevEnd += 3; + } else { + // Insert relative path where {@docRoot} was located + buf.append(pathToRoot.isEmpty() ? "." : pathToRoot.getPath()); + } + // Append slash if next character is not a slash + if (prevEnd < htmlstr.length() && htmlstr.charAt(prevEnd) != '/') { + buf.append('/'); + } + } while (docrootMatcher.find()); + buf.append(htmlstr.substring(prevEnd)); + return buf.toString(); + } + //where: + // Note: {@docRoot} is not case sensitive when passed in w/command line option: + private static final Pattern docrootPattern = + Pattern.compile(Pattern.quote("{@docroot}"), Pattern.CASE_INSENSITIVE); + + /** + * Get the script to show or hide the All classes link. + * + * @param id id of the element to show or hide + * @return a content tree for the script + */ + public Content getAllClassesLinkScript(String id) { + HtmlTree script = HtmlTree.SCRIPT(); + String scriptCode = "" + DocletConstants.NL; + Content scriptContent = new RawHtml(scriptCode); + script.addContent(scriptContent); + Content div = HtmlTree.DIV(script); + Content div_noscript = HtmlTree.DIV(getResource("doclet.No_Script_Message")); + Content noScript = HtmlTree.NOSCRIPT(div_noscript); + div.addContent(noScript); + return div; + } + + /** + * Add method information. + * + * @param method the method to be documented + * @param dl the content tree to which the method information will be added + */ + private void addMethodInfo(ExecutableElement method, Content dl) { + TypeElement enclosing = utils.getEnclosingTypeElement(method); + List intfacs = enclosing.getInterfaces(); + ExecutableElement overriddenMethod = utils.overriddenMethod(method); + // Check whether there is any implementation or overridden info to be + // printed. If no overridden or implementation info needs to be + // printed, do not print this section. + if ((!intfacs.isEmpty() + && new ImplementedMethods(method, this.configuration).build().isEmpty() == false) + || overriddenMethod != null) { + MethodWriterImpl.addImplementsInfo(this, method, dl); + if (overriddenMethod != null) { + MethodWriterImpl.addOverridden(this, + utils.overriddenType(method), + overriddenMethod, + dl); + } + } + } + + /** + * Adds the tags information. + * + * @param e the Element for which the tags will be generated + * @param htmltree the documentation tree to which the tags will be added + */ + protected void addTagsInfo(Element e, Content htmltree) { + if (configuration.nocomment) { + return; + } + Content dl = new HtmlTree(HtmlTag.DL); + if (utils.isExecutableElement(e) && !utils.isConstructor(e)) { + addMethodInfo((ExecutableElement)e, dl); + } + Content output = new ContentBuilder(); + TagletWriter.genTagOutput(configuration.tagletManager, e, + configuration.tagletManager.getCustomTaglets(e), + getTagletWriterInstance(false), output); + dl.addContent(output); + htmltree.addContent(dl); + } + + /** + * Check whether there are any tags for Serialization Overview + * section to be printed. + * + * @param field the VariableElement object to check for tags. + * @return true if there are tags to be printed else return false. + */ + protected boolean hasSerializationOverviewTags(VariableElement field) { + Content output = new ContentBuilder(); + TagletWriter.genTagOutput(configuration.tagletManager, field, + configuration.tagletManager.getCustomTaglets(field), + getTagletWriterInstance(false), output); + return !output.isEmpty(); + } + + /** + * Returns a TagletWriter that knows how to write HTML. + * + * @return a TagletWriter that knows how to write HTML. + */ + public TagletWriter getTagletWriterInstance(boolean isFirstSentence) { + return new TagletWriterImpl(this, isFirstSentence); + } + + /** + * Get Package link, with target frame. + * + * @param pkg The link will be to the "package-summary.html" page for this package + * @param target name of the target frame + * @param label tag for the link + * @return a content for the target package link + */ + public Content getTargetPackageLink(PackageElement pkg, String target, + Content label) { + return getHyperLink(pathString(pkg, DocPaths.PACKAGE_SUMMARY), label, "", target); + } + + + public void addClassesSummary(SortedSet classes, String label, + String tableSummary, List tableHeader, Content summaryContentTree) { + if (!classes.isEmpty()) { + Content caption = getTableCaption(new RawHtml(label)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) + : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (TypeElement te : classes) { + if (!utils.isCoreClass(te) || + !configuration.isGeneratedDoc(te)) { + continue; + } + Content classContent = getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.PACKAGE, te)); + Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); + HtmlTree tr = HtmlTree.TR(tdClass); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); + tdClassDescription.addStyle(HtmlStyle.colLast); + if (utils.isDeprecated(te)) { + tdClassDescription.addContent(deprecatedLabel); + List tags = utils.getDeprecatedTrees(te); + if (!tags.isEmpty()) { + addSummaryDeprecatedComment(te, tags.get(0), tdClassDescription); + } + } else { + addSummaryComment(te, tdClassDescription); + } + tr.addContent(tdClassDescription); + tbody.addContent(tr); + } + table.addContent(tbody); + summaryContentTree.addContent(table); + } + } + + /** + * Generates the HTML document tree and prints it out. + * + * @param metakeywords Array of String keywords for META tag. Each element + * of the array is assigned to a separate META tag. + * Pass in null for no array + * @param includeScript true if printing windowtitle script + * false for files that appear in the left-hand frames + * @param body the body htmltree to be included in the document + */ + public void printHtmlDocument(List metakeywords, boolean includeScript, + Content body) throws IOException { + Content htmlDocType = configuration.isOutputHtml5() + ? DocType.HTML5 + : DocType.TRANSITIONAL; + Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); + Content head = new HtmlTree(HtmlTag.HEAD); + head.addContent(getGeneratedBy(!configuration.notimestamp)); + head.addContent(getTitle()); + Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, + (configuration.charset.length() > 0) ? + configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); + head.addContent(meta); + if (!configuration.notimestamp) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + meta = HtmlTree.META(configuration.isOutputHtml5() + ? "dc.created" + : "date", dateFormat.format(new Date())); + head.addContent(meta); + } + if (metakeywords != null) { + for (String metakeyword : metakeywords) { + meta = HtmlTree.META("keywords", metakeyword); + head.addContent(meta); + } + } + addStyleSheetProperties(head); + addScriptProperties(head); + Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), + head, body); + Content htmlDocument = new HtmlDocument(htmlDocType, + htmlComment, htmlTree); + write(htmlDocument); + } + + /** + * Get the window title. + * + * @param title the title string to construct the complete window title + * @return the window title string + */ + public String getWindowTitle(String title) { + if (configuration.windowtitle.length() > 0) { + title += " (" + configuration.windowtitle + ")"; + } + return title; + } + + /** + * Get user specified header and the footer. + * + * @param header if true print the user provided header else print the + * user provided footer. + */ + public Content getUserHeaderFooter(boolean header) { + String content; + if (header) { + content = replaceDocRootDir(configuration.header); + } else { + if (configuration.footer.length() != 0) { + content = replaceDocRootDir(configuration.footer); + } else { + content = replaceDocRootDir(configuration.header); + } + } + Content rawContent = new RawHtml(content); + return rawContent; + } + + /** + * Adds the user specified top. + * + * @param htmlTree the content tree to which user specified top will be added + */ + public void addTop(Content htmlTree) { + Content top = new RawHtml(replaceDocRootDir(configuration.top)); + fixedNavDiv.addContent(top); + } + + /** + * Adds the user specified bottom. + * + * @param htmlTree the content tree to which user specified bottom will be added + */ + public void addBottom(Content htmlTree) { + Content bottom = new RawHtml(replaceDocRootDir(configuration.bottom)); + Content small = HtmlTree.SMALL(bottom); + Content p = HtmlTree.P(HtmlStyle.legalCopy, small); + htmlTree.addContent(p); + } + + /** + * Adds the navigation bar for the Html page at the top and and the bottom. + * + * @param header If true print navigation bar at the top of the page else + * @param htmlTree the HtmlTree to which the nav links will be added + */ + protected void addNavLinks(boolean header, Content htmlTree) { + if (!configuration.nonavbar) { + Content tree = (configuration.allowTag(HtmlTag.NAV)) + ? HtmlTree.NAV() + : htmlTree; + String allClassesId = "allclasses_"; + HtmlTree navDiv = new HtmlTree(HtmlTag.DIV); + fixedNavDiv.addStyle(HtmlStyle.fixedNav); + Content skipNavLinks = configuration.getResource("doclet.Skip_navigation_links"); + if (header) { + fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR); + navDiv.addStyle(HtmlStyle.topNav); + allClassesId += "navbar_top"; + Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); + //WCAG - Hyperlinks should contain text or an image with alt text - for AT tools + navDiv.addContent(a); + Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( + getDocLink(SectionName.SKIP_NAVBAR_TOP), skipNavLinks, + skipNavLinks.toString(), "")); + navDiv.addContent(skipLinkContent); + } else { + tree.addContent(HtmlConstants.START_OF_BOTTOM_NAVBAR); + navDiv.addStyle(HtmlStyle.bottomNav); + allClassesId += "navbar_bottom"; + Content a = getMarkerAnchor(SectionName.NAVBAR_BOTTOM); + navDiv.addContent(a); + Content skipLinkContent = HtmlTree.DIV(HtmlStyle.skipNav, getHyperLink( + getDocLink(SectionName.SKIP_NAVBAR_BOTTOM), skipNavLinks, + skipNavLinks.toString(), "")); + navDiv.addContent(skipLinkContent); + } + if (header) { + navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_TOP_FIRSTROW)); + } else { + navDiv.addContent(getMarkerAnchor(SectionName.NAVBAR_BOTTOM_FIRSTROW)); + } + HtmlTree navList = new HtmlTree(HtmlTag.UL); + navList.addStyle(HtmlStyle.navList); + navList.addAttr(HtmlAttr.TITLE, + configuration.getText("doclet.Navigation")); + if (configuration.createoverview) { + navList.addContent(getNavLinkContents()); + } + if (configuration.packages.size() == 1) { + navList.addContent(getNavLinkPackage(configuration.packages.first())); + } else if (!configuration.packages.isEmpty()) { + navList.addContent(getNavLinkPackage()); + } + navList.addContent(getNavLinkClass()); + if(configuration.classuse) { + navList.addContent(getNavLinkClassUse()); + } + if(configuration.createtree) { + navList.addContent(getNavLinkTree()); + } + if(!(configuration.nodeprecated || + configuration.nodeprecatedlist)) { + navList.addContent(getNavLinkDeprecated()); + } + if(configuration.createindex) { + navList.addContent(getNavLinkIndex()); + } + if (!configuration.nohelp) { + navList.addContent(getNavLinkHelp()); + } + navDiv.addContent(navList); + Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); + navDiv.addContent(aboutDiv); + if (header) { + fixedNavDiv.addContent(navDiv); + } else { + tree.addContent(navDiv); + } + Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); + ulNav.addContent(getNavLinkNext()); + Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); + Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists()); + ulFrames.addContent(getNavHideLists(filename)); + subDiv.addContent(ulFrames); + HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); + ulAllClasses.addAttr(HtmlAttr.ID, allClassesId); + subDiv.addContent(ulAllClasses); + if (header && configuration.createindex) { + HtmlTree inputText = HtmlTree.INPUT("text", "search"); + HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); + Content searchTxt = configuration.getResource("doclet.search"); + searchTxt.addContent(getSpace()); + HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); + liInput.addContent(inputText); + liInput.addContent(inputReset); + HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); + subDiv.addContent(ulSearch); + } + subDiv.addContent(getAllClassesLinkScript(allClassesId)); + addSummaryDetailLinks(subDiv); + if (header) { + subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); + fixedNavDiv.addContent(subDiv); + fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); + tree.addContent(fixedNavDiv); + } else { + subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); + tree.addContent(subDiv); + tree.addContent(HtmlConstants.END_OF_BOTTOM_NAVBAR); + } + if (configuration.allowTag(HtmlTag.NAV)) { + htmlTree.addContent(tree); + } + } + } + + /** + * Get the word "NEXT" to indicate that no link is available. Override + * this method to customize next link. + * + * @return a content tree for the link + */ + protected Content getNavLinkNext() { + return getNavLinkNext(null); + } + + /** + * Get the word "PREV" to indicate that no link is available. Override + * this method to customize prev link. + * + * @return a content tree for the link + */ + protected Content getNavLinkPrevious() { + return getNavLinkPrevious(null); + } + + /** + * Do nothing. This is the default method. + */ + protected void addSummaryDetailLinks(Content navDiv) { + } + + /** + * Get link to the "overview-summary.html" page. + * + * @return a content tree for the link + */ + protected Content getNavLinkContents() { + Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY), + overviewLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link to the "package-summary.html" page for the package passed. + * + * @param pkg Package to which link will be generated + * @return a content tree for the link + */ + protected Content getNavLinkPackage(PackageElement pkg) { + Content linkContent = getPackageLink(pkg, packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the word "Package" , to indicate that link is not available here. + * + * @return a content tree for the link + */ + protected Content getNavLinkPackage() { + Content li = HtmlTree.LI(packageLabel); + return li; + } + + /** + * Get the word "Use", to indicate that link is not available. + * + * @return a content tree for the link + */ + protected Content getNavLinkClassUse() { + Content li = HtmlTree.LI(useLabel); + return li; + } + + /** + * Get link for previous file. + * + * @param prev File name for the prev link + * @return a content tree for the link + */ + public Content getNavLinkPrevious(DocPath prev) { + Content li; + if (prev != null) { + li = HtmlTree.LI(getHyperLink(prev, prevLabel, "", "")); + } + else + li = HtmlTree.LI(prevLabel); + return li; + } + + /** + * Get link for next file. If next is null, just print the label + * without linking it anywhere. + * + * @param next File name for the next link + * @return a content tree for the link + */ + public Content getNavLinkNext(DocPath next) { + Content li; + if (next != null) { + li = HtmlTree.LI(getHyperLink(next, nextLabel, "", "")); + } + else + li = HtmlTree.LI(nextLabel); + return li; + } + + /** + * Get "FRAMES" link, to switch to the frame version of the output. + * + * @param link File to be linked, "index.html" + * @return a content tree for the link + */ + protected Content getNavShowLists(DocPath link) { + DocLink dl = new DocLink(link, path.getPath(), null); + Content framesContent = getHyperLink(dl, framesLabel, "", "_top"); + Content li = HtmlTree.LI(framesContent); + return li; + } + + /** + * Get "FRAMES" link, to switch to the frame version of the output. + * + * @return a content tree for the link + */ + protected Content getNavShowLists() { + return getNavShowLists(pathToRoot.resolve(DocPaths.INDEX)); + } + + /** + * Get "NO FRAMES" link, to switch to the non-frame version of the output. + * + * @param link File to be linked + * @return a content tree for the link + */ + protected Content getNavHideLists(DocPath link) { + Content noFramesContent = getHyperLink(link, noframesLabel, "", "_top"); + Content li = HtmlTree.LI(noFramesContent); + return li; + } + + /** + * Get "Tree" link in the navigation bar. If there is only one package + * specified on the command line, then the "Tree" link will be to the + * only "package-tree.html" file otherwise it will be to the + * "overview-tree.html" file. + * + * @return a content tree for the link + */ + protected Content getNavLinkTree() { + List packages = new ArrayList<>(utils.getSpecifiedPackages()); + DocPath docPath = packages.size() == 1 && utils.getSpecifiedClasses().isEmpty() + ? pathString(packages.get(0), DocPaths.PACKAGE_TREE) + : pathToRoot.resolve(DocPaths.OVERVIEW_TREE); + return HtmlTree.LI(getHyperLink(docPath, treeLabel, "", "")); + } + + /** + * Get the overview tree link for the main tree. + * + * @param label the label for the link + * @return a content tree for the link + */ + protected Content getNavLinkMainTree(String label) { + Content mainTreeContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_TREE), + new StringContent(label)); + Content li = HtmlTree.LI(mainTreeContent); + return li; + } + + /** + * Get the word "Class", to indicate that class link is not available. + * + * @return a content tree for the link + */ + protected Content getNavLinkClass() { + Content li = HtmlTree.LI(classLabel); + return li; + } + + /** + * Get "Deprecated" API link in the navigation bar. + * + * @return a content tree for the link + */ + protected Content getNavLinkDeprecated() { + Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.DEPRECATED_LIST), + deprecatedLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get link for generated index. If the user has used "-splitindex" + * command line option, then link to file "index-files/index-1.html" is + * generated otherwise link to file "index-all.html" is generated. + * + * @return a content tree for the link + */ + protected Content getNavLinkClassIndex() { + Content allClassesContent = getHyperLink(pathToRoot.resolve( + DocPaths.ALLCLASSES_NOFRAME), + allclassesLabel, "", ""); + Content li = HtmlTree.LI(allClassesContent); + return li; + } + + /** + * Get link for generated class index. + * + * @return a content tree for the link + */ + protected Content getNavLinkIndex() { + Content linkContent = getHyperLink(pathToRoot.resolve( + (configuration.splitindex + ? DocPaths.INDEX_FILES.resolve(DocPaths.indexN(1)) + : DocPaths.INDEX_ALL)), + indexLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get help file link. If user has provided a help file, then generate a + * link to the user given file, which is already copied to current or + * destination directory. + * + * @return a content tree for the link + */ + protected Content getNavLinkHelp() { + String helpfile = configuration.helpfile; + DocPath helpfilenm; + if (helpfile.isEmpty()) { + helpfilenm = DocPaths.HELP_DOC; + } else { + DocFile file = DocFile.createFileForInput(configuration, helpfile); + helpfilenm = DocPath.create(file.getName()); + } + Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm), + helpLabel, "", ""); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get summary table header. + * + * @param header the header for the table + * @param scope the scope of the headers + * @return a content tree for the header + */ + public Content getSummaryTableHeader(List header, String scope) { + Content tr = new HtmlTree(HtmlTag.TR); + final int size = header.size(); + Content tableHeader; + if (size == 1) { + tableHeader = new StringContent(header.get(0)); + tr.addContent(HtmlTree.TH(HtmlStyle.colOne, scope, tableHeader)); + return tr; + } + for (int i = 0; i < size; i++) { + tableHeader = new StringContent(header.get(i)); + if(i == 0) + tr.addContent(HtmlTree.TH(HtmlStyle.colFirst, scope, tableHeader)); + else if(i == (size - 1)) + tr.addContent(HtmlTree.TH(HtmlStyle.colLast, scope, tableHeader)); + else + tr.addContent(HtmlTree.TH(scope, tableHeader)); + } + return tr; + } + + /** + * Get table caption. + * + * @param rawText the caption for the table which could be raw Html + * @return a content tree for the caption + */ + public Content getTableCaption(Content title) { + Content captionSpan = HtmlTree.SPAN(title); + Content space = getSpace(); + Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, space); + Content caption = HtmlTree.CAPTION(captionSpan); + caption.addContent(tabSpan); + return caption; + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param anchorName the anchor name attribute + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(String anchorName) { + return getMarkerAnchor(getName(anchorName), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param sectionName the section name anchor attribute for page + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(SectionName sectionName) { + return getMarkerAnchor(sectionName.getName(), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param sectionName the section name anchor attribute for page + * @param anchorName the anchor name combined with section name attribute for the page + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(SectionName sectionName, String anchorName) { + return getMarkerAnchor(sectionName.getName() + getName(anchorName), null); + } + + /** + * Get the marker anchor which will be added to the documentation tree. + * + * @param anchorName the anchor name or id attribute + * @param anchorContent the content that should be added to the anchor + * @return a content tree for the marker anchor + */ + public Content getMarkerAnchor(String anchorName, Content anchorContent) { + if (anchorContent == null) + anchorContent = new Comment(" "); + Content markerAnchor = HtmlTree.A(configuration.htmlVersion, anchorName, anchorContent); + return markerAnchor; + } + + /** + * Returns a packagename content. + * + * @param packageElement the package to check + * @return package name content + */ + public Content getPackageName(PackageElement packageElement) { + return packageElement == null || packageElement.isUnnamed() + ? defaultPackageLabel + : getPackageLabel(packageElement.getQualifiedName().toString()); + } + + /** + * Returns a package name label. + * + * @param packageName the package name + * @return the package name content + */ + public Content getPackageLabel(String packageName) { + return new StringContent(packageName); + } + + /** + * Add package deprecation information to the documentation tree + * + * @param deprPkgs list of deprecated packages + * @param headingKey the caption for the deprecated package table + * @param tableSummary the summary for the deprecated package table + * @param tableHeader table headers for the deprecated package table + * @param contentTree the content tree to which the deprecated package table will be added + */ + protected void addPackageDeprecatedAPI(SortedSet deprPkgs, String headingKey, + String tableSummary, List tableHeader, Content contentTree) { + if (deprPkgs.size() > 0) { + Content caption = getTableCaption(configuration.getResource(headingKey)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.deprecatedSummary, caption) + : HtmlTree.TABLE(HtmlStyle.deprecatedSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (Element e : deprPkgs) { + PackageElement pkg = (PackageElement) e; + HtmlTree td = HtmlTree.TD(HtmlStyle.colOne, + getPackageLink(pkg, getPackageName(pkg))); + List tags = utils.getDeprecatedTrees(pkg); + if (!tags.isEmpty()) { + addInlineDeprecatedComment(pkg, tags.get(0), td); + } + HtmlTree tr = HtmlTree.TR(td); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + Content ul = HtmlTree.UL(HtmlStyle.blockList, li); + contentTree.addContent(ul); + } + } + + /** + * Return the path to the class page for a typeElement. + * + * @param te TypeElement for which the path is requested. + * @param name Name of the file(doesn't include path). + */ + protected DocPath pathString(TypeElement te, DocPath name) { + return pathString(utils.containingPackage(te), name); + } + + /** + * Return path to the given file name in the given package. So if the name + * passed is "Object.html" and the name of the package is "java.lang", and + * if the relative path is "../.." then returned string will be + * "../../java/lang/Object.html" + * + * @param packageElement Package in which the file name is assumed to be. + * @param name File name, to which path string is. + */ + protected DocPath pathString(PackageElement packageElement, DocPath name) { + return pathToRoot.resolve(DocPath.forPackage(packageElement).resolve(name)); + } + + /** + * Given a package, return the name to be used in HTML anchor tag. + * @param packageElement the package. + * @return the name to be used in HTML anchor tag. + */ + public String getPackageAnchorName(PackageElement packageElement) { + return packageElement == null || packageElement.isUnnamed() + ? SectionName.UNNAMED_PACKAGE_ANCHOR.getName() + : utils.getPackageName(packageElement); + } + + /** + * Return the link to the given package. + * + * @param packageElement the package to link to. + * @param label the label for the link. + * @return a content tree for the package link. + */ + public Content getPackageLink(PackageElement packageElement, String label) { + return getPackageLink(packageElement, new StringContent(label)); + } + + public Content getPackageLink(PackageElement packageElement) { + StringContent content = packageElement.isUnnamed() + ? new StringContent() + : new StringContent(utils.getPackageName(packageElement)); + return getPackageLink(packageElement, content); + } + + /** + * Return the link to the given package. + * + * @param packageElement the package to link to. + * @param label the label for the link. + * @return a content tree for the package link. + */ + public Content getPackageLink(PackageElement packageElement, Content label) { + boolean included = packageElement != null && utils.isIncluded(packageElement); + if (!included) { + for (PackageElement p : configuration.packages) { + if (p.equals(packageElement)) { + included = true; + break; + } + } + } + if (included || packageElement == null) { + return getHyperLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY), + label); + } else { + DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement)); + if (crossPkgLink != null) { + return getHyperLink(crossPkgLink, label); + } else { + return label; + } + } + } + + public Content interfaceName(TypeElement typeElement, boolean qual) { + Content name = new StringContent((qual) + ? typeElement.getQualifiedName().toString() + : utils.getSimpleName(typeElement)); + return (utils.isInterface(typeElement)) ? HtmlTree.SPAN(HtmlStyle.interfaceName, name) : name; + } + + /** + * Add the link to the content tree. + * + * @param typeElement program element typeElement for which the link will be added + * @param label label for the link + * @param htmltree the content tree to which the link will be added + */ + public void addSrcLink(Element typeElement, Content label, Content htmltree) { + if (typeElement == null) { + return; + } + TypeElement te = utils.getEnclosingTypeElement(typeElement); + if (te == null) { + // must be a typeElement since in has no containing class. + te = (TypeElement) typeElement; + } + DocPath href = pathToRoot + .resolve(DocPaths.SOURCE_OUTPUT) + .resolve(DocPath.forClass(utils, te)); + Content linkContent = getHyperLink(href + .fragment(SourceToHTMLConverter.getAnchorName(utils, typeElement)), label, "", ""); + htmltree.addContent(linkContent); + } + + /** + * Return the link to the given class. + * + * @param linkInfo the information about the link. + * + * @return the link for the given class. + */ + public Content getLink(LinkInfoImpl linkInfo) { + LinkFactoryImpl factory = new LinkFactoryImpl(this); + return factory.getLink(linkInfo); + } + + /** + * Return the type parameters for the given class. + * + * @param linkInfo the information about the link. + * @return the type for the given class. + */ + public Content getTypeParameterLinks(LinkInfoImpl linkInfo) { + LinkFactoryImpl factory = new LinkFactoryImpl(this); + return factory.getTypeParameterLinks(linkInfo, false); + } + + /************************************************************* + * Return a class cross link to external class documentation. + * The name must be fully qualified to determine which package + * the class is in. The -link option does not allow users to + * link to external classes in the "default" package. + * + * @param qualifiedClassName the qualified name of the external class. + * @param refMemName the name of the member being referenced. This should + * be null or empty string if no member is being referenced. + * @param label the label for the external link. + * @param strong true if the link should be strong. + * @param style the style of the link. + * @param code true if the label should be code font. + */ + public Content getCrossClassLink(String qualifiedClassName, String refMemName, + Content label, boolean strong, String style, + boolean code) { + String className = ""; + String packageName = qualifiedClassName == null ? "" : qualifiedClassName; + int periodIndex; + while ((periodIndex = packageName.lastIndexOf('.')) != -1) { + className = packageName.substring(periodIndex + 1, packageName.length()) + + (className.length() > 0 ? "." + className : ""); + Content defaultLabel = new StringContent(className); + if (code) + defaultLabel = HtmlTree.CODE(defaultLabel); + packageName = packageName.substring(0, periodIndex); + if (getCrossPackageLink(packageName) != null) { + /* + The package exists in external documentation, so link to the external + class (assuming that it exists). This is definitely a limitation of + the -link option. There are ways to determine if an external package + exists, but no way to determine if the external class exists. We just + have to assume that it does. + */ + DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot, + className + ".html", refMemName); + return getHyperLink(link, + (label == null) || label.isEmpty() ? defaultLabel : label, + strong, style, + configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName), + ""); + } + } + return null; + } + + public boolean isClassLinkable(TypeElement typeElement) { + if (utils.isIncluded(typeElement)) { + return configuration.isGeneratedDoc(typeElement); + } + return configuration.extern.isExternal(typeElement); + } + + public DocLink getCrossPackageLink(String pkgName) { + return configuration.extern.getExternalLink(pkgName, pathToRoot, + DocPaths.PACKAGE_SUMMARY.getPath()); + } + + /** + * Get the class link. + * + * @param context the id of the context where the link will be added + * @param element to link to + * @return a content tree for the link + */ + public Content getQualifiedClassLink(LinkInfoImpl.Kind context, Element element) { + LinkInfoImpl linkInfoImpl = new LinkInfoImpl(configuration, context, (TypeElement)element); + return getLink(linkInfoImpl.label(utils.getFullyQualifiedName(element))); + } + + /** + * Add the class link. + * + * @param context the id of the context where the link will be added + * @param typeElement to link to + * @param contentTree the content tree to which the link will be added + */ + public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { + addPreQualifiedClassLink(context, typeElement, false, contentTree); + } + + /** + * Retrieve the class link with the package portion of the label in + * plain text. If the qualifier is excluded, it will not be included in the + * link label. + * + * @param typeElement the class to link to. + * @param isStrong true if the link should be strong. + * @return the link with the package portion of the label in plain text. + */ + public Content getPreQualifiedClassLink(LinkInfoImpl.Kind context, + TypeElement typeElement, boolean isStrong) { + ContentBuilder classlink = new ContentBuilder(); + PackageElement pkg = utils.containingPackage(typeElement); + if (pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { + classlink.addContent(getEnclosingPackageName(typeElement)); + } + classlink.addContent(getLink(new LinkInfoImpl(configuration, + context, typeElement).label(utils.getSimpleName(typeElement)).strong(isStrong))); + return classlink; + } + + /** + * Add the class link with the package portion of the label in + * plain text. If the qualifier is excluded, it will not be included in the + * link label. + * + * @param context the id of the context where the link will be added + * @param typeElement the class to link to + * @param isStrong true if the link should be strong + * @param contentTree the content tree to which the link with be added + */ + public void addPreQualifiedClassLink(LinkInfoImpl.Kind context, + TypeElement typeElement, boolean isStrong, Content contentTree) { + PackageElement pkg = utils.containingPackage(typeElement); + if(pkg != null && ! configuration.shouldExcludeQualifier(pkg.getSimpleName().toString())) { + contentTree.addContent(getEnclosingPackageName(typeElement)); + } + LinkInfoImpl linkinfo = new LinkInfoImpl(configuration, context, typeElement) + .label(utils.getSimpleName(typeElement)) + .strong(isStrong); + Content link = getLink(linkinfo); + contentTree.addContent(link); + } + + /** + * Add the class link, with only class name as the strong link and prefixing + * plain package name. + * + * @param context the id of the context where the link will be added + * @param typeElement the class to link to + * @param contentTree the content tree to which the link with be added + */ + public void addPreQualifiedStrongClassLink(LinkInfoImpl.Kind context, TypeElement typeElement, Content contentTree) { + addPreQualifiedClassLink(context, typeElement, true, contentTree); + } + + /** + * Get the link for the given member. + * + * @param context the id of the context where the link will be added + * @param element the member being linked to + * @param label the label for the link + * @return a content tree for the element link + */ + public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label) { + return getDocLink(context, utils.getEnclosingTypeElement(element), element, + new StringContent(label)); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, Element element, String label, + boolean strong) { + return getDocLink(context, utils.getEnclosingTypeElement(element), element, label, strong); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + String label, boolean strong) { + return getDocLink(context, typeElement, element, label, strong, false); + } + + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label, boolean strong) { + return getDocLink(context, typeElement, element, label, strong, false); + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be printed. + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments. + * @param element the member being linked to. + * @param label the label for the link. + * @param strong true if the link should be strong. + * @param isProperty true if the element parameter is a JavaFX property. + * @return the link for the given member. + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + String label, boolean strong, boolean isProperty) { + return getDocLink(context, typeElement, element, new StringContent(check(label)), strong, isProperty); + } + + String check(String s) { + if (s.matches(".*[&<>].*")) { + throw new IllegalArgumentException(s); + } + return s; + } + + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label, boolean strong, boolean isProperty) { + if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { + return label; + } else if (utils.isExecutableElement(element)) { + ExecutableElement ee = (ExecutableElement)element; + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(getAnchor(ee, isProperty))) + .strong(strong)); + } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(element.getSimpleName().toString())) + .strong(strong)); + } else { + return label; + } + } + + /** + * Return the link for the given member. + * + * @param context the id of the context where the link will be added + * @param typeElement the typeElement that we should link to. This is not + necessarily equal to element.containingClass(). We may be + inheriting comments + * @param element the member being linked to + * @param label the label for the link + * @return the link for the given member + */ + public Content getDocLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element element, + Content label) { + if (! (utils.isIncluded(element) || utils.isLinkable(typeElement))) { + return label; + } else if (utils.isExecutableElement(element)) { + ExecutableElement emd = (ExecutableElement) element; + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label) + .where(getName(getAnchor(emd)))); + } else if (utils.isVariableElement(element) || utils.isTypeElement(element)) { + return getLink(new LinkInfoImpl(configuration, context, typeElement) + .label(label).where(getName(element.getSimpleName().toString()))); + } else { + return label; + } + } + + public String getAnchor(ExecutableElement executableElement) { + return getAnchor(executableElement, false); + } + + public String getAnchor(ExecutableElement executableElement, boolean isProperty) { + if (isProperty) { + return executableElement.getSimpleName().toString(); + } + String signature = utils.signature(executableElement); + StringBuilder signatureParsed = new StringBuilder(); + int counter = 0; + for (int i = 0; i < signature.length(); i++) { + char c = signature.charAt(i); + if (c == '<') { + counter++; + } else if (c == '>') { + counter--; + } else if (counter == 0) { + signatureParsed.append(c); + } + } + return utils.getSimpleName(executableElement) + signatureParsed.toString(); + } + + public Content seeTagToContent(Element element, DocTree see) { + + Kind kind = see.getKind(); + if (!(kind == LINK || kind == SEE || kind == LINK_PLAIN)) { + return new ContentBuilder(); + } + + CommentHelper ch = utils.getCommentHelper(element); + String tagName = ch.getTagName(see); + String seetext = replaceDocRootDir(utils.normalizeNewlines(ch.getText(see))); + // Check if @see is an href or "string" + if (seetext.startsWith("<") || seetext.startsWith("\"")) { + return new RawHtml(seetext); + } + boolean isLinkPlain = kind == LINK_PLAIN; + Content label = plainOrCode(isLinkPlain, new RawHtml(ch.getLabel(configuration, see))); + + //The text from the @see tag. We will output this text when a label is not specified. + Content text = plainOrCode(kind == LINK_PLAIN, new RawHtml(seetext)); + + TypeElement refClass = ch.getReferencedClass(configuration, see); + String refClassName = ch.getReferencedClassName(configuration, see); + Element refMem = ch.getReferencedMember(configuration, see); + String refMemName = ch.getReferencedMemberName(see); + + if (refMemName == null && refMem != null) { + refMemName = refMem.toString(); + } + if (refClass == null) { + //@see is not referencing an included class + PackageElement refPackage = ch.getReferencedPackage(configuration, see); + if (refPackage != null && utils.isIncluded(refPackage)) { + //@see is referencing an included package + if (label.isEmpty()) + label = plainOrCode(isLinkPlain, + new StringContent(refPackage.getQualifiedName().toString())); + return getPackageLink(refPackage, label); + } else { + // @see is not referencing an included class or package. Check for cross links. + Content classCrossLink; + DocLink packageCrossLink = getCrossPackageLink(refClassName); + if (packageCrossLink != null) { + // Package cross link found + return getHyperLink(packageCrossLink, + (label.isEmpty() ? text : label)); + } else if ((classCrossLink = getCrossClassLink(refClassName, + refMemName, label, false, "", !isLinkPlain)) != null) { + // Class cross link found (possibly to a member in the class) + return classCrossLink; + } else { + // No cross link found so print warning + configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(see), + "doclet.see.class_or_package_not_found", + "@" + tagName, + seetext); + return (label.isEmpty() ? text: label); + } + } + } else if (refMemName == null) { + // Must be a class reference since refClass is not null and refMemName is null. + if (label.isEmpty()) { + /* + * it seems to me this is the right thing to do, but it causes comparator failures. + */ + if (!configuration.backwardCompatibility) { + StringContent content = utils.isEnclosingPackageIncluded(refClass) + ? new StringContent(utils.getSimpleName(refClass)) + : new StringContent(utils.getFullyQualifiedName(refClass)); + label = plainOrCode(isLinkPlain, content); + } else { + label = plainOrCode(isLinkPlain, + new StringContent(utils.getSimpleName(refClass))); + } + + } + return getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, refClass) + .label(label)); + } else if (refMem == null) { + // Must be a member reference since refClass is not null and refMemName is not null. + // However, refMem is null, so this referenced member does not exist. + return (label.isEmpty() ? text: label); + } else { + // Must be a member reference since refClass is not null and refMemName is not null. + // refMem is not null, so this @see tag must be referencing a valid member. + TypeElement containing = utils.getEnclosingTypeElement(refMem); + if (ch.getText(see).trim().startsWith("#") && + ! (utils.isPublic(containing) || utils.isLinkable(containing))) { + // Since the link is relative and the holder is not even being + // documented, this must be an inherited link. Redirect it. + // The current class either overrides the referenced member or + // inherits it automatically. + if (this instanceof ClassWriterImpl) { + containing = ((ClassWriterImpl) this).getTypeElement(); + } else if (!utils.isPublic(containing)) { + configuration.getDocletSpecificMsg().warning( + ch.getDocTreePath(see), "doclet.see.class_or_package_not_accessible", + tagName, utils.getFullyQualifiedName(containing)); + } else { + configuration.getDocletSpecificMsg().warning( + ch.getDocTreePath(see), "doclet.see.class_or_package_not_found", + tagName, seetext); + } + } + if (configuration.currentTypeElement != containing) { + refMemName = (utils.isConstructor(refMem)) + ? refMemName + : utils.getSimpleName(containing) + "." + refMemName; + } + if (utils.isExecutableElement(refMem)) { + if (refMemName.indexOf('(') < 0) { + refMemName += utils.makeSignature((ExecutableElement)refMem, true); + } + } + + text = plainOrCode(kind == LINK_PLAIN, new StringContent(refMemName)); + + return getDocLink(LinkInfoImpl.Kind.SEE_TAG, containing, + refMem, (label.isEmpty() ? text: label), false); + } + } + + private Content plainOrCode(boolean plain, Content body) { + return (plain || body.isEmpty()) ? body : HtmlTree.CODE(body); + } + + /** + * Add the inline comment. + * + * @param element the Element for which the inline comment will be added + * @param tag the inline tag to be added + * @param htmltree the content tree to which the comment will be added + */ + public void addInlineComment(Element element, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(element); + List description = ch.getDescription(configuration, tag); + addCommentTags(element, tag, description, false, false, htmltree); + } + + /** + * Add the inline deprecated comment. + * + * @param e the Element for which the inline deprecated comment will be added + * @param tag the inline tag to be added + * @param htmltree the content tree to which the comment will be added + */ + public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(e); + addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree); + } + + /** + * Adds the summary content. + * + * @param element the Element for which the summary will be generated + * @param htmltree the documentation tree to which the summary will be added + */ + public void addSummaryComment(Element element, Content htmltree) { + addSummaryComment(element, utils.getFirstSentenceTrees(element), htmltree); + } + + /** + * Adds the summary content. + * + * @param element the Element for which the summary will be generated + * @param firstSentenceTags the first sentence tags for the doc + * @param htmltree the documentation tree to which the summary will be added + */ + public void addSummaryComment(Element element, List firstSentenceTags, Content htmltree) { + addCommentTags(element, firstSentenceTags, false, true, htmltree); + } + + public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) { + CommentHelper ch = utils.getCommentHelper(element); + List body = ch.getBody(configuration, tag); + addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree); + } + + /** + * Adds the inline comment. + * + * @param element the Element for which the inline comments will be generated + * @param htmltree the documentation tree to which the inline comments will be added + */ + public void addInlineComment(Element element, Content htmltree) { + addCommentTags(element, utils.getBody(element), false, false, htmltree); + } + + /** + * Adds the comment tags. + * + * @param element the Element for which the comment tags will be generated + * @param tags the first sentence tags for the doc + * @param depr true if it is deprecated + * @param first true if the first sentence tags should be added + * @param htmltree the documentation tree to which the comment tags will be added + */ + private void addCommentTags(Element element, List tags, boolean depr, + boolean first, Content htmltree) { + addCommentTags(element, null, tags, depr, first, htmltree); + } + + /** + * Adds the comment tags. + * + * @param element for which the comment tags will be generated + * @param holderTag the block tag context for the inline tags + * @param tags the first sentence tags for the doc + * @param depr true if it is deprecated + * @param first true if the first sentence tags should be added + * @param htmltree the documentation tree to which the comment tags will be added + */ + private void addCommentTags(Element element, DocTree holderTag, List tags, boolean depr, + boolean first, Content htmltree) { + if(configuration.nocomment){ + return; + } + Content div; + Content result = commentTagsToContent(null, element, tags, first); + if (depr) { + Content italic = HtmlTree.SPAN(HtmlStyle.deprecationComment, result); + div = HtmlTree.DIV(HtmlStyle.block, italic); + htmltree.addContent(div); + } + else { + div = HtmlTree.DIV(HtmlStyle.block, result); + htmltree.addContent(div); + } + if (tags.isEmpty()) { + htmltree.addContent(getSpace()); + } + } + + boolean ignoreNonInlineTag(DocTree dtree) { + Name name = null; + if (dtree.getKind() == Kind.START_ELEMENT) { + StartElementTree setree = (StartElementTree)dtree; + name = setree.getName(); + } else if (dtree.getKind() == Kind.END_ELEMENT) { + EndElementTree eetree = (EndElementTree)dtree; + name = eetree.getName(); + } + + if (name != null) { + com.sun.tools.doclint.HtmlTag htmlTag = com.sun.tools.doclint.HtmlTag.get(name); + if (htmlTag != null && + htmlTag.blockType != com.sun.tools.doclint.HtmlTag.BlockType.INLINE) { + return true; + } + } + return false; + } + + boolean isAllWhiteSpace(String body) { + for (int i = 0 ; i < body.length(); i++) { + if (!Character.isWhitespace(body.charAt(i))) + return false; + } + return true; + } + + /** + * Converts inline tags and text to text strings, expanding the + * inline tags along the way. Called wherever text can contain + * an inline tag, such as in comments or in free-form text arguments + * to non-inline tags. + * + * @param holderTag specific tag where comment resides + * @param element specific element where comment resides + * @param tags array of text tags and inline tags (often alternating) + present in the text of interest for this element + * @param isFirstSentence true if text is first sentence + * @return a Content object + */ + public Content commentTagsToContent(DocTree holderTag, Element element, + List tags, boolean isFirstSentence) { + + final Content result = new ContentBuilder() { + @Override + public void addContent(String text) { + super.addContent(utils.normalizeNewlines(text)); + } + }; + CommentHelper ch = utils.getCommentHelper(element); + // Array of all possible inline tags for this javadoc run + configuration.tagletManager.checkTags(utils, element, tags, true); + for (ListIterator iterator = tags.listIterator(); iterator.hasNext();) { + DocTree tag = iterator.next(); + // zap block tags + if (isFirstSentence && ignoreNonInlineTag(tag)) + continue; + + if (isFirstSentence && iterator.nextIndex() == tags.size() && + (tag.getKind() == TEXT && isAllWhiteSpace(ch.getText(tag)))) + continue; + + boolean allDone = new SimpleDocTreeVisitor() { + // notify the next DocTree handler to take necessary action + boolean commentRemoved = false; + + private boolean isLast(DocTree node) { + return node.equals(tags.get(tags.size() - 1)); + } + + private boolean isFirst(DocTree node) { + return node.equals(tags.get(0)); + } + + private boolean inAnAtag() { + if (utils.isStartElement(tag)) { + StartElementTree st = (StartElementTree)tag; + Name name = st.getName(); + if (name != null) { + com.sun.tools.doclint.HtmlTag htag = + com.sun.tools.doclint.HtmlTag.get(name); + return htag != null && htag.equals(com.sun.tools.doclint.HtmlTag.A); + } + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitAttribute(AttributeTree node, Content c) { + StringBuilder sb = new StringBuilder(SPACER).append(node.getName()); + if (node.getValueKind() == ValueKind.EMPTY) { + result.addContent(sb.toString()); + return false; + } + sb.append("="); + String quote; + switch (node.getValueKind()) { + case DOUBLE: + quote = "\""; + break; + case SINGLE: + quote = "\'"; + break; + default: + quote = ""; + break; + } + sb.append(quote); + result.addContent(sb.toString()); + Content docRootContent = new ContentBuilder(); + + for (DocTree dt : node.getValue()) { + if (utils.isText(dt) && inAnAtag()) { + String text = ((TextTree) dt).getBody(); + if (text.startsWith("/..") && !configuration.docrootparent.isEmpty()) { + result.addContent(configuration.docrootparent); + docRootContent = new ContentBuilder(); + text = textCleanup(text.substring(3), isLast(node)); + } else { + if (!docRootContent.isEmpty()) { + docRootContent = copyDocRootContent(docRootContent); + } else { + text = redirectRelativeLinks(element, (TextTree) dt); + } + text = textCleanup(text, isLast(node)); + } + result.addContent(text); + } else { + docRootContent = copyDocRootContent(docRootContent); + dt.accept(this, docRootContent); + } + } + copyDocRootContent(docRootContent); + result.addContent(quote); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitComment(CommentTree node, Content c) { + if (isFirstSentence && isFirst(node)) { + commentRemoved = true; + return this.visit(iterator.next(), c); + } + result.addContent(new RawHtml(node.getBody())); + return false; + } + + private Content copyDocRootContent(Content content) { + if (!content.isEmpty()) { + result.addContent(content); + return new ContentBuilder(); + } + return content; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitDocRoot(DocRootTree node, Content c) { + Content docRootContent = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, + holderTag, + node, + getTagletWriterInstance(isFirstSentence)); + if (c != null) { + c.addContent(docRootContent); + } else { + result.addContent(docRootContent); + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitEndElement(EndElementTree node, Content c) { + RawHtml rawHtml = new RawHtml(""); + result.addContent(rawHtml); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitEntity(EntityTree node, Content c) { + result.addContent(new RawHtml(node.toString())); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitErroneous(ErroneousTree node, Content c) { + configuration.getDocletSpecificMsg().warning(ch.getDocTreePath(node), + "doclet.tag.invalid_usage", node); + result.addContent(new RawHtml(node.toString())); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitInheritDoc(InheritDocTree node, Content c) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, + tag, getTagletWriterInstance(isFirstSentence)); + result.addContent(output); + // if we obtained the first sentence successfully, nothing more to do + return (isFirstSentence && !output.isEmpty()); + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitIndex(IndexTree node, Content p) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, tag, + getTagletWriterInstance(isFirstSentence)); + if (output != null) { + result.addContent(output); + } + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitLink(LinkTree node, Content c) { + // we need to pass the DocTreeImpl here, so ignore node + result.addContent(seeTagToContent(element, tag)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitLiteral(LiteralTree node, Content c) { + String s = node.getBody().toString(); + Content content = new StringContent(utils.normalizeNewlines(s)); + if (node.getKind() == CODE) + content = HtmlTree.CODE(content); + result.addContent(content); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitSee(SeeTree node, Content c) { + // we need to pass the DocTreeImpl here, so ignore node + result.addContent(seeTagToContent(element, tag)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitStartElement(StartElementTree node, Content c) { + String text = "<" + node.getName(); + text = utils.normalizeNewlines(text); + RawHtml rawHtml = new RawHtml(text); + result.addContent(rawHtml); + + for (DocTree dt : node.getAttributes()) { + dt.accept(this, null); + } + result.addContent(new RawHtml(node.isSelfClosing() ? "/>" : ">")); + return false; + } + + private String textCleanup(String text, boolean isLast) { + return textCleanup(text, isLast, false); + } + + private String textCleanup(String text, boolean isLast, boolean trimLeader) { + if (trimLeader) { + text = removeLeadingWhitespace(text); + } + if (isFirstSentence && isLast) { + text = removeTrailingWhitespace(text); + } + text = utils.replaceTabs(text); + text = utils.normalizeNewlines(text); + return text; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + public Boolean visitText(TextTree node, Content c) { + String text = node.getBody(); + text = textCleanup(text, isLast(node), commentRemoved); + commentRemoved = false; + result.addContent(new RawHtml(text)); + return false; + } + + @Override @DefinedBy(Api.COMPILER_TREE) + protected Boolean defaultAction(DocTree node, Content c) { + Content output = TagletWriter.getInlineTagOutput(element, + configuration.tagletManager, holderTag, tag, + getTagletWriterInstance(isFirstSentence)); + if (output != null) { + result.addContent(output); + } + return false; + } + + }.visit(tag, null); + if (allDone) + break; + } + return result; + } + + private String removeTrailingWhitespace(String text) { + char[] buf = text.toCharArray(); + for (int i = buf.length - 1; i > 0 ; i--) { + if (!Character.isWhitespace(buf[i])) + return text.substring(0, i + 1); + } + return text; + } + + private String removeLeadingWhitespace(String text) { + char[] buf = text.toCharArray(); + for (int i = 0; i < buf.length; i++) { + if (!Character.isWhitespace(buf[i])) { + return text.substring(i); + } + } + return text; + } + + /** + * Return true if relative links should not be redirected. + * + * @return Return true if a relative link should not be redirected. + */ + private boolean shouldNotRedirectRelativeLinks() { + return this instanceof AnnotationTypeWriter || + this instanceof ClassWriter || + this instanceof PackageSummaryWriter; + } + + /** + * Suppose a piece of documentation has a relative link. When you copy + * that documentation to another place such as the index or class-use page, + * that relative link will no longer work. We should redirect those links + * so that they will work again. + *

+ * Here is the algorithm used to fix the link: + *

+ * {@literal => docRoot + + } + *

+ * For example, suppose DocletEnvironment has this link: + * {@literal The package Page } + *

+ * If this link appeared in the index, we would redirect + * the link like this: + * + * {@literal The package Page} + * + * @param element the Element object whose documentation is being written. + * @param text the text being written. + * + * @return the text, with all the relative links redirected to work. + */ + private String redirectRelativeLinks(Element element, TextTree tt) { + String text = tt.getBody(); + if (element == null || utils.isOverviewElement(element) || shouldNotRedirectRelativeLinks()) { + return text; + } + + DocPath redirectPathFromRoot = new SimpleElementVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitType(TypeElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitPackage(PackageElement e, Void p) { + return DocPath.forPackage(e); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitVariable(VariableElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public DocPath visitExecutable(ExecutableElement e, Void p) { + return DocPath.forPackage(utils.containingPackage(e)); + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected DocPath defaultAction(Element e, Void p) { + return null; + } + }.visit(element); + if (redirectPathFromRoot == null) { + return text; + } + String lower = Utils.toLowerCase(text); + if (!(lower.startsWith("mailto:") + || lower.startsWith("http:") + || lower.startsWith("https:") + || lower.startsWith("file:"))) { + text = "{@" + (new DocRootTaglet()).getName() + "}/" + + redirectPathFromRoot.resolve(text).getPath(); + text = replaceDocRootDir(text); + } + return text; + } + + static final Set blockTags = new HashSet<>(); + static { + for (HtmlTag t: HtmlTag.values()) { + if (t.blockType == HtmlTag.BlockType.BLOCK) + blockTags.add(t.value); + } + } + + /** + * Add a link to the stylesheet file. + * + * @param head the content tree to which the files will be added + */ + public void addStyleSheetProperties(Content head) { + String stylesheetfile = configuration.stylesheetfile; + DocPath stylesheet; + if (stylesheetfile.isEmpty()) { + stylesheet = DocPaths.STYLESHEET; + } else { + DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); + stylesheet = DocPath.create(file.getName()); + } + HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(stylesheet).getPath(), + "Style"); + head.addContent(link); + if (configuration.createindex) { + HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(), + "Style"); + head.addContent(jq_link); + } + } + + /** + * Add a link to the JavaScript file. + * + * @param head the content tree to which the files will be added + */ + public void addScriptProperties(Content head) { + HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); + head.addContent(javascript); + if (configuration.createindex) { + if (pathToRoot != null && script != null) { + String path = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); + script.addContent(new RawHtml("var pathtoroot = \"" + path + "/\";loadScripts(document, \'script\');")); + } + addJQueryFile(head, DocPaths.JSZIP_MIN); + addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); + head.addContent(new RawHtml("")); + addJQueryFile(head, DocPaths.JQUERY_JS_1_10); + addJQueryFile(head, DocPaths.JQUERY_JS); + } + } + + /** + * Add a link to the JQuery javascript file. + * + * @param head the content tree to which the files will be added + * @param filePath the DocPath of the file that needs to be added + */ + private void addJQueryFile(Content head, DocPath filePath) { + HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); + head.addContent(jqyeryScriptFile); + } + + /** + * According to + * The Java™ Language Specification, + * all the outer classes and static nested classes are core classes. + */ + public boolean isCoreClass(TypeElement typeElement) { + return utils.getEnclosingTypeElement(typeElement) == null || utils.isStatic(typeElement); + } + + /** + * Adds the annotation types for the given packageElement. + * + * @param packageElement the package to write annotations for. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + public void addAnnotationInfo(PackageElement packageElement, Content htmltree) { + addAnnotationInfo(packageElement, packageElement.getAnnotationMirrors(), htmltree); + } + + /** + * Add the annotation types of the executable receiver. + * + * @param method the executable to write the receiver annotations for. + * @param descList list of annotation description. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + public void addReceiverAnnotationInfo(ExecutableElement method, List descList, + Content htmltree) { + addAnnotationInfo(0, method, descList, false, htmltree); + } + + /* + * this is a hack to delay dealing with Annotations in the writers, the assumption + * is that all necessary checks have been made to get here. + */ + public void addReceiverAnnotationInfo(ExecutableElement method, TypeMirror rcvrTypeMirror, + List annotationMirrors, Content htmltree) { + TypeMirror rcvrType = method.getReceiverType(); + List annotationMirrors1 = rcvrType.getAnnotationMirrors(); + addAnnotationInfo(0, method, annotationMirrors1, false, htmltree); + } + + /** + * Adds the annotatation types for the given element. + * + * @param element the package to write annotations for + * @param htmltree the content tree to which the annotation types will be added + */ + public void addAnnotationInfo(Element element, Content htmltree) { + addAnnotationInfo(element, element.getAnnotationMirrors(), htmltree); + } + + /** + * Add the annotatation types for the given element and parameter. + * + * @param indent the number of spaces to indent the parameters. + * @param element the element to write annotations for. + * @param param the parameter to write annotations for. + * @param tree the content tree to which the annotation types will be added + */ + public boolean addAnnotationInfo(int indent, Element element, VariableElement param, + Content tree) { + return addAnnotationInfo(indent, element, param.getAnnotationMirrors(), false, tree); + } + + /** + * Adds the annotatation types for the given Element. + * + * @param element the element to write annotations for. + * @param descList the array of {@link AnnotationDesc}. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + private void addAnnotationInfo(Element element, List descList, + Content htmltree) { + addAnnotationInfo(0, element, descList, true, htmltree); + } + + /** + * Adds the annotation types for the given element. + * + * @param indent the number of extra spaces to indent the annotations. + * @param element the element to write annotations for. + * @param descList the array of {@link AnnotationDesc}. + * @param htmltree the documentation tree to which the annotation info will be + * added + */ + private boolean addAnnotationInfo(int indent, Element element, + List descList, boolean lineBreak, Content htmltree) { + List annotations = getAnnotations(indent, descList, lineBreak); + String sep = ""; + if (annotations.isEmpty()) { + return false; + } + for (Content annotation: annotations) { + htmltree.addContent(sep); + htmltree.addContent(annotation); + if (!lineBreak) { + sep = " "; + } + } + return true; + } + + /** + * Return the string representations of the annotation types for + * the given doc. + * + * @param indent the number of extra spaces to indent the annotations. + * @param descList the array of {@link AnnotationDesc}. + * @param linkBreak if true, add new line between each member value. + * @return an array of strings representing the annotations being + * documented. + */ + private List getAnnotations(int indent, List descList, boolean linkBreak) { + return getAnnotations(indent, descList, linkBreak, true); + } + + private List getAnnotations(int indent, AnnotationMirror amirror, boolean linkBreak) { + List descList = new ArrayList<>(); + descList.add(amirror); + return getAnnotations(indent, descList, linkBreak, true); + } + + /** + * Return the string representations of the annotation types for + * the given doc. + * + * A {@code null} {@code elementType} indicates that all the + * annotations should be returned without any filtering. + * + * @param indent the number of extra spaces to indent the annotations. + * @param descList the array of {@link AnnotationDesc}. + * @param linkBreak if true, add new line between each member value. + * @param isJava5DeclarationLocation + * @return an array of strings representing the annotations being + * documented. + */ + public List getAnnotations(int indent, List descList, + boolean linkBreak, boolean isJava5DeclarationLocation) { + List results = new ArrayList<>(); + ContentBuilder annotation; + for (AnnotationMirror aDesc : descList) { + TypeElement annotationElement = (TypeElement)aDesc.getAnnotationType().asElement(); + // If an annotation is not documented, do not add it to the list. If + // the annotation is of a repeatable type, and if it is not documented + // and also if its container annotation is not documented, do not add it + // to the list. If an annotation of a repeatable type is not documented + // but its container is documented, it will be added to the list. + if (!utils.isDocumentedAnnotation(annotationElement) && + (!isAnnotationDocumented && !isContainerDocumented)) { + continue; + } + /* TODO: check logic here to correctly handle declaration + * and type annotations. + if (utils.isDeclarationAnnotation(annotationElement, isJava5DeclarationLocation)) { + continue; + }*/ + annotation = new ContentBuilder(); + isAnnotationDocumented = false; + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.ANNOTATION, annotationElement); + Map pairs = aDesc.getElementValues(); + // If the annotation is synthesized, do not print the container. + if (utils.configuration.workArounds.isSynthesized(aDesc)) { + for (ExecutableElement ee : pairs.keySet()) { + AnnotationValue annotationValue = pairs.get(ee); + List annotationTypeValues = new ArrayList<>(); + + new SimpleAnnotationValueVisitor9>() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, List p) { + p.addAll(vals); + return null; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Void defaultAction(Object o, List p) { + p.add(annotationValue); + return null; + } + }.visit(annotationValue, annotationTypeValues); + + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = " "; + } + } + } else if (isAnnotationArray(pairs)) { + // If the container has 1 or more value defined and if the + // repeatable type annotation is not documented, do not print + // the container. + if (pairs.size() == 1 && isAnnotationDocumented) { + List annotationTypeValues = new ArrayList<>(); + for (AnnotationValue a : pairs.values()) { + new SimpleAnnotationValueVisitor9>() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, List annotationTypeValues) { + for (AnnotationValue av : vals) { + annotationTypeValues.add(av); + } + return null; + } + }.visit(a, annotationTypeValues); + } + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = " "; + } + } + // If the container has 1 or more value defined and if the + // repeatable type annotation is not documented, print the container. + else { + addAnnotations(annotationElement, linkInfo, annotation, pairs, + indent, false); + } + } + else { + addAnnotations(annotationElement, linkInfo, annotation, pairs, + indent, linkBreak); + } + annotation.addContent(linkBreak ? DocletConstants.NL : ""); + results.add(annotation); + } + return results; + } + + /** + * Add annotation to the annotation string. + * + * @param annotationDoc the annotation being documented + * @param linkInfo the information about the link + * @param annotation the annotation string to which the annotation will be added + * @param pairs annotation type element and value pairs + * @param indent the number of extra spaces to indent the annotations. + * @param linkBreak if true, add new line between each member value + */ + private void addAnnotations(TypeElement annotationDoc, LinkInfoImpl linkInfo, + ContentBuilder annotation, Mapmap, + int indent, boolean linkBreak) { + linkInfo.label = new StringContent("@" + annotationDoc.getSimpleName().toString()); + annotation.addContent(getLink(linkInfo)); + if (!map.isEmpty()) { + annotation.addContent("("); + boolean isFirst = true; + for (ExecutableElement element : map.keySet()) { + if (isFirst) { + isFirst = false; + } else { + annotation.addContent(","); + if (linkBreak) { + annotation.addContent(DocletConstants.NL); + int spaces = annotationDoc.getSimpleName().toString().length() + 2; + for (int k = 0; k < (spaces + indent); k++) { + annotation.addContent(" "); + } + } + } + annotation.addContent(getDocLink(LinkInfoImpl.Kind.ANNOTATION, + element, element.getSimpleName().toString(), false)); + annotation.addContent("="); + AnnotationValue annotationValue = map.get(element); + List annotationTypeValues = new ArrayList<>(); + new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Void visitArray(List vals, AnnotationValue p) { + annotationTypeValues.addAll(vals); + return null; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Void defaultAction(Object o, AnnotationValue p) { + annotationTypeValues.add(p); + return null; + } + }.visit(annotationValue, annotationValue); + annotation.addContent(annotationTypeValues.size() == 1 ? "" : "{"); + String sep = ""; + for (AnnotationValue av : annotationTypeValues) { + annotation.addContent(sep); + annotation.addContent(annotationValueToContent(av)); + sep = ","; + } + annotation.addContent(annotationTypeValues.size() == 1 ? "" : "}"); + isContainerDocumented = false; + } + annotation.addContent(")"); + } + } + + /** + * Check if the annotation contains an array of annotation as a value. This + * check is to verify if a repeatable type annotation is present or not. + * + * @param pairs annotation type element and value pairs + * + * @return true if the annotation contains an array of annotation as a value. + */ + private boolean isAnnotationArray(Map pairs) { + AnnotationValue annotationValue; + for (ExecutableElement ee : pairs.keySet()) { + annotationValue = pairs.get(ee); + boolean rvalue = new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitArray(List vals, Void p) { + if (vals.size() > 1) { + if (vals.get(0) instanceof AnnotationMirror) { + isContainerDocumented = true; + return new SimpleAnnotationValueVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Boolean visitAnnotation(AnnotationMirror a, Void p) { + isContainerDocumented = true; + Element asElement = a.getAnnotationType().asElement(); + if (utils.isDocumentedAnnotation((TypeElement)asElement)) { + isAnnotationDocumented = true; + } + return true; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Boolean defaultAction(Object o, Void p) { + return false; + } + }.visit(vals.get(0)); + } + } + return false; + } + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Boolean defaultAction(Object o, Void p) { + return false; + } + }.visit(annotationValue); + if (rvalue) { + return true; + } + } + return false; + } + + private Content annotationValueToContent(AnnotationValue annotationValue) { + return new SimpleAnnotationValueVisitor9() { + + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitType(TypeMirror t, Void p) { + return new SimpleTypeVisitor9() { + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitDeclared(DeclaredType t, Void p) { + LinkInfoImpl linkInfo = new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.ANNOTATION, t); + String name = utils.isIncluded(t.asElement()) + ? t.asElement().getSimpleName().toString() + : utils.getFullyQualifiedName(t.asElement()); + linkInfo.label = new StringContent(name + utils.getDimension(t) + ".class"); + return getLink(linkInfo); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Content defaultAction(TypeMirror e, Void p) { + return new StringContent(t + utils.getDimension(t) + ".class"); + } + }.visit(t); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitAnnotation(AnnotationMirror a, Void p) { + List list = getAnnotations(0, a, false); + ContentBuilder buf = new ContentBuilder(); + for (Content c : list) { + buf.addContent(c); + } + return buf; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitEnumConstant(VariableElement c, Void p) { + return getDocLink(LinkInfoImpl.Kind.ANNOTATION, + c, c.getSimpleName().toString(), false); + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + public Content visitArray(List vals, Void p) { + ContentBuilder buf = new ContentBuilder(); + String sep = ""; + for (AnnotationValue av : vals) { + buf.addContent(sep); + buf.addContent(visit(av)); + sep = " "; + } + return buf; + } + @Override @DefinedBy(Api.LANGUAGE_MODEL) + protected Content defaultAction(Object o, Void p) { + return new StringContent(annotationValue.toString()); + } + }.visit(annotationValue); + } + + /** + * Return the configuration for this doclet. + * + * @return the configuration for this doclet. + */ + public Configuration configuration() { + return configuration; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java new file mode 100644 index 00000000000..5421c278411 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.util.*; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +import com.sun.source.doctree.DocTree; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter; +import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; + +/** + * Generate serialized form for serializable fields. + * Documentation denoted by the tags serial and + * serialField is processed. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Joe Fialli + * @author Bhavesh Patel (Modified) + */ +public class HtmlSerialFieldWriter extends FieldWriterImpl + implements SerializedFormWriter.SerialFieldWriter { + + public HtmlSerialFieldWriter(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + public SortedSet members(TypeElement te) { + return utils.serializableFields(te); + } + + /** + * Return the header for serializable fields section. + * + * @return a content tree for the header + */ + public Content getSerializableFieldsHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * Return the header for serializable fields content section. + * + * @param isLastContent true if the cotent being documented is the last content. + * @return a content tree for the header + */ + public Content getFieldsContentHeader(boolean isLastContent) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + if (isLastContent) + li.addStyle(HtmlStyle.blockListLast); + else + li.addStyle(HtmlStyle.blockList); + return li; + } + + /** + * Add serializable fields. + * + * @param heading the heading for the section + * @param serializableFieldsTree the tree to be added to the serializable fileds + * content tree + * @return a content tree for the serializable fields content + */ + public Content getSerializableFields(String heading, Content serializableFieldsTree) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + if (serializableFieldsTree.isValid()) { + Content headingContent = new StringContent(heading); + Content serialHeading = HtmlTree.HEADING(HtmlConstants.SERIALIZED_MEMBER_HEADING, + headingContent); + li.addContent(serialHeading); + li.addContent(serializableFieldsTree); + } + return li; + } + + /** + * Add the member header. + * + * @param fieldType the class document to be listed + * @param fieldTypeStr the string for the field type to be documented + * @param fieldDimensions the dimensions of the field string to be added + * @param fieldName name of the field to be added + * @param contentTree the content tree to which the member header will be added + */ + public void addMemberHeader(TypeElement fieldType, String fieldTypeStr, + String fieldDimensions, String fieldName, Content contentTree) { + Content nameContent = new RawHtml(fieldName); + Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, nameContent); + contentTree.addContent(heading); + Content pre = new HtmlTree(HtmlTag.PRE); + if (fieldType == null) { + pre.addContent(fieldTypeStr); + } else { + Content fieldContent = writer.getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.SERIAL_MEMBER, fieldType)); + pre.addContent(fieldContent); + } + pre.addContent(fieldDimensions + " "); + pre.addContent(fieldName); + contentTree.addContent(pre); + } + + /** + * Add the deprecated information for this member. + * + * @param field the field to document. + * @param contentTree the tree to which the deprecated info will be added + */ + public void addMemberDeprecatedInfo(VariableElement field, Content contentTree) { + addDeprecatedInfo(field, contentTree); + } + + /** + * Add the description text for this member. + * + * @param field the field to document. + * @param contentTree the tree to which the deprecated info will be added + */ + public void addMemberDescription(VariableElement field, Content contentTree) { + if (!utils.getBody(field).isEmpty()) { + writer.addInlineComment(field, contentTree); + } + List tags = utils.getBlockTags(field, DocTree.Kind.SERIAL); + if (!tags.isEmpty()) { + writer.addInlineComment(field, tags.get(0), contentTree); + } + } + + /** + * Add the description text for this member represented by the tag. + * + * @param serialFieldTag the field to document (represented by tag) + * @param contentTree the tree to which the deprecated info will be added + */ + public void addMemberDescription(VariableElement field, DocTree serialFieldTag, Content contentTree) { + CommentHelper ch = utils.getCommentHelper(field); + List description = ch.getDescription(configuration, serialFieldTag); + if (!description.isEmpty()) { + Content serialFieldContent = new RawHtml(ch.getText(description)); + Content div = HtmlTree.DIV(HtmlStyle.block, serialFieldContent); + contentTree.addContent(div); + } + } + + /** + * Add the tag information for this member. + * + * @param field the field to document. + * @param contentTree the tree to which the member tags info will be added + */ + public void addMemberTags(VariableElement field, Content contentTree) { + Content tagContent = new ContentBuilder(); + TagletWriter.genTagOutput(configuration.tagletManager, field, + configuration.tagletManager.getCustomTaglets(field), + writer.getTagletWriterInstance(false), tagContent); + Content dlTags = new HtmlTree(HtmlTag.DL); + dlTags.addContent(tagContent); + contentTree.addContent(dlTags); // TODO: what if empty? + } + + /** + * Check to see if overview details should be printed. If + * nocomment option set or if there is no text to be printed + * for deprecation info, comment or tags, do not print overview details. + * + * @param field the field to check overview details for. + * @return true if overview details need to be printed + */ + public boolean shouldPrintOverview(VariableElement field) { + if (!configuration.nocomment) { + if(!utils.getBody(field).isEmpty() || + writer.hasSerializationOverviewTags(field)) + return true; + } + if (utils.isDeprecated(field)) + return true; + return false; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialMethodWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialMethodWriter.java new file mode 100644 index 00000000000..ca58a2e4dea --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialMethodWriter.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter; +import jdk.javadoc.internal.doclets.toolkit.taglets.TagletManager; +import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; + + +/** + * Generate serialized form for Serializable/Externalizable methods. + * Documentation denoted by the serialData tag is processed. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Joe Fialli + * @author Bhavesh Patel (Modified) + */ +public class HtmlSerialMethodWriter extends MethodWriterImpl implements + SerializedFormWriter.SerialMethodWriter{ + + public HtmlSerialMethodWriter(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + /** + * Return the header for serializable methods section. + * + * @return a content tree for the header + */ + public Content getSerializableMethodsHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * Return the header for serializable methods content section. + * + * @param isLastContent true if the cotent being documented is the last content. + * @return a content tree for the header + */ + public Content getMethodsContentHeader(boolean isLastContent) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + if (isLastContent) + li.addStyle(HtmlStyle.blockListLast); + else + li.addStyle(HtmlStyle.blockList); + return li; + } + + /** + * Add serializable methods. + * + * @param heading the heading for the section + * @param serializableMethodContent the tree to be added to the serializable methods + * content tree + * @return a content tree for the serializable methods content + */ + public Content getSerializableMethods(String heading, Content serializableMethodContent) { + Content headingContent = new StringContent(heading); + Content serialHeading = HtmlTree.HEADING(HtmlConstants.SERIALIZED_MEMBER_HEADING, + headingContent); + Content li = HtmlTree.LI(HtmlStyle.blockList, serialHeading); + li.addContent(serializableMethodContent); + return li; + } + + /** + * Return the no customization message. + * + * @param msg the message to be displayed + * @return no customization message content + */ + public Content getNoCustomizationMsg(String msg) { + Content noCustomizationMsg = new StringContent(msg); + return noCustomizationMsg; + } + + /** + * Add the member header. + * + * @param member the method document to be listed + * @param methodsContentTree the content tree to which the member header will be added + */ + public void addMemberHeader(ExecutableElement member, Content methodsContentTree) { + methodsContentTree.addContent(getHead(member)); + methodsContentTree.addContent(getSignature(member)); + } + + /** + * Add the deprecated information for this member. + * + * @param member the method to document. + * @param methodsContentTree the tree to which the deprecated info will be added + */ + public void addDeprecatedMemberInfo(ExecutableElement member, Content methodsContentTree) { + addDeprecatedInfo(member, methodsContentTree); + } + + /** + * Add the description text for this member. + * + * @param member the method to document. + * @param methodsContentTree the tree to which the deprecated info will be added + */ + public void addMemberDescription(ExecutableElement member, Content methodsContentTree) { + addComment(member, methodsContentTree); + } + + /** + * Add the tag information for this member. + * + * @param member the method to document. + * @param methodsContentTree the tree to which the member tags info will be added + */ + public void addMemberTags(ExecutableElement member, Content methodsContentTree) { + Content tagContent = new ContentBuilder(); + TagletManager tagletManager = + configuration.tagletManager; + TagletWriter.genTagOutput(tagletManager, member, + tagletManager.getSerializedFormTaglets(), + writer.getTagletWriterInstance(false), tagContent); + Content dlTags = new HtmlTree(HtmlTag.DL); + dlTags.addContent(tagContent); + methodsContentTree.addContent(dlTags); + if (name(member).compareTo("writeExternal") == 0 + && utils.getSerialDataTrees(member).isEmpty()) { + serialWarning(member, "doclet.MissingSerialDataTag", + utils.getFullyQualifiedName(member.getEnclosingElement()), name(member)); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkFactoryImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkFactoryImpl.java new file mode 100644 index 00000000000..dbb476920be --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkFactoryImpl.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.util.List; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.doclets.toolkit.util.links.LinkFactory; +import jdk.javadoc.internal.doclets.toolkit.util.links.LinkInfo; + +import static jdk.javadoc.internal.doclets.formats.html.LinkInfoImpl.Kind.MEMBER_TYPE_PARAMS; + +/** + * A factory that returns a link given the information about it. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + */ +public class LinkFactoryImpl extends LinkFactory { + + private final HtmlDocletWriter m_writer; + + public LinkFactoryImpl(HtmlDocletWriter writer) { + m_writer = writer; + } + + /** + * {@inheritDoc} + */ + @Override + protected Content newContent() { + return new ContentBuilder(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getClassLink(LinkInfo linkInfo) { + Configuration configuration = m_writer.configuration; + Utils utils = configuration.utils; + LinkInfoImpl classLinkInfo = (LinkInfoImpl) linkInfo; + boolean noLabel = linkInfo.label == null || linkInfo.label.isEmpty(); + TypeElement typeElement = classLinkInfo.typeElement; + // Create a tool tip if we are linking to a class or interface. Don't + // create one if we are linking to a member. + String title = ""; + if (classLinkInfo.where == null || classLinkInfo.where.length() == 0) { + boolean isTypeLink = classLinkInfo.type != null && + utils.isTypeVariable(utils.getComponentType(classLinkInfo.type)); + title = getClassToolTip(typeElement, isTypeLink); + } + Content label = classLinkInfo.getClassLinkLabel(m_writer.configuration); + + Content link = new ContentBuilder(); + if (utils.isIncluded(typeElement)) { + if (configuration.isGeneratedDoc(typeElement)) { + DocPath filename = getPath(classLinkInfo); + if (linkInfo.linkToSelf || + !(DocPath.forName(utils, typeElement)).equals(m_writer.filename)) { + link.addContent(m_writer.getHyperLink( + filename.fragment(classLinkInfo.where), + label, + classLinkInfo.isStrong, classLinkInfo.styleName, + title, classLinkInfo.target)); + if (noLabel && !classLinkInfo.excludeTypeParameterLinks) { + link.addContent(getTypeParameterLinks(linkInfo)); + } + return link; + } + } + } else { + Content crossLink = m_writer.getCrossClassLink( + typeElement.getQualifiedName().toString(), classLinkInfo.where, + label, classLinkInfo.isStrong, classLinkInfo.styleName, + true); + if (crossLink != null) { + link.addContent(crossLink); + if (noLabel && !classLinkInfo.excludeTypeParameterLinks) { + link.addContent(getTypeParameterLinks(linkInfo)); + } + return link; + } + } + // Can't link so just write label. + link.addContent(label); + if (noLabel && !classLinkInfo.excludeTypeParameterLinks) { + link.addContent(getTypeParameterLinks(linkInfo)); + } + return link; + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getTypeParameterLink(LinkInfo linkInfo, TypeMirror typeParam) { + LinkInfoImpl typeLinkInfo = new LinkInfoImpl(m_writer.configuration, + ((LinkInfoImpl) linkInfo).getContext(), typeParam); + typeLinkInfo.excludeTypeBounds = linkInfo.excludeTypeBounds; + typeLinkInfo.excludeTypeParameterLinks = linkInfo.excludeTypeParameterLinks; + typeLinkInfo.linkToSelf = linkInfo.linkToSelf; + typeLinkInfo.isJava5DeclarationLocation = false; + return getLink(typeLinkInfo); + } + + @Override + protected Content getTypeAnnotationLink(LinkInfo linkInfo, AnnotationMirror annotation) { + throw new RuntimeException("Not implemented yet!"); + } + + @Override + public Content getTypeAnnotationLinks(LinkInfo linkInfo) { + Utils utils = ((LinkInfoImpl)linkInfo).utils; + ContentBuilder links = new ContentBuilder(); + List annotations; + if (utils.isAnnotated(linkInfo.type)) { + annotations = linkInfo.type.getAnnotationMirrors(); + } else if (utils.isTypeVariable(linkInfo.type)) { + // TODO: use the context for now, and special case for Receiver_Types, + // which takes the default case. + switch (((LinkInfoImpl)linkInfo).context) { + case MEMBER_TYPE_PARAMS: + case EXECUTABLE_MEMBER_PARAM: + case CLASS_SIGNATURE: + Element element = utils.typeUtils.asElement(linkInfo.type); + annotations = element.getAnnotationMirrors(); + break; + default: + annotations = linkInfo.type.getAnnotationMirrors(); + break; + } + + } else { + return links; + } + + if (annotations.isEmpty()) + return links; + + List annos = m_writer.getAnnotations(0, annotations, false, linkInfo.isJava5DeclarationLocation); + + boolean isFirst = true; + for (Content anno : annos) { + if (!isFirst) { + links.addContent(" "); + } + links.addContent(anno); + isFirst = false; + } + if (!annos.isEmpty()) { + links.addContent(" "); + } + + return links; + } + + /** + * Given a class, return the appropriate tool tip. + * + * @param typeElement the class to get the tool tip for. + * @return the tool tip for the appropriate class. + */ + private String getClassToolTip(TypeElement typeElement, boolean isTypeLink) { + Configuration configuration = m_writer.configuration; + Utils utils = configuration.utils; + if (isTypeLink) { + return configuration.getText("doclet.Href_Type_Param_Title", + utils.getSimpleName(typeElement)); + } else if (utils.isInterface(typeElement)){ + return configuration.getText("doclet.Href_Interface_Title", + utils.getPackageName(utils.containingPackage(typeElement))); + } else if (utils.isAnnotationType(typeElement)) { + return configuration.getText("doclet.Href_Annotation_Title", + utils.getPackageName(utils.containingPackage(typeElement))); + } else if (utils.isEnum(typeElement)) { + return configuration.getText("doclet.Href_Enum_Title", + utils.getPackageName(utils.containingPackage(typeElement))); + } else { + return configuration.getText("doclet.Href_Class_Title", + utils.getPackageName(utils.containingPackage(typeElement))); + } + } + + /** + * Return path to the given file name in the given package. So if the name + * passed is "Object.html" and the name of the package is "java.lang", and + * if the relative path is "../.." then returned string will be + * "../../java/lang/Object.html" + * + * @param linkInfo the information about the link. + */ + private DocPath getPath(LinkInfoImpl linkInfo) { + if (linkInfo.context == LinkInfoImpl.Kind.PACKAGE_FRAME) { + //Not really necessary to do this but we want to be consistent + //with 1.4.2 output. + return DocPath.forName(linkInfo.utils, linkInfo.typeElement); + } + return m_writer.pathToRoot.resolve(DocPath.forClass(linkInfo.utils, linkInfo.typeElement)); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkInfoImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkInfoImpl.java new file mode 100644 index 00000000000..01cd9508a1a --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkInfoImpl.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; +import jdk.javadoc.internal.doclets.toolkit.util.links.LinkInfo; + + +/** + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class LinkInfoImpl extends LinkInfo { + + public enum Kind { + DEFAULT, + + /** + * Indicate that the link appears in a class list. + */ + ALL_CLASSES_FRAME, + + /** + * Indicate that the link appears in a class documentation. + */ + CLASS, + + /** + * Indicate that the link appears in member documentation. + */ + MEMBER, + + /** + * Indicate that the link appears in class use documentation. + */ + CLASS_USE, + + /** + * Indicate that the link appears in index documentation. + */ + INDEX, + + /** + * Indicate that the link appears in constant value summary. + */ + CONSTANT_SUMMARY, + + /** + * Indicate that the link appears in serialized form documentation. + */ + SERIALIZED_FORM, + + /** + * Indicate that the link appears in serial member documentation. + */ + SERIAL_MEMBER, + + /** + * Indicate that the link appears in package documentation. + */ + PACKAGE, + + /** + * Indicate that the link appears in see tag documentation. + */ + SEE_TAG, + + /** + * Indicate that the link appears in value tag documentation. + */ + VALUE_TAG, + + /** + * Indicate that the link appears in tree documentation. + */ + TREE, + + /** + * Indicate that the link appears in a class list. + */ + PACKAGE_FRAME, + + /** + * The header in the class documentation. + */ + CLASS_HEADER, + + /** + * The signature in the class documentation. + */ + CLASS_SIGNATURE, + + /** + * The return type of a method. + */ + RETURN_TYPE, + + /** + * The return type of a method in a member summary. + */ + SUMMARY_RETURN_TYPE, + + /** + * The type of a method/constructor parameter. + */ + EXECUTABLE_MEMBER_PARAM, + + /** + * Super interface links. + */ + SUPER_INTERFACES, + + /** + * Implemented interface links. + */ + IMPLEMENTED_INTERFACES, + + /** + * Implemented class links. + */ + IMPLEMENTED_CLASSES, + + /** + * Subinterface links. + */ + SUBINTERFACES, + + /** + * Subclasses links. + */ + SUBCLASSES, + + /** + * The signature in the class documentation (implements/extends portion). + */ + CLASS_SIGNATURE_PARENT_NAME, + + /** + * The header for method documentation copied from parent. + */ + EXECUTABLE_ELEMENT_COPY, + + /** + * Method "specified by" link. + */ + METHOD_SPECIFIED_BY, + + /** + * Method "overrides" link. + */ + METHOD_OVERRIDES, + + /** + * Annotation link. + */ + ANNOTATION, + + /** + * The header for field documentation copied from parent. + */ + VARIABLE_ELEMENT_COPY, + + /** + * The parent nodes in the class tree. + */ + CLASS_TREE_PARENT, + + /** + * The type parameters of a method or constructor. + */ + MEMBER_TYPE_PARAMS, + + /** + * Indicate that the link appears in class use documentation. + */ + CLASS_USE_HEADER, + + /** + * The header for property documentation copied from parent. + */ + PROPERTY_COPY, + + /** + * A receiver type + */ + RECEIVER_TYPE + } + + public final ConfigurationImpl configuration; + + /** + * The location of the link. + */ + public Kind context = Kind.DEFAULT; + + /** + * The value of the marker #. + */ + public String where = ""; + + /** + * String style of text defined in style sheet. + */ + public String styleName = ""; + + /** + * The value of the target. + */ + public String target = ""; + public final Utils utils; + /** + * Construct a LinkInfo object. + * + * @param configuration the configuration data for the doclet + * @param context the context of the link. + * @param ee the member to link to. + */ + public LinkInfoImpl(ConfigurationImpl configuration, Kind context, ExecutableElement ee) { + this.configuration = configuration; + this.utils = configuration.utils; + this.executableElement = ee; + setContext(context); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content newContent() { + return new ContentBuilder(); + } + + /** + * Construct a LinkInfo object. + * + * @param configuration the configuration data for the doclet + * @param context the context of the link. + * @param typeElement the class to link to. + */ + public LinkInfoImpl(ConfigurationImpl configuration, Kind context, TypeElement typeElement) { + this.configuration = configuration; + this.utils = configuration.utils; + this.typeElement = typeElement; + setContext(context); + } + + /** + * Construct a LinkInfo object. + * + * @param configuration the configuration data for the doclet + * @param context the context of the link. + * @param type the class to link to. + */ + public LinkInfoImpl(ConfigurationImpl configuration, Kind context, TypeMirror type) { + this.configuration = configuration; + this.utils = configuration.utils; + this.type = type; + setContext(context); + } + + /** + * Set the label for the link. + * @param label plain-text label for the link + */ + public LinkInfoImpl label(String label) { + this.label = new StringContent(label); + return this; + } + + /** + * Set the label for the link. + */ + public LinkInfoImpl label(Content label) { + this.label = label; + return this; + } + + /** + * Set whether or not the link should be strong. + */ + public LinkInfoImpl strong(boolean strong) { + this.isStrong = strong; + return this; + } + + /** + * Set the style to be used for the link. + * @param styleName String style of text defined in style sheet. + */ + public LinkInfoImpl styleName(String styleName) { + this.styleName = styleName; + return this; + } + + /** + * Set the target to be used for the link. + * @param styleName String style of text defined in style sheet. + */ + public LinkInfoImpl target(String target) { + this.target = target; + return this; + } + + /** + * Set whether or not this is a link to a varargs parameter. + */ + public LinkInfoImpl varargs(boolean varargs) { + this.isVarArg = varargs; + return this; + } + + /** + * Set the fragment specifier for the link. + */ + public LinkInfoImpl where(String where) { + this.where = where; + return this; + } + + /** + * {@inheritDoc} + */ + public Kind getContext() { + return context; + } + + /** + * {@inheritDoc} + * + * This method sets the link attributes to the appropriate values + * based on the context. + * + * @param c the context id to set. + */ + public final void setContext(Kind c) { + //NOTE: Put context specific link code here. + switch (c) { + case ALL_CLASSES_FRAME: + case PACKAGE_FRAME: + case IMPLEMENTED_CLASSES: + case SUBCLASSES: + case EXECUTABLE_ELEMENT_COPY: + case VARIABLE_ELEMENT_COPY: + case PROPERTY_COPY: + case CLASS_USE_HEADER: + includeTypeInClassLinkLabel = false; + break; + + case ANNOTATION: + excludeTypeParameterLinks = true; + excludeTypeBounds = true; + break; + + case IMPLEMENTED_INTERFACES: + case SUPER_INTERFACES: + case SUBINTERFACES: + case CLASS_TREE_PARENT: + case TREE: + case CLASS_SIGNATURE_PARENT_NAME: + excludeTypeParameterLinks = true; + excludeTypeBounds = true; + includeTypeInClassLinkLabel = false; + includeTypeAsSepLink = true; + break; + + case PACKAGE: + case CLASS_USE: + case CLASS_HEADER: + case CLASS_SIGNATURE: + case RECEIVER_TYPE: + excludeTypeParameterLinks = true; + includeTypeAsSepLink = true; + includeTypeInClassLinkLabel = false; + break; + + case MEMBER_TYPE_PARAMS: + includeTypeAsSepLink = true; + includeTypeInClassLinkLabel = false; + break; + + case RETURN_TYPE: + case SUMMARY_RETURN_TYPE: + excludeTypeBounds = true; + break; + case EXECUTABLE_MEMBER_PARAM: + excludeTypeBounds = true; + break; + } + context = c; + if (type != null && + utils.isTypeVariable(type) && + utils.isExecutableElement(utils.asTypeElement(type).getEnclosingElement())) { + excludeTypeParameterLinks = true; + } + } + + /** + * Return true if this link is linkable and false if we can't link to the + * desired place. + * + * @return true if this link is linkable and false if we can't link to the + * desired place. + */ + @Override + public boolean isLinkable() { + return configuration.utils.isLinkable(typeElement); + } + + @Override + public String toString() { + return "LinkInfoImpl{" + + "context=" + context + + ", where=" + where + + ", styleName=" + styleName + + ", target=" + target + + super.toString() + '}'; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkOutputImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkOutputImpl.java new file mode 100644 index 00000000000..f13d7e9a282 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/LinkOutputImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import jdk.javadoc.internal.doclets.toolkit.util.links.LinkOutput; + +/** + * Stores output of a link. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + */ +public class LinkOutputImpl implements LinkOutput { + + /** + * The output of the link. + */ + public StringBuilder output; + + /** + * Construct a new LinkOutputImpl. + */ + public LinkOutputImpl() { + output = new StringBuilder(); + } + + /** + * {@inheritDoc} + */ + public void append(Object o) { + output.append(o instanceof String ? + (String) o : ((LinkOutputImpl)o).toString()); + } + + /** + * {@inheritDoc} + */ + public void insert(int offset, Object o) { + output.insert(offset, o.toString()); + } + + /** + * {@inheritDoc} + */ + public String toString() { + return output.toString(); + } + +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java new file mode 100644 index 00000000000..40b00cdd1fd --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java @@ -0,0 +1,457 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.MethodWriter; +import jdk.javadoc.internal.doclets.toolkit.util.ImplementedMethods; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +/** + * Writes method documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Jamie Ho (rewrite) + * @author Bhavesh Patel (Modified) + */ +public class MethodWriterImpl extends AbstractExecutableMemberWriter + implements MethodWriter, MemberSummaryWriter { + + /** + * Construct a new MethodWriterImpl. + * + * @param writer the writer for the class that the methods belong to. + * @param typeElement the class being documented. + */ + public MethodWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + /** + * Construct a new MethodWriterImpl. + * + * @param writer The writer for the class that the methods belong to. + */ + public MethodWriterImpl(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_METHOD_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMethodDetailsTreeHeader(TypeElement typeElement, Content memberDetailsTree) { + memberDetailsTree.addContent(HtmlConstants.START_OF_METHOD_DETAILS); + Content methodDetailsTree = writer.getMemberTreeHeader(); + methodDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.METHOD_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.methodDetailsLabel); + methodDetailsTree.addContent(heading); + return methodDetailsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMethodDocTreeHeader(ExecutableElement method, Content methodDetailsTree) { + String erasureAnchor; + if ((erasureAnchor = getErasureAnchor(method)) != null) { + methodDetailsTree.addContent(writer.getMarkerAnchor((erasureAnchor))); + } + methodDetailsTree.addContent( + writer.getMarkerAnchor(writer.getAnchor(method))); + Content methodDocTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(name(method)); + methodDocTree.addContent(heading); + return methodDocTree; + } + + /** + * Get the signature for the given method. + * + * @param method the method being documented. + * @return a content object for the signature + */ + @Override + public Content getSignature(ExecutableElement method) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(method, pre); + int annotationLength = pre.charCount(); + addModifiers(method, pre); + addTypeParameters(method, pre); + addReturnType(method, pre); + if (configuration.linksource) { + Content methodName = new StringContent(name(method)); + writer.addSrcLink(method, methodName, pre); + } else { + addName(name(method), pre); + } + int indent = pre.charCount() - annotationLength; + addParameters(method, pre, indent); + addExceptions(method, pre, indent); + return pre; + } + + /** + * {@inheritDoc} + */ + @Override + public void addDeprecated(ExecutableElement method, Content methodDocTree) { + addDeprecatedInfo(method, methodDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public void addComments(TypeMirror holderType, ExecutableElement method, Content methodDocTree) { + TypeElement holder = utils.asTypeElement(holderType); + if (!utils.getBody(method).isEmpty()) { + if (holder.equals(typeElement) || + !(utils.isPublic(holder) || + utils.isLinkable(holder))) { + writer.addInlineComment(method, methodDocTree); + } else { + Content link = + writer.getDocLink(LinkInfoImpl.Kind.EXECUTABLE_ELEMENT_COPY, + holder, method, + utils.isIncluded(holder) + ? utils.getSimpleName(holder) + : utils.getFullyQualifiedName(holder), + false); + Content codelLink = HtmlTree.CODE(link); + Content descfrmLabel = HtmlTree.SPAN(HtmlStyle.descfrmTypeLabel, + utils.isClass(holder) + ? writer.descfrmClassLabel + : writer.descfrmInterfaceLabel); + descfrmLabel.addContent(writer.getSpace()); + descfrmLabel.addContent(codelLink); + methodDocTree.addContent(HtmlTree.DIV(HtmlStyle.block, descfrmLabel)); + writer.addInlineComment(method, methodDocTree); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addTags(ExecutableElement method, Content methodDocTree) { + writer.addTagsInfo(method, methodDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMethodDetails(Content methodDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(methodDetailsTree)); + return htmlTree; + } + return getMemberTree(methodDetailsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMethodDoc(Content methodDocTree, + boolean isLastContent) { + return getMemberTree(methodDocTree, isLastContent); + } + + /** + * Close the writer. + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Method_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Method_Summary"), + configuration.getText("doclet.methods")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Methods"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Method"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.METHOD_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + inheritedTree.addContent(writer.getMarkerAnchor( + SectionName.METHODS_INHERITANCE, configuration.getClassName(typeElement))); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + Content classLink = writer.getPreQualifiedClassLink( + LinkInfoImpl.Kind.MEMBER, typeElement, false); + Content label = new StringContent(utils.isClass(typeElement) + ? configuration.getText("doclet.Methods_Inherited_From_Class") + : configuration.getText("doclet.Methods_Inherited_From_Interface")); + Content labelHeading = HtmlTree.HEADING(HtmlConstants.INHERITED_SUMMARY_HEADING, + label); + labelHeading.addContent(writer.getSpace()); + labelHeading.addContent(classLink); + inheritedTree.addContent(labelHeading); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + ExecutableElement meth = (ExecutableElement)member; + addModifierAndType(meth, utils.getReturnType(meth), tdSummaryType); + } + + /** + * {@inheritDoc} + */ + protected static void addOverridden(HtmlDocletWriter writer, + TypeMirror overriddenType, ExecutableElement method, Content dl) { + if (writer.configuration.nocomment) { + return; + } + Utils utils = writer.configuration().utils; + TypeElement holder = utils.getEnclosingTypeElement(method); + if (!(utils.isPublic(holder) || + utils.isLinkable(holder))) { + //This is an implementation detail that should not be documented. + return; + } + if (utils.isIncluded(holder) && ! utils.isIncluded(method)) { + //The class is included but the method is not. That means that it + //is not visible so don't document this. + return; + } + Content label = writer.overridesLabel; + LinkInfoImpl.Kind context = LinkInfoImpl.Kind.METHOD_OVERRIDES; + + if (method != null) { + if (utils.isAbstract(holder) && utils.isAbstract(method)){ + //Abstract method is implemented from abstract class, + //not overridden + label = writer.specifiedByLabel; + context = LinkInfoImpl.Kind.METHOD_SPECIFIED_BY; + } + Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.overrideSpecifyLabel, label)); + dl.addContent(dt); + Content overriddenTypeLink = + writer.getLink(new LinkInfoImpl(writer.configuration, context, overriddenType)); + Content codeOverridenTypeLink = HtmlTree.CODE(overriddenTypeLink); + String name = method.getSimpleName().toString(); + Content methlink = writer.getLink( + new LinkInfoImpl(writer.configuration, LinkInfoImpl.Kind.MEMBER, + holder) + .where(writer.getName(writer.getAnchor(method))).label(name)); + Content codeMethLink = HtmlTree.CODE(methlink); + Content dd = HtmlTree.DD(codeMethLink); + dd.addContent(writer.getSpace()); + dd.addContent(writer.getResource("doclet.in_class")); + dd.addContent(writer.getSpace()); + dd.addContent(codeOverridenTypeLink); + dl.addContent(dd); + } + } + + /** + * {@inheritDoc} + */ + protected static void addImplementsInfo(HtmlDocletWriter writer, + ExecutableElement method, Content dl) { + if (writer.configuration.nocomment) { + return; + } + Utils utils = writer.utils; + ImplementedMethods implementedMethodsFinder = + new ImplementedMethods(method, writer.configuration); + SortedSet implementedMethods = + new TreeSet<>(utils.makeOverrideUseComparator()); + implementedMethods.addAll(implementedMethodsFinder.build()); + for (ExecutableElement implementedMeth : implementedMethods) { + TypeMirror intfac = implementedMethodsFinder.getMethodHolder(implementedMeth); + intfac = utils.getDeclaredType(utils.getEnclosingTypeElement(method), intfac); + Content intfaclink = writer.getLink(new LinkInfoImpl( + writer.configuration, LinkInfoImpl.Kind.METHOD_SPECIFIED_BY, intfac)); + Content codeIntfacLink = HtmlTree.CODE(intfaclink); + Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.overrideSpecifyLabel, writer.specifiedByLabel)); + dl.addContent(dt); + Content methlink = writer.getDocLink( + LinkInfoImpl.Kind.MEMBER, implementedMeth, + implementedMeth.getSimpleName().toString(), false); + Content codeMethLink = HtmlTree.CODE(methlink); + Content dd = HtmlTree.DD(codeMethLink); + dd.addContent(writer.getSpace()); + dd.addContent(writer.getResource("doclet.in_interface")); + dd.addContent(writer.getSpace()); + dd.addContent(codeIntfacLink); + dl.addContent(dd); + } + } + + /** + * Add the return type. + * + * @param method the method being documented. + * @param htmltree the content tree to which the return type will be added + */ + protected void addReturnType(ExecutableElement method, Content htmltree) { + TypeMirror type = utils.getReturnType(method); + if (type != null) { + Content linkContent = writer.getLink( + new LinkInfoImpl(configuration, LinkInfoImpl.Kind.RETURN_TYPE, type)); + htmltree.addContent(linkContent); + htmltree.addContent(writer.getSpace()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + if (typeElement == null) { + return writer.getHyperLink( + SectionName.METHOD_SUMMARY, + writer.getResource("doclet.navMethod")); + } else { + return writer.getHyperLink( + SectionName.METHODS_INHERITANCE, + configuration.getClassName(typeElement), writer.getResource("doclet.navMethod")); + } + } else { + return writer.getResource("doclet.navMethod"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.METHOD_DETAIL, writer.getResource("doclet.navMethod"))); + } else { + liNav.addContent(writer.getResource("doclet.navMethod")); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriterImpl.java new file mode 100644 index 00000000000..c0828b8d5e2 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/NestedClassWriterImpl.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; + +/** + * Writes nested class documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Jamie Ho (rewrite) + * @author Bhavesh Patel (Modified) + */ +public class NestedClassWriterImpl extends AbstractMemberWriter + implements MemberSummaryWriter { + + public NestedClassWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + public NestedClassWriterImpl(SubWriterHolderWriter writer) { + super(writer); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_NESTED_CLASS_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * Close the writer. + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Nested_Class_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Nested_Class_Summary"), + configuration.getText("doclet.nested_classes")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Nested_Classes"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + if (utils.isInterface(member)) { + return Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Interface"), + configuration.getText("doclet.Description"))); + + } else { + return Arrays.asList(writer.getModifierTypeHeader(), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Class"), + configuration.getText("doclet.Description"))); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.NESTED_CLASS_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + inheritedTree.addContent(writer.getMarkerAnchor( + SectionName.NESTED_CLASSES_INHERITANCE, + utils.getFullyQualifiedName(typeElement))); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + Content classLink = writer.getPreQualifiedClassLink( + LinkInfoImpl.Kind.MEMBER, typeElement, false); + Content label = new StringContent(utils.isInterface(typeElement) + ? configuration.getText("doclet.Nested_Classes_Interface_Inherited_From_Interface") + : configuration.getText("doclet.Nested_Classes_Interfaces_Inherited_From_Class")); + Content labelHeading = HtmlTree.HEADING(HtmlConstants.INHERITED_SUMMARY_HEADING, + label); + labelHeading.addContent(writer.getSpace()); + labelHeading.addContent(classLink); + inheritedTree.addContent(labelHeading); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getLink(new LinkInfoImpl(configuration, context, (TypeElement)member))); + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addInheritedSummaryLink(TypeElement typeElement, Element member, Content linksTree) { + linksTree.addContent( + writer.getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, + (TypeElement)member))); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + addModifierAndType(member, null, tdSummaryType); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getDeprecatedLink(Element member) { + return writer.getQualifiedClassLink(LinkInfoImpl.Kind.MEMBER, member); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + if (typeElement == null) { + return writer.getHyperLink( + SectionName.NESTED_CLASS_SUMMARY, + writer.getResource("doclet.navNested")); + } else { + return writer.getHyperLink( + SectionName.NESTED_CLASSES_INHERITANCE, + utils.getFullyQualifiedName(typeElement), writer.getResource("doclet.navNested")); + } + } else { + return writer.getResource("doclet.navNested"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageFrameWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageFrameWriter.java new file mode 100644 index 00000000000..4925b55d00b --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageFrameWriter.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Class to generate file for each package contents in the left-hand bottom + * frame. This will list all the Class Kinds in the package. A click on any + * class-kind will update the right-hand frame with the clicked class-kind page. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class PackageFrameWriter extends HtmlDocletWriter { + + /** + * The package being documented. + */ + private PackageElement packageElement; + + /** + * The classes to be documented. Use this to filter out classes + * that will not be documented. + */ + private SortedSet documentedClasses; + + /** + * Constructor to construct PackageFrameWriter object and to generate + * "package-frame.html" file in the respective package directory. + * For example for package "java.lang" this will generate file + * "package-frame.html" file in the "java/lang" directory. It will also + * create "java/lang" directory in the current or the destination directory + * if it doesn't exist. + * + * @param configuration the configuration of the doclet. + * @param packageElement PackageElement under consideration. + */ + public PackageFrameWriter(ConfigurationImpl configuration, PackageElement packageElement) + throws IOException { + super(configuration, DocPath.forPackage(packageElement).resolve(DocPaths.PACKAGE_FRAME)); + this.packageElement = packageElement; + if (utils.getSpecifiedPackages().isEmpty()) { + documentedClasses = new TreeSet<>(utils.makeGeneralPurposeComparator()); + documentedClasses.addAll(configuration.root.getIncludedClasses()); + } + } + + /** + * Generate a package summary page for the left-hand bottom frame. Construct + * the PackageFrameWriter object and then uses it generate the file. + * + * @param configuration the current configuration of the doclet. + * @param packageElement The package for which "pacakge-frame.html" is to be generated. + */ + public static void generate(ConfigurationImpl configuration, PackageElement packageElement) { + PackageFrameWriter packgen; + try { + packgen = new PackageFrameWriter(configuration, packageElement); + String pkgName = configuration.utils.getPackageName(packageElement); + HtmlTree body = packgen.getBody(false, packgen.getWindowTitle(pkgName)); + Content pkgNameContent = new StringContent(pkgName); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN() + : body; + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, HtmlStyle.bar, + packgen.getTargetPackageLink(packageElement, "classFrame", pkgNameContent)); + htmlTree.addContent(heading); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.indexContainer); + packgen.addClassListing(div); + htmlTree.addContent(div); + if (configuration.allowTag(HtmlTag.MAIN)) { + body.addContent(htmlTree); + } + packgen.printHtmlDocument( + configuration.metakeywords.getMetaKeywords(packageElement), false, body); + packgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), DocPaths.PACKAGE_FRAME.getPath()); + throw new DocletAbortException(exc); + } + } + + /** + * Add class listing for all the classes in this package. Divide class + * listing as per the class kind and generate separate listing for + * Classes, Interfaces, Exceptions and Errors. + * + * @param contentTree the content tree to which the listing will be added + */ + protected void addClassListing(HtmlTree contentTree) { + Configuration config = configuration; + if (utils.isIncluded(packageElement)) { + addClassKindListing(utils.getInterfaces(packageElement), + getResource("doclet.Interfaces"), contentTree); + addClassKindListing(utils.getOrdinaryClasses(packageElement), + getResource("doclet.Classes"), contentTree); + addClassKindListing(utils.getEnums(packageElement), + getResource("doclet.Enums"), contentTree); + addClassKindListing(utils.getExceptions(packageElement), + getResource("doclet.Exceptions"), contentTree); + addClassKindListing(utils.getErrors(packageElement), + getResource("doclet.Errors"), contentTree); + addClassKindListing(utils.getAnnotationTypes(packageElement), + getResource("doclet.AnnotationTypes"), contentTree); + } else { + addClassKindListing(config.typeElementCatalog.interfaces(packageElement), + getResource("doclet.Interfaces"), contentTree); + addClassKindListing(config.typeElementCatalog.ordinaryClasses(packageElement), + getResource("doclet.Classes"), contentTree); + addClassKindListing(config.typeElementCatalog.enums(packageElement), + getResource("doclet.Enums"), contentTree); + addClassKindListing(config.typeElementCatalog.exceptions(packageElement), + getResource("doclet.Exceptions"), contentTree); + addClassKindListing(config.typeElementCatalog.errors(packageElement), + getResource("doclet.Errors"), contentTree); + addClassKindListing(config.typeElementCatalog.annotationTypes(packageElement), + getResource("doclet.AnnotationTypes"), contentTree); + } + } + + /** + * Add specific class kind listing. Also add label to the listing. + * + * @param arr Array of specific class kinds, namely Class or Interface or Exception or Error + * @param labelContent content tree of the label to be added + * @param contentTree the content tree to which the class kind listing will be added + */ + protected void addClassKindListing(Iterable list, Content labelContent, + HtmlTree contentTree) { + SortedSet tset = utils.filterOutPrivateClasses(list, configuration.javafx); + if(!tset.isEmpty()) { + boolean printedHeader = false; + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.SECTION() + : contentTree; + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.setTitle(labelContent); + for (TypeElement typeElement : tset) { + if (documentedClasses != null && !documentedClasses.contains(typeElement)) { + continue; + } + if (!utils.isCoreClass(typeElement) || !configuration.isGeneratedDoc(typeElement)) { + continue; + } + if (!printedHeader) { + Content heading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, + true, labelContent); + htmlTree.addContent(heading); + printedHeader = true; + } + Content arr_i_name = new StringContent(utils.getSimpleName(typeElement)); + if (utils.isInterface(typeElement)) + arr_i_name = HtmlTree.SPAN(HtmlStyle.interfaceName, arr_i_name); + Content link = getLink(new LinkInfoImpl(configuration, + LinkInfoImpl.Kind.PACKAGE_FRAME, typeElement).label(arr_i_name).target("classFrame")); + Content li = HtmlTree.LI(link); + ul.addContent(li); + } + htmlTree.addContent(ul); + if (configuration.allowTag(HtmlTag.SECTION)) { + contentTree.addContent(htmlTree); + } + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexFrameWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexFrameWriter.java new file mode 100644 index 00000000000..064a93ce9d2 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexFrameWriter.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.IOException; +import java.util.Collection; + +import javax.lang.model.element.PackageElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Generate the package index for the left-hand frame in the generated output. + * A click on the package name in this frame will update the page in the bottom + * left hand frame with the listing of contents of the clicked package. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + */ +public class PackageIndexFrameWriter extends AbstractPackageIndexWriter { + + /** + * Construct the PackageIndexFrameWriter object. + * + * @param filename Name of the package index file to be generated. + */ + public PackageIndexFrameWriter(ConfigurationImpl configuration, + DocPath filename) throws IOException { + super(configuration, filename); + } + + /** + * Generate the package index file named "overview-frame.html". + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration) { + PackageIndexFrameWriter packgen; + DocPath filename = DocPaths.OVERVIEW_FRAME; + try { + packgen = new PackageIndexFrameWriter(configuration, filename); + packgen.buildPackageIndexFile("doclet.Window_Overview", false); + packgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * {@inheritDoc} + */ + protected void addPackagesList(Collection packages, String text, + String tableSummary, Content body) { + Content heading = HtmlTree.HEADING(HtmlConstants.PACKAGE_HEADING, true, + packagesLabel); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN(HtmlStyle.indexContainer, heading) + : HtmlTree.DIV(HtmlStyle.indexContainer, heading); + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.setTitle(packagesLabel); + for (PackageElement aPackage : packages) { + // Do not list the package if -nodeprecated option is set and the + // package is marked as deprecated. + if (aPackage != null && + (!(configuration.nodeprecated && utils.isDeprecated(aPackage)))) { + ul.addContent(getPackage(aPackage)); + } + } + htmlTree.addContent(ul); + body.addContent(htmlTree); + } + + /** + * Returns each package name as a separate link. + * + * @param pe PackageElement + * @return content for the package link + */ + protected Content getPackage(PackageElement pe) { + Content packageLinkContent; + Content packageLabel; + if (pe.isUnnamed()) { + packageLabel = new StringContent(""); + packageLinkContent = getHyperLink(DocPaths.PACKAGE_FRAME, + packageLabel, "", "packageFrame"); + } else { + packageLabel = getPackageLabel(pe.getQualifiedName().toString()); + packageLinkContent = getHyperLink(pathString(pe, + DocPaths.PACKAGE_FRAME), packageLabel, "", + "packageFrame"); + } + Content li = HtmlTree.LI(packageLinkContent); + return li; + } + + /** + * {@inheritDoc} + */ + protected void addNavigationBarHeader(Content body) { + Content headerContent; + if (configuration.packagesheader.length() > 0) { + headerContent = new RawHtml(replaceDocRootDir(configuration.packagesheader)); + } else { + headerContent = new RawHtml(replaceDocRootDir(configuration.header)); + } + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.bar, headerContent); + body.addContent(heading); + } + + /** + * Do nothing as there is no overview information in this page. + */ + protected void addOverviewHeader(Content body) { + } + + /** + * Adds "All Classes" link for the top of the left-hand frame page to the + * documentation tree. + * + * @param ul the Content object to which the "All Classes" link should be added + */ + protected void addAllClassesLink(Content ul) { + Content linkContent = getHyperLink(DocPaths.ALLCLASSES_FRAME, + allclassesLabel, "", "packageFrame"); + Content li = HtmlTree.LI(linkContent); + ul.addContent(li); + } + + /** + * {@inheritDoc} + */ + protected void addNavigationBarFooter(Content body) { + Content p = HtmlTree.P(getSpace()); + body.addContent(p); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java new file mode 100644 index 00000000000..c9a3a987038 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; + +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.Group; + +/** + * Generate the package index page "overview-summary.html" for the right-hand + * frame. A click on the package name on this page will update the same frame + * with the "package-summary.html" file for the clicked package. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class PackageIndexWriter extends AbstractPackageIndexWriter { + + /** + * Root of the program structure. Used for "overview" documentation. + */ + private DocletEnvironment root; + + /** + * Map representing the group of packages as specified on the command line. + * + * @see Group + */ + private Map> groupPackageMap; + + /** + * List to store the order groups as specified on the command line. + */ + private List groupList; + + /** + * HTML tree for main tag. + */ + private HtmlTree htmlTree = HtmlTree.MAIN(); + + /** + * Construct the PackageIndexWriter. Also constructs the grouping + * information as provided on the command line by "-group" option. Stores + * the order of groups specified by the user. + * + * @see Group + */ + public PackageIndexWriter(ConfigurationImpl configuration, DocPath filename) throws IOException { + super(configuration, filename); + this.root = configuration.root; + groupPackageMap = configuration.group.groupPackages(packages); + groupList = configuration.group.getGroupList(); + } + + /** + * Generate the package index page for the right-hand frame. + * + * @param configuration the current configuration of the doclet. + */ + public static void generate(ConfigurationImpl configuration) { + PackageIndexWriter packgen; + DocPath filename = DocPaths.OVERVIEW_SUMMARY; + try { + packgen = new PackageIndexWriter(configuration, filename); + packgen.buildPackageIndexFile("doclet.Window_Overview_Summary", true); + packgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Depending upon the grouping information and their titles, add + * separate table indices for each package group. + * + * @param body the documentation tree to which the index will be added + */ + protected void addIndex(Content body) { + for (String groupname : groupList) { + SortedSet list = groupPackageMap.get(groupname); + if (list != null && !list.isEmpty()) { + addIndexContents(list, + groupname, configuration.getText("doclet.Member_Table_Summary", + groupname, configuration.getText("doclet.packages")), body); + } + } + } + + /** + * {@inheritDoc} + */ + protected void addPackagesList(Collection packages, String text, + String tableSummary, Content body) { + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.overviewSummary, getTableCaption(new RawHtml(text))) + : HtmlTree.TABLE(HtmlStyle.overviewSummary, tableSummary, getTableCaption(new RawHtml(text))); + table.addContent(getSummaryTableHeader(packageTableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + addPackagesList(packages, tbody); + table.addContent(tbody); + Content div = HtmlTree.DIV(HtmlStyle.contentContainer, table); + if (configuration.allowTag(HtmlTag.MAIN)) { + htmlTree.addContent(div); + } else { + body.addContent(div); + } + } + + /** + * Adds list of packages in the index table. Generate link to each package. + * + * @param packages Packages to which link is to be generated + * @param tbody the documentation tree to which the list will be added + */ + protected void addPackagesList(Collection packages, Content tbody) { + boolean altColor = true; + for (PackageElement pkg : packages) { + if (!pkg.isUnnamed()) { + if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) { + Content packageLinkContent = getPackageLink(pkg, getPackageName(pkg)); + Content tdPackage = HtmlTree.TD(HtmlStyle.colFirst, packageLinkContent); + HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); + tdSummary.addStyle(HtmlStyle.colLast); + addSummaryComment(pkg, tdSummary); + HtmlTree tr = HtmlTree.TR(tdPackage); + tr.addContent(tdSummary); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + tbody.addContent(tr); + } + } + altColor = !altColor; + } + } + + /** + * Adds the overview summary comment for this documentation. Add one line + * summary at the top of the page and generate a link to the description, + * which is added at the end of this page. + * + * @param body the documentation tree to which the overview header will be added + */ + protected void addOverviewHeader(Content body) { + addConfigurationTitle(body); + if (!utils.getBody(configuration.overviewElement).isEmpty()) { + HtmlTree subTitleDiv = new HtmlTree(HtmlTag.DIV); + subTitleDiv.addStyle(HtmlStyle.subTitle); + addSummaryComment(configuration.overviewElement, subTitleDiv); + Content div = HtmlTree.DIV(HtmlStyle.header, subTitleDiv); + Content see = seeLabel; + see.addContent(" "); + Content descPara = HtmlTree.P(see); + Content descLink = getHyperLink(getDocLink( + SectionName.OVERVIEW_DESCRIPTION), + descriptionLabel, "", ""); + descPara.addContent(descLink); + div.addContent(descPara); + if (configuration.allowTag(HtmlTag.MAIN)) { + htmlTree.addContent(div); + } else { + body.addContent(div); + } + } + } + + /** + * Adds the overview comment as provided in the file specified by the + * "-overview" option on the command line. + * + * @param htmltree the documentation tree to which the overview comment will + * be added + */ + protected void addOverviewComment(Content htmltree) { + if (!utils.getBody(configuration.overviewElement).isEmpty()) { + htmltree.addContent(getMarkerAnchor(SectionName.OVERVIEW_DESCRIPTION)); + addInlineComment(configuration.overviewElement, htmltree); + } + } + + /** + * Adds the tag information as provided in the file specified by the + * "-overview" option on the command line. + * + * @param body the documentation tree to which the overview will be added + */ + protected void addOverview(Content body) throws IOException { + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.contentContainer); + addOverviewComment(div); + if (configuration.allowTag(HtmlTag.MAIN)) { + htmlTree.addContent(div); + body.addContent(htmlTree); + } else { + body.addContent(div); + } + } + + /** + * Adds the top text (from the -top option), the upper + * navigation bar, and then the title (from the"-title" + * option), at the top of page. + * + * @param body the documentation tree to which the navigation bar header will be added + */ + protected void addNavigationBarHeader(Content body) { + Content htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : body; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + body.addContent(htmlTree); + } + } + + /** + * Adds the lower navigation bar and the bottom text + * (from the -bottom option) at the bottom of page. + * + * @param body the documentation tree to which the navigation bar footer will be added + */ + protected void addNavigationBarFooter(Content body) { + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : body; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java new file mode 100644 index 00000000000..9dac9a3bf18 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageTreeWriter.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import javax.lang.model.element.PackageElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + + +/** + * Class to generate Tree page for a package. The name of the file generated is + * "package-tree.html" and it is generated in the respective package directory. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class PackageTreeWriter extends AbstractTreeWriter { + + /** + * Package for which tree is to be generated. + */ + protected PackageElement packageElement; + + /** + * The previous package name in the alpha-order list. + */ + protected PackageElement prev; + + /** + * The next package name in the alpha-order list. + */ + protected PackageElement next; + + /** + * Constructor. + * @param configuration the configuration + * @param path the docpath to generate files into + * @param packageElement the current package + * @param prev the previous package + * @param next the next package + * @throws IOException + * @throws DocletAbortException + */ + public PackageTreeWriter(ConfigurationImpl configuration, + DocPath path, + PackageElement packageElement, + PackageElement prev, PackageElement next) + throws IOException { + super(configuration, path, + new ClassTree(configuration.typeElementCatalog.allClasses(packageElement), configuration)); + this.packageElement = packageElement; + this.prev = prev; + this.next = next; + } + + /** + * Construct a PackageTreeWriter object and then use it to generate the + * package tree page. + * + * @param configuration the configuration for this run. + * @param pkg Package for which tree file is to be generated. + * @param prev Previous package in the alpha-ordered list. + * @param next Next package in the alpha-ordered list. + * @param noDeprecated If true, do not generate any information for + * deprecated classe or interfaces. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, + PackageElement pkg, PackageElement prev, + PackageElement next, boolean noDeprecated) { + PackageTreeWriter packgen; + DocPath path = DocPath.forPackage(pkg).resolve(DocPaths.PACKAGE_TREE); + try { + packgen = new PackageTreeWriter(configuration, path, pkg, + prev, next); + packgen.generatePackageTreeFile(); + packgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), path.getPath()); + throw new DocletAbortException(exc); + } + } + + /** + * Generate a separate tree file for each package. + * @throws java.io.IOException + */ + protected void generatePackageTreeFile() throws IOException { + HtmlTree body = getPackageTreeHeader(); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN() + : body; + Content headContent = getResource("doclet.Hierarchy_For_Package", + utils.getPackageName(packageElement)); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, false, + HtmlStyle.title, headContent); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + if (configuration.packages.size() > 1) { + addLinkToMainTree(div); + } + htmlTree.addContent(div); + HtmlTree divTree = new HtmlTree(HtmlTag.DIV); + divTree.addStyle(HtmlStyle.contentContainer); + addTree(classtree.baseClasses(), "doclet.Class_Hierarchy", divTree); + addTree(classtree.baseInterfaces(), "doclet.Interface_Hierarchy", divTree); + addTree(classtree.baseAnnotationTypes(), "doclet.Annotation_Type_Hierarchy", divTree); + addTree(classtree.baseEnums(), "doclet.Enum_Hierarchy", divTree, true); + htmlTree.addContent(divTree); + if (configuration.allowTag(HtmlTag.MAIN)) { + body.addContent(htmlTree); + } + HtmlTree tree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : body; + addNavLinks(false, tree); + addBottom(tree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(tree); + } + printHtmlDocument(null, true, body); + } + + /** + * Get the package tree header. + * + * @return a content tree for the header + */ + protected HtmlTree getPackageTreeHeader() { + String packageName = packageElement.isUnnamed() ? "" : utils.getPackageName(packageElement); + String title = packageName + " " + configuration.getText("doclet.Window_Class_Hierarchy"); + HtmlTree bodyTree = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + return bodyTree; + } + + /** + * Add a link to the tree for all the packages. + * + * @param div the content tree to which the link will be added + */ + protected void addLinkToMainTree(Content div) { + Content span = HtmlTree.SPAN(HtmlStyle.packageHierarchyLabel, + getResource("doclet.Package_Hierarchies")); + div.addContent(span); + HtmlTree ul = new HtmlTree (HtmlTag.UL); + ul.addStyle(HtmlStyle.horizontal); + ul.addContent(getNavLinkMainTree(configuration.getText("doclet.All_Packages"))); + div.addContent(ul); + } + + /** + * Get link for the previous package tree file. + * + * @return a content tree for the link + */ + protected Content getNavLinkPrevious() { + if (prev == null) { + return getNavLinkPrevious(null); + } else { + DocPath path = DocPath.relativePath(packageElement, prev); + return getNavLinkPrevious(path.resolve(DocPaths.PACKAGE_TREE)); + } + } + + /** + * Get link for the next package tree file. + * + * @return a content tree for the link + */ + protected Content getNavLinkNext() { + if (next == null) { + return getNavLinkNext(null); + } else { + DocPath path = DocPath.relativePath(packageElement, next); + return getNavLinkNext(path.resolve(DocPaths.PACKAGE_TREE)); + } + } + + /** + * Get link to the package summary page for the package of this tree. + * + * @return a content tree for the package link + */ + protected Content getNavLinkPackage() { + Content linkContent = getHyperLink(DocPaths.PACKAGE_SUMMARY, + packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java new file mode 100644 index 00000000000..3763e725892 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageUseWriter.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +/** + * Generate package usage information. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert G. Field + * @author Bhavesh Patel (Modified) + */ +public class PackageUseWriter extends SubWriterHolderWriter { + + final PackageElement packageElement; + final SortedMap> usingPackageToUsedClasses = new TreeMap<>(); + protected HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * Constructor. + * + * @param filename the file to be generated. + * @throws IOException + * @throws DocletAbortException + */ + public PackageUseWriter(ConfigurationImpl configuration, + ClassUseMapper mapper, DocPath filename, + PackageElement pkgElement) throws IOException { + super(configuration, DocPath.forPackage(pkgElement).resolve(filename)); + this.packageElement = pkgElement; + + // by examining all classes in this package, find what packages + // use these classes - produce a map between using package and + // used classes. + for (TypeElement usedClass : utils.getEnclosedTypeElements(pkgElement)) { + Set usingClasses = mapper.classToClass.get(usedClass); + if (usingClasses != null) { + for (TypeElement usingClass : usingClasses) { + PackageElement usingPackage = utils.containingPackage(usingClass); + Set usedClasses = usingPackageToUsedClasses + .get(utils.getPackageName(usingPackage)); + if (usedClasses == null) { + usedClasses = new TreeSet<>(utils.makeGeneralPurposeComparator()); + usingPackageToUsedClasses.put(utils.getPackageName(usingPackage), + usedClasses); + } + usedClasses.add(usedClass); + } + } + } + } + + /** + * Generate a class page. + * + * @param configuration the current configuration of the doclet. + * @param mapper the mapping of the class usage. + * @param pkgElement the package being documented. + */ + public static void generate(ConfigurationImpl configuration, + ClassUseMapper mapper, PackageElement pkgElement) { + PackageUseWriter pkgusegen; + DocPath filename = DocPaths.PACKAGE_USE; + try { + pkgusegen = new PackageUseWriter(configuration, mapper, filename, pkgElement); + pkgusegen.generatePackageUseFile(); + pkgusegen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the package use list. + */ + protected void generatePackageUseFile() throws IOException { + HtmlTree body = getPackageUseHeader(); + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.contentContainer); + if (usingPackageToUsedClasses.isEmpty()) { + div.addContent(getResource("doclet.ClassUse_No.usage.of.0", utils.getPackageName(packageElement))); + } else { + addPackageUse(div); + } + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + body.addContent(mainTree); + } else { + body.addContent(div); + } + HtmlTree tree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : body; + addNavLinks(false, tree); + addBottom(tree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(tree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add the package use information. + * + * @param contentTree the content tree to which the package use information will be added + */ + protected void addPackageUse(Content contentTree) throws IOException { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + if (configuration.packages.size() > 1) { + addPackageList(ul); + } + addClassList(ul); + contentTree.addContent(ul); + } + + /** + * Add the list of packages that use the given package. + * + * @param contentTree the content tree to which the package list will be added + */ + protected void addPackageList(Content contentTree) throws IOException { + Content caption = getTableCaption(configuration.getResource( + "doclet.ClassUse_Packages.that.use.0", + getPackageLink(packageElement, utils.getPackageName(packageElement)))); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.useSummary, caption) + : HtmlTree.TABLE(HtmlStyle.useSummary, useTableSummary, caption); + table.addContent(getSummaryTableHeader(packageTableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (String pkgname: usingPackageToUsedClasses.keySet()) { + PackageElement pkg = utils.elementUtils.getPackageElement(pkgname); + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + addPackageUse(pkg, tr); + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + contentTree.addContent(li); + } + + /** + * Add the list of classes that use the given package. + * + * @param contentTree the content tree to which the class list will be added + */ + protected void addClassList(Content contentTree) throws IOException { + List classTableHeader = Arrays.asList( + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Class"), + configuration.getText("doclet.Description"))); + for (String packageName : usingPackageToUsedClasses.keySet()) { + PackageElement usingPackage = utils.elementUtils.getPackageElement(packageName); + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + if (usingPackage != null) { + li.addContent(getMarkerAnchor(utils.getPackageName(usingPackage))); + } + String tableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.classes")); + Content caption = getTableCaption(configuration.getResource( + "doclet.ClassUse_Classes.in.0.used.by.1", + getPackageLink(packageElement, utils.getPackageName(packageElement)), + getPackageLink(usingPackage, utils.getPackageName(usingPackage)))); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.useSummary, caption) + : HtmlTree.TABLE(HtmlStyle.useSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(classTableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = true; + for (TypeElement te : usingPackageToUsedClasses.get(packageName)) { + HtmlTree tr = new HtmlTree(HtmlTag.TR); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + altColor = !altColor; + addClassRow(te, usingPackage, tr); + tbody.addContent(tr); + } + table.addContent(tbody); + li.addContent(table); + contentTree.addContent(li); + } + } + + /** + * Add a row for the class that uses the given package. + * + * @param usedClass the class that uses the given package + * @param pkg the package to which the class belongs + * @param contentTree the content tree to which the row will be added + */ + protected void addClassRow(TypeElement usedClass, PackageElement pkg, + Content contentTree) { + DocPath dp = pathString(usedClass, + DocPaths.CLASS_USE.resolve(DocPath.forName(utils, usedClass))); + StringContent stringContent = new StringContent(utils.getSimpleName(usedClass)); + Content td = HtmlTree.TD(HtmlStyle.colOne, + getHyperLink(dp.fragment(getPackageAnchorName(pkg)), stringContent)); + addIndexComment(usedClass, td); + contentTree.addContent(td); + } + + /** + * Add the package use information. + * + * @param pkg the package that used the given package + * @param contentTree the content tree to which the information will be added + */ + protected void addPackageUse(PackageElement pkg, Content contentTree) throws IOException { + Content tdFirst = HtmlTree.TD(HtmlStyle.colFirst, + getHyperLink(utils.getPackageName(pkg), + new StringContent(utils.getPackageName(pkg)))); + contentTree.addContent(tdFirst); + HtmlTree tdLast = new HtmlTree(HtmlTag.TD); + tdLast.addStyle(HtmlStyle.colLast); + if (pkg != null && !pkg.isUnnamed()) { + addSummaryComment(pkg, tdLast); + } else { + tdLast.addContent(getSpace()); + } + contentTree.addContent(tdLast); + } + + /** + * Get the header for the package use listing. + * + * @return a content tree representing the package use header + */ + protected HtmlTree getPackageUseHeader() { + String packageText = configuration.getText("doclet.Package"); + String name = packageElement.isUnnamed() ? "" : utils.getPackageName(packageElement); + String title = configuration.getText("doclet.Window_ClassUse_Header", packageText, name); + HtmlTree bodyTree = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + ContentBuilder headContent = new ContentBuilder(); + headContent.addContent(getResource("doclet.ClassUse_Title", packageText)); + headContent.addContent(new HtmlTree(HtmlTag.BR)); + headContent.addContent(name); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.title, headContent); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * Get this package link. + * + * @return a content tree for the package link + */ + protected Content getNavLinkPackage() { + Content linkContent = getHyperLink(DocPaths.PACKAGE_SUMMARY, + packageLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } + + /** + * Get the use link. + * + * @return a content tree for the use link + */ + protected Content getNavLinkClassUse() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, useLabel); + return li; + } + + /** + * Get the tree link. + * + * @return a content tree for the tree link + */ + protected Content getNavLinkTree() { + Content linkContent = getHyperLink(DocPaths.PACKAGE_TREE, + treeLabel); + Content li = HtmlTree.LI(linkContent); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriterImpl.java new file mode 100644 index 00000000000..8689b0f76e8 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageWriterImpl.java @@ -0,0 +1,377 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; + +/** + * Class to generate file for each package contents in the right-hand + * frame. This will list all the Class Kinds in the package. A click on any + * class-kind will update the frame with the clicked class-kind page. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class PackageWriterImpl extends HtmlDocletWriter + implements PackageSummaryWriter { + + /** + * The prev package name in the alpha-order list. + */ + protected PackageElement prev; + + /** + * The next package name in the alpha-order list. + */ + protected PackageElement next; + + /** + * The package being documented. + */ + protected PackageElement packageElement; + + /** + * The HTML tree for main tag. + */ + protected HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * The HTML tree for section tag. + */ + protected HtmlTree sectionTree = HtmlTree.SECTION(); + + /** + * Constructor to construct PackageWriter object and to generate + * "package-summary.html" file in the respective package directory. + * For example for package "java.lang" this will generate file + * "package-summary.html" file in the "java/lang" directory. It will also + * create "java/lang" directory in the current or the destination directory + * if it doesn't exist. + * + * @param configuration the configuration of the doclet. + * @param packageElement PackageElement under consideration. + * @param prev Previous package in the sorted array. + * @param next Next package in the sorted array. + */ + public PackageWriterImpl(ConfigurationImpl configuration, + PackageElement packageElement, PackageElement prev, PackageElement next) + throws IOException { + super(configuration, DocPath + .forPackage(packageElement) + .resolve(DocPaths.PACKAGE_SUMMARY)); + this.prev = prev; + this.next = next; + this.packageElement = packageElement; + } + + /** + * {@inheritDoc} + */ + public Content getPackageHeader(String heading) { + HtmlTree bodyTree = getBody(true, getWindowTitle(utils.getPackageName(packageElement))); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.header); + Content annotationContent = new HtmlTree(HtmlTag.P); + addAnnotationInfo(packageElement, annotationContent); + div.addContent(annotationContent); + Content tHeading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.title, packageLabel); + tHeading.addContent(getSpace()); + Content packageHead = new StringContent(heading); + tHeading.addContent(packageHead); + div.addContent(tHeading); + addDeprecationInfo(div); + if (!utils.getBody(packageElement).isEmpty() && !configuration.nocomment) { + HtmlTree docSummaryDiv = new HtmlTree(HtmlTag.DIV); + docSummaryDiv.addStyle(HtmlStyle.docSummary); + addSummaryComment(packageElement, docSummaryDiv); + div.addContent(docSummaryDiv); + Content space = getSpace(); + Content descLink = getHyperLink(getDocLink( + SectionName.PACKAGE_DESCRIPTION), + descriptionLabel, "", ""); + Content descPara = new HtmlTree(HtmlTag.P, seeLabel, space, descLink); + div.addContent(descPara); + } + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * {@inheritDoc} + */ + public Content getContentHeader() { + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.contentContainer); + return div; + } + + /** + * Add the package deprecation information to the documentation tree. + * + * @param div the content tree to which the deprecation information will be added + */ + public void addDeprecationInfo(Content div) { + List deprs = utils.getBlockTags(packageElement, DocTree.Kind.DEPRECATED); + if (utils.isDeprecated(packageElement)) { + CommentHelper ch = utils.getCommentHelper(packageElement); + HtmlTree deprDiv = new HtmlTree(HtmlTag.DIV); + deprDiv.addStyle(HtmlStyle.deprecatedContent); + Content deprPhrase = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + deprDiv.addContent(deprPhrase); + if (!deprs.isEmpty()) { + List commentTags = ch.getDescription(configuration, deprs.get(0)); + if (!commentTags.isEmpty()) { + addInlineDeprecatedComment(packageElement, deprs.get(0), deprDiv); + } + } + div.addContent(deprDiv); + } + } + + /** + * {@inheritDoc} + */ + public Content getSummaryHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * {@inheritDoc} + */ + public void addClassesSummary(SortedSet classes, String label, + String tableSummary, List tableHeader, Content summaryContentTree) { + if(!classes.isEmpty()) { + Content caption = getTableCaption(new RawHtml(label)); + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.typeSummary, caption) + : HtmlTree.TABLE(HtmlStyle.typeSummary, tableSummary, caption); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + boolean altColor = false; + for (TypeElement klass : classes) { + altColor = !altColor; + if (!utils.isCoreClass(klass) || + !configuration.isGeneratedDoc(klass)) { + continue; + } + Content classContent = getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.PACKAGE, klass)); + Content tdClass = HtmlTree.TD(HtmlStyle.colFirst, classContent); + HtmlTree tr = HtmlTree.TR(tdClass); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + + HtmlTree tdClassDescription = new HtmlTree(HtmlTag.TD); + tdClassDescription.addStyle(HtmlStyle.colLast); + if (utils.isDeprecated(klass)) { + tdClassDescription.addContent(deprecatedLabel); + List tags = utils.getDeprecatedTrees(klass); + if (!tags.isEmpty()) { + addSummaryDeprecatedComment(klass, tags.get(0), tdClassDescription); + } + } else { + addSummaryComment(klass, tdClassDescription); + } + tr.addContent(tdClassDescription); + tbody.addContent(tr); + } + table.addContent(tbody); + Content li = HtmlTree.LI(HtmlStyle.blockList, table); + summaryContentTree.addContent(li); + } + } + + /** + * {@inheritDoc} + */ + public void addPackageDescription(Content packageContentTree) { + if (!utils.getBody(packageElement).isEmpty()) { + packageContentTree.addContent( + getMarkerAnchor(SectionName.PACKAGE_DESCRIPTION)); + Content h2Content = new StringContent( + configuration.getText("doclet.Package_Description", + packageElement.isUnnamed() ? "" : utils.getPackageName(packageElement))); + Content heading = HtmlTree.HEADING(HtmlConstants.PACKAGE_HEADING, true, h2Content); + if (configuration.allowTag(HtmlTag.SECTION)) { + sectionTree.addContent(heading); + addInlineComment(packageElement, sectionTree); + } else { + packageContentTree.addContent(heading); + addInlineComment(packageElement, packageContentTree); + } + } + } + + /** + * {@inheritDoc} + */ + public void addPackageTags(Content packageContentTree) { + Content htmlTree = (configuration.allowTag(HtmlTag.SECTION)) + ? sectionTree + : packageContentTree; + addTagsInfo(packageElement, htmlTree); + } + + /** + * {@inheritDoc} + */ + public void addPackageContent(Content contentTree, Content packageContentTree) { + if (configuration.allowTag(HtmlTag.MAIN)) { + packageContentTree.addContent(sectionTree); + mainTree.addContent(packageContentTree); + contentTree.addContent(mainTree); + } else { + contentTree.addContent(packageContentTree); + } + } + + /** + * {@inheritDoc} + */ + public void addPackageFooter(Content contentTree) { + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : contentTree; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + contentTree.addContent(htmlTree); + } + } + + /** + * {@inheritDoc} + */ + public void printDocument(Content contentTree) throws IOException { + printHtmlDocument(configuration.metakeywords.getMetaKeywords(packageElement), + true, contentTree); + } + + /** + * Get "Use" link for this pacakge in the navigation bar. + * + * @return a content tree for the class use link + */ + protected Content getNavLinkClassUse() { + Content useLink = getHyperLink(DocPaths.PACKAGE_USE, + useLabel, "", ""); + Content li = HtmlTree.LI(useLink); + return li; + } + + /** + * Get "PREV PACKAGE" link in the navigation bar. + * + * @return a content tree for the previous link + */ + public Content getNavLinkPrevious() { + Content li; + if (prev == null) { + li = HtmlTree.LI(prevpackageLabel); + } else { + DocPath path = DocPath.relativePath(packageElement, prev); + li = HtmlTree.LI(getHyperLink(path.resolve(DocPaths.PACKAGE_SUMMARY), + prevpackageLabel, "", "")); + } + return li; + } + + /** + * Get "NEXT PACKAGE" link in the navigation bar. + * + * @return a content tree for the next link + */ + public Content getNavLinkNext() { + Content li; + if (next == null) { + li = HtmlTree.LI(nextpackageLabel); + } else { + DocPath path = DocPath.relativePath(packageElement, next); + li = HtmlTree.LI(getHyperLink(path.resolve(DocPaths.PACKAGE_SUMMARY), + nextpackageLabel, "", "")); + } + return li; + } + + /** + * Get "Tree" link in the navigation bar. This will be link to the package + * tree file. + * + * @return a content tree for the tree link + */ + protected Content getNavLinkTree() { + Content useLink = getHyperLink(DocPaths.PACKAGE_TREE, + treeLabel, "", ""); + Content li = HtmlTree.LI(useLink); + return li; + } + + /** + * Highlight "Package" in the navigation bar, as this is the package page. + * + * @return a content tree for the package link + */ + protected Content getNavLinkPackage() { + Content li = HtmlTree.LI(HtmlStyle.navBarCell1Rev, packageLabel); + return li; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java new file mode 100644 index 00000000000..3a1eebc23cf --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java @@ -0,0 +1,369 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import java.util.Arrays; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.PropertyWriter; + + +/** + * Writes property documentation in HTML format. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Jamie Ho (rewrite) + * @author Bhavesh Patel (Modified) + */ +public class PropertyWriterImpl extends AbstractMemberWriter + implements PropertyWriter, MemberSummaryWriter { + + public PropertyWriterImpl(SubWriterHolderWriter writer, TypeElement typeElement) { + super(writer, typeElement); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getMemberSummaryHeader(TypeElement typeElement, + Content memberSummaryTree) { + memberSummaryTree.addContent(HtmlConstants.START_OF_PROPERTY_SUMMARY); + Content memberTree = writer.getMemberTreeHeader(); + writer.addSummaryHeader(this, typeElement, memberTree); + return memberTree; + } + + /** + * {@inheritDoc} + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + writer.addMemberTree(memberSummaryTree, memberTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getPropertyDetailsTreeHeader(TypeElement typeElement, + Content memberDetailsTree) { + memberDetailsTree.addContent(HtmlConstants.START_OF_PROPERTY_DETAILS); + Content propertyDetailsTree = writer.getMemberTreeHeader(); + propertyDetailsTree.addContent(writer.getMarkerAnchor( + SectionName.PROPERTY_DETAIL)); + Content heading = HtmlTree.HEADING(HtmlConstants.DETAILS_HEADING, + writer.propertyDetailsLabel); + propertyDetailsTree.addContent(heading); + return propertyDetailsTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getPropertyDocTreeHeader(ExecutableElement property, + Content propertyDetailsTree) { + propertyDetailsTree.addContent( + writer.getMarkerAnchor(name(property))); + Content propertyDocTree = writer.getMemberTreeHeader(); + Content heading = new HtmlTree(HtmlConstants.MEMBER_HEADING); + heading.addContent(utils.getPropertyLabel(name(property))); + propertyDocTree.addContent(heading); + return propertyDocTree; + } + + /** + * {@inheritDoc} + */ + @Override + public Content getSignature(ExecutableElement property) { + Content pre = new HtmlTree(HtmlTag.PRE); + writer.addAnnotationInfo(property, pre); + addModifiers(property, pre); + Content propertylink = writer.getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.MEMBER, + utils.getReturnType(property))); + pre.addContent(propertylink); + pre.addContent(" "); + if (configuration.linksource) { + Content propertyName = new StringContent(name(property)); + writer.addSrcLink(property, propertyName, pre); + } else { + addName(name(property), pre); + } + return pre; + } + + /** + * {@inheritDoc} + */ + @Override + public void addDeprecated(ExecutableElement property, Content propertyDocTree) { + } + + /** + * {@inheritDoc} + */ + @Override + public void addComments(ExecutableElement property, Content propertyDocTree) { + TypeElement holder = (TypeElement)property.getEnclosingElement(); + if (!utils.getBody(property).isEmpty()) { + if (holder.equals(typeElement) || + (!utils.isPublic(holder) || utils.isLinkable(holder))) { + writer.addInlineComment(property, propertyDocTree); + } else { + Content link = + writer.getDocLink(LinkInfoImpl.Kind.PROPERTY_COPY, + holder, property, + utils.isIncluded(holder) + ? holder.toString() : utils.getFullyQualifiedName(holder), + false); + Content codeLink = HtmlTree.CODE(link); + Content descfrmLabel = HtmlTree.SPAN(HtmlStyle.descfrmTypeLabel, + utils.isClass(holder) + ? writer.descfrmClassLabel + : writer.descfrmInterfaceLabel); + descfrmLabel.addContent(writer.getSpace()); + descfrmLabel.addContent(codeLink); + propertyDocTree.addContent(HtmlTree.DIV(HtmlStyle.block, descfrmLabel)); + writer.addInlineComment(property, propertyDocTree); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addTags(ExecutableElement property, Content propertyDocTree) { + writer.addTagsInfo(property, propertyDocTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getPropertyDetails(Content propertyDetailsTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(propertyDetailsTree)); + return htmlTree; + } + return getMemberTree(propertyDetailsTree); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getPropertyDoc(Content propertyDocTree, + boolean isLastContent) { + return getMemberTree(propertyDocTree, isLastContent); + } + + /** + * Close the writer. + */ + @Override + public void close() throws IOException { + writer.close(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryLabel(Content memberTree) { + Content label = HtmlTree.HEADING(HtmlConstants.SUMMARY_HEADING, + writer.getResource("doclet.Property_Summary")); + memberTree.addContent(label); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTableSummary() { + return configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Property_Summary"), + configuration.getText("doclet.properties")); + } + + /** + * {@inheritDoc} + */ + @Override + public Content getCaption() { + return configuration.getResource("doclet.Properties"); + } + + /** + * {@inheritDoc} + */ + @Override + public List getSummaryTableHeader(Element member) { + List header = Arrays.asList(configuration.getText("doclet.Type"), + configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Property"), + configuration.getText("doclet.Description"))); + return header; + } + + /** + * {@inheritDoc} + */ + @Override + public void addSummaryAnchor(TypeElement typeElement, Content memberTree) { + memberTree.addContent(writer.getMarkerAnchor( + SectionName.PROPERTY_SUMMARY)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryAnchor(TypeElement typeElement, Content inheritedTree) { + inheritedTree.addContent(writer.getMarkerAnchor( + SectionName.PROPERTIES_INHERITANCE, + configuration.getClassName(typeElement))); + } + + /** + * {@inheritDoc} + */ + @Override + public void addInheritedSummaryLabel(TypeElement typeElement, Content inheritedTree) { + Content classLink = writer.getPreQualifiedClassLink( + LinkInfoImpl.Kind.MEMBER, typeElement, false); + Content label = new StringContent( + utils.isClass(typeElement) + ? configuration.getText("doclet.Properties_Inherited_From_Class") + : configuration.getText("doclet.Properties_Inherited_From_Interface")); + Content labelHeading = HtmlTree.HEADING(HtmlConstants.INHERITED_SUMMARY_HEADING, + label); + labelHeading.addContent(writer.getSpace()); + labelHeading.addContent(classLink); + inheritedTree.addContent(labelHeading); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryLink(LinkInfoImpl.Kind context, TypeElement typeElement, Element member, + Content tdSummary) { + Content memberLink = HtmlTree.SPAN(HtmlStyle.memberNameLink, + writer.getDocLink(context, typeElement, + member, + utils.getPropertyLabel(name(member)), + false, + true)); + + Content code = HtmlTree.CODE(memberLink); + tdSummary.addContent(code); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addInheritedSummaryLink(TypeElement typeElement, Element member, Content linksTree) { + String mname = name(member); + Content content = writer.getDocLink(LinkInfoImpl.Kind.MEMBER, typeElement, member, + utils.isProperty(mname) ? utils.getPropertyName(mname) : mname, + false, true); + linksTree.addContent(content); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addSummaryType(Element member, Content tdSummaryType) { + addModifierAndType(member, utils.getReturnType((ExecutableElement)member), tdSummaryType); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getDeprecatedLink(Element member) { + return writer.getDocLink(LinkInfoImpl.Kind.MEMBER, member, + utils.getFullyQualifiedName(member)); + } + + /** + * {@inheritDoc} + */ + @Override + protected Content getNavSummaryLink(TypeElement typeElement, boolean link) { + if (link) { + if (typeElement == null) { + return writer.getHyperLink( + SectionName.PROPERTY_SUMMARY, + writer.getResource("doclet.navProperty")); + } else { + return writer.getHyperLink( + SectionName.PROPERTIES_INHERITANCE, + configuration.getClassName(typeElement), writer.getResource("doclet.navProperty")); + } + } else { + return writer.getResource("doclet.navProperty"); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void addNavDetailLink(boolean link, Content liNav) { + if (link) { + liNav.addContent(writer.getHyperLink( + SectionName.PROPERTY_DETAIL, + writer.getResource("doclet.navProperty"))); + } else { + liNav.addContent(writer.getResource("doclet.navProperty")); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchIndexItem.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchIndexItem.java new file mode 100644 index 00000000000..d260a9b5538 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchIndexItem.java @@ -0,0 +1,122 @@ +/* + * 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.javadoc.internal.doclets.formats.html; + +/** + * Index item for search. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class SearchIndexItem { + + private String label = ""; + private String url = ""; + private String category = ""; + private String containingPackage = ""; + private String containingClass = ""; + private String holder = ""; + private String description = ""; + + public void setLabel(String l) { + label = l; + } + + public String getLabel() { + return label; + } + + public void setUrl(String u) { + url = u; + } + + public String getUrl() { + return url; + } + + public void setContainingPackage(String p) { + containingPackage = p; + } + + public void setContainingClass(String c) { + containingClass = c; + } + + public void setCategory(String c) { + category = c; + } + + public void setHolder(String h) { + holder = h; + } + + public String getHolder() { + return holder; + } + + public void setDescription(String d) { + description = d; + } + + public String getDescription() { + return description; + } + + public String toString() { + StringBuilder item = new StringBuilder(""); + if (category.equals("Packages")) { + item.append("{") + .append("\"l\":\"").append(label).append("\"") + .append("}"); + } else if (category.equals("Types")) { + item.append("{") + .append("\"p\":\"").append(containingPackage).append("\",") + .append("\"l\":\"").append(label).append("\"") + .append("}"); + } else if (category.equals("Members")) { + item.append("{") + .append("\"p\":\"").append(containingPackage).append("\",") + .append("\"c\":\"").append(containingClass).append("\",") + .append("\"l\":\"").append(label).append("\""); + if (!url.equals("")) { + item.append(",\"url\":\"").append(url).append("\""); + } + item.append("}"); + } else { + item.append("{") + .append("\"l\":\"").append(label).append("\",") + .append("\"h\":\"").append(holder).append("\","); + if (!description.equals("")) { + item.append("\"d\":\"").append(description).append("\","); + } + item.append("\"u\":\"").append(url).append("\"") + .append("}"); + } + return item.toString(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java new file mode 100644 index 00000000000..a9ff3e50311 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java @@ -0,0 +1,80 @@ +/* + * 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. 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.javadoc.internal.doclets.formats.html; + +/** + * Enum representing various section names of generated API documentation. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public enum SectionName { + + ANNOTATION_TYPE_ELEMENT_DETAIL("annotation.type.element.detail"), + ANNOTATION_TYPE_FIELD_DETAIL("annotation.type.field.detail"), + ANNOTATION_TYPE_FIELD_SUMMARY("annotation.type.field.summary"), + ANNOTATION_TYPE_OPTIONAL_ELEMENT_SUMMARY("annotation.type.optional.element.summary"), + ANNOTATION_TYPE_REQUIRED_ELEMENT_SUMMARY("annotation.type.required.element.summary"), + CONSTRUCTOR_DETAIL("constructor.detail"), + CONSTRUCTOR_SUMMARY("constructor.summary"), + ENUM_CONSTANT_DETAIL("enum.constant.detail"), + ENUM_CONSTANTS_INHERITANCE("enum.constants.inherited.from.class."), + ENUM_CONSTANT_SUMMARY("enum.constant.summary"), + FIELD_DETAIL("field.detail"), + FIELDS_INHERITANCE("fields.inherited.from.class."), + FIELD_SUMMARY("field.summary"), + METHOD_DETAIL("method.detail"), + METHODS_INHERITANCE("methods.inherited.from.class."), + METHOD_SUMMARY("method.summary"), + NAVBAR_BOTTOM("navbar.bottom"), + NAVBAR_BOTTOM_FIRSTROW("navbar.bottom.firstrow"), + NAVBAR_TOP("navbar.top"), + NAVBAR_TOP_FIRSTROW("navbar.top.firstrow"), + NESTED_CLASSES_INHERITANCE("nested.classes.inherited.from.class."), + NESTED_CLASS_SUMMARY("nested.class.summary"), + OVERVIEW_DESCRIPTION("overview.description"), + PACKAGE_DESCRIPTION("package.description"), + PROPERTY_DETAIL("property.detail"), + PROPERTIES_INHERITANCE("properties.inherited.from.class."), + PROPERTY_SUMMARY("property.summary"), + SKIP_NAVBAR_BOTTOM("skip.navbar.bottom"), + SKIP_NAVBAR_TOP("skip.navbar.top"), + UNNAMED_PACKAGE_ANCHOR("unnamed.package"); + + private final String value; + + SectionName(String sName) { + this.value = sName; + } + + public String getName() { + return this.value; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriterImpl.java new file mode 100644 index 00000000000..764810f3902 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SerializedFormWriterImpl.java @@ -0,0 +1,301 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter.SerialFieldWriter; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter.SerialMethodWriter; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +/** + * Generate the Serialized Form Information Page. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + */ +public class SerializedFormWriterImpl extends SubWriterHolderWriter + implements SerializedFormWriter { + + Set visibleClasses; + + /** + * HTML tree for main tag. + */ + private HtmlTree mainTree = HtmlTree.MAIN(); + + /** + * @param configuration the configuration data for the doclet + * @throws IOException + * @throws DocletAbortException + */ + public SerializedFormWriterImpl(ConfigurationImpl configuration) + throws IOException { + super(configuration, DocPaths.SERIALIZED_FORM); + visibleClasses = configuration.root.getIncludedClasses(); + } + + /** + * Get the given header. + * + * @param header the header to write + * @return the body content tree + */ + public Content getHeader(String header) { + HtmlTree bodyTree = getBody(true, getWindowTitle(header)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + Content h1Content = new StringContent(header); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, true, + HtmlStyle.title, h1Content); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(div); + } else { + bodyTree.addContent(div); + } + return bodyTree; + } + + /** + * Get the serialized form summaries header. + * + * @return the serialized form summary header tree + */ + public Content getSerializedSummariesHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * Get the package serialized form header. + * + * @return the package serialized form header tree + */ + public Content getPackageSerializedHeader() { + HtmlTree htmlTree; + if (configuration.allowTag(HtmlTag.SECTION)) { + htmlTree = HtmlTree.SECTION(); + } else { + htmlTree = new HtmlTree(HtmlTag.LI); + htmlTree.addStyle(HtmlStyle.blockList); + } + return htmlTree; + } + + /** + * Get the given package header. + * + * @param packageName the package header to write + * @return a content tree for the package header + */ + public Content getPackageHeader(String packageName) { + Content heading = HtmlTree.HEADING(HtmlConstants.PACKAGE_HEADING, true, + packageLabel); + heading.addContent(getSpace()); + heading.addContent(packageName); + return heading; + } + + /** + * Get the serialized class header. + * + * @return a content tree for the serialized class header + */ + public Content getClassSerializedHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * Checks if a class is generated and is visible. + * + * @param typeElement the class being processed. + * @return true if the class, that is being processed, is generated and is visible. + */ + public boolean isVisibleClass(TypeElement typeElement) { + return visibleClasses.contains(typeElement) && configuration.isGeneratedDoc(typeElement); + } + + /** + * Get the serializable class heading. + * + * @param typeElement the class being processed + * @return a content tree for the class header + */ + public Content getClassHeader(TypeElement typeElement) { + Content classLink = (isVisibleClass(typeElement)) + ? getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.DEFAULT, typeElement) + .label(configuration.getClassName(typeElement))) + : new StringContent(utils.getFullyQualifiedName(typeElement)); + Content li = HtmlTree.LI(HtmlStyle.blockList, getMarkerAnchor( + utils.getFullyQualifiedName(typeElement))); + Content superClassLink = typeElement.getSuperclass() != null + ? getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.SERIALIZED_FORM, + typeElement.getSuperclass())) + : null; + + //Print the heading. + Content className = superClassLink == null ? + configuration.getResource( + "doclet.Class_0_implements_serializable", classLink) : + configuration.getResource( + "doclet.Class_0_extends_implements_serializable", classLink, + superClassLink); + li.addContent(HtmlTree.HEADING(HtmlConstants.SERIALIZED_MEMBER_HEADING, + className)); + return li; + } + + /** + * Get the serial UID info header. + * + * @return a content tree for the serial uid info header + */ + public Content getSerialUIDInfoHeader() { + HtmlTree dl = new HtmlTree(HtmlTag.DL); + dl.addStyle(HtmlStyle.nameValue); + return dl; + } + + /** + * Adds the serial UID info. + * + * @param header the header that will show up before the UID. + * @param serialUID the serial UID to print. + * @param serialUidTree the serial UID content tree to which the serial UID + * content will be added + */ + public void addSerialUIDInfo(String header, String serialUID, + Content serialUidTree) { + Content headerContent = new StringContent(header); + serialUidTree.addContent(HtmlTree.DT(headerContent)); + Content serialContent = new StringContent(serialUID); + serialUidTree.addContent(HtmlTree.DD(serialContent)); + } + + /** + * Get the class serialize content header. + * + * @return a content tree for the class serialize content header + */ + public Content getClassContentHeader() { + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.blockList); + return ul; + } + + /** + * Get the serialized content tree section. + * + * @param serializedTreeContent the serialized content tree to be added + * @return a div content tree + */ + public Content getSerializedContent(Content serializedTreeContent) { + HtmlTree divContent = HtmlTree.DIV(HtmlStyle.serializedFormContainer, + serializedTreeContent); + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(divContent); + return mainTree; + } else { + return divContent; + } + } + + /** + * {@inheritDoc} + */ + public void addPackageSerializedTree(Content serializedSummariesTree, + Content packageSerializedTree) { + serializedSummariesTree.addContent((configuration.allowTag(HtmlTag.SECTION)) + ? HtmlTree.LI(HtmlStyle.blockList, packageSerializedTree) + : packageSerializedTree); + } + + /** + * Add the footer. + * + * @param serializedTree the serialized tree to be added + */ + public void addFooter(Content serializedTree) { + Content htmlTree = (configuration.allowTag(HtmlTag.FOOTER)) + ? HtmlTree.FOOTER() + : serializedTree; + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + serializedTree.addContent(htmlTree); + } + } + + /** + * {@inheritDoc} + */ + public void printDocument(Content serializedTree) throws IOException { + printHtmlDocument(null, true, serializedTree); + } + + /** + * Return an instance of a SerialFieldWriter. + * + * @return an instance of a SerialFieldWriter. + */ + public SerialFieldWriter getSerialFieldWriter(TypeElement typeElement) { + return new HtmlSerialFieldWriter(this, typeElement); + } + + /** + * Return an instance of a SerialMethodWriter. + * + * @return an instance of a SerialMethodWriter. + */ + public SerialMethodWriter getSerialMethodWriter(TypeElement typeElement) { + return new HtmlSerialMethodWriter(this, typeElement); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SingleIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SingleIndexWriter.java new file mode 100644 index 00000000000..81189d6ee61 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SingleIndexWriter.java @@ -0,0 +1,156 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; + + +/** + * Generate only one index file for all the Member Names with Indexing in + * Unicode Order. The name of the generated file is "index-all.html" and it is + * generated in current or the destination directory. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see java.lang.Character + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class SingleIndexWriter extends AbstractIndexWriter { + + private Set elements; + + /** + * Construct the SingleIndexWriter with filename "index-all.html" and the + * {@link IndexBuilder} + * + * @param filename Name of the index file to be generated. + * @param indexbuilder Unicode based Index from {@link IndexBuilder} + */ + public SingleIndexWriter(ConfigurationImpl configuration, + DocPath filename, + IndexBuilder indexbuilder) throws IOException { + super(configuration, filename, indexbuilder); + } + + /** + * Generate single index file, for all Unicode characters. + * + * @param indexbuilder IndexBuilder built by {@link IndexBuilder} + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, + IndexBuilder indexbuilder) { + SingleIndexWriter indexgen; + DocPath filename = DocPaths.INDEX_ALL; + try { + indexgen = new SingleIndexWriter(configuration, + filename, indexbuilder); + indexgen.generateIndexFile(); + indexgen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the contents of each index file, with Header, Footer, + * Member Field, Method and Constructor Description. + */ + protected void generateIndexFile() throws IOException { + String title = configuration.getText("doclet.Window_Single_Index"); + HtmlTree body = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : body; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + body.addContent(htmlTree); + } + HtmlTree divTree = new HtmlTree(HtmlTag.DIV); + divTree.addStyle(HtmlStyle.contentContainer); + elements = new TreeSet<>(indexbuilder.getIndexMap().keySet()); + elements.addAll(configuration.tagSearchIndexKeys); + addLinksForIndexes(divTree); + for (Character unicode : elements) { + if (configuration.tagSearchIndexMap.get(unicode) == null) { + addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + } else if (indexbuilder.getMemberList(unicode) == null) { + addSearchContents(unicode, configuration.tagSearchIndexMap.get(unicode), divTree); + } else { + addContents(unicode, indexbuilder.getMemberList(unicode), + configuration.tagSearchIndexMap.get(unicode), divTree); + } + } + addLinksForIndexes(divTree); + body.addContent((configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN(divTree) + : divTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + htmlTree = HtmlTree.FOOTER(); + } + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + createSearchIndexFiles(); + printHtmlDocument(null, true, body); + } + + /** + * Add links for all the Index Files per unicode character. + * + * @param contentTree the content tree to which the links for indexes will be added + */ + protected void addLinksForIndexes(Content contentTree) { + for (Object ch : elements) { + String unicode = ch.toString(); + contentTree.addContent( + getHyperLink(getNameForIndex(unicode), + new StringContent(unicode))); + contentTree.addContent(getSpace()); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java new file mode 100644 index 00000000000..0396188d8be --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; + +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.internal.doclets.formats.html.markup.DocType; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +/** + * Converts Java Source Code to HTML. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ +public class SourceToHTMLConverter { + + /** + * The number of trailing blank lines at the end of the page. + * This is inserted so that anchors at the bottom of small pages + * can be reached. + */ + private static final int NUM_BLANK_LINES = 60; + + /** + * New line to be added to the documentation. + */ + private static final String NEW_LINE = DocletConstants.NL; + + private final ConfigurationImpl configuration; + private final Utils utils; + + private final DocletEnvironment rootDoc; + + private DocPath outputdir; + + /** + * Relative path from the documentation root to the file that is being + * generated. + */ + private DocPath relativePath = DocPath.empty; + + private SourceToHTMLConverter(ConfigurationImpl configuration, DocletEnvironment rd, + DocPath outputdir) { + this.configuration = configuration; + this.utils = configuration.utils; + this.rootDoc = rd; + this.outputdir = outputdir; + } + + /** + * Translate the TypeElements in the given DocletEnvironment to HTML representation. + * + * @param configuration the configuration. + * @param root the DocletEnvironment to convert. + * @param outputdir the name of the directory to output to. + */ + public static void convertRoot(ConfigurationImpl configuration, DocletEnvironment root, + DocPath outputdir) { + new SourceToHTMLConverter(configuration, root, outputdir).generate(); + } + + void generate() { + if (rootDoc == null || outputdir == null) { + return; + } + for (PackageElement pkg : utils.getSpecifiedPackages()) { + // If -nodeprecated option is set and the package is marked as deprecated, + // do not convert the package files to HTML. + if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) + convertPackage(pkg, outputdir); + } + for (TypeElement te : utils.getSpecifiedClasses()) { + // If -nodeprecated option is set and the class is marked as deprecated + // or the containing package is deprecated, do not convert the + // package files to HTML. + if (!(configuration.nodeprecated && + (utils.isDeprecated(te) || utils.isDeprecated(utils.containingPackage(te))))) + convertClass(te, outputdir); + } + } + + /** + * Convert the Classes in the given Package to an HTML. + * + * @param pkg the Package to convert. + * @param outputdir the name of the directory to output to. + */ + public void convertPackage(PackageElement pkg, DocPath outputdir) { + if (pkg == null) { + return; + } + for (Element te : utils.getAllClasses(pkg)) { + // If -nodeprecated option is set and the class is marked as deprecated, + // do not convert the package files to HTML. We do not check for + // containing package deprecation since it is already check in + // the calling method above. + if (!(configuration.nodeprecated && utils.isDeprecated(te))) + convertClass((TypeElement)te, outputdir); + } + } + + /** + * Convert the given Class to an HTML. + * + * @param te the class to convert. + * @param outputdir the name of the directory to output to. + */ + public void convertClass(TypeElement te, DocPath outputdir) { + if (te == null) { + return; + } + try { + FileObject fo = utils.getFileObject(te); + if (fo == null) + return; + Reader r = fo.openReader(true); + int lineno = 1; + String line; + relativePath = DocPaths.SOURCE_OUTPUT + .resolve(DocPath.forPackage(utils, te)) + .invert(); + Content body = getHeader(); + Content pre = new HtmlTree(HtmlTag.PRE); + try (LineNumberReader reader = new LineNumberReader(r)) { + while ((line = reader.readLine()) != null) { + addLineNo(pre, lineno); + addLine(pre, line, lineno); + lineno++; + } + } + addBlankLines(pre); + Content div = HtmlTree.DIV(HtmlStyle.sourceContainer, pre); + body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div); + writeToFile(body, outputdir.resolve(DocPath.forClass(utils, te))); + } catch (IOException e) { + throw new DocletAbortException(e); + } + } + + /** + * Write the output to the file. + * + * @param body the documentation content to be written to the file. + * @param path the path for the file. + */ + private void writeToFile(Content body, DocPath path) throws IOException { + Content htmlDocType = configuration.isOutputHtml5() + ? DocType.HTML5 + : DocType.TRANSITIONAL; + Content head = new HtmlTree(HtmlTag.HEAD); + head.addContent(HtmlTree.TITLE(new StringContent( + configuration.getText("doclet.Window_Source_title")))); + head.addContent(getStyleSheetProperties()); + Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), + head, body); + Content htmlDocument = new HtmlDocument(htmlDocType, htmlTree); + configuration.message.notice("doclet.Generating_0", path.getPath()); + DocFile df = DocFile.createFileForOutput(configuration, path); + try (Writer w = df.openWriter()) { + htmlDocument.write(w, true); + } + + } + + /** + * Returns a link to the stylesheet file. + * + * @return an HtmlTree for the lINK tag which provides the stylesheet location + */ + public HtmlTree getStyleSheetProperties() { + String filename = configuration.stylesheetfile; + DocPath stylesheet; + if (filename.length() > 0) { + DocFile file = DocFile.createFileForInput(configuration, filename); + stylesheet = DocPath.create(file.getName()); + } else { + stylesheet = DocPaths.STYLESHEET; + } + DocPath p = relativePath.resolve(stylesheet); + HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", p.getPath(), "Style"); + return link; + } + + /** + * Get the header. + * + * @return the header content for the HTML file + */ + private static Content getHeader() { + return new HtmlTree(HtmlTag.BODY); + } + + /** + * Add the line numbers for the source code. + * + * @param pre the content tree to which the line number will be added + * @param lineno The line number + */ + private static void addLineNo(Content pre, int lineno) { + HtmlTree span = new HtmlTree(HtmlTag.SPAN); + span.addStyle(HtmlStyle.sourceLineNo); + if (lineno < 10) { + span.addContent("00" + Integer.toString(lineno)); + } else if (lineno < 100) { + span.addContent("0" + Integer.toString(lineno)); + } else { + span.addContent(Integer.toString(lineno)); + } + pre.addContent(span); + } + + /** + * Add a line from source to the HTML file that is generated. + * + * @param pre the content tree to which the line will be added. + * @param line the string to format. + * @param currentLineNo the current number. + */ + private void addLine(Content pre, String line, int currentLineNo) { + if (line != null) { + Content anchor = HtmlTree.A(configuration.htmlVersion, + "line." + Integer.toString(currentLineNo), + new StringContent(utils.replaceTabs(line))); + pre.addContent(anchor); + pre.addContent(NEW_LINE); + } + } + + /** + * Add trailing blank lines at the end of the page. + * + * @param pre the content tree to which the blank lines will be added. + */ + private static void addBlankLines(Content pre) { + for (int i = 0; i < NUM_BLANK_LINES; i++) { + pre.addContent(NEW_LINE); + } + } + + /** + * Given a Doc, return an anchor name for it. + * + * @param d the Doc to check. + * @return the name of the anchor. + */ + public static String getAnchorName(Utils utils, Element e) { + return "line." + utils.getLineNumber(e); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SplitIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SplitIndexWriter.java new file mode 100644 index 00000000000..f73407cd5e2 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SplitIndexWriter.java @@ -0,0 +1,218 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +import java.util.TreeSet; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.IndexBuilder; + + +/** + * Generate Separate Index Files for all the member names with Indexing in + * Unicode Order. This will create "index-files" directory in the current or + * destination directory and will generate separate file for each unicode index. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see java.lang.Character + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class SplitIndexWriter extends AbstractIndexWriter { + + /** + * Previous unicode character index in the built index. + */ + protected int prev; + + /** + * Next unicode character in the built index. + */ + protected int next; + + private List indexElements; + + /** + * Construct the SplitIndexWriter. Uses path to this file and relative path + * from this file. + * + * @param path Path to the file which is getting generated. + * @param indexbuilder Unicode based Index from {@link IndexBuilder} + */ + public SplitIndexWriter(ConfigurationImpl configuration, + DocPath path, + IndexBuilder indexbuilder, + Collection elements, + int prev, int next) throws IOException { + super(configuration, path, indexbuilder); + this.indexElements = new ArrayList<>(elements); + this.prev = prev; + this.next = next; + } + + /** + * Generate separate index files, for each Unicode character, listing all + * the members starting with the particular unicode character. + * + * @param indexbuilder IndexBuilder built by {@link IndexBuilder} + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, + IndexBuilder indexbuilder) { + SplitIndexWriter indexgen; + DocPath filename = DocPath.empty; + DocPath path = DocPaths.INDEX_FILES; + try { + Set keys = new TreeSet<>(indexbuilder.getIndexMap().keySet()); + keys.addAll(configuration.tagSearchIndexKeys); + ListIterator li = new ArrayList<>(keys).listIterator(); + while (li.hasNext()) { + Object ch = li.next(); + filename = DocPaths.indexN(li.nextIndex()); + indexgen = new SplitIndexWriter(configuration, + path.resolve(filename), + indexbuilder, keys, li.previousIndex(), li.nextIndex()); + indexgen.generateIndexFile((Character) ch); + if (!li.hasNext()) { + indexgen.createSearchIndexFiles(); + } + indexgen.close(); + } + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename.getPath()); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the contents of each index file, with Header, Footer, + * Member Field, Method and Constructor Description. + * + * @param unicode Unicode character referring to the character for the + * index. + */ + protected void generateIndexFile(Character unicode) throws IOException { + String title = configuration.getText("doclet.Window_Split_Index", + unicode.toString()); + HtmlTree body = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : body; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + body.addContent(htmlTree); + } + HtmlTree divTree = new HtmlTree(HtmlTag.DIV); + divTree.addStyle(HtmlStyle.contentContainer); + addLinksForIndexes(divTree); + if (configuration.tagSearchIndexMap.get(unicode) == null) { + addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + } else if (indexbuilder.getMemberList(unicode) == null) { + addSearchContents(unicode, configuration.tagSearchIndexMap.get(unicode), divTree); + } else { + addContents(unicode, indexbuilder.getMemberList(unicode), + configuration.tagSearchIndexMap.get(unicode), divTree); + } + addLinksForIndexes(divTree); + body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(divTree) : divTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + htmlTree = HtmlTree.FOOTER(); + } + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add links for all the Index Files per unicode character. + * + * @param contentTree the content tree to which the links for indexes will be added + */ + protected void addLinksForIndexes(Content contentTree) { + for (int i = 0; i < indexElements.size(); i++) { + int j = i + 1; + contentTree.addContent(getHyperLink(DocPaths.indexN(j), + new StringContent(indexElements.get(i).toString()))); + contentTree.addContent(getSpace()); + } + } + + /** + * Get link to the previous unicode character. + * + * @return a content tree for the link + */ + public Content getNavLinkPrevious() { + Content prevletterLabel = getResource("doclet.Prev_Letter"); + if (prev == -1) { + return HtmlTree.LI(prevletterLabel); + } + else { + Content prevLink = getHyperLink(DocPaths.indexN(prev), + prevletterLabel); + return HtmlTree.LI(prevLink); + } + } + + /** + * Get link to the next unicode character. + * + * @return a content tree for the link + */ + public Content getNavLinkNext() { + Content nextletterLabel = getResource("doclet.Next_Letter"); + if (next == -1) { + return HtmlTree.LI(nextletterLabel); + } + else { + Content nextLink = getHyperLink(DocPaths.indexN(next), + nextletterLabel); + return HtmlTree.LI(nextLink); + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java new file mode 100644 index 00000000000..11888eb26db --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SubWriterHolderWriter.java @@ -0,0 +1,368 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.MethodTypes; + +/** + * This abstract class exists to provide functionality needed in the + * the formatting of member information. Since AbstractSubWriter and its + * subclasses control this, they would be the logical place to put this. + * However, because each member type has its own subclass, subclassing + * can not be used effectively to change formatting. The concrete + * class subclass of this class can be subclassed to change formatting. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see AbstractMemberWriter + * @see ClassWriterImpl + * + * @author Robert Field + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public abstract class SubWriterHolderWriter extends HtmlDocletWriter { + + /** + * The HTML tree for main tag. + */ + protected HtmlTree mainTree = HtmlTree.MAIN(); + + public SubWriterHolderWriter(ConfigurationImpl configuration, DocPath filename) + throws IOException { + super(configuration, filename); + } + + /** + * Add the summary header. + * + * @param mw the writer for the member being documented + * @param typeElement the te to be documented + * @param memberTree the content tree to which the summary header will be added + */ + public void addSummaryHeader(AbstractMemberWriter mw, TypeElement typeElement, + Content memberTree) { + mw.addSummaryAnchor(typeElement, memberTree); + mw.addSummaryLabel(memberTree); + } + + /** + * Get the summary table. + * + * @param mw the writer for the member being documented + * @param typeElement the te to be documented + * @param tableContents list of summary table contents + * @param showTabs true if the table needs to show tabs + * @return the content tree for the summary table + */ + public Content getSummaryTableTree(AbstractMemberWriter mw, TypeElement typeElement, + List tableContents, boolean showTabs) { + Content caption; + if (showTabs) { + caption = getTableCaption(mw.methodTypes); + generateMethodTypesScript(mw.typeMap, mw.methodTypes); + } + else { + caption = getTableCaption(mw.getCaption()); + } + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(HtmlStyle.memberSummary, caption) + : HtmlTree.TABLE(HtmlStyle.memberSummary, mw.getTableSummary(), caption); + table.addContent(getSummaryTableHeader(mw.getSummaryTableHeader(typeElement), "col")); + for (Content tableContent : tableContents) { + table.addContent(tableContent); + } + return table; + } + + /** + * Get the summary table caption. + * + * @param methodTypes set comprising of method types to show as table caption + * @return the caption for the summary table + */ + public Content getTableCaption(Set methodTypes) { + Content tabbedCaption = new HtmlTree(HtmlTag.CAPTION); + for (MethodTypes type : methodTypes) { + Content captionSpan; + Content span; + if (type.isDefaultTab()) { + captionSpan = HtmlTree.SPAN(configuration.getResource(type.resourceKey())); + span = HtmlTree.SPAN(type.tabId(), + HtmlStyle.activeTableTab, captionSpan); + } else { + captionSpan = HtmlTree.SPAN(getMethodTypeLinks(type)); + span = HtmlTree.SPAN(type.tabId(), + HtmlStyle.tableTab, captionSpan); + } + Content tabSpan = HtmlTree.SPAN(HtmlStyle.tabEnd, getSpace()); + span.addContent(tabSpan); + tabbedCaption.addContent(span); + } + return tabbedCaption; + } + + /** + * Get the method type links for the table caption. + * + * @param methodType the method type to be displayed as link + * @return the content tree for the method type link + */ + public Content getMethodTypeLinks(MethodTypes methodType) { + String jsShow = "javascript:show(" + methodType.value() +");"; + HtmlTree link = HtmlTree.A(jsShow, configuration.getResource(methodType.resourceKey())); + return link; + } + + /** + * Add the inherited summary header. + * + * @param mw the writer for the member being documented + * @param typeElement the te to be documented + * @param inheritedTree the content tree to which the inherited summary header will be added + */ + public void addInheritedSummaryHeader(AbstractMemberWriter mw, TypeElement typeElement, + Content inheritedTree) { + mw.addInheritedSummaryAnchor(typeElement, inheritedTree); + mw.addInheritedSummaryLabel(typeElement, inheritedTree); + } + + /** + * Add the index comment. + * + * @param member the member being documented + * @param contentTree the content tree to which the comment will be added + */ + protected void addIndexComment(Element member, Content contentTree) { + List tags = utils.getFirstSentenceTrees(member); + addIndexComment(member, tags, contentTree); + } + + /** + * Add the index comment. + * + * @param member the member being documented + * @param firstSentenceTags the first sentence tags for the member to be documented + * @param tdSummary the content tree to which the comment will be added + */ + protected void addIndexComment(Element member, List firstSentenceTags, + Content tdSummary) { + List deprs = utils.getBlockTags(member, DocTree.Kind.DEPRECATED); + Content div; + if (utils.isDeprecated(member)) { + Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + div.addContent(getSpace()); + if (!deprs.isEmpty()) { + addInlineDeprecatedComment(member, deprs.get(0), div); + } + tdSummary.addContent(div); + return; + } else { + Element te = member.getEnclosingElement(); + if (te != null && utils.isTypeElement(te) && utils.isDeprecated(te)) { + Content deprLabel = HtmlTree.SPAN(HtmlStyle.deprecatedLabel, deprecatedPhrase); + div = HtmlTree.DIV(HtmlStyle.block, deprLabel); + div.addContent(getSpace()); + tdSummary.addContent(div); + } + } + addSummaryComment(member, firstSentenceTags, tdSummary); + } + + /** + * Add the summary type for the member. + * + * @param mw the writer for the member being documented + * @param member the member to be documented + * @param tdSummaryType the content tree to which the type will be added + */ + public void addSummaryType(AbstractMemberWriter mw, Element member, Content tdSummaryType) { + mw.addSummaryType(member, tdSummaryType); + } + + /** + * Add the summary link for the member. + * + * @param mw the writer for the member being documented + * @param member the member to be documented + * @param contentTree the content tree to which the link will be added + */ + public void addSummaryLinkComment(AbstractMemberWriter mw, Element member, Content contentTree) { + List tags = utils.getFirstSentenceTrees(member); + addSummaryLinkComment(mw, member, tags, contentTree); + } + + /** + * Add the summary link comment. + * + * @param mw the writer for the member being documented + * @param member the member being documented + * @param firstSentenceTags the first sentence tags for the member to be documented + * @param tdSummary the content tree to which the comment will be added + */ + public void addSummaryLinkComment(AbstractMemberWriter mw, + Element member, List firstSentenceTags, Content tdSummary) { + addIndexComment(member, firstSentenceTags, tdSummary); + } + + /** + * Add the inherited member summary. + * + * @param mw the writer for the member being documented + * @param typeElement the class being documented + * @param member the member being documented + * @param isFirst true if its the first link being documented + * @param linksTree the content tree to which the summary will be added + */ + public void addInheritedMemberSummary(AbstractMemberWriter mw, TypeElement typeElement, + Element member, boolean isFirst, Content linksTree) { + if (! isFirst) { + linksTree.addContent(", "); + } + mw.addInheritedSummaryLink(typeElement, member, linksTree); + } + + /** + * Get the document content header tree + * + * @return a content tree the document content header + */ + public Content getContentHeader() { + HtmlTree div = new HtmlTree(HtmlTag.DIV); + div.addStyle(HtmlStyle.contentContainer); + return div; + } + + /** + * Add the class content tree. + * + * @param contentTree content tree to which the class content will be added + * @param classContentTree class content tree which will be added to the content tree + */ + public void addClassContentTree(Content contentTree, Content classContentTree) { + if (configuration.allowTag(HtmlTag.MAIN)) { + mainTree.addContent(classContentTree); + contentTree.addContent(mainTree); + } else { + contentTree.addContent(classContentTree); + } + } + + /** + * Add the annotation content tree. + * + * @param contentTree content tree to which the annotation content will be added + * @param annotationContentTree annotation content tree which will be added to the content tree + */ + public void addAnnotationContentTree(Content contentTree, Content annotationContentTree) { + addClassContentTree(contentTree, annotationContentTree); + } + + /** + * Get the member header tree + * + * @return a content tree the member header + */ + public Content getMemberTreeHeader() { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + return li; + } + + /** + * Add the member tree. + * + * @param memberSummaryTree the content tree representing the member summary + * @param memberTree the content tree representing the member + */ + public void addMemberTree(Content memberSummaryTree, Content memberTree) { + if (configuration.allowTag(HtmlTag.SECTION)) { + HtmlTree htmlTree = HtmlTree.SECTION(getMemberTree(memberTree)); + memberSummaryTree.addContent(htmlTree); + } else { + memberSummaryTree.addContent(getMemberTree(memberTree)); + } + } + + /** + * Get the member tree + * + * @param contentTree the tree used to generate the complete member tree + * @return a content tree for the member + */ + public Content getMemberTree(Content contentTree) { + Content ul = HtmlTree.UL(HtmlStyle.blockList, contentTree); + return ul; + } + + /** + * Get the member summary tree + * + * @param contentTree the tree used to generate the member summary tree + * @return a content tree for the member summary + */ + public Content getMemberSummaryTree(Content contentTree) { + return getMemberTree(HtmlStyle.summary, contentTree); + } + + /** + * Get the member details tree + * + * @param contentTree the tree used to generate the member details tree + * @return a content tree for the member details + */ + public Content getMemberDetailsTree(Content contentTree) { + return getMemberTree(HtmlStyle.details, contentTree); + } + + /** + * Get the member tree + * + * @param style the style class to be added to the content tree + * @param contentTree the tree used to generate the complete member tree + */ + public Content getMemberTree(HtmlStyle style, Content contentTree) { + Content div = HtmlTree.DIV(style, getMemberTree(contentTree)); + return div; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java new file mode 100644 index 00000000000..1ae644233b2 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.util.List; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleElementVisitor9; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.IndexTree; +import com.sun.tools.javac.util.DefinedBy; + +import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.builders.SerializedFormBuilder; +import jdk.javadoc.internal.doclets.toolkit.taglets.TagletWriter; +import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; +import jdk.javadoc.internal.doclets.toolkit.util.DocLink; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; +import jdk.javadoc.internal.doclets.toolkit.util.MessageRetriever; +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +/** + * The taglet writer that writes HTML. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + * @author Bhavesh Patel (Modified) + */ + +public class TagletWriterImpl extends TagletWriter { + + private final HtmlDocletWriter htmlWriter; + private final ConfigurationImpl configuration; + private final Utils utils; + + public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence) { + super(isFirstSentence); + this.htmlWriter = htmlWriter; + configuration = htmlWriter.configuration; + this.utils = configuration.utils; + } + + /** + * {@inheritDoc} + */ + public Content getOutputInstance() { + return new ContentBuilder(); + } + + /** + * {@inheritDoc} + */ + protected Content codeTagOutput(Element element, DocTree tag) { + CommentHelper ch = utils.getCommentHelper(element); + String str = utils.normalizeNewlines(ch.getText(tag)); + StringContent content = new StringContent(str); + Content result = HtmlTree.CODE(content); + return result; + } + + protected Content indexTagOutput(Element element, DocTree tag) { + CommentHelper ch = utils.getCommentHelper(element); + IndexTree itt = (IndexTree)tag; + + String tagText = ch.getText(itt.getSearchTerm()); + if (tagText.charAt(0) == '"' && tagText.charAt(tagText.length() - 1) == '"') { + tagText = tagText.substring(1, tagText.length() - 1); + } + String desc = ch.getText(itt.getDescription()); + + String anchorName = htmlWriter.getName(tagText); + Content result = HtmlTree.A_ID(anchorName, new StringContent(tagText)); + if (configuration.createindex && !tagText.isEmpty()) { + SearchIndexItem si = new SearchIndexItem(); + si.setLabel(tagText); + si.setDescription(desc); + new SimpleElementVisitor9() { + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + public Void visitPackage(PackageElement e, Void p) { + si.setUrl(DocPath.forPackage(e).getPath() + + "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName); + si.setHolder(utils.getSimpleName(element)); + return null; + } + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + public Void visitType(TypeElement e, Void p) { + si.setUrl(DocPath.forClass(utils, e).getPath() + "#" + anchorName); + si.setHolder(utils.getFullyQualifiedName(e)); + return null; + } + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + public Void visitVariable(VariableElement e, Void p) { + TypeElement te = utils.getEnclosingTypeElement(e); + si.setUrl(DocPath.forClass(utils, te).getPath() + "#" + anchorName); + si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e)); + return null; + } + + @Override @DefinedBy(DefinedBy.Api.LANGUAGE_MODEL) + protected Void defaultAction(Element e, Void p) { + TypeElement te = utils.getEnclosingTypeElement(e); + si.setUrl(DocPath.forClass(utils, te).getPath() + "#" + anchorName); + si.setHolder(utils.getFullyQualifiedName(e)); + return null; + } + }.visit(element); + si.setCategory(configuration.getResource("doclet.SearchTags").toString()); + configuration.tagSearchIndex.add(si); + } + return result; + } + + /** + * {@inheritDoc} + */ + public Content getDocRootOutput() { + String path; + if (htmlWriter.pathToRoot.isEmpty()) + path = "."; + else + path = htmlWriter.pathToRoot.getPath(); + return new StringContent(path); + } + + /** + * {@inheritDoc} + */ + public Content deprecatedTagOutput(Element element) { + ContentBuilder result = new ContentBuilder(); + CommentHelper ch = utils.getCommentHelper(element); + List deprs = utils.getBlockTags(element, DocTree.Kind.DEPRECATED); + if (utils.isTypeElement(element)) { + if (utils.isDeprecated(element)) { + result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + new StringContent(configuration.getText("doclet.Deprecated")))); + result.addContent(RawHtml.nbsp); + if (!deprs.isEmpty()) { + List commentTags = ch.getDescription(configuration, deprs.get(0)); + if (!commentTags.isEmpty()) { + result.addContent(commentTagsToOutput(null, element, commentTags, false)); + } + } + } + } else { + if (utils.isDeprecated(element)) { + result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + new StringContent(configuration.getText("doclet.Deprecated")))); + result.addContent(RawHtml.nbsp); + if (!deprs.isEmpty()) { + List bodyTags = ch.getBody(configuration, deprs.get(0)); + Content body = commentTagsToOutput(null, element, bodyTags, false); + if (!body.isEmpty()) + result.addContent(HtmlTree.SPAN(HtmlStyle.deprecationComment, body)); + } + } else { + if (utils.isDeprecated(utils.getEnclosingTypeElement(element))) { + result.addContent(HtmlTree.SPAN(HtmlStyle.deprecatedLabel, + new StringContent(configuration.getText("doclet.Deprecated")))); + result.addContent(RawHtml.nbsp); + } + } + } + return result; + } + + /** + * {@inheritDoc} + */ + protected Content literalTagOutput(Element element, DocTree tag) { + CommentHelper ch = utils.getCommentHelper(element); + Content result = new StringContent(utils.normalizeNewlines(ch.getText(tag))); + return result; + } + + /** + * {@inheritDoc} + */ + public MessageRetriever getMsgRetriever() { + return configuration.message; + } + + /** + * {@inheritDoc} + */ + public Content getParamHeader(String header) { + HtmlTree result = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.paramLabel, + new StringContent(header))); + return result; + } + + /** + * {@inheritDoc} + */ + public Content paramTagOutput(Element element, DocTree paramTag, String paramName) { + ContentBuilder body = new ContentBuilder(); + CommentHelper ch = utils.getCommentHelper(element); + body.addContent(HtmlTree.CODE(new RawHtml(paramName))); + body.addContent(" - "); + List description = ch.getDescription(configuration, paramTag); + body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false)); + HtmlTree result = HtmlTree.DD(body); + return result; + } + + /** + * {@inheritDoc} + */ + public Content propertyTagOutput(Element element, DocTree tag, String prefix) { + Content body = new ContentBuilder(); + CommentHelper ch = utils.getCommentHelper(element); + body.addContent(new RawHtml(prefix)); + body.addContent(" "); + body.addContent(HtmlTree.CODE(new RawHtml(ch.getText(tag)))); + body.addContent("."); + Content result = HtmlTree.P(body); + return result; + } + + /** + * {@inheritDoc} + */ + public Content returnTagOutput(Element element, DocTree returnTag) { + ContentBuilder result = new ContentBuilder(); + CommentHelper ch = utils.getCommentHelper(element); + result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.returnLabel, + new StringContent(configuration.getText("doclet.Returns"))))); + result.addContent(HtmlTree.DD(htmlWriter.commentTagsToContent( + returnTag, element, ch.getDescription(configuration, returnTag), false))); + return result; + } + + /** + * {@inheritDoc} + */ + public Content seeTagOutput(Element holder, List seeTags) { + ContentBuilder body = new ContentBuilder(); + if (!seeTags.isEmpty()) { + for (DocTree dt : seeTags) { + appendSeparatorIfNotEmpty(body); + body.addContent(htmlWriter.seeTagToContent(holder, dt)); + } + } + if (utils.isVariableElement(holder) && ((VariableElement)holder).getConstantValue() != null && + htmlWriter instanceof ClassWriterImpl) { + //Automatically add link to constant values page for constant fields. + appendSeparatorIfNotEmpty(body); + DocPath constantsPath = + htmlWriter.pathToRoot.resolve(DocPaths.CONSTANT_VALUES); + String whichConstant = + ((ClassWriterImpl) htmlWriter).getTypeElement().getQualifiedName() + "." + + utils.getSimpleName(holder); + DocLink link = constantsPath.fragment(whichConstant); + body.addContent(htmlWriter.getHyperLink(link, + new StringContent(configuration.getText("doclet.Constants_Summary")))); + } + if (utils.isClass(holder) && utils.isSerializable((TypeElement)holder)) { + //Automatically add link to serialized form page for serializable classes. + if (SerializedFormBuilder.serialInclude(utils, holder) && + SerializedFormBuilder.serialInclude(utils, utils.containingPackage(holder))) { + appendSeparatorIfNotEmpty(body); + DocPath serialPath = htmlWriter.pathToRoot.resolve(DocPaths.SERIALIZED_FORM); + DocLink link = serialPath.fragment(utils.getFullyQualifiedName(holder)); + body.addContent(htmlWriter.getHyperLink(link, + new StringContent(configuration.getText("doclet.Serialized_Form")))); + } + } + if (body.isEmpty()) + return body; + + ContentBuilder result = new ContentBuilder(); + result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.seeLabel, + new StringContent(configuration.getText("doclet.See_Also"))))); + result.addContent(HtmlTree.DD(body)); + return result; + + } + + private void appendSeparatorIfNotEmpty(ContentBuilder body) { + if (!body.isEmpty()) { + body.addContent(", "); + body.addContent(DocletConstants.NL); + } + } + + /** + * {@inheritDoc} + */ + public Content simpleTagOutput(Element element, List simpleTags, String header) { + CommentHelper ch = utils.getCommentHelper(element); + ContentBuilder result = new ContentBuilder(); + result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header)))); + ContentBuilder body = new ContentBuilder(); + boolean many = false; + for (DocTree simpleTag : simpleTags) { + if (many) { + body.addContent(", "); + } + List bodyTags = ch.getBody(configuration, simpleTag); + body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false)); + many = true; + } + result.addContent(HtmlTree.DD(body)); + return result; + } + + /** + * {@inheritDoc} + */ + public Content simpleTagOutput(Element element, DocTree simpleTag, String header) { + ContentBuilder result = new ContentBuilder(); + result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header)))); + CommentHelper ch = utils.getCommentHelper(element); + List description = ch.getDescription(configuration, simpleTag); + Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false); + result.addContent(HtmlTree.DD(body)); + return result; + } + + /** + * {@inheritDoc} + */ + public Content getThrowsHeader() { + HtmlTree result = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.throwsLabel, + new StringContent(configuration.getText("doclet.Throws")))); + return result; + } + + /** + * {@inheritDoc} + */ + public Content throwsTagOutput(Element element, DocTree throwsTag) { + ContentBuilder body = new ContentBuilder(); + CommentHelper ch = utils.getCommentHelper(element); + Element exception = ch.getException(configuration, throwsTag); + Content excName; + if (exception == null) { + excName = new RawHtml(ch.getExceptionName(throwsTag).toString()); + } else if (exception.asType() == null) { + excName = new RawHtml(utils.getFullyQualifiedName(exception)); + } else { + LinkInfoImpl link = new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, + exception.asType()); + link.excludeTypeBounds = true; + excName = htmlWriter.getLink(link); + } + body.addContent(HtmlTree.CODE(excName)); + List description = ch.getDescription(configuration, throwsTag); + Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false); + if (desc != null && !desc.isEmpty()) { + body.addContent(" - "); + body.addContent(desc); + } + HtmlTree result = HtmlTree.DD(body); + return result; + } + + /** + * {@inheritDoc} + */ + public Content throwsTagOutput(TypeMirror throwsType) { + HtmlTree result = HtmlTree.DD(HtmlTree.CODE(htmlWriter.getLink( + new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER, throwsType)))); + return result; + } + + /** + * {@inheritDoc} + */ + public Content valueTagOutput(VariableElement field, String constantVal, boolean includeLink) { + return includeLink ? + htmlWriter.getDocLink(LinkInfoImpl.Kind.VALUE_TAG, field, + constantVal, false) : new RawHtml(constantVal); + } + + /** + * {@inheritDoc} + */ + public Content commentTagsToOutput(DocTree holderTag, List tags) { + return commentTagsToOutput(holderTag, null, tags, false); + } + + /** + * {@inheritDoc} + */ + public Content commentTagsToOutput(Element holder, List tags) { + return commentTagsToOutput(null, holder, tags, false); + } + + /** + * {@inheritDoc} + */ + public Content commentTagsToOutput(DocTree holderTag, + Element holder, List tags, boolean isFirstSentence) { + return htmlWriter.commentTagsToContent(holderTag, holder, + tags, isFirstSentence); + } + + /** + * {@inheritDoc} + */ + public Configuration configuration() { + return configuration; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java new file mode 100644 index 00000000000..0987cc2d825 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TreeWriter.java @@ -0,0 +1,206 @@ +/* + * 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 + * 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.javadoc.internal.doclets.formats.html; + +import java.io.IOException; +import java.util.SortedSet; + +import javax.lang.model.element.PackageElement; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +/** + * Generate Class Hierarchy page for all the Classes in this run. Use + * ClassTree for building the Tree. The name of + * the generated file is "overview-tree.html" and it is generated in the + * current or the destination directory. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class TreeWriter extends AbstractTreeWriter { + + /** + * Packages in this run. + */ + SortedSet packages; + + /** + * True if there are no packages specified on the command line, + * False otherwise. + */ + private boolean classesonly; + + /** + * Constructor to construct TreeWriter object. + * + * @param configuration the current configuration of the doclet. + * @param filename String filename + * @param classtree the tree being built. + */ + public TreeWriter(ConfigurationImpl configuration, + DocPath filename, ClassTree classtree) throws IOException { + super(configuration, filename, classtree); + packages = configuration.packages; + classesonly = packages.isEmpty(); + } + + /** + * Create a TreeWriter object and use it to generate the + * "overview-tree.html" file. + * + * @param classtree the class tree being documented. + * @throws DocletAbortException + */ + public static void generate(ConfigurationImpl configuration, + ClassTree classtree) { + TreeWriter treegen; + DocPath filename = DocPaths.OVERVIEW_TREE; + try { + treegen = new TreeWriter(configuration, filename, classtree); + treegen.generateTreeFile(); + treegen.close(); + } catch (IOException exc) { + configuration.standardmessage.error( + "doclet.exception_encountered", + exc.toString(), filename); + throw new DocletAbortException(exc); + } + } + + /** + * Generate the interface hierarchy and class hierarchy. + */ + public void generateTreeFile() throws IOException { + HtmlTree body = getTreeHeader(); + Content headContent = getResource("doclet.Hierarchy_For_All_Packages"); + Content heading = HtmlTree.HEADING(HtmlConstants.TITLE_HEADING, false, + HtmlStyle.title, headContent); + Content div = HtmlTree.DIV(HtmlStyle.header, heading); + addPackageTreeLinks(div); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.MAIN)) + ? HtmlTree.MAIN() + : body; + htmlTree.addContent(div); + HtmlTree divTree = new HtmlTree(HtmlTag.DIV); + divTree.addStyle(HtmlStyle.contentContainer); + addTree(classtree.baseClasses(), "doclet.Class_Hierarchy", divTree); + addTree(classtree.baseInterfaces(), "doclet.Interface_Hierarchy", divTree); + addTree(classtree.baseAnnotationTypes(), "doclet.Annotation_Type_Hierarchy", divTree); + addTree(classtree.baseEnums(), "doclet.Enum_Hierarchy", divTree, true); + htmlTree.addContent(divTree); + if (configuration.allowTag(HtmlTag.MAIN)) { + body.addContent(htmlTree); + } + if (configuration.allowTag(HtmlTag.FOOTER)) { + htmlTree = HtmlTree.FOOTER(); + } else { + htmlTree = body; + } + addNavLinks(false, htmlTree); + addBottom(htmlTree); + if (configuration.allowTag(HtmlTag.FOOTER)) { + body.addContent(htmlTree); + } + printHtmlDocument(null, true, body); + } + + /** + * Add the links to all the package tree files. + * + * @param contentTree the content tree to which the links will be added + */ + protected void addPackageTreeLinks(Content contentTree) { + //Do nothing if only unnamed package is used + if (isUnnamedPackage()) { + return; + } + if (!classesonly) { + Content span = HtmlTree.SPAN(HtmlStyle.packageHierarchyLabel, + getResource("doclet.Package_Hierarchies")); + contentTree.addContent(span); + HtmlTree ul = new HtmlTree(HtmlTag.UL); + ul.addStyle(HtmlStyle.horizontal); + int i = 0; + for (PackageElement pkg : packages) { + // If the package name length is 0 or if -nodeprecated option + // is set and the package is marked as deprecated, do not include + // the page in the list of package hierarchies. + if (pkg.isUnnamed() || + (configuration.nodeprecated && utils.isDeprecated(pkg))) { + i++; + continue; + } + DocPath link = pathString(pkg, DocPaths.PACKAGE_TREE); + Content li = HtmlTree.LI(getHyperLink(link, + new StringContent(utils.getPackageName(pkg)))); + if (i < packages.size() - 1) { + li.addContent(", "); + } + ul.addContent(li); + i++; + } + contentTree.addContent(ul); + } + } + + /** + * Get the tree header. + * + * @return a content tree for the tree header + */ + protected HtmlTree getTreeHeader() { + String title = configuration.getText("doclet.Window_Class_Hierarchy"); + HtmlTree bodyTree = getBody(true, getWindowTitle(title)); + HtmlTree htmlTree = (configuration.allowTag(HtmlTag.HEADER)) + ? HtmlTree.HEADER() + : bodyTree; + addTop(htmlTree); + addNavLinks(true, htmlTree); + if (configuration.allowTag(HtmlTag.HEADER)) { + bodyTree.addContent(htmlTree); + } + return bodyTree; + } + + private boolean isUnnamedPackage() { + return packages.size() == 1 && packages.first().isUnnamed(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/WriterFactoryImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/WriterFactoryImpl.java new file mode 100644 index 00000000000..cce4052b9b3 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/WriterFactoryImpl.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html; + +import java.io.IOException; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeFieldWriter; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeOptionalMemberWriter; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeRequiredMemberWriter; +import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter; +import jdk.javadoc.internal.doclets.toolkit.ClassWriter; +import jdk.javadoc.internal.doclets.toolkit.ConstantsSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter; +import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter; +import jdk.javadoc.internal.doclets.toolkit.WriterFactory; +import jdk.javadoc.internal.doclets.toolkit.util.ClassTree; +import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap; + +import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberMap.Kind.*; + +/** + * The factory that returns HTML writers. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Jamie Ho + */ +public class WriterFactoryImpl implements WriterFactory { + + private final ConfigurationImpl configuration; + public WriterFactoryImpl(ConfigurationImpl configuration) { + this.configuration = configuration; + } + + /** + * {@inheritDoc} + */ + @Override + public ConstantsSummaryWriter getConstantsSummaryWriter() throws Exception { + return new ConstantsSummaryWriterImpl(configuration); + } + + /** + * {@inheritDoc} + */ + @Override + public PackageSummaryWriter getPackageSummaryWriter(PackageElement packageElement, + PackageElement prevPkg, PackageElement nextPkg) throws Exception { + return new PackageWriterImpl(configuration, packageElement, prevPkg, nextPkg); + } + + /** + * {@inheritDoc} + */ + @Override + public ClassWriter getClassWriter(TypeElement typeElement, TypeElement prevClass, + TypeElement nextClass, ClassTree classTree) throws IOException { + return new ClassWriterImpl(configuration, typeElement, prevClass, nextClass, classTree); + } + + /** + * {@inheritDoc} + */ + @Override + public AnnotationTypeWriter getAnnotationTypeWriter(TypeElement annotationType, + TypeMirror prevType, TypeMirror nextType) throws Exception { + return new AnnotationTypeWriterImpl(configuration, annotationType, prevType, nextType); + } + + /** + * {@inheritDoc} + */ + @Override + public AnnotationTypeFieldWriter + getAnnotationTypeFieldWriter(AnnotationTypeWriter annotationTypeWriter) throws Exception { + TypeElement te = annotationTypeWriter.getAnnotationTypeElement(); + return new AnnotationTypeFieldWriterImpl( + (SubWriterHolderWriter) annotationTypeWriter, te); + } + + /** + * {@inheritDoc} + */ + @Override + public AnnotationTypeOptionalMemberWriter + getAnnotationTypeOptionalMemberWriter( + AnnotationTypeWriter annotationTypeWriter) throws Exception { + TypeElement te = annotationTypeWriter.getAnnotationTypeElement(); + return new AnnotationTypeOptionalMemberWriterImpl( + (SubWriterHolderWriter) annotationTypeWriter, te); + } + + /** + * {@inheritDoc} + */ + @Override + public AnnotationTypeRequiredMemberWriter + getAnnotationTypeRequiredMemberWriter(AnnotationTypeWriter annotationTypeWriter) throws Exception { + TypeElement te = annotationTypeWriter.getAnnotationTypeElement(); + return new AnnotationTypeRequiredMemberWriterImpl( + (SubWriterHolderWriter) annotationTypeWriter, te); + } + + /** + * {@inheritDoc} + */ + @Override + public EnumConstantWriterImpl getEnumConstantWriter(ClassWriter classWriter) + throws Exception { + return new EnumConstantWriterImpl((SubWriterHolderWriter) classWriter, + classWriter.getTypeElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public FieldWriterImpl getFieldWriter(ClassWriter classWriter) + throws Exception { + return new FieldWriterImpl((SubWriterHolderWriter) classWriter, classWriter.getTypeElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public PropertyWriterImpl getPropertyWriter(ClassWriter classWriter) + throws Exception { + return new PropertyWriterImpl((SubWriterHolderWriter) classWriter, + classWriter.getTypeElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public MethodWriterImpl getMethodWriter(ClassWriter classWriter) + throws Exception { + return new MethodWriterImpl((SubWriterHolderWriter) classWriter, classWriter.getTypeElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public ConstructorWriterImpl getConstructorWriter(ClassWriter classWriter) + throws Exception { + return new ConstructorWriterImpl((SubWriterHolderWriter) classWriter, + classWriter.getTypeElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public MemberSummaryWriter getMemberSummaryWriter( + ClassWriter classWriter, VisibleMemberMap.Kind memberType) + throws Exception { + switch (memberType) { + case CONSTRUCTORS: + return getConstructorWriter(classWriter); + case ENUM_CONSTANTS: + return getEnumConstantWriter(classWriter); + case FIELDS: + return getFieldWriter(classWriter); + case PROPERTIES: + return getPropertyWriter(classWriter); + case INNER_CLASSES: + return new NestedClassWriterImpl((SubWriterHolderWriter) + classWriter, classWriter.getTypeElement()); + case METHODS: + return getMethodWriter(classWriter); + default: + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public MemberSummaryWriter getMemberSummaryWriter( + AnnotationTypeWriter annotationTypeWriter, VisibleMemberMap.Kind memberType) + throws Exception { + switch (memberType) { + case ANNOTATION_TYPE_FIELDS: + return (AnnotationTypeFieldWriterImpl) + getAnnotationTypeFieldWriter(annotationTypeWriter); + case ANNOTATION_TYPE_MEMBER_OPTIONAL: + return (AnnotationTypeOptionalMemberWriterImpl) + getAnnotationTypeOptionalMemberWriter(annotationTypeWriter); + case ANNOTATION_TYPE_MEMBER_REQUIRED: + return (AnnotationTypeRequiredMemberWriterImpl) + getAnnotationTypeRequiredMemberWriter(annotationTypeWriter); + default: + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public SerializedFormWriter getSerializedFormWriter() throws Exception { + return new SerializedFormWriterImpl(configuration); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java new file mode 100644 index 00000000000..0adfc088106 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Comment.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; + +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +/** + * Class for generating a comment for HTML pages of javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class Comment extends Content { + + private String commentText; + + /** + * Constructor to construct a Comment object. + * + * @param comment comment text for the comment + */ + public Comment(String comment) { + commentText = nullCheck(comment); + } + + /** + * This method is not supported by the class. + * + * @param content content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(Content content) { + throw new DocletAbortException("not supported"); + } + + /** + * This method is not supported by the class. + * + * @param stringContent string content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(String stringContent) { + throw new DocletAbortException("not supported"); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return commentText.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean write(Writer out, boolean atNewline) throws IOException { + if (!atNewline) + out.write(DocletConstants.NL); + out.write("" + DocletConstants.NL); + return true; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java new file mode 100644 index 00000000000..64ceef82473 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java @@ -0,0 +1,96 @@ +/* + * 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. 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.javadoc.internal.doclets.toolkit.Content; + +/** + * A sequence of Content nodes. + */ +public class ContentBuilder extends Content { + protected List contents = Collections.emptyList(); + + @Override + public void addContent(Content content) { + nullCheck(content); + ensureMutableContents(); + if (content instanceof ContentBuilder) { + contents.addAll(((ContentBuilder) content).contents); + } else + contents.add(content); + } + + @Override + public void addContent(String text) { + if (text.isEmpty()) + return; + ensureMutableContents(); + Content c = contents.isEmpty() ? null : contents.get(contents.size() - 1); + StringContent sc; + if (c != null && c instanceof StringContent) { + sc = (StringContent) c; + } else { + contents.add(sc = new StringContent()); + } + sc.addContent(text); + } + + @Override + public boolean write(Writer writer, boolean atNewline) throws IOException { + for (Content content: contents) { + atNewline = content.write(writer, atNewline); + } + return atNewline; + } + + @Override + public boolean isEmpty() { + for (Content content: contents) { + if (!content.isEmpty()) + return false; + } + return true; + } + + @Override + public int charCount() { + int n = 0; + for (Content c : contents) + n += c.charCount(); + return n; + } + + private void ensureMutableContents() { + if (contents.isEmpty()) + contents = new ArrayList<>(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java new file mode 100644 index 00000000000..469ee40743f --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/DocType.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; + +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +/** + * Class for generating document type for HTML pages of javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class DocType extends Content { + + private String docType; + + public static final DocType TRANSITIONAL = + new DocType("Transitional", "http://www.w3.org/TR/html4/loose.dtd"); + + public static final DocType HTML5 = new DocType(); + + /** + * Constructor to construct a DocType object. + * + * @param type the doctype to be added + * @param dtd the dtd of the doctype + */ + private DocType(String type, String dtd) { + docType = "" + DocletConstants.NL; + } + + /** + * Constructor to construct a DocType object. + */ + private DocType() { + docType = "" + DocletConstants.NL; + } + + /** + * This method is not supported by the class. + * + * @param content content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(Content content) { + throw new DocletAbortException("not supported"); + } + + /** + * This method is not supported by the class. + * + * @param stringContent string content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(String stringContent) { + throw new DocletAbortException("not supported"); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return (docType.length() == 0); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean write(Writer out, boolean atNewline) throws IOException { + out.write(docType); + return true; // guaranteed by constructor + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java new file mode 100644 index 00000000000..69ae2aa5c0e --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlAttr.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +/** + * Enum representing HTML tag attributes. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public enum HtmlAttr { + ALT, + CLASS, + CLEAR, + COLS, + CONTENT, + DISABLED, + HREF, + HTTP_EQUIV("http-equiv"), + ID, + LANG, + NAME, + ONLOAD, + REL, + ROLE, + ROWS, + SCOPE, + SCROLLING, + SRC, + SUMMARY, + TARGET, + TITLE, + TYPE, + VALUE, + WIDTH; + + private final String value; + + public enum Role { + + BANNER, + CONTENTINFO, + MAIN, + NAVIGATION, + REGION; + + private final String role; + + Role() { + role = Utils.toLowerCase(name()); + } + + public String toString() { + return role; + } + } + + HtmlAttr() { + this.value = Utils.toLowerCase(name()); + } + + HtmlAttr(String name) { + this.value = name; + } + + public String toString() { + return value; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java new file mode 100644 index 00000000000..b1e03e3e49b --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import jdk.javadoc.internal.doclets.toolkit.Content; + +/** + * Stores constants for Html Doclet. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class HtmlConstants { + + /** + * Marker to identify start of top navigation bar. + */ + public static final Content START_OF_TOP_NAVBAR = + new Comment("========= START OF TOP NAVBAR ======="); + + /** + * Marker to identify start of bottom navigation bar. + */ + public static final Content START_OF_BOTTOM_NAVBAR = + new Comment("======= START OF BOTTOM NAVBAR ======"); + + /** + * Marker to identify end of top navigation bar. + */ + public static final Content END_OF_TOP_NAVBAR = + new Comment("========= END OF TOP NAVBAR ========="); + + /** + * Marker to identify end of bottom navigation bar. + */ + public static final Content END_OF_BOTTOM_NAVBAR = + new Comment("======== END OF BOTTOM NAVBAR ======="); + + /** + * Marker to identify start of class data. + */ + public static final Content START_OF_CLASS_DATA = + new Comment("======== START OF CLASS DATA ========"); + + /** + * Marker to identify end of class data. + */ + public static final Content END_OF_CLASS_DATA = + new Comment("========= END OF CLASS DATA ========="); + + /** + * Marker to identify start of nested class summary. + */ + public static final Content START_OF_NESTED_CLASS_SUMMARY = + new Comment("======== NESTED CLASS SUMMARY ========"); + + /** + * Marker to identify start of annotation type optional member summary. + */ + public static final Content START_OF_ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY = + new Comment("=========== ANNOTATION TYPE OPTIONAL MEMBER SUMMARY ==========="); + + /** + * Marker to identify start of annotation type required member summary. + */ + public static final Content START_OF_ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY = + new Comment("=========== ANNOTATION TYPE REQUIRED MEMBER SUMMARY ==========="); + + /** + * Marker to identify start of annotation type required member summary. + */ + public static final Content START_OF_ANNOTATION_TYPE_FIELD_SUMMARY = + new Comment("=========== ANNOTATION TYPE FIELD SUMMARY ==========="); + + /** + * Marker to identify start of constructor summary. + */ + public static final Content START_OF_CONSTRUCTOR_SUMMARY = + new Comment("======== CONSTRUCTOR SUMMARY ========"); + + /** + * Marker to identify start of enum constants summary. + */ + public static final Content START_OF_ENUM_CONSTANT_SUMMARY = + new Comment("=========== ENUM CONSTANT SUMMARY ==========="); + + /** + * Marker to identify start of field summary. + */ + public static final Content START_OF_FIELD_SUMMARY = + new Comment("=========== FIELD SUMMARY ==========="); + + /** + * Marker to identify start of properties summary. + */ + public static final Content START_OF_PROPERTY_SUMMARY = + new Comment("=========== PROPERTY SUMMARY ==========="); + + /** + * Marker to identify start of method summary. + */ + public static final Content START_OF_METHOD_SUMMARY = + new Comment("========== METHOD SUMMARY ==========="); + + /** + * Marker to identify start of annotation type details. + */ + public static final Content START_OF_ANNOTATION_TYPE_DETAILS = + new Comment("============ ANNOTATION TYPE MEMBER DETAIL ==========="); + + /** + * Marker to identify start of annotation type field details. + */ + public static final Content START_OF_ANNOTATION_TYPE_FIELD_DETAILS = + new Comment("============ ANNOTATION TYPE FIELD DETAIL ==========="); + + /** + * Marker to identify start of method details. + */ + public static final Content START_OF_METHOD_DETAILS = + new Comment("============ METHOD DETAIL =========="); + + /** + * Marker to identify start of field details. + */ + public static final Content START_OF_FIELD_DETAILS = + new Comment("============ FIELD DETAIL ==========="); + + /** + * Marker to identify start of property details. + */ + public static final Content START_OF_PROPERTY_DETAILS = + new Comment("============ PROPERTY DETAIL ==========="); + + /** + * Marker to identify start of constructor details. + */ + public static final Content START_OF_CONSTRUCTOR_DETAILS = + new Comment("========= CONSTRUCTOR DETAIL ========"); + + /** + * Marker to identify start of enum constants details. + */ + public static final Content START_OF_ENUM_CONSTANT_DETAILS = + new Comment("============ ENUM CONSTANT DETAIL ==========="); + + /** + * Html tag for the page title heading. + */ + public static final HtmlTag TITLE_HEADING = HtmlTag.H1; + + /** + * Html tag for the class page title heading. + */ + public static final HtmlTag CLASS_PAGE_HEADING = HtmlTag.H2; + + /** + * Html tag for the content heading. + */ + public static final HtmlTag CONTENT_HEADING = HtmlTag.H2; + + /** + * Html tag for the package name heading. + */ + public static final HtmlTag PACKAGE_HEADING = HtmlTag.H2; + + /** + * Html tag for the member summary heading. + */ + public static final HtmlTag SUMMARY_HEADING = HtmlTag.H3; + + /** + * Html tag for the inherited member summary heading. + */ + public static final HtmlTag INHERITED_SUMMARY_HEADING = HtmlTag.H3; + + /** + * Html tag for the member details heading. + */ + public static final HtmlTag DETAILS_HEADING = HtmlTag.H3; + + /** + * Html tag for the serialized member heading. + */ + public static final HtmlTag SERIALIZED_MEMBER_HEADING = HtmlTag.H3; + + /** + * Html tag for the member heading. + */ + public static final HtmlTag MEMBER_HEADING = HtmlTag.H4; + + /** + * Default charset for HTML. + */ + public static final String HTML_DEFAULT_CHARSET = "utf-8"; +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java new file mode 100644 index 00000000000..d7aec841525 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocWriter.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.*; +import java.util.*; + +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +import jdk.javadoc.internal.doclets.formats.html.ConfigurationImpl; +import jdk.javadoc.internal.doclets.formats.html.SectionName; +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocLink; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; + + +/** + * Class for the Html Format Code Generation specific to JavaDoc. + * This Class contains methods related to the Html Code Generation which + * are used by the Sub-Classes in the package jdk.javadoc.internal.tool.standard. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Robert Field + */ +public abstract class HtmlDocWriter extends HtmlWriter { + + public static final String CONTENT_TYPE = "text/html"; + + DocPath pathToRoot; + + /** + * Constructor. Initializes the destination file name through the super + * class HtmlWriter. + * + * @param filename String file name. + */ + public HtmlDocWriter(Configuration configuration, DocPath filename) + throws IOException { + super(configuration, filename); + this.pathToRoot = filename.parent().invert(); + configuration.message.notice("doclet.Generating_0", + DocFile.createFileForOutput(configuration, filename).getPath()); + } + + /** + * Accessor for configuration. + */ + public abstract Configuration configuration(); + + public Content getHyperLink(DocPath link, String label) { + return getHyperLink(link, new StringContent(label), false, "", "", ""); + } + + /** + * Get Html Hyper Link Content. + * + * @param where Position of the link in the file. Character '#' is not + * needed. + * @param label Tag for the link. + * @return a content tree for the hyper link + */ + public Content getHyperLink(String where, + Content label) { + return getHyperLink(getDocLink(where), label, "", ""); + } + + /** + * Get Html Hyper Link Content. + * + * @param sectionName The section name to which the link will be created. + * @param label Tag for the link. + * @return a content tree for the hyper link + */ + public Content getHyperLink(SectionName sectionName, + Content label) { + return getHyperLink(getDocLink(sectionName), label, "", ""); + } + + /** + * Get Html Hyper Link Content. + * + * @param sectionName The section name combined with where to which the link + * will be created. + * @param where The fragment combined with sectionName to which the link + * will be created. + * @param label Tag for the link. + * @return a content tree for the hyper link + */ + public Content getHyperLink(SectionName sectionName, String where, + Content label) { + return getHyperLink(getDocLink(sectionName, where), label, "", ""); + } + + /** + * Get the link. + * + * @param where Position of the link in the file. + * @return a DocLink object for the hyper link + */ + public DocLink getDocLink(String where) { + return DocLink.fragment(getName(where)); + } + + /** + * Get the link. + * + * @param sectionName The section name to which the link will be created. + * @return a DocLink object for the hyper link + */ + public DocLink getDocLink(SectionName sectionName) { + return DocLink.fragment(sectionName.getName()); + } + + /** + * Get the link. + * + * @param sectionName The section name combined with where to which the link + * will be created. + * @param where The fragment combined with sectionName to which the link + * will be created. + * @return a DocLink object for the hyper link + */ + public DocLink getDocLink(SectionName sectionName, String where) { + return DocLink.fragment(sectionName.getName() + getName(where)); + } + + /** + * Convert the name to a valid HTML name. + * + * @param name the name that needs to be converted to valid HTML name. + * @return a valid HTML name string. + */ + public String getName(String name) { + StringBuilder sb = new StringBuilder(); + char ch; + /* The HTML 4 spec at http://www.w3.org/TR/html4/types.html#h-6.2 mentions + * that the name/id should begin with a letter followed by other valid characters. + * The HTML 5 spec (draft) is more permissive on names/ids where the only restriction + * is that it should be at least one character long and should not contain spaces. + * The spec draft is @ http://www.w3.org/html/wg/drafts/html/master/dom.html#the-id-attribute. + * + * For HTML 4, we need to check for non-characters at the beginning of the name and + * substitute it accordingly, "_" and "$" can appear at the beginning of a member name. + * The method substitutes "$" with "Z:Z:D" and will prefix "_" with "Z:Z". + */ + for (int i = 0; i < name.length(); i++) { + ch = name.charAt(i); + switch (ch) { + case '(': + case ')': + case '<': + case '>': + case ',': + sb.append('-'); + break; + case ' ': + case '[': + break; + case ']': + sb.append(":A"); + break; + // Any appearance of $ needs to be substituted with ":D" and not with hyphen + // since a field name "P$$ and a method P(), both valid member names, can end + // up as "P--". A member name beginning with $ needs to be substituted with + // "Z:Z:D". + case '$': + if (i == 0) + sb.append("Z:Z"); + sb.append(":D"); + break; + // A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor + // names can only begin with a letter. + case '_': + if (i == 0) + sb.append("Z:Z"); + sb.append(ch); + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } + + /** + * Get Html hyperlink. + * + * @param link path of the file. + * @param label Tag for the link. + * @return a content tree for the hyper link + */ + public Content getHyperLink(DocPath link, Content label) { + return getHyperLink(link, label, "", ""); + } + + public Content getHyperLink(DocLink link, Content label) { + return getHyperLink(link, label, "", ""); + } + + public Content getHyperLink(DocPath link, + Content label, boolean strong, + String stylename, String title, String target) { + return getHyperLink(new DocLink(link), label, strong, + stylename, title, target); + } + + public Content getHyperLink(DocLink link, + Content label, boolean strong, + String stylename, String title, String target) { + Content body = label; + if (strong) { + body = HtmlTree.SPAN(HtmlStyle.typeNameLink, body); + } + if (stylename != null && stylename.length() != 0) { + HtmlTree t = new HtmlTree(HtmlTag.FONT, body); + t.addAttr(HtmlAttr.CLASS, stylename); + body = t; + } + HtmlTree l = HtmlTree.A(link.toString(), body); + if (title != null && title.length() != 0) { + l.addAttr(HtmlAttr.TITLE, title); + } + if (target != null && target.length() != 0) { + l.addAttr(HtmlAttr.TARGET, target); + } + return l; + } + + /** + * Get Html Hyper Link. + * + * @param link String name of the file. + * @param label Tag for the link. + * @param title String that describes the link's content for accessibility. + * @param target Target frame. + * @return a content tree for the hyper link. + */ + public Content getHyperLink(DocPath link, Content label, String title, String target) { + return getHyperLink(new DocLink(link), label, title, target); + } + + public Content getHyperLink(DocLink link, Content label, String title, String target) { + HtmlTree anchor = HtmlTree.A(link.toString(), label); + if (title != null && title.length() != 0) { + anchor.addAttr(HtmlAttr.TITLE, title); + } + if (target != null && target.length() != 0) { + anchor.addAttr(HtmlAttr.TARGET, target); + } + return anchor; + } + + /** + * Get the enclosed name of the package + * + * @param te TypeElement + * @return the name + */ + public String getEnclosingPackageName(TypeElement te) { + + PackageElement encl = configuration.utils.containingPackage(te); + return (encl.isUnnamed()) ? "" : (encl.getQualifiedName() + "."); + } + + public boolean getMemberDetailsListPrinted() { + return memberDetailsListPrinted; + } + + /** + * Print the frames version of the Html file header. + * Called only when generating an HTML frames file. + * + * @param title Title of this HTML document + * @param configuration the configuration object + * @param body the body content tree to be added to the HTML document + */ + public void printFramesDocument(String title, ConfigurationImpl configuration, + HtmlTree body) throws IOException { + Content htmlDocType = configuration.isOutputHtml5() + ? DocType.HTML5 + : DocType.TRANSITIONAL; + Content htmlComment = new Comment(configuration.getText("doclet.New_Page")); + Content head = new HtmlTree(HtmlTag.HEAD); + head.addContent(getGeneratedBy(!configuration.notimestamp)); + Content windowTitle = HtmlTree.TITLE(new StringContent(title)); + head.addContent(windowTitle); + Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, + (configuration.charset.length() > 0) ? + configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET); + head.addContent(meta); + head.addContent(getStyleSheetProperties(configuration)); + head.addContent(getFramesJavaScript()); + Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), + head, body); + Content htmlDocument = new HtmlDocument(htmlDocType, + htmlComment, htmlTree); + write(htmlDocument); + } + + /** + * Returns a link to the stylesheet file. + * + * @return an HtmlTree for the lINK tag which provides the stylesheet location + */ + public HtmlTree getStyleSheetProperties(ConfigurationImpl configuration) { + String stylesheetfile = configuration.stylesheetfile; + DocPath stylesheet; + if (stylesheetfile.isEmpty()) { + stylesheet = DocPaths.STYLESHEET; + } else { + DocFile file = DocFile.createFileForInput(configuration, stylesheetfile); + stylesheet = DocPath.create(file.getName()); + } + HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(stylesheet).getPath(), + "Style"); + return link; + } + + protected Comment getGeneratedBy(boolean timestamp) { + String text = "Generated by javadoc"; // marker string, deliberately not localized + if (timestamp) { + Calendar calendar = new GregorianCalendar(TimeZone.getDefault()); + Date today = calendar.getTime(); + text += " ("+ configuration.getDocletSpecificBuildDate() + ") on " + today; + } + return new Comment(text); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java new file mode 100644 index 00000000000..8f995c7c772 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlDocument.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; +import java.util.*; + +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; + +/** + * Class for generating an HTML document for javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class HtmlDocument extends Content { + + private List docContent = Collections.emptyList(); + + /** + * Constructor to construct an HTML document. + * + * @param docType document type for the HTML document + * @param docComment comment for the document + * @param htmlTree HTML tree of the document + */ + public HtmlDocument(Content docType, Content docComment, Content htmlTree) { + docContent = new ArrayList<>(); + addContent(nullCheck(docType)); + addContent(nullCheck(docComment)); + addContent(nullCheck(htmlTree)); + } + + /** + * Constructor to construct an HTML document. + * + * @param docType document type for the HTML document + * @param htmlTree HTML tree of the document + */ + public HtmlDocument(Content docType, Content htmlTree) { + docContent = new ArrayList<>(); + addContent(nullCheck(docType)); + addContent(nullCheck(htmlTree)); + } + + /** + * Adds content for the HTML document. + * + * @param htmlContent html content to be added + */ + public final void addContent(Content htmlContent) { + if (htmlContent.isValid()) + docContent.add(htmlContent); + } + + /** + * This method is not supported by the class. + * + * @param stringContent string content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(String stringContent) { + throw new DocletAbortException("not supported"); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return (docContent.isEmpty()); + } + + /** + * {@inheritDoc} + */ + public boolean write(Writer out, boolean atNewline) throws IOException { + for (Content c : docContent) + atNewline = c.write(out, atNewline); + return atNewline; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java new file mode 100644 index 00000000000..ccaf4769276 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +/** + * Enum representing HTML styles. The name map to values in the CSS file. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public enum HtmlStyle { + aboutLanguage, + activeTableTab, + altColor, + bar, + block, + blockList, + blockListLast, + bottomNav, + circle, + classUseContainer, + colFirst, + colLast, + colOne, + constantsSummary, + constantValuesContainer, + contentContainer, + deprecatedContent, + deprecatedLabel, + deprecatedSummary, + deprecationComment, + description, + descfrmTypeLabel, + details, + docSummary, + emphasizedPhrase, + fixedNav, + header, + horizontal, + footer, + indexContainer, + indexNav, + inheritance, + interfaceName, + leftContainer, + leftTop, + leftBottom, + legalCopy, + mainContainer, + memberNameLabel, + memberNameLink, + memberSummary, + nameValue, + navBarCell1Rev, + navList, + navListSearch, + overrideSpecifyLabel, + overviewSummary, + packageHierarchyLabel, + paramLabel, + returnLabel, + rightContainer, + rightIframe, + rowColor, + searchTagLink, + seeLabel, + serializedFormContainer, + simpleTagLabel, + skipNav, + sourceContainer, + sourceLineNo, + subNav, + subNavList, + subTitle, + summary, + tabEnd, + tableTab, + throwsLabel, + title, + topNav, + typeNameLabel, + typeNameLink, + typeSummary, + useSummary +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTag.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTag.java new file mode 100644 index 00000000000..3bc64ea959f --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTag.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import jdk.javadoc.internal.doclets.toolkit.util.Utils; + +/** + * Enum representing HTML tags. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public enum HtmlTag { + A(BlockType.INLINE, EndTag.END), + BLOCKQUOTE, + BODY(BlockType.OTHER, EndTag.END), + BR(BlockType.INLINE, EndTag.NOEND), + CAPTION, + CENTER(HtmlVersion.HTML4), + CODE(BlockType.INLINE, EndTag.END), + DD, + DIR(HtmlVersion.HTML4), + DIV, + DL, + DT, + EM(BlockType.INLINE, EndTag.END), + FONT(HtmlVersion.HTML4, BlockType.INLINE, EndTag.END), + FOOTER(HtmlVersion.HTML5), + H1, + H2, + H3, + H4, + H5, + H6, + HEAD(BlockType.OTHER, EndTag.END), + HEADER(HtmlVersion.HTML5), + HR(BlockType.BLOCK, EndTag.NOEND), + HTML(BlockType.OTHER, EndTag.END), + I(BlockType.INLINE, EndTag.END), + IFRAME(BlockType.OTHER, EndTag.END), + IMG(BlockType.INLINE, EndTag.NOEND), + INPUT(BlockType.BLOCK, EndTag.NOEND), + LI, + LISTING, + LINK(BlockType.OTHER, EndTag.NOEND), + MAIN(HtmlVersion.HTML5), + MENU, + META(BlockType.OTHER, EndTag.NOEND), + NAV(HtmlVersion.HTML5), + NOSCRIPT(BlockType.OTHER, EndTag.END), + OL, + P, + PRE, + SCRIPT(BlockType.OTHER, EndTag.END), + SECTION(HtmlVersion.HTML5), + SMALL(BlockType.INLINE, EndTag.END), + SPAN(BlockType.INLINE, EndTag.END), + STRONG(BlockType.INLINE, EndTag.END), + SUB(BlockType.INLINE, EndTag.END), + TABLE, + TBODY, + TD, + TH, + TITLE(BlockType.OTHER, EndTag.END), + TR, + TT(HtmlVersion.HTML4, BlockType.INLINE, EndTag.END), + UL; + + public final BlockType blockType; + public final EndTag endTag; + public final String value; + public final HtmlVersion htmlVersion; + + /** + * Enum representing the type of HTML element. + */ + public static enum BlockType { + BLOCK, + INLINE, + OTHER + } + + /** + * Enum representing HTML end tag requirement. + */ + public static enum EndTag { + END, + NOEND + } + + HtmlTag() { + this(HtmlVersion.ALL, BlockType.BLOCK, EndTag.END); + } + + HtmlTag(HtmlVersion htmlVersion) { + this(htmlVersion, BlockType.BLOCK, EndTag.END); + } + + HtmlTag(BlockType blockType, EndTag endTag ) { + this(HtmlVersion.ALL, blockType, endTag); + } + + HtmlTag(HtmlVersion htmlVersion, BlockType blockType, EndTag endTag ) { + this.htmlVersion = htmlVersion; + this.blockType = blockType; + this.endTag = endTag; + this.value = Utils.toLowerCase(name()); + } + + /** + * Returns true if the end tag is required. This is specific to the standard + * doclet and does not exactly resemble the W3C specifications. + * + * @return true if end tag needs to be displayed else return false + */ + public boolean endTagRequired() { + return (endTag == EndTag.END); + } + + /** + * Returns true if the tag is allowed in the output HTML version of this javadoc run. + * + * @param htmlVer the output HTML version for this javadoc run + * @return true if the tag is allowed + */ + public boolean allowTag(HtmlVersion htmlVer) { + return (this.htmlVersion == HtmlVersion.ALL || this.htmlVersion == htmlVer); + } + + public String toString() { + return value; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java new file mode 100644 index 00000000000..93b9071dcf7 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java @@ -0,0 +1,978 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr.Role; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +/** + * Class for generating HTML tree for javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class HtmlTree extends Content { + + private HtmlTag htmlTag; + private Map attrs = Collections.emptyMap(); + private List content = Collections.emptyList(); + public static final Content EMPTY = new StringContent(""); + + /** + * Constructor to construct HtmlTree object. + * + * @param tag HTML tag for the HtmlTree object + */ + public HtmlTree(HtmlTag tag) { + htmlTag = nullCheck(tag); + } + + /** + * Constructor to construct HtmlTree object. + * + * @param tag HTML tag for the HtmlTree object + * @param contents contents to be added to the tree + */ + public HtmlTree(HtmlTag tag, Content... contents) { + this(tag); + for (Content content: contents) + addContent(content); + } + + /** + * Adds an attribute for the HTML tag. + * + * @param attrName name of the attribute + * @param attrValue value of the attribute + */ + public void addAttr(HtmlAttr attrName, String attrValue) { + if (attrs.isEmpty()) + attrs = new LinkedHashMap<>(3); + attrs.put(nullCheck(attrName), escapeHtmlChars(attrValue)); + } + + public void setTitle(Content body) { + addAttr(HtmlAttr.TITLE, stripHtml(body)); + } + + public void setRole(Role role) { + addAttr(HtmlAttr.ROLE, role.toString()); + } + + /** + * Adds a style for the HTML tag. + * + * @param style style to be added + */ + public void addStyle(HtmlStyle style) { + addAttr(HtmlAttr.CLASS, style.toString()); + } + + /** + * Adds content for the HTML tag. + * + * @param tagContent tag content to be added + */ + public void addContent(Content tagContent) { + if (tagContent instanceof ContentBuilder) { + for (Content content: ((ContentBuilder)tagContent).contents) { + addContent(content); + } + } + else if (tagContent == HtmlTree.EMPTY || tagContent.isValid()) { + if (content.isEmpty()) + content = new ArrayList<>(); + content.add(tagContent); + } + } + + /** + * This method adds a string content to the htmltree. If the last content member + * added is a StringContent, append the string to that StringContent or else + * create a new StringContent and add it to the html tree. + * + * @param stringContent string content that needs to be added + */ + public void addContent(String stringContent) { + if (!content.isEmpty()) { + Content lastContent = content.get(content.size() - 1); + if (lastContent instanceof StringContent) + lastContent.addContent(stringContent); + else + addContent(new StringContent(stringContent)); + } + else + addContent(new StringContent(stringContent)); + } + + public int charCount() { + int n = 0; + for (Content c : content) + n += c.charCount(); + return n; + } + + /** + * Given a string, escape all special html characters and + * return the result. + * + * @param s The string to check. + * @return the original string with all of the HTML characters escaped. + */ + private static String escapeHtmlChars(String s) { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + // only start building a new string if we need to + case '<': case '>': case '&': + StringBuilder sb = new StringBuilder(s.substring(0, i)); + for ( ; i < s.length(); i++) { + ch = s.charAt(i); + switch (ch) { + case '<': sb.append("<"); break; + case '>': sb.append(">"); break; + case '&': sb.append("&"); break; + default: sb.append(ch); break; + } + } + return sb.toString(); + } + } + return s; + } + + /** + * A set of ASCII URI characters to be left unencoded. + */ + public static final BitSet NONENCODING_CHARS = new BitSet(256); + + static { + // alphabetic characters + for (int i = 'a'; i <= 'z'; i++) { + NONENCODING_CHARS.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + NONENCODING_CHARS.set(i); + } + // numeric characters + for (int i = '0'; i <= '9'; i++) { + NONENCODING_CHARS.set(i); + } + // Reserved characters as per RFC 3986. These are set of delimiting characters. + String noEnc = ":/?#[]@!$&'()*+,;="; + // Unreserved characters as per RFC 3986 which should not be percent encoded. + noEnc += "-._~"; + for (int i = 0; i < noEnc.length(); i++) { + NONENCODING_CHARS.set(noEnc.charAt(i)); + } + } + + private static String encodeURL(String url) { + StringBuilder sb = new StringBuilder(); + for (byte c : url.getBytes(Charset.forName("UTF-8"))) { + if (NONENCODING_CHARS.get(c & 0xFF)) { + sb.append((char) c); + } else { + sb.append(String.format("%%%02X", c & 0xFF)); + } + } + return sb.toString(); + } + + /** + * Generates an HTML anchor tag. + * + * @param ref reference url for the anchor tag + * @param body content for the anchor tag + * @return an HtmlTree object + */ + public static HtmlTree A(String ref, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.A, nullCheck(body)); + htmltree.addAttr(HtmlAttr.HREF, encodeURL(ref)); + return htmltree; + } + + /** + * Generates an HTML anchor tag with an id or a name attribute and content. + * + * @param htmlVersion the version of the generated HTML + * @param attr name or id attribute for the anchor tag + * @param body content for the anchor tag + * @return an HtmlTree object + */ + public static HtmlTree A(HtmlVersion htmlVersion, String attr, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.A); + htmltree.addAttr((htmlVersion == HtmlVersion.HTML4) + ? HtmlAttr.NAME + : HtmlAttr.ID, + nullCheck(attr)); + htmltree.addContent(nullCheck(body)); + return htmltree; + } + + /** + * Generates an HTML anchor tag with id attribute and a body. + * + * @param id id for the anchor tag + * @param body body for the anchor tag + * @return an HtmlTree object + */ + public static HtmlTree A_ID(String id, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.A); + htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); + htmltree.addContent(nullCheck(body)); + return htmltree; + } + + /** + * Generates a CAPTION tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the CAPTION tag + */ + public static HtmlTree CAPTION(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.CAPTION, nullCheck(body)); + return htmltree; + } + + /** + * Generates a CODE tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the CODE tag + */ + public static HtmlTree CODE(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.CODE, nullCheck(body)); + return htmltree; + } + + /** + * Generates a DD tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the DD tag + */ + public static HtmlTree DD(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.DD, nullCheck(body)); + return htmltree; + } + + /** + * Generates a DL tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the DL tag + */ + public static HtmlTree DL(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.DL, nullCheck(body)); + return htmltree; + } + + /** + * Generates a DIV tag with the style class attributes. It also encloses + * a content. + * + * @param styleClass stylesheet class for the tag + * @param body content for the tag + * @return an HtmlTree object for the DIV tag + */ + public static HtmlTree DIV(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.DIV, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a DIV tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the DIV tag + */ + public static HtmlTree DIV(Content body) { + return DIV(null, body); + } + + /** + * Generates a DT tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the DT tag + */ + public static HtmlTree DT(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.DT, nullCheck(body)); + return htmltree; + } + + /** + * Generates a FOOTER tag with role attribute. + * + * @return an HtmlTree object for the FOOTER tag + */ + public static HtmlTree FOOTER() { + HtmlTree htmltree = new HtmlTree(HtmlTag.FOOTER); + htmltree.setRole(Role.CONTENTINFO); + return htmltree; + } + + /** + * Generates a HEADER tag with role attribute. + * + * @return an HtmlTree object for the HEADER tag + */ + public static HtmlTree HEADER() { + HtmlTree htmltree = new HtmlTree(HtmlTag.HEADER); + htmltree.setRole(Role.BANNER); + return htmltree; + } + + /** + * Generates a heading tag (h1 to h6) with the title and style class attributes. It also encloses + * a content. + * + * @param headingTag the heading tag to be generated + * @param printTitle true if title for the tag needs to be printed else false + * @param styleClass stylesheet class for the tag + * @param body content for the tag + * @return an HtmlTree object for the tag + */ + public static HtmlTree HEADING(HtmlTag headingTag, boolean printTitle, + HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(headingTag, nullCheck(body)); + if (printTitle) + htmltree.setTitle(body); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a heading tag (h1 to h6) with style class attribute. It also encloses + * a content. + * + * @param headingTag the heading tag to be generated + * @param styleClass stylesheet class for the tag + * @param body content for the tag + * @return an HtmlTree object for the tag + */ + public static HtmlTree HEADING(HtmlTag headingTag, HtmlStyle styleClass, Content body) { + return HEADING(headingTag, false, styleClass, body); + } + + /** + * Generates a heading tag (h1 to h6) with the title attribute. It also encloses + * a content. + * + * @param headingTag the heading tag to be generated + * @param printTitle true if the title for the tag needs to be printed else false + * @param body content for the tag + * @return an HtmlTree object for the tag + */ + public static HtmlTree HEADING(HtmlTag headingTag, boolean printTitle, Content body) { + return HEADING(headingTag, printTitle, null, body); + } + + /** + * Generates a heading tag (h1 to h6) with some content. + * + * @param headingTag the heading tag to be generated + * @param body content for the tag + * @return an HtmlTree object for the tag + */ + public static HtmlTree HEADING(HtmlTag headingTag, Content body) { + return HEADING(headingTag, false, null, body); + } + + /** + * Generates an HTML tag with lang attribute. It also adds head and body + * content to the HTML tree. + * + * @param lang language for the HTML document + * @param head head for the HTML tag + * @param body body for the HTML tag + * @return an HtmlTree object for the HTML tag + */ + public static HtmlTree HTML(String lang, Content head, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.HTML, nullCheck(head), nullCheck(body)); + htmltree.addAttr(HtmlAttr.LANG, nullCheck(lang)); + return htmltree; + } + + /** + * Generates a IFRAME tag. + * + * @param src the url of the document to be shown in the frame + * @param name specifies the name of the frame + * @param title the title for the frame + * @return an HtmlTree object for the IFRAME tag + */ + public static HtmlTree IFRAME(String src, String name, String title) { + HtmlTree htmltree = new HtmlTree(HtmlTag.IFRAME); + htmltree.addAttr(HtmlAttr.SRC, nullCheck(src)); + htmltree.addAttr(HtmlAttr.NAME, nullCheck(name)); + htmltree.addAttr(HtmlAttr.TITLE, nullCheck(title)); + return htmltree; + } + + /** + * Generates a INPUT tag with some id. + * + * @param type the type of input + * @param id id for the tag + * @return an HtmlTree object for the INPUT tag + */ + public static HtmlTree INPUT(String type, String id) { + HtmlTree htmltree = new HtmlTree(HtmlTag.INPUT); + htmltree.addAttr(HtmlAttr.TYPE, nullCheck(type)); + htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); + htmltree.addAttr(HtmlAttr.VALUE, " "); + htmltree.addAttr(HtmlAttr.DISABLED, "disabled"); + return htmltree; + } + + /** + * Generates a LI tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the LI tag + */ + public static HtmlTree LI(Content body) { + return LI(null, body); + } + + /** + * Generates a LI tag with some content. + * + * @param styleClass style for the tag + * @param body content for the tag + * @return an HtmlTree object for the LI tag + */ + public static HtmlTree LI(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.LI, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a LINK tag with the rel, type, href and title attributes. + * + * @param rel relevance of the link + * @param type type of link + * @param href the path for the link + * @param title title for the link + * @return an HtmlTree object for the LINK tag + */ + public static HtmlTree LINK(String rel, String type, String href, String title) { + HtmlTree htmltree = new HtmlTree(HtmlTag.LINK); + htmltree.addAttr(HtmlAttr.REL, nullCheck(rel)); + htmltree.addAttr(HtmlAttr.TYPE, nullCheck(type)); + htmltree.addAttr(HtmlAttr.HREF, nullCheck(href)); + htmltree.addAttr(HtmlAttr.TITLE, nullCheck(title)); + return htmltree; + } + + /** + * Generates a MAIN tag with role attribute. + * + * @return an HtmlTree object for the MAIN tag + */ + public static HtmlTree MAIN() { + HtmlTree htmltree = new HtmlTree(HtmlTag.MAIN); + htmltree.setRole(Role.MAIN); + return htmltree; + } + + /** + * Generates a MAIN tag with role attribute and some content. + * + * @param body content of the MAIN tag + * @return an HtmlTree object for the MAIN tag + */ + public static HtmlTree MAIN(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.MAIN, nullCheck(body)); + htmltree.setRole(Role.MAIN); + return htmltree; + } + + /** + * Generates a MAIN tag with role attribute, style attribute and some content. + * + * @param styleClass style of the MAIN tag + * @param body content of the MAIN tag + * @return an HtmlTree object for the MAIN tag + */ + public static HtmlTree MAIN(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = HtmlTree.MAIN(body); + if (styleClass != null) { + htmltree.addStyle(styleClass); + } + return htmltree; + } + + /** + * Generates a META tag with the http-equiv, content and charset attributes. + * + * @param httpEquiv http equiv attribute for the META tag + * @param content type of content + * @param charSet character set used + * @return an HtmlTree object for the META tag + */ + public static HtmlTree META(String httpEquiv, String content, String charSet) { + HtmlTree htmltree = new HtmlTree(HtmlTag.META); + String contentCharset = content + "; charset=" + charSet; + htmltree.addAttr(HtmlAttr.HTTP_EQUIV, nullCheck(httpEquiv)); + htmltree.addAttr(HtmlAttr.CONTENT, contentCharset); + return htmltree; + } + + /** + * Generates a META tag with the name and content attributes. + * + * @param name name attribute + * @param content type of content + * @return an HtmlTree object for the META tag + */ + public static HtmlTree META(String name, String content) { + HtmlTree htmltree = new HtmlTree(HtmlTag.META); + htmltree.addAttr(HtmlAttr.NAME, nullCheck(name)); + htmltree.addAttr(HtmlAttr.CONTENT, nullCheck(content)); + return htmltree; + } + + /** + * Generates a NAV tag with the role attribute. + * + * @return an HtmlTree object for the NAV tag + */ + public static HtmlTree NAV() { + HtmlTree htmltree = new HtmlTree(HtmlTag.NAV); + htmltree.setRole(Role.NAVIGATION); + return htmltree; + } + + /** + * Generates a NOSCRIPT tag with some content. + * + * @param body content of the noscript tag + * @return an HtmlTree object for the NOSCRIPT tag + */ + public static HtmlTree NOSCRIPT(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.NOSCRIPT, nullCheck(body)); + return htmltree; + } + + /** + * Generates a P tag with some content. + * + * @param body content of the Paragraph tag + * @return an HtmlTree object for the P tag + */ + public static HtmlTree P(Content body) { + return P(null, body); + } + + /** + * Generates a P tag with some content. + * + * @param styleClass style of the Paragraph tag + * @param body content of the Paragraph tag + * @return an HtmlTree object for the P tag + */ + public static HtmlTree P(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.P, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a SCRIPT tag with the type and src attributes. + * + * @param type type of link + * @param src the path for the script + * @return an HtmlTree object for the SCRIPT tag + */ + public static HtmlTree SCRIPT(String src) { + HtmlTree htmltree = HtmlTree.SCRIPT(); + htmltree.addAttr(HtmlAttr.SRC, nullCheck(src)); + return htmltree; + } + + /** + * Generates a SCRIPT tag with the type attribute. + * + * @return an HtmlTree object for the SCRIPT tag + */ + public static HtmlTree SCRIPT() { + HtmlTree htmltree = new HtmlTree(HtmlTag.SCRIPT); + htmltree.addAttr(HtmlAttr.TYPE, "text/javascript"); + return htmltree; + } + + /** + * Generates a SECTION tag with role attribute. + * + * @return an HtmlTree object for the SECTION tag + */ + public static HtmlTree SECTION() { + HtmlTree htmltree = new HtmlTree(HtmlTag.SECTION); + htmltree.setRole(Role.REGION); + return htmltree; + } + + /** + * Generates a SECTION tag with role attribute and some content. + * + * @param body content of the section tag + * @return an HtmlTree object for the SECTION tag + */ + public static HtmlTree SECTION(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.SECTION, nullCheck(body)); + htmltree.setRole(Role.REGION); + return htmltree; + } + + /** + * Generates a SMALL tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the SMALL tag + */ + public static HtmlTree SMALL(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.SMALL, nullCheck(body)); + return htmltree; + } + + /** + * Generates a SPAN tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the SPAN tag + */ + public static HtmlTree SPAN(Content body) { + return SPAN(null, body); + } + + /** + * Generates a SPAN tag with style class attribute and some content. + * + * @param styleClass style class for the tag + * @param body content for the tag + * @return an HtmlTree object for the SPAN tag + */ + public static HtmlTree SPAN(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.SPAN, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a SPAN tag with id and style class attributes. It also encloses + * a content. + * + * @param id the id for the tag + * @param styleClass stylesheet class for the tag + * @param body content for the tag + * @return an HtmlTree object for the SPAN tag + */ + public static HtmlTree SPAN(String id, HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.SPAN, nullCheck(body)); + htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a Table tag with style class and summary attributes and some content. + * + * @param styleClass style of the table + * @param summary summary for the table + * @param body content for the table + * @return an HtmlTree object for the TABLE tag + */ + public static HtmlTree TABLE(HtmlStyle styleClass, String summary, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TABLE, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + htmltree.addAttr(HtmlAttr.SUMMARY, nullCheck(summary)); + return htmltree; + } + + /** + * Generates a Table tag with style class attribute and some content. + * + * @param styleClass style of the table + * @param body content for the table + * @return an HtmlTree object for the TABLE tag + */ + public static HtmlTree TABLE(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TABLE, nullCheck(body)); + if (styleClass != null) { + htmltree.addStyle(styleClass); + } + return htmltree; + } + + /** + * Generates a TD tag with style class attribute and some content. + * + * @param styleClass style for the tag + * @param body content for the tag + * @return an HtmlTree object for the TD tag + */ + public static HtmlTree TD(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TD, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + return htmltree; + } + + /** + * Generates a TD tag for an HTML table with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the TD tag + */ + public static HtmlTree TD(Content body) { + return TD(null, body); + } + + /** + * Generates a TH tag with style class and scope attributes and some content. + * + * @param styleClass style for the tag + * @param scope scope of the tag + * @param body content for the tag + * @return an HtmlTree object for the TH tag + */ + public static HtmlTree TH(HtmlStyle styleClass, String scope, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TH, nullCheck(body)); + if (styleClass != null) + htmltree.addStyle(styleClass); + htmltree.addAttr(HtmlAttr.SCOPE, nullCheck(scope)); + return htmltree; + } + + /** + * Generates a TH tag with scope attribute and some content. + * + * @param scope scope of the tag + * @param body content for the tag + * @return an HtmlTree object for the TH tag + */ + public static HtmlTree TH(String scope, Content body) { + return TH(null, scope, body); + } + + /** + * Generates a TITLE tag with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the TITLE tag + */ + public static HtmlTree TITLE(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, nullCheck(body)); + return htmltree; + } + + /** + * Generates a TR tag for an HTML table with some content. + * + * @param body content for the tag + * @return an HtmlTree object for the TR tag + */ + public static HtmlTree TR(Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.TR, nullCheck(body)); + return htmltree; + } + + /** + * Generates a UL tag with the style class attribute and some content. + * + * @param styleClass style for the tag + * @param body content for the tag + * @return an HtmlTree object for the UL tag + */ + public static HtmlTree UL(HtmlStyle styleClass, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.UL, nullCheck(body)); + htmltree.addStyle(nullCheck(styleClass)); + return htmltree; + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return (!hasContent() && !hasAttrs()); + } + + /** + * Returns true if the HTML tree has content. + * + * @return true if the HTML tree has content else return false + */ + public boolean hasContent() { + return (!content.isEmpty()); + } + + /** + * Returns true if the HTML tree has attributes. + * + * @return true if the HTML tree has attributes else return false + */ + public boolean hasAttrs() { + return (!attrs.isEmpty()); + } + + /** + * Returns true if the HTML tree has a specific attribute. + * + * @param attrName name of the attribute to check within the HTML tree + * @return true if the HTML tree has the specified attribute else return false + */ + public boolean hasAttr(HtmlAttr attrName) { + return (attrs.containsKey(attrName)); + } + + /** + * Returns true if the HTML tree is valid. This check is more specific to + * standard doclet and not exactly similar to W3C specifications. But it + * ensures HTML validation. + * + * @return true if the HTML tree is valid + */ + public boolean isValid() { + switch (htmlTag) { + case A : + return (hasAttr(HtmlAttr.NAME) || hasAttr(HtmlAttr.ID) || (hasAttr(HtmlAttr.HREF) && hasContent())); + case BR : + return (!hasContent() && (!hasAttrs() || hasAttr(HtmlAttr.CLEAR))); + case IFRAME : + return (hasAttr(HtmlAttr.SRC) && !hasContent()); + case HR : + case INPUT: + return (!hasContent()); + case IMG : + return (hasAttr(HtmlAttr.SRC) && hasAttr(HtmlAttr.ALT) && !hasContent()); + case LINK : + return (hasAttr(HtmlAttr.HREF) && !hasContent()); + case META : + return (hasAttr(HtmlAttr.CONTENT) && !hasContent()); + case SCRIPT : + return ((hasAttr(HtmlAttr.TYPE) && hasAttr(HtmlAttr.SRC) && !hasContent()) || + (hasAttr(HtmlAttr.TYPE) && hasContent())); + default : + return hasContent(); + } + } + + /** + * Returns true if the element is an inline element. + * + * @return true if the HTML tag is an inline element + */ + public boolean isInline() { + return (htmlTag.blockType == HtmlTag.BlockType.INLINE); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean write(Writer out, boolean atNewline) throws IOException { + if (!isInline() && !atNewline) + out.write(DocletConstants.NL); + String tagString = htmlTag.toString(); + out.write("<"); + out.write(tagString); + Iterator iterator = attrs.keySet().iterator(); + HtmlAttr key; + String value; + while (iterator.hasNext()) { + key = iterator.next(); + value = attrs.get(key); + out.write(" "); + out.write(key.toString()); + if (!value.isEmpty()) { + out.write("=\""); + out.write(value); + out.write("\""); + } + } + out.write(">"); + boolean nl = false; + for (Content c : content) + nl = c.write(out, nl); + if (htmlTag.endTagRequired()) { + out.write(""); + } + if (!isInline()) { + out.write(DocletConstants.NL); + return true; + } else { + return false; + } + } + + /** + * Given a Content node, strips all html characters and + * return the result. + * + * @param body The content node to check. + * @return the plain text from the content node + * + */ + private static String stripHtml(Content body) { + String rawString = body.toString(); + // remove HTML tags + rawString = rawString.replaceAll("\\<.*?>", " "); + // consolidate multiple spaces between a word to a single space + rawString = rawString.replaceAll("\\b\\s{2,}\\b", " "); + // remove extra whitespaces + return rawString.trim(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlVersion.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlVersion.java new file mode 100644 index 00000000000..c3b3e6ded5e --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlVersion.java @@ -0,0 +1,37 @@ +/* + * 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.javadoc.internal.doclets.formats.html.markup; + +/** + * Enum representing the version of HTML generated by javadoc. + * + * @author Bhavesh Patel + */ +public enum HtmlVersion { + HTML4, + HTML5, + ALL +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java new file mode 100644 index 00000000000..589f342ef79 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.*; +import java.util.*; + +import jdk.javadoc.internal.doclets.toolkit.Configuration; +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocFile; +import jdk.javadoc.internal.doclets.toolkit.util.DocPath; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; +import jdk.javadoc.internal.doclets.toolkit.util.MethodTypes; + + +/** + * Class for the Html format code generation. + * Initializes PrintWriter with FileWriter, to enable print + * related methods to generate the code to the named File through FileWriter. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Atul M Dambalkar + * @author Bhavesh Patel (Modified) + */ +public class HtmlWriter { + + /** + * The window title of this file + */ + protected String winTitle; + + /** + * The configuration + */ + protected Configuration configuration; + + /** + * The flag to indicate whether a member details list is printed or not. + */ + protected boolean memberDetailsListPrinted; + + /** + * Header for tables displaying packages and description.. + */ + protected final List packageTableHeader; + + /** + * Summary for use tables displaying class and package use. + */ + protected final String useTableSummary; + + /** + * Column header for class docs displaying Modifier and Type header. + */ + protected final String modifierTypeHeader; + + public final Content overviewLabel; + + public final Content defaultPackageLabel; + + public final Content packageLabel; + + public final Content useLabel; + + public final Content prevLabel; + + public final Content nextLabel; + + public final Content prevclassLabel; + + public final Content nextclassLabel; + + public final Content summaryLabel; + + public final Content detailLabel; + + public final Content framesLabel; + + public final Content noframesLabel; + + public final Content treeLabel; + + public final Content classLabel; + + public final Content deprecatedLabel; + + public final Content deprecatedPhrase; + + public final Content allclassesLabel; + + public final Content allpackagesLabel; + + public final Content indexLabel; + + public final Content helpLabel; + + public final Content seeLabel; + + public final Content descriptionLabel; + + public final Content prevpackageLabel; + + public final Content nextpackageLabel; + + public final Content packagesLabel; + + public final Content methodDetailsLabel; + + public final Content annotationTypeDetailsLabel; + + public final Content fieldDetailsLabel; + + public final Content propertyDetailsLabel; + + public final Content constructorDetailsLabel; + + public final Content enumConstantsDetailsLabel; + + public final Content specifiedByLabel; + + public final Content overridesLabel; + + public final Content descfrmClassLabel; + + public final Content descfrmInterfaceLabel; + + private final Writer writer; + + protected Content script; + + + /** + * Constructor. + * + * @param path The directory path to be created for this file + * or null if none to be created. + * @exception IOException Exception raised by the FileWriter is passed on + * to next level. + * @exception UnsupportedEncodingException Exception raised by the + * OutputStreamWriter is passed on to next level. + */ + public HtmlWriter(Configuration configuration, DocPath path) + throws IOException, UnsupportedEncodingException { + writer = DocFile.createFileForOutput(configuration, path).openWriter(); + this.configuration = configuration; + this.memberDetailsListPrinted = false; + packageTableHeader = new ArrayList<>(); + packageTableHeader.add(configuration.getText("doclet.Package")); + packageTableHeader.add(configuration.getText("doclet.Description")); + useTableSummary = configuration.getText("doclet.Use_Table_Summary", + configuration.getText("doclet.packages")); + modifierTypeHeader = configuration.getText("doclet.0_and_1", + configuration.getText("doclet.Modifier"), + configuration.getText("doclet.Type")); + overviewLabel = getResource("doclet.Overview"); + defaultPackageLabel = new StringContent(DocletConstants.DEFAULT_PACKAGE_NAME); + packageLabel = getResource("doclet.Package"); + useLabel = getResource("doclet.navClassUse"); + prevLabel = getResource("doclet.Prev"); + nextLabel = getResource("doclet.Next"); + prevclassLabel = getNonBreakResource("doclet.Prev_Class"); + nextclassLabel = getNonBreakResource("doclet.Next_Class"); + summaryLabel = getResource("doclet.Summary"); + detailLabel = getResource("doclet.Detail"); + framesLabel = getResource("doclet.Frames"); + noframesLabel = getNonBreakResource("doclet.No_Frames"); + treeLabel = getResource("doclet.Tree"); + classLabel = getResource("doclet.Class"); + deprecatedLabel = getResource("doclet.navDeprecated"); + deprecatedPhrase = getResource("doclet.Deprecated"); + allclassesLabel = getNonBreakResource("doclet.All_Classes"); + allpackagesLabel = getNonBreakResource("doclet.All_Packages"); + indexLabel = getResource("doclet.Index"); + helpLabel = getResource("doclet.Help"); + seeLabel = getResource("doclet.See"); + descriptionLabel = getResource("doclet.Description"); + prevpackageLabel = getNonBreakResource("doclet.Prev_Package"); + nextpackageLabel = getNonBreakResource("doclet.Next_Package"); + packagesLabel = getResource("doclet.Packages"); + methodDetailsLabel = getResource("doclet.Method_Detail"); + annotationTypeDetailsLabel = getResource("doclet.Annotation_Type_Member_Detail"); + fieldDetailsLabel = getResource("doclet.Field_Detail"); + propertyDetailsLabel = getResource("doclet.Property_Detail"); + constructorDetailsLabel = getResource("doclet.Constructor_Detail"); + enumConstantsDetailsLabel = getResource("doclet.Enum_Constant_Detail"); + specifiedByLabel = getResource("doclet.Specified_By"); + overridesLabel = getResource("doclet.Overrides"); + descfrmClassLabel = getResource("doclet.Description_From_Class"); + descfrmInterfaceLabel = getResource("doclet.Description_From_Interface"); + } + + public void write(Content c) throws IOException { + c.write(writer, true); + } + + public void close() throws IOException { + writer.close(); + } + + /** + * Get the configuration string as a content. + * + * @param key the key to look for in the configuration file + * @return a content tree for the text + */ + public Content getResource(String key) { + return configuration.getResource(key); + } + + /** + * Get the configuration string as a content, replacing spaces + * with non-breaking spaces. + * + * @param key the key to look for in the configuration file + * @return a content tree for the text + */ + public Content getNonBreakResource(String key) { + String text = configuration.getText(key); + Content c = configuration.newContent(); + int start = 0; + int p; + while ((p = text.indexOf(" ", start)) != -1) { + c.addContent(text.substring(start, p)); + c.addContent(RawHtml.nbsp); + start = p + 1; + } + c.addContent(text.substring(start)); + return c; + } + + /** + * Get the configuration string as a content. + * + * @param key the key to look for in the configuration file + * @param o string or content argument added to configuration text + * @return a content tree for the text + */ + public Content getResource(String key, Object o) { + return configuration.getResource(key, o); + } + + /** + * Get the configuration string as a content. + * + * @param key the key to look for in the configuration file + * @param o1 string or content argument added to configuration text + * @param o2 string or content argument added to configuration text + * @return a content tree for the text + */ + public Content getResource(String key, Object o0, Object o1) { + return configuration.getResource(key, o0, o1); + } + + /** + * Returns an HtmlTree for the SCRIPT tag. + * + * @return an HtmlTree for the SCRIPT tag + */ + protected HtmlTree getWinTitleScript(){ + HtmlTree script = HtmlTree.SCRIPT(); + if(winTitle != null && winTitle.length() > 0) { + String scriptCode = "" + DocletConstants.NL; + RawHtml scriptContent = new RawHtml(scriptCode); + script.addContent(scriptContent); + } + return script; + } + + /** + * Returns a String with escaped special JavaScript characters. + * + * @param s String that needs to be escaped + * @return a valid escaped JavaScript string + */ + private static String escapeJavaScriptChars(String s) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '\b': + sb.append("\\b"); + break; + case '\t': + sb.append("\\t"); + break; + case '\n': + sb.append("\\n"); + break; + case '\f': + sb.append("\\f"); + break; + case '\r': + sb.append("\\r"); + break; + case '"': + sb.append("\\\""); + break; + case '\'': + sb.append("\\\'"); + break; + case '\\': + sb.append("\\\\"); + break; + default: + if (ch < 32 || ch >= 127) { + sb.append(String.format("\\u%04X", (int)ch)); + } else { + sb.append(ch); + } + break; + } + } + return sb.toString(); + } + + /** + * Returns a content tree for the SCRIPT tag for the main page(index.html). + * + * @return a content for the SCRIPT tag + */ + protected Content getFramesJavaScript() { + HtmlTree script = HtmlTree.SCRIPT(); + String scriptCode = DocletConstants.NL + + " targetPage = \"\" + window.location.search;" + DocletConstants.NL + + " if (targetPage != \"\" && targetPage != \"undefined\")" + DocletConstants.NL + + " targetPage = targetPage.substring(1);" + DocletConstants.NL + + " if (targetPage.indexOf(\":\") != -1 || (targetPage != \"\" && !validURL(targetPage)))" + DocletConstants.NL + + " targetPage = \"undefined\";" + DocletConstants.NL + + " function validURL(url) {" + DocletConstants.NL + + " try {" + DocletConstants.NL + + " url = decodeURIComponent(url);" + DocletConstants.NL + + " }" + DocletConstants.NL + + " catch (error) {" + DocletConstants.NL + + " return false;" + DocletConstants.NL + + " }" + DocletConstants.NL + + " var pos = url.indexOf(\".html\");" + DocletConstants.NL + + " if (pos == -1 || pos != url.length - 5)" + DocletConstants.NL + + " return false;" + DocletConstants.NL + + " var allowNumber = false;" + DocletConstants.NL + + " var allowSep = false;" + DocletConstants.NL + + " var seenDot = false;" + DocletConstants.NL + + " for (var i = 0; i < url.length - 5; i++) {" + DocletConstants.NL + + " var ch = url.charAt(i);" + DocletConstants.NL + + " if ('a' <= ch && ch <= 'z' ||" + DocletConstants.NL + + " 'A' <= ch && ch <= 'Z' ||" + DocletConstants.NL + + " ch == '$' ||" + DocletConstants.NL + + " ch == '_' ||" + DocletConstants.NL + + " ch.charCodeAt(0) > 127) {" + DocletConstants.NL + + " allowNumber = true;" + DocletConstants.NL + + " allowSep = true;" + DocletConstants.NL + + " } else if ('0' <= ch && ch <= '9'" + DocletConstants.NL + + " || ch == '-') {" + DocletConstants.NL + + " if (!allowNumber)" + DocletConstants.NL + + " return false;" + DocletConstants.NL + + " } else if (ch == '/' || ch == '.') {" + DocletConstants.NL + + " if (!allowSep)" + DocletConstants.NL + + " return false;" + DocletConstants.NL + + " allowNumber = false;" + DocletConstants.NL + + " allowSep = false;" + DocletConstants.NL + + " if (ch == '.')" + DocletConstants.NL + + " seenDot = true;" + DocletConstants.NL + + " if (ch == '/' && seenDot)" + DocletConstants.NL + + " return false;" + DocletConstants.NL + + " } else {" + DocletConstants.NL + + " return false;"+ DocletConstants.NL + + " }" + DocletConstants.NL + + " }" + DocletConstants.NL + + " return true;" + DocletConstants.NL + + " }" + DocletConstants.NL; + RawHtml scriptContent = new RawHtml(scriptCode); + script.addContent(scriptContent); + return script; + } + + /** + * Returns an HtmlTree for the BODY tag. + * + * @param includeScript set true if printing windowtitle script + * @param title title for the window + * @return an HtmlTree for the BODY tag + */ + public HtmlTree getBody(boolean includeScript, String title) { + HtmlTree body = new HtmlTree(HtmlTag.BODY); + // Set window title string which is later printed + this.winTitle = title; + // Don't print windowtitle script for overview-frame, allclasses-frame + // and package-frame + if (includeScript) { + this.script = getWinTitleScript(); + body.addContent(script); + Content noScript = HtmlTree.NOSCRIPT( + HtmlTree.DIV(getResource("doclet.No_Script_Message"))); + body.addContent(noScript); + } + return body; + } + + /** + * Generated javascript variables for the document. + * + * @param typeMap map comprising of method and type relationship + * @param methodTypes set comprising of all methods types for this class + */ + public void generateMethodTypesScript(Map typeMap, + Set methodTypes) { + String sep = ""; + StringBuilder vars = new StringBuilder("var methods = {"); + for (Map.Entry entry : typeMap.entrySet()) { + vars.append(sep); + sep = ","; + vars.append("\"") + .append(entry.getKey()) + .append("\":") + .append(entry.getValue()); + } + vars.append("};").append(DocletConstants.NL); + sep = ""; + vars.append("var tabs = {"); + for (MethodTypes entry : methodTypes) { + vars.append(sep); + sep = ","; + vars.append(entry.value()) + .append(":") + .append("[") + .append("\"") + .append(entry.tabId()) + .append("\"") + .append(sep) + .append("\"") + .append(configuration.getText(entry.resourceKey())) + .append("\"]"); + } + vars.append("};") + .append(DocletConstants.NL); + addStyles(HtmlStyle.altColor, vars); + addStyles(HtmlStyle.rowColor, vars); + addStyles(HtmlStyle.tableTab, vars); + addStyles(HtmlStyle.activeTableTab, vars); + script.addContent(new RawHtml(vars.toString())); + } + + /** + * Adds javascript style variables to the document. + * + * @param style style to be added as a javascript variable + * @param vars variable string to which the style variable will be added + */ + public void addStyles(HtmlStyle style, StringBuilder vars) { + vars.append("var ").append(style).append(" = \"").append(style) + .append("\";").append(DocletConstants.NL); + } + + /** + * Returns an HtmlTree for the TITLE tag. + * + * @return an HtmlTree for the TITLE tag + */ + public HtmlTree getTitle() { + HtmlTree title = HtmlTree.TITLE(new StringContent(winTitle)); + return title; + } + + public String codeText(String text) { + return "" + text + ""; + } + + /** + * Return "&nbsp;", non-breaking space. + */ + public Content getSpace() { + return RawHtml.nbsp; + } + + /* + * Returns a header for Modifier and Type column of a table. + */ + public String getModifierTypeHeader() { + return modifierTypeHeader; + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java new file mode 100644 index 00000000000..fe425dd743a --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/RawHtml.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; + +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +/** + * Class for generating raw HTML content to be added to HTML pages of javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class RawHtml extends Content { + + private String rawHtmlContent; + + public static final Content nbsp = new RawHtml(" "); + + /** + * Constructor to construct a RawHtml object. + * + * @param rawHtml raw HTML text to be added + */ + public RawHtml(String rawHtml) { + rawHtmlContent = nullCheck(rawHtml); + } + + /** + * This method is not supported by the class. + * + * @param content content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(Content content) { + throw new DocletAbortException("not supported"); + } + + /** + * This method is not supported by the class. + * + * @param stringContent string content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + public void addContent(String stringContent) { + throw new DocletAbortException("not supported"); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return rawHtmlContent.isEmpty(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return rawHtmlContent; + } + + private enum State { TEXT, ENTITY, TAG, STRING } + + @Override + public int charCount() { + return charCount(rawHtmlContent); + } + + static int charCount(String htmlText) { + State state = State.TEXT; + int count = 0; + for (int i = 0; i < htmlText.length(); i++) { + char c = htmlText.charAt(i); + switch (state) { + case TEXT: + switch (c) { + case '<': + state = State.TAG; + break; + case '&': + state = State.ENTITY; + count++; + break; + default: + count++; + } + break; + + case ENTITY: + if (!Character.isLetterOrDigit(c)) + state = State.TEXT; + break; + + case TAG: + switch (c) { + case '"': + state = State.STRING; + break; + case '>': + state = State.TEXT; + break; + } + break; + + case STRING: + switch (c) { + case '"': + state = State.TAG; + break; + } + } + } + return count; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean write(Writer out, boolean atNewline) throws IOException { + out.write(rawHtmlContent); + return rawHtmlContent.endsWith(DocletConstants.NL); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/StringContent.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/StringContent.java new file mode 100644 index 00000000000..6b7c7dde2ed --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/StringContent.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2010, 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.javadoc.internal.doclets.formats.html.markup; + +import java.io.IOException; +import java.io.Writer; + +import jdk.javadoc.internal.doclets.toolkit.Content; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; +import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; + +/** + * Class for generating string content for HTML tags of javadoc output. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @author Bhavesh Patel + */ +public class StringContent extends Content { + + private StringBuilder stringContent; + + /** + * Constructor to construct StringContent object. + */ + public StringContent() { + stringContent = new StringBuilder(); + } + + /** + * Constructor to construct StringContent object with some initial content. + * + * @param initialContent initial content for the object + */ + public StringContent(String initialContent) { + stringContent = new StringBuilder(); + appendChars(initialContent); + } + + /** + * This method is not supported by the class. + * + * @param content content that needs to be added + * @throws DocletAbortException this method will always throw a + * DocletAbortException because it + * is not supported. + */ + @Override + public void addContent(Content content) { + throw new DocletAbortException("not supported"); + } + + /** + * Adds content for the StringContent object. The method escapes + * HTML characters for the string content that is added. + * + * @param strContent string content to be added + */ + @Override + public void addContent(String strContent) { + appendChars(strContent); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmpty() { + return (stringContent.length() == 0); + } + + @Override + public int charCount() { + return RawHtml.charCount(stringContent.toString()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return stringContent.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean write(Writer out, boolean atNewline) throws IOException { + String s = stringContent.toString(); + out.write(s); + return s.endsWith(DocletConstants.NL); + } + + private void appendChars(String s) { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '<': stringContent.append("<"); break; + case '>': stringContent.append(">"); break; + case '&': stringContent.append("&"); break; + default: stringContent.append(ch); break; + } + } + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java new file mode 100644 index 00000000000..41069a3939c --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/package-info.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 package contains classes that write HTML markup tags. + +

This is NOT part of any supported API. + If you write code that depends on this, you do so at your own risk. + This code and its internal interfaces are subject to change or + deletion without notice. + */ + +package jdk.javadoc.internal.doclets.formats.html.markup; diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/package-info.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/package-info.java new file mode 100644 index 00000000000..6219b40bdc2 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/package-info.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 is the default HTML doclet provided with the JDK. + * + *

+ * This is NOT part of any supported API. If you write code that depends on this, you do so at + * your own risk. This code and its internal interfaces are subject to change or deletion without + * notice. + */ +package jdk.javadoc.internal.doclets.formats.html; diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.png b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.png new file mode 100644 index 00000000000..a7f591f467a Binary files /dev/null and b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.png differ diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js new file mode 100644 index 00000000000..c5c648255c1 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/external/jquery/jquery.js @@ -0,0 +1,9789 @@ +/*! + * jQuery JavaScript Library v1.10.2 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03T13:48Z + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<10 + // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + location = window.location, + document = window.document, + docElem = document.documentElement, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.2", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.10.2 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent.attachEvent && parent !== parent.top ) { + parent.attachEvent( "onbeforeunload", function() { + setDocument(); + }); + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "

"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; + } + }); +} + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each(function() { + if ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("\n" + + ""); + } + + void html5NegatedOutput() { + // Negated test for overview-frame page + checkOutput("overview-frame.html", false, + "", + "\n" + + "
    \n" + + "
  • All Classes
  • ", + "
    \n" + + "

    Packages

    "); + + // Negated test for allclasses-frame page + checkOutput("allclasses-frame.html", false, + "", + "\n" + + "
      \n" + + "
    • "); + + // Negated test for allclasses-noframe page + checkOutput("allclasses-noframe.html", false, + "", + "\n" + + "
        \n" + + "
      • "); + + // Negated test for overview-summary page + checkOutput("overview-summary.html", false, + "", + "\n" + + "\n" + + "", + "
\n" + + "
", + "\n" + + "
\n" + + ""); + + // Negated test for package-frame page + checkOutput("pkg/package-frame.html", false, + "", + "\n" + + "

pkg

"); + + // Negated test for package-summary page + checkOutput("pkg/package-summary.html", false, + "", + "\n" + + "\n" + + "", + "", + "
", + "
", + "
", + "
", + "
"); + + // Negated test for package-tree page + checkOutput("pkg/package-tree.html", false, + "", + "\n" + + "\n" + + ""); + + // Negated test for package-use page + checkOutput("pkg1/package-use.html", false, + "", + "\n" + + "\n" + + "", + "
"); + + // Negated test for constant-values page + checkOutput("constant-values.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
", + "
"); + + // Negated test for deprecated-list page + checkOutput("deprecated-list.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
\n" + + "

Deprecated API

\n" + + "

Contents

", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
"); + + // Negated test for serialized-form page + checkOutput("serialized-form.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
", + "
  • \n" + + "

    Package pkg

    "); + + // Negated test for overview-tree page + checkOutput("overview-tree.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
  • \n" + + "
    ", + "
    \n" + + "

    Class Hierarchy

    ", + "\n" + + "

    Interface Hierarchy

    ", + "\n" + + "

    Enum Hierarchy

    "); + + // Negated test for index-all page + checkOutput("index-all.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
    \n" + + "
    "); + + // Negated test for src-html page + checkOutput("src-html/pkg/AnotherClass.html", false, + "", + "\n" + + "
    "); + + // Negated test for help-doc page + checkOutput("help-doc.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
    \n" + + "
    ", + "
      \n" + + "
    • \n" + + "

      Overview

      ", + "
    • \n" + + "

      Package

      ", + "
    • \n" + + "

      Class/Interface

      "); + + // Negated test for a regular class page and members (nested class, field, constructore and method) + checkOutput("pkg/AnotherClass.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
      ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Field Detail

      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Constructor Detail

        ", + "\n" + + "
          \n" + + "
        • \n" + + "\n" + + "\n" + + "

          Method Detail

          "); + + // Negated test for enum page + checkOutput("pkg/AnotherClass.ModalExclusionType.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
          ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Enum Constant Detail

      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Method Detail

        "); + + // Negated test for interface page + checkOutput("pkg2/Interface.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
        ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Method Detail

      "); + + // Negated test for error page + checkOutput("pkg/TestError.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Constructor Summary

        ", + "\n" + + "
          \n" + + "
        • \n" + + "\n" + + "\n" + + "

          Constructor Detail

          "); + + // Negated test for exception page + checkOutput("pkg/TestException.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
          ", + "\n" + + "
            \n" + + "
          • \n" + + "\n" + + "\n" + + "

            Constructor Summary

            ", + "\n" + + "
              \n" + + "
            • \n" + + "\n" + + "\n" + + "

              Constructor Detail

              "); + + // Negated test for annotation page + checkOutput("pkg2/TestAnnotationType.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "
              ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Element Detail

      "); + + // Negated test for class use page + checkOutput("pkg1/class-use/RegClass.html", false, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
      ", + "
    ", + "
  • \n" + + "\n" + + "\n" + + "

    Uses of RegClass in pkg

    \n" + + "
  • "); + + // Negated test for main index page + checkOutput("index.html", false, + "", + "\n" + + "
    \n"); + } + + void html4Output() { + // Test for overview-frame page + checkOutput("overview-frame.html", true, + "", + "\n" + + "
      \n" + + "
    • All Classes
    • ", + "
      \n" + + "

      Packages

      "); + + // Test for allclasses-frame page + checkOutput("allclasses-frame.html", true, + "", + "\n" + + "
        \n" + + "
      • "); + + // Test for allclasses-noframe page + checkOutput("allclasses-noframe.html", true, + "", + "\n" + + "
          \n" + + "
        • "); + + // Test for overview-summary page + checkOutput("overview-summary.html", true, + "", + "\n" + + "\n" + + "", + "
    \n" + + "
    ", + "\n" + + "
    \n" + + ""); + + // Test for package-frame page + checkOutput("pkg/package-frame.html", true, + "", + "\n" + + "

    pkg

    "); + + // Test for package-summary page + checkOutput("pkg/package-summary.html", true, + "", + "\n" + + "\n" + + "", + "", + "
    ", + "
    ", + "
    ", + "
    ", + "
    "); + + // Test for package-tree page + checkOutput("pkg/package-tree.html", true, + "", + "\n" + + "\n" + + "", + "
  • "); + + // Test for package-use page + checkOutput("pkg1/package-use.html", true, + "", + "\n" + + "\n" + + "", + "
  • "); + + // Test for constant-values page + checkOutput("constant-values.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
    ", + "
    "); + + // Test for deprecated-list page + checkOutput("deprecated-list.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
    \n" + + "

    Deprecated API

    \n" + + "

    Contents

    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    ", + "
    "); + + // Test for serialized-form page + checkOutput("serialized-form.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
    ", + "
  • \n" + + "

    Package pkg

    "); + + // Test for overview-tree page + checkOutput("overview-tree.html", true, + "", + "\n" + + "\n" + + "", + "
  • ", + "\n" + + "
  • \n" + + "
    ", + "

    Hierarchy For All Packages

    \n" + + "Package Hierarchies:", + "
    \n" + + "

    Class Hierarchy

    ", + "\n" + + "

    Interface Hierarchy

    ", + "\n" + + "

    Enum Hierarchy

    "); + + // Test for index-all page + checkOutput("index-all.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
    \n" + + "
    "); + + // Test for src-html page + checkOutput("src-html/pkg/AnotherClass.html", true, + "", + "\n" + + "
    "); + + // Test for help-doc page + checkOutput("help-doc.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
    \n" + + "
    ", + "
      \n" + + "
    • \n" + + "

      Overview

      ", + "
    • \n" + + "

      Package

      ", + "
    • \n" + + "

      Class/Interface

      "); + + // Test for a regular class page and members (nested class, field, constructore and method) + checkOutput("pkg/AnotherClass.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
      ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Field Detail

      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Constructor Detail

        ", + "\n" + + "
          \n" + + "
        • \n" + + "\n" + + "\n" + + "

          Method Detail

          "); + + // Test for enum page + checkOutput("pkg/AnotherClass.ModalExclusionType.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
          ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Enum Constant Detail

      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Method Detail

        "); + + // Test for interface page + checkOutput("pkg2/Interface.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
        ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Method Detail

      "); + + // Test for error page + checkOutput("pkg/TestError.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
      ", + "\n" + + "
        \n" + + "
      • \n" + + "\n" + + "\n" + + "

        Constructor Summary

        ", + "\n" + + "
          \n" + + "
        • \n" + + "\n" + + "\n" + + "

          Constructor Detail

          "); + + // Test for exception page + checkOutput("pkg/TestException.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
          ", + "\n" + + "
            \n" + + "
          • \n" + + "\n" + + "\n" + + "

            Constructor Summary

            ", + "\n" + + "
              \n" + + "
            • \n" + + "\n" + + "\n" + + "

              Constructor Detail

              "); + + // Test for annotation page + checkOutput("pkg2/TestAnnotationType.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "
              ", + "\n" + + "
    ", + "\n" + + "
    ", + "\n" + + "
      \n" + + "
    • \n" + + "\n" + + "\n" + + "

      Element Detail

      "); + + // Test for class use page + checkOutput("pkg1/class-use/RegClass.html", true, + "", + "\n" + + "\n" + + "", + "\n" + + "\n" + + "
      ", + "
    ", + "
  • \n" + + "\n" + + "\n" + + "

    Uses of RegClass in pkg

    \n" + + "
  • "); + + // Test for main index page + checkOutput("index.html", true, + "", + "", + "\n" + + "
    \n" + + "
    \n" + + "
    \n" + + "\n" + + "
    "); + } + + void html4NegatedOutput() { + // Negated test for overview-frame page + checkOutput("overview-frame.html", false, + "", + "\n" + + "
      \n" + + "
    • All Classes
    • ", + "
      \n" + + "

      Packages

      "); + + // Negated test for allclasses-frame page + checkOutput("allclasses-frame.html", false, + "", + "\n" + + "
        \n" + + "
      • "); + + // Negated test for allclasses-noframe page + checkOutput("allclasses-noframe.html", false, + "", + "\n" + + "
          \n" + + "
        • "); + + // Negated test for overview-summary page + checkOutput("overview-summary.html", false, + "", + "\n" + + "\n" + + "", + "
    \n" + + "
    ", + "
    \n" + + "