changeset 35443:d0ab5664b35a

Merge
author amurillo
date Mon, 01 Feb 2016 10:07:35 -0800
parents 3f077c6ba1bf 086c682bd8c5
children 4d323e794486
files jdk/test/java/net/SocketPermission/policy langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTaskImpl.java langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/api/JavadocTool.java langtools/test/tools/javadoc/api/basic/JavadocTaskImplTest.java langtools/test/tools/javadoc/api/basic/RunTest.java langtools/test/tools/javadoc/doclint/DocLintTest.java langtools/test/tools/javadoc/doclint/ImplicitHeadersTest.java
diffstat 1313 files changed, 215536 insertions(+), 3478 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -345,3 +345,4 @@
 4379223f8806626852c46c52d4e7a27a584b406e jdk-9+100
 80f67512daa15cf37b4825c1c62a675d524d7c49 jdk-9+101
 2dc4c11fe48831854916d53c3913bdb7d49023ea jdk-9+102
+4a652e4ca9523422149958673033e0ac740d5e1e jdk-9+103
--- a/common/autoconf/generated-configure.sh	Thu Jan 28 16:30:36 2016 -0800
+++ b/common/autoconf/generated-configure.sh	Mon Feb 01 10:07:35 2016 -0800
@@ -787,8 +787,6 @@
 ac_ct_CXX
 CXXFLAGS
 CXX
-ac_ct_PROPER_COMPILER_CXX
-PROPER_COMPILER_CXX
 TOOLCHAIN_PATH_CXX
 POTENTIAL_CXX
 OBJEXT
@@ -798,8 +796,6 @@
 LDFLAGS
 CFLAGS
 CC
-ac_ct_PROPER_COMPILER_CC
-PROPER_COMPILER_CC
 TOOLCHAIN_PATH_CC
 POTENTIAL_CC
 TOOLCHAIN_VERSION
@@ -4839,7 +4835,7 @@
 #CUSTOM_AUTOCONF_INCLUDE
 
 # Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1453964370
+DATE_WHEN_GENERATED=1454146111
 
 ###############################################################################
 #
@@ -32012,12 +32008,10 @@
   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
@@ -32036,13 +32030,13 @@
     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`
@@ -32062,474 +32056,25 @@
         let COUNTER=COUNTER+1
       done
       cd $STARTDIR
-      TEST_COMPILER=$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=""
-  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
-
-
-  # 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"`
-    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; }
+      SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file
+    fi
+  fi
+
+  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
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5
+$as_echo "$SYMLINK_ORIGINAL" >&6; }
+
+    # 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
   fi
 
 
@@ -33760,12 +33305,10 @@
   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
@@ -33784,13 +33327,13 @@
     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`
@@ -33810,474 +33353,25 @@
         let COUNTER=COUNTER+1
       done
       cd $STARTDIR
-      TEST_COMPILER=$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=""
-  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
-
-
-  # 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"`
-    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; }
+      SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file
+    fi
+  fi
+
+  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
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5
+$as_echo "$SYMLINK_ORIGINAL" >&6; }
+
+    # 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
   fi
 
 
@@ -58548,6 +57642,10 @@
       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"
+
 
 
 
--- a/common/autoconf/libraries.m4	Thu Jan 28 16:30:36 2016 -0800
+++ b/common/autoconf/libraries.m4	Mon Feb 01 10:07:35 2016 -0800
@@ -144,6 +144,10 @@
       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)
--- a/common/autoconf/toolchain.m4	Thu Jan 28 16:30:36 2016 -0800
+++ b/common/autoconf/toolchain.m4	Mon Feb 01 10:07:35 2016 -0800
@@ -433,39 +433,22 @@
   # 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.
+  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([$SYMLINK_ORIGINAL])
 
-    # 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"
-  else
-    AC_MSG_RESULT([no, keeping $1])
+    # 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])
--- a/common/bin/compare.sh	Thu Jan 28 16:30:36 2016 -0800
+++ b/common/bin/compare.sh	Mon Feb 01 10:07:35 2016 -0800
@@ -306,7 +306,7 @@
         ! -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 @@
     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
--- a/corba/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/corba/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -345,3 +345,4 @@
 791d0d3ac0138faeb6110bd840a4545bc1950df2 jdk-9+100
 30dfb3bd3d06b4bb80a087babc0d1841edba187b jdk-9+101
 9c4662334d933d299928d1f599d02ff50777cbf8 jdk-9+102
+0680fb7dae4da1ee6cf783c4b74184e3e08d3179 jdk-9+103
--- a/hotspot/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/hotspot/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -505,3 +505,4 @@
 bdb0acafc63c42e84d9d8195bf2e2b25ee9c3306 jdk-9+100
 9f45d3d57d6948cf526fbc2e2891a9a74ac6941a jdk-9+101
 d5239fc1b69749ae50793c61b899fcdacf3df857 jdk-9+102
+c5f55130b1b69510d9a6f4a3105b58e21cd7ffe1 jdk-9+103
--- a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java	Mon Feb 01 10:07:35 2016 -0800
@@ -64,14 +64,18 @@
         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());
     }
--- a/jaxp/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/jaxp/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -345,3 +345,4 @@
 d45bcd374f6057851e3c2dcd45607cd362afadfa jdk-9+100
 d3e834ff74e724a2b92a558e18e8cbf81c6dbc59 jdk-9+101
 9dcf193c0b6cf22c0e89e2dc705a2c0f520ae064 jdk-9+102
+bdbf2342b21bd8ecad1b4e6499a0dfb314952bd7 jdk-9+103
--- a/jaxws/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/jaxws/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -348,3 +348,4 @@
 d0a97e57d2336238edf6a4cd60aafe67deb7258d jdk-9+100
 3e99318616da903e0dc8f07f9f9203dc1bd49921 jdk-9+101
 0868b93587cc99df3a4f4d3817a1aa756bea60ab jdk-9+102
+eb5e005a17e50d7d8340daaf21a5c3c5ae358d68 jdk-9+103
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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.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 @@
 
     /**
      * 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 @@
                 throw (RuntimeException) t;
             if (t instanceof Error)
                 throw (Error) t;
+            return t;
         }
+        return x;
     }
 
 
@@ -157,9 +160,10 @@
         } 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 @@
             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 @@
                 // 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 @@
             }
             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);
 
-            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 @@
         }
     }
 
+    private static Object instantiateProviderIfNecessary(Method m) throws JAXBException {
+        Class<?> declaringClass = m.getDeclaringClass();
+        try {
+            if (JAXBContextFactory.class.isAssignableFrom(declaringClass)) {
+                return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                    @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 @@
         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 @@
 
         } 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);
         }
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
  * 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 @@
  *
  * <h3>Discovery of JAXB implementation</h3>
  * <p>
- * 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.
+ * <p>
+ * 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.
+ * <p>
+ * Implementation discovery consists of following steps:
  *
  * <ol>
  *
  * <li>
- * 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 &mdash;
+ * 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 &mdash;
  * this is {@link Class#getClassLoader() the owner class loader} for a {@link Class} argument, and for a package
  * the specified {@link ClassLoader}.
  *
  * <p>
- * 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.
  *
  * <p>
  * This phase of the look up allows some packages to force the use of a certain JAXB implementation.
@@ -222,7 +229,9 @@
  *
  * <li>
  * 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.
  *
  * <li>
  * Provider of {@link javax.xml.bind.JAXBContextFactory} is loaded using the service-provider loading
@@ -235,43 +244,58 @@
  * <br>
  * In case of {@link java.util.ServiceConfigurationError service
  * configuration error} a {@link javax.xml.bind.JAXBException} will be thrown.
- * </li>
  *
  * <li>
  * 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.
  *
+ * This configuration method is deprecated.
+ *
+ * <li>
+ * 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 Java SE can have its own JAXB implementation as the last resort.
+ * </ol>
+ *
+ * <p>
+ * 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:
+ * <ol>
+ *     <li>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.
+ *     <li>the class is not implementation of interface above and then it is mandated to implement the following
+ *     static method signatures:
  * <pre>
  *
  * public static JAXBContext createContext(
  *                                      String contextPath,
  *                                      ClassLoader classLoader,
- *                                      Map&lt;String,Object&gt; properties throws JAXBException
+ *                                      Map&lt;String,Object&gt; properties ) throws JAXBException
  *
  * public static JAXBContext createContext(
  *                                      Class[] classes,
  *                                      Map&lt;String,Object&gt; properties ) throws JAXBException
  * </pre>
- * This configuration method is deprecated.
- *
- * <li>
- * 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.
+ *      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.
  * </ol>
- *
  * <p>
- * 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}.
+ * 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
- * <p>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.
+ * <p>
+ * 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 @@
      * the context class loader of the current thread.
      *
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *               {@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 @@
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *               {@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 @@
      * the instantiation of {@link JAXBContext}.
      *
      * <p>
-     * 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 @@
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *                       {@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 @@
 
         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 @@
      * @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):
-     * <ol>
-     *  <li>No JAXB implementation was discovered
-     *  <li>Classes use JAXB annotations incorrectly
-     *  <li>Classes have colliding annotations (i.e., two classes with the same type name)
-     *  <li>The JAXB implementation was unable to locate
-     *      provider-specific out-of-band information (such as additional
-     *      files generated at the development time.)
-     * </ol>
+     * @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 @@
      * @since 1.6, JAXB 2.0
      */
     public static JAXBContext newInstance( Class<?> ... classesToBeBound )
-        throws JAXBException {
+            throws JAXBException {
 
         return newInstance(classesToBeBound,Collections.<String,Object>emptyMap());
     }
@@ -614,7 +611,7 @@
      * to configure 'properties' for this instantiation of {@link JAXBContext}.
      *
      * <p>
-     * 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 @@
      * @since 1.6, JAXB 2.0
      */
     public static JAXBContext newInstance( Class<?>[] classesToBeBound, Map<String,?> properties )
-        throws JAXBException {
+            throws JAXBException {
 
         if (classesToBeBound == null) {
-                throw new IllegalArgumentException();
+            throw new IllegalArgumentException();
         }
 
         // but it is an error to have nulls in it.
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
      *
      * @throws JAXBException
      *      if an error was encountered while creating the
-     *      {@code JAXBContext}, such as (but not limited to):
-     * <ol>
-     *  <li>Classes use JAXB annotations incorrectly
-     *  <li>Classes have colliding annotations (i.e., two classes with the same type name)
-     *  <li>The JAXB implementation was unable to locate
-     *      provider-specific out-of-band information (such as additional
-     *      files generated at the development time.)
-     * </ol>
+     *      {@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 @@
      * For semantics see {@link javax.xml.bind.JAXBContext#newInstance(String, ClassLoader, java.util.Map)}
      *
      * <p>
-     * 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 @@
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *      {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details.
+     *
      * @since 9, JAXB 2.3
      */
     JAXBContext createContext(String contextPath,
--- a/jdk/.hgtags	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/.hgtags	Mon Feb 01 10:07:35 2016 -0800
@@ -345,3 +345,4 @@
 3d452840f48299a36842760d17c0c8402f0e1266 jdk-9+100
 5e8370fb3ed925335164afe340d1e54beab2d4d5 jdk-9+101
 6eb3c8132e489dab81adde4ce29844904ce15482 jdk-9+102
+eee1ced1d8e78293f2a004af818ca474387dbebf jdk-9+103
--- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk	Mon Feb 01 10:07:35 2016 -0800
@@ -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, \
 ))
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Integer.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
         // 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 @@
      * 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 @@
         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 @@
         if (negative) {
             StringUTF16.putChar(buf, --charPos, '-');
         }
+        return charPos;
     }
 
     // Left here for compatibility reasons, see JDK-8143900.
--- a/jdk/src/java.base/share/classes/java/lang/Long.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Long.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
         // 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 @@
      * 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 @@
         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 @@
         if (negative) {
             StringUTF16.putChar(buf, --charPos, '-');
         }
+        return charPos;
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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.*;
+
+/**
+ * <p>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 <em>bootstrap methods</em> for {@code
+ * invokedynamic} call sites, to support the <em>string concatenation</em>
+ * feature of the Java Programming Language.
+ *
+ * <p>Indirect access to the behavior specified by the provided {@code
+ * MethodHandle} proceeds in order through two phases:
+ *
+ * <ol>
+ *     <li><em>Linkage</em> 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 <em>recipe</em>, 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.</li>
+ *
+ * <li><em>Invocation</em> 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...)}.</li>
+ * </ol>
+ *
+ * <p> 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
+ * <p>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<Key, MethodHandle> CACHE;
+
+    static {
+        // Poke the privileged block once, taking everything we need:
+        final Object[] values = new Object[3];
+        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+            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<RecipeElement> elements;
+        private final List<RecipeElement> elementsRev;
+
+        public Recipe(String src, Object[] constants) {
+            List<RecipeElement> 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<RecipeElement> getElements() {
+            return elements;
+        }
+
+        public Collection<RecipeElement> 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 <em>bootstrap method</em> for {@code
+     * invokedynamic} call sites, to support the <em>string concatenation</em>
+     * feature of the Java Programming Language.
+     *
+     * <p>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.
+     *
+     * <p>Assume the linkage arguments are as follows:
+     *
+     * <ul>
+     *     <li>{@code concatType}, describing the {@code CallSite} signature</li>
+     * </ul>
+     *
+     * <p>Then the following linkage invariants must hold:
+     *
+     * <ul>
+     *     <li>The parameter count in {@code concatType} is less than or equal to 200</li>
+     *
+     *     <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li>
+     * </ul>
+     *
+     * @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 <em>bootstrap method</em> for {@code
+     * invokedynamic} call sites, to support the <em>string concatenation</em>
+     * feature of the Java Programming Language.
+     *
+     * <p>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.
+     *
+     * <p>The concatenation <em>recipe</em> 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:
+     *
+     * <ul>
+     *
+     *   <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This
+     *   input is passed through dynamic argument, and is provided during the
+     *   concatenation method invocation. This input can be null.</li>
+     *
+     *   <li><em>\2 (Unicode point 0002):</em> 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.</li>
+     *
+     *   <li><em>Any other char value:</em> a single character constant.</li>
+     * </ul>
+     *
+     * <p>Assume the linkage arguments are as follows:
+     *
+     * <ul>
+     *   <li>{@code concatType}, describing the {@code CallSite} signature</li>
+     *   <li>{@code recipe}, describing the String recipe</li>
+     *   <li>{@code constants}, the vararg array of constants</li>
+     * </ul>
+     *
+     * <p>Then the following linkage invariants must hold:
+     *
+     * <ul>
+     *   <li>The parameter count in {@code concatType} is less than or equal to
+     *   200</li>
+     *
+     *   <li>The parameter count in {@code concatType} equals to number of \1 tags
+     *   in {@code recipe}</li>
+     *
+     *   <li>The return type in {@code concatType} is assignable
+     *   from {@link java.lang.String}, and matches the return type of the
+     *   returned {@link MethodHandle}</li>
+     *
+     *   <li>The number of elements in {@code constants} equals to number of \2
+     *   tags in {@code recipe}</li>
+     * </ul>
+     *
+     * @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.
+     *
+     * <p>This strategy operates in three modes, gated by {@link Mode}.
+     *
+     * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
+     *
+     * <p>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.
+     *
+     * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
+     * sized".</b>
+     *
+     * <p>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.
+     *
+     * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
+     * sized exactly".</b>
+     *
+     * <p>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",
+                        "<init>",
+                        "(I)V",
+                        false
+                );
+            } else {
+                mv.visitMethodInsn(
+                        INVOKESPECIAL,
+                        "java/lang/StringBuilder",
+                        "<init>",
+                        "()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",
+                        "<init>",
+                        "(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.
+     *
+     * <p>This strategy operates in two modes, gated by {@link Mode}.
+     *
+     * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
+     * sized".</b>
+     *
+     * <p>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.
+     *
+     * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
+     * sized exactly".</b>
+     *
+     * <p>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<Class<?>> 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, <args>) 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 (<ints>)int shape:
+            MethodHandle sum = getReducerFor(pc + 1);
+            MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial);
+
+            // b) Apply lengthers to transform arguments to lengths, producing (<args>)int
+            adder = MethodHandles.filterArguments(adder, 0, lengthers);
+
+            // c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder
+            MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER);
+
+            // d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder
+            MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder);
+
+            // Convert non-primitive arguments to Strings
+            mh = MethodHandles.filterArguments(mh, 0, filters);
+
+            // Convert (<args>)StringBuilder to (<args>)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<Integer, MethodHandle> SUMMERS;
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
+            @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;
+            }
+        }
+
+    }
+
+
+    /**
+     * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
+     * sized exactly".</b>
+     *
+     * <p>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<Class<?>> ptypesList = Arrays.asList(ptypes);
+
+            // Start building the combinator tree. The tree "starts" with (<parameters>)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, <args>)String = ("index", "coder", <args>)
+            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", <args>)
+                        mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
+
+                        // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
+                        //    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", <args>)
+                        //    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", <args>)
+                        break;
+                    }
+                    default:
+                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                }
+            }
+
+            // Insert initial lengths and coders here.
+            // The method handle shape here is (<args>).
+            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<Class<?>> 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<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, 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<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, 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<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, 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<Class<?>, MethodHandle> PREPENDERS;
+        private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
+        private static final ConcurrentMap<Class<?>, 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<Class<?>, MethodHandle> MOST = new Function<Class<?>, 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<Class<?>, MethodHandle> ANY = new Function<Class<?>, 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<Class<?>, MethodHandle> STRINGIFIERS_MOST = new ConcurrentHashMap<>();
+        private static final ConcurrentMap<Class<?>, 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
+    }
+
+}
--- a/jdk/src/java.base/share/classes/java/net/URI.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/URI.java	Mon Feb 01 10:07:35 2016 -0800
@@ -1080,11 +1080,8 @@
      *          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 --
--- a/jdk/src/java.base/share/classes/java/net/URL.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/net/URL.java	Mon Feb 01 10:07:35 2016 -0800
@@ -36,6 +36,7 @@
 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 @@
             }
         }
 
-        protocol = protocol.toLowerCase();
+        protocol = protocol.toLowerCase(Locale.ROOT);
         this.protocol = protocol;
         if (host != null) {
 
@@ -579,8 +580,7 @@
             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 @@
         }
     }
 
+    /**
+     * 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 @@
         }
     }
 
-    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;
     }
 
--- a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
     /**
      * Read the file owner.
      *
-     * <p> It it implementation specific if the file owner can be a {@link
+     * <p> It is implementation specific if the file owner can be a {@link
      * GroupPrincipal group}.
      *
      * @return  the file owner
@@ -78,7 +78,7 @@
     /**
      * Updates the file owner.
      *
-     * <p> It it implementation specific if the file owner can be a {@link
+     * <p> 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.
--- a/jdk/src/java.base/share/classes/java/util/Map.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Map.java	Mon Feb 01 10:07:35 2016 -0800
@@ -649,7 +649,7 @@
             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 @@
             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 @@
 
             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 @@
      * 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 @@
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V computeIfAbsent(K key,
@@ -1058,6 +1061,9 @@
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V computeIfPresent(K key,
@@ -1103,7 +1109,7 @@
      * <pre> {@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 @@
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V compute(K key,
@@ -1239,6 +1248,9 @@
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @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 @@
         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);
--- a/jdk/src/java.base/share/classes/java/util/Queue.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java	Mon Feb 01 10:07:35 2016 -0800
@@ -129,14 +129,6 @@
  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
- * @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 <E> the type of elements held in this queue
--- a/jdk/src/java.base/share/classes/java/util/Vector.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Vector.java	Mon Feb 01 10:07:35 2016 -0800
@@ -233,42 +233,56 @@
     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 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 @@
             throw new ArrayIndexOutOfBoundsException(index
                                                      + " > " + elementCount);
         }
-        ensureCapacityHelper(elementCount + 1);
-        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
+        modCount++;
+        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;
-        modCount++;
-        elementCount++;
+        elementCount = s + 1;
     }
 
     /**
@@ -623,9 +639,8 @@
      * @param   obj   the component to be added
      */
     public synchronized void addElement(E obj) {
-        ensureCapacityHelper(elementCount + 1);
         modCount++;
-        elementData[elementCount++] = obj;
+        add(obj, elementData, elementCount);
     }
 
     /**
@@ -781,6 +796,18 @@
     }
 
     /**
+     * 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.
      *
      * @param e element to be appended to this Vector
@@ -788,9 +815,8 @@
      * @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 boolean addAll(Collection<? extends E> 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 @@
             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;
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java	Mon Feb 01 10:07:35 2016 -0800
@@ -301,19 +301,15 @@
      *
      * @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}:
      *
      * <pre> {@code
-     * if (map.get(key) == null) {
-     *   V newValue = mappingFunction.apply(key);
-     *   if (newValue != null)
-     *     return map.putIfAbsent(key, newValue);
-     * }}</pre>
-     *
-     * 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;}</pre>
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -323,16 +319,19 @@
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V computeIfAbsent(K key,
             Function<? super K, ? extends V> 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 @@
      *
      * @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}:
      *
      * <pre> {@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);
-     * }}</pre>
-     *
-     * 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;}</pre>
+     * When multiple threads attempt updates, map operations and the
+     * remapping function may be called multiple times.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -365,22 +361,21 @@
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V computeIfPresent(K key,
             BiFunction<? super K, ? super V, ? extends V> 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 @@
      *
      * @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}:
      *
      * <pre> {@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;
+     *   }
      * }}</pre>
-     *
-     * 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.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -418,50 +409,29 @@
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V compute(K key,
-            BiFunction<? super K, ? super V, ? extends V> 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<? super K, ? super V, ? extends V> 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 @@
      *
      * @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}:
      *
      * <pre> {@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);}</pre>
-     *
-     * <p>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;
+     *   }
+     * }}</pre>
+     * When multiple threads attempt updates, map operations and the
+     * remapping function may be called multiple times.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -495,6 +469,7 @@
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
@@ -502,20 +477,23 @@
             BiFunction<? super V, ? super V, ? extends V> 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;
                 }
             }
         }
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Thu Jan 28 16:30:36 2016 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Mon Feb 01 10:07:35 2016 -0800
@@ -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 @@
     // 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> bufferCache =
         new ThreadLocal<BufferCache>()
@@ -55,6 +58,52 @@
     };
 
     /**
+     * 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<String>() {
+                @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.
      */
     private static class BufferCache {
@@ -80,6 +129,9 @@
          * 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 @@
         }
 
         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 @@
         }