jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
changeset 40272 6af4511ee5a4
parent 40256 c5e03eaf7ba2
child 40810 b88d5910ea1e
equal deleted inserted replaced
40271:82752776be2f 40272:6af4511ee5a4
   283                         el.add(new RecipeElement(cnst));
   283                         el.add(new RecipeElement(cnst));
   284                     } else if (c == TAG_ARG) {
   284                     } else if (c == TAG_ARG) {
   285                         el.add(new RecipeElement(argC++));
   285                         el.add(new RecipeElement(argC++));
   286                     }
   286                     }
   287                 } else {
   287                 } else {
   288                     // Not a special characters, this is a constant embedded into
   288                     // Not a special character, this is a constant embedded into
   289                     // the recipe itself.
   289                     // the recipe itself.
   290                     acc.append(c);
   290                     acc.append(c);
   291                 }
   291                 }
   292             }
   292             }
   293 
   293 
   319     }
   319     }
   320 
   320 
   321     private static final class RecipeElement {
   321     private static final class RecipeElement {
   322         private final Object value;
   322         private final Object value;
   323         private final int argPos;
   323         private final int argPos;
       
   324         private final char tag;
   324 
   325 
   325         public RecipeElement(Object cnst) {
   326         public RecipeElement(Object cnst) {
   326             this.value = Objects.requireNonNull(cnst);
   327             this.value = Objects.requireNonNull(cnst);
   327             this.argPos = -1;
   328             this.argPos = -1;
       
   329             this.tag = TAG_CONST;
   328         }
   330         }
   329 
   331 
   330         public RecipeElement(int arg) {
   332         public RecipeElement(int arg) {
   331             this.value = null;
   333             this.value = null;
   332             assert (arg >= 0);
       
   333             this.argPos = arg;
   334             this.argPos = arg;
       
   335             this.tag = TAG_ARG;
   334         }
   336         }
   335 
   337 
   336         public Object getValue() {
   338         public Object getValue() {
   337             assert (isConst());
   339             assert (tag == TAG_CONST);
   338             return value;
   340             return value;
   339         }
   341         }
   340 
   342 
   341         public int getArgPos() {
   343         public int getArgPos() {
   342             assert (!isConst());
   344             assert (tag == TAG_ARG);
   343             return argPos;
   345             return argPos;
   344         }
   346         }
   345 
   347 
   346         public boolean isConst() {
   348         public char getTag() {
   347             return argPos == -1;
   349             return tag;
   348         }
   350         }
   349 
   351 
   350         @Override
   352         @Override
   351         public boolean equals(Object o) {
   353         public boolean equals(Object o) {
   352             if (this == o) return true;
   354             if (this == o) return true;
   353             if (o == null || getClass() != o.getClass()) return false;
   355             if (o == null || getClass() != o.getClass()) return false;
   354 
   356 
   355             RecipeElement that = (RecipeElement) o;
   357             RecipeElement that = (RecipeElement) o;
   356 
   358 
   357             boolean isConst = isConst();
   359             if (this.tag != that.tag) return false;
   358             if (isConst != that.isConst()) return false;
   360             if (this.tag == TAG_CONST && (!value.equals(that.value))) return false;
   359             if (isConst && (!value.equals(that.value))) return false;
   361             if (this.tag == TAG_ARG && (argPos != that.argPos)) return false;
   360             if (!isConst && (argPos != that.argPos)) return false;
       
   361             return true;
   362             return true;
   362         }
   363         }
   363 
   364 
   364         @Override
   365         @Override
   365         public int hashCode() {
   366         public int hashCode() {
   366             return argPos;
   367             return (int)tag;
   367         }
   368         }
   368     }
   369     }
   369 
   370 
   370     /**
   371     /**
   371      * Facilitates the creation of optimized String concatenation methods, that
   372      * Facilitates the creation of optimized String concatenation methods, that
   641      *
   642      *
   642      * @param args actual argument types
   643      * @param args actual argument types
   643      * @return argument types the strategy is going to use
   644      * @return argument types the strategy is going to use
   644      */
   645      */
   645     private static MethodType adaptType(MethodType args) {
   646     private static MethodType adaptType(MethodType args) {
   646         Class<?>[] ptypes = args.parameterArray();
   647         Class<?>[] ptypes = null;
   647         boolean changed = false;
   648         for (int i = 0; i < args.parameterCount(); i++) {
   648         for (int i = 0; i < ptypes.length; i++) {
   649             Class<?> ptype = args.parameterType(i);
   649             Class<?> ptype = ptypes[i];
       
   650             if (!ptype.isPrimitive() &&
   650             if (!ptype.isPrimitive() &&
   651                     ptype != String.class &&
   651                     ptype != String.class &&
   652                     ptype != Object.class) { // truncate to Object
   652                     ptype != Object.class) { // truncate to Object
       
   653                 if (ptypes == null) {
       
   654                     ptypes = args.parameterArray();
       
   655                 }
   653                 ptypes[i] = Object.class;
   656                 ptypes[i] = Object.class;
   654                 changed = true;
       
   655             }
   657             }
   656             // else other primitives or String or Object (unchanged)
   658             // else other primitives or String or Object (unchanged)
   657         }
   659         }
   658         return changed
   660         return (ptypes != null)
   659                 ? MethodType.methodType(args.returnType(), ptypes)
   661                 ? MethodType.methodType(args.returnType(), ptypes)
   660                 : args;
   662                 : args;
   661     }
   663     }
   662 
   664 
   663     private static String getClassName(Class<?> hostClass) throws StringConcatException {
   665     private static String getClassName(Class<?> hostClass) throws StringConcatException {
   872                     need to do null-checks early, not make the append chain shape simpler.
   874                     need to do null-checks early, not make the append chain shape simpler.
   873                  */
   875                  */
   874 
   876 
   875                 int off = 0;
   877                 int off = 0;
   876                 for (RecipeElement el : recipe.getElements()) {
   878                 for (RecipeElement el : recipe.getElements()) {
   877                     if (el.isConst()) {
   879                     switch (el.getTag()) {
   878                         // Guaranteed non-null, no null check required.
   880                         case TAG_CONST:
   879                     } else {
   881                             // Guaranteed non-null, no null check required.
   880                         // Null-checks are needed only for String arguments, and when a previous stage
   882                             break;
   881                         // did not do implicit null-checks. If a String is null, we eagerly replace it
   883                         case TAG_ARG:
   882                         // with "null" constant. Note, we omit Objects here, because we don't call
   884                             // Null-checks are needed only for String arguments, and when a previous stage
   883                         // .length() on them down below.
   885                             // did not do implicit null-checks. If a String is null, we eagerly replace it
   884                         int ac = el.getArgPos();
   886                             // with "null" constant. Note, we omit Objects here, because we don't call
   885                         Class<?> cl = arr[ac];
   887                             // .length() on them down below.
   886                         if (cl == String.class && !guaranteedNonNull[ac]) {
   888                             int ac = el.getArgPos();
   887                             Label l0 = new Label();
   889                             Class<?> cl = arr[ac];
   888                             mv.visitIntInsn(ALOAD, off);
   890                             if (cl == String.class && !guaranteedNonNull[ac]) {
   889                             mv.visitJumpInsn(IFNONNULL, l0);
   891                                 Label l0 = new Label();
   890                             mv.visitLdcInsn("null");
   892                                 mv.visitIntInsn(ALOAD, off);
   891                             mv.visitIntInsn(ASTORE, off);
   893                                 mv.visitJumpInsn(IFNONNULL, l0);
   892                             mv.visitLabel(l0);
   894                                 mv.visitLdcInsn("null");
   893                         }
   895                                 mv.visitIntInsn(ASTORE, off);
   894                         off += getParameterSize(cl);
   896                                 mv.visitLabel(l0);
       
   897                             }
       
   898                             off += getParameterSize(cl);
       
   899                             break;
       
   900                         default:
       
   901                             throw new StringConcatException("Unhandled tag: " + el.getTag());
   895                     }
   902                     }
   896                 }
   903                 }
   897             }
   904             }
   898 
   905 
   899             // Prepare StringBuilder instance
   906             // Prepare StringBuilder instance
   910                 int off = 0;
   917                 int off = 0;
   911 
   918 
   912                 mv.visitInsn(ICONST_0);
   919                 mv.visitInsn(ICONST_0);
   913 
   920 
   914                 for (RecipeElement el : recipe.getElements()) {
   921                 for (RecipeElement el : recipe.getElements()) {
   915                     if (el.isConst()) {
   922                     switch (el.getTag()) {
   916                         Object cnst = el.getValue();
   923                         case TAG_CONST:
   917                         len += cnst.toString().length();
   924                             Object cnst = el.getValue();
   918                     } else {
   925                             len += cnst.toString().length();
   919                         /*
   926                             break;
   920                             If an argument is String, then we can call .length() on it. Sized/Exact modes have
   927                         case TAG_ARG:
   921                             converted arguments for us. If an argument is primitive, we can provide a guess
   928                             /*
   922                             for its String representation size.
   929                                 If an argument is String, then we can call .length() on it. Sized/Exact modes have
   923                         */
   930                                 converted arguments for us. If an argument is primitive, we can provide a guess
   924                         Class<?> cl = arr[el.getArgPos()];
   931                                 for its String representation size.
   925                         if (cl == String.class) {
   932                             */
   926                             mv.visitIntInsn(ALOAD, off);
   933                             Class<?> cl = arr[el.getArgPos()];
   927                             mv.visitMethodInsn(
   934                             if (cl == String.class) {
   928                                     INVOKEVIRTUAL,
   935                                 mv.visitIntInsn(ALOAD, off);
   929                                     "java/lang/String",
   936                                 mv.visitMethodInsn(
   930                                     "length",
   937                                         INVOKEVIRTUAL,
   931                                     "()I",
   938                                         "java/lang/String",
   932                                     false
   939                                         "length",
   933                             );
   940                                         "()I",
   934                             mv.visitInsn(IADD);
   941                                         false
   935                         } else if (cl.isPrimitive()) {
   942                                 );
   936                             len += estimateSize(cl);
   943                                 mv.visitInsn(IADD);
   937                         }
   944                             } else if (cl.isPrimitive()) {
   938                         off += getParameterSize(cl);
   945                                 len += estimateSize(cl);
       
   946                             }
       
   947                             off += getParameterSize(cl);
       
   948                             break;
       
   949                         default:
       
   950                             throw new StringConcatException("Unhandled tag: " + el.getTag());
   939                     }
   951                     }
   940                 }
   952                 }
   941 
   953 
   942                 // Constants have non-zero length, mix in
   954                 // Constants have non-zero length, mix in
   943                 if (len > 0) {
   955                 if (len > 0) {
   965             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
   977             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
   966             {
   978             {
   967                 int off = 0;
   979                 int off = 0;
   968                 for (RecipeElement el : recipe.getElements()) {
   980                 for (RecipeElement el : recipe.getElements()) {
   969                     String desc;
   981                     String desc;
   970                     if (el.isConst()) {
   982                     switch (el.getTag()) {
   971                         Object cnst = el.getValue();
   983                         case TAG_CONST:
   972                         mv.visitLdcInsn(cnst);
   984                             Object cnst = el.getValue();
   973                         desc = getSBAppendDesc(cnst.getClass());
   985                             mv.visitLdcInsn(cnst);
   974                     } else {
   986                             desc = getSBAppendDesc(cnst.getClass());
   975                         Class<?> cl = arr[el.getArgPos()];
   987                             break;
   976                         mv.visitVarInsn(getLoadOpcode(cl), off);
   988                         case TAG_ARG:
   977                         off += getParameterSize(cl);
   989                             Class<?> cl = arr[el.getArgPos()];
   978                         desc = getSBAppendDesc(cl);
   990                             mv.visitVarInsn(getLoadOpcode(cl), off);
       
   991                             off += getParameterSize(cl);
       
   992                             desc = getSBAppendDesc(cl);
       
   993                             break;
       
   994                         default:
       
   995                             throw new StringConcatException("Unhandled tag: " + el.getTag());
   979                     }
   996                     }
   980 
   997 
   981                     mv.visitMethodInsn(
   998                     mv.visitMethodInsn(
   982                             INVOKEVIRTUAL,
   999                             INVOKEVIRTUAL,
   983                             "java/lang/StringBuilder",
  1000                             "java/lang/StringBuilder",
  1243                     filters[i] = filter;
  1260                     filters[i] = filter;
  1244                     ptypes[i] = filter.type().returnType();
  1261                     ptypes[i] = filter.type().returnType();
  1245                 }
  1262                 }
  1246             }
  1263             }
  1247 
  1264 
  1248             List<Class<?>> ptypesList = Arrays.asList(ptypes);
       
  1249             MethodHandle[] lengthers = new MethodHandle[pc];
  1265             MethodHandle[] lengthers = new MethodHandle[pc];
  1250 
  1266 
  1251             // Figure out lengths: constants' lengths can be deduced on the spot.
  1267             // Figure out lengths: constants' lengths can be deduced on the spot.
  1252             // All reference arguments were filtered to String in the combinators below, so we can
  1268             // All reference arguments were filtered to String in the combinators below, so we can
  1253             // call the usual String.length(). Primitive values string sizes can be estimated.
  1269             // call the usual String.length(). Primitive values string sizes can be estimated.
  1254             int initial = 0;
  1270             int initial = 0;
  1255             for (RecipeElement el : recipe.getElements()) {
  1271             for (RecipeElement el : recipe.getElements()) {
  1256                 if (el.isConst()) {
  1272                 switch (el.getTag()) {
  1257                     Object cnst = el.getValue();
  1273                     case TAG_CONST:
  1258                     initial += cnst.toString().length();
  1274                         Object cnst = el.getValue();
  1259                 } else {
  1275                         initial += cnst.toString().length();
  1260                     final int i = el.getArgPos();
  1276                         break;
  1261                     Class<?> type = ptypesList.get(i);
  1277                     case TAG_ARG:
  1262                     if (type.isPrimitive()) {
  1278                         final int i = el.getArgPos();
  1263                         MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
  1279                         Class<?> type = ptypes[i];
  1264                         est = MethodHandles.dropArguments(est, 0, type);
  1280                         if (type.isPrimitive()) {
  1265                         lengthers[i] = est;
  1281                             MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
  1266                     } else {
  1282                             est = MethodHandles.dropArguments(est, 0, type);
  1267                         lengthers[i] = STRING_LENGTH;
  1283                             lengthers[i] = est;
  1268                     }
  1284                         } else {
       
  1285                             lengthers[i] = STRING_LENGTH;
       
  1286                         }
       
  1287                         break;
       
  1288                     default:
       
  1289                         throw new StringConcatException("Unhandled tag: " + el.getTag());
  1269                 }
  1290                 }
  1270             }
  1291             }
  1271 
  1292 
  1272             // Create (StringBuilder, <args>) shape for appending:
  1293             // Create (StringBuilder, <args>) shape for appending:
  1273             MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList);
  1294             MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypes);
  1274 
  1295 
  1275             // Compose append calls. This is done in reverse because the application order is
  1296             // Compose append calls. This is done in reverse because the application order is
  1276             // reverse as well.
  1297             // reverse as well.
  1277             List<RecipeElement> elements = recipe.getElements();
  1298             List<RecipeElement> elements = recipe.getElements();
  1278             for (int i = elements.size() - 1; i >= 0; i--) {
  1299             for (int i = elements.size() - 1; i >= 0; i--) {
  1279                 RecipeElement el = elements.get(i);
  1300                 RecipeElement el = elements.get(i);
  1280                 MethodHandle appender;
  1301                 MethodHandle appender;
  1281                 if (el.isConst()) {
  1302                 switch (el.getTag()) {
  1282                     Object constant = el.getValue();
  1303                     case TAG_CONST:
  1283                     MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
  1304                         Object constant = el.getValue();
  1284                     appender = MethodHandles.insertArguments(mh, 1, constant);
  1305                         MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
  1285                 } else {
  1306                         appender = MethodHandles.insertArguments(mh, 1, constant);
  1286                     int ac = el.getArgPos();
  1307                         break;
  1287                     appender = appender(ptypesList.get(ac));
  1308                     case TAG_ARG:
  1288 
  1309                         int ac = el.getArgPos();
  1289                     // Insert dummy arguments to match the prefix in the signature.
  1310                         appender = appender(ptypes[ac]);
  1290                     // The actual appender argument will be the ac-ith argument.
  1311 
  1291                     if (ac != 0) {
  1312                         // Insert dummy arguments to match the prefix in the signature.
  1292                         appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
  1313                         // The actual appender argument will be the ac-ith argument.
  1293                     }
  1314                         if (ac != 0) {
       
  1315                             appender = MethodHandles.dropArguments(appender, 1, Arrays.copyOf(ptypes, ac));
       
  1316                         }
       
  1317                         break;
       
  1318                     default:
       
  1319                         throw new StringConcatException("Unhandled tag: " + el.getTag());
  1294                 }
  1320                 }
  1295                 builder = MethodHandles.foldArguments(builder, appender);
  1321                 builder = MethodHandles.foldArguments(builder, appender);
  1296             }
  1322             }
  1297 
  1323 
  1298             // Build the sub-tree that adds the sizes and produces a StringBuilder:
  1324             // Build the sub-tree that adds the sizes and produces a StringBuilder:
  1458                     }
  1484                     }
  1459                     filters[i] = filter;
  1485                     filters[i] = filter;
  1460                     ptypes[i] = filter.type().returnType();
  1486                     ptypes[i] = filter.type().returnType();
  1461                 }
  1487                 }
  1462             }
  1488             }
  1463             List<Class<?>> ptypesList = Arrays.asList(ptypes);
       
  1464 
  1489 
  1465             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
  1490             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
  1466             // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
  1491             // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
  1467             // which makes the code arguably hard to read.
  1492             // which makes the code arguably hard to read.
  1468 
  1493 
  1479             // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already
  1504             // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already
  1480             // known from the combinators below. We are assembling the string backwards, so "index" is the
  1505             // known from the combinators below. We are assembling the string backwards, so "index" is the
  1481             // *ending* index.
  1506             // *ending* index.
  1482             for (RecipeElement el : recipe.getElements()) {
  1507             for (RecipeElement el : recipe.getElements()) {
  1483                 MethodHandle prepender;
  1508                 MethodHandle prepender;
  1484                 if (el.isConst()) {
  1509                 switch (el.getTag()) {
  1485                     Object cnst = el.getValue();
  1510                     case TAG_CONST:
  1486                     prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
  1511                         Object cnst = el.getValue();
  1487                 } else {
  1512                         prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
  1488                     int pos = el.getArgPos();
  1513                         break;
  1489                     prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
  1514                     case TAG_ARG:
       
  1515                         int pos = el.getArgPos();
       
  1516                         prepender = selectArgument(prepender(ptypes[pos]), 3, ptypes, pos);
       
  1517                         break;
       
  1518                     default:
       
  1519                         throw new StringConcatException("Unhandled tag: " + el.getTag());
  1490                 }
  1520                 }
  1491 
  1521 
  1492                 // Remove "old" index from arguments
  1522                 // Remove "old" index from arguments
  1493                 mh = MethodHandles.dropArguments(mh, 1, int.class);
  1523                 mh = MethodHandles.dropArguments(mh, 1, int.class);
  1494 
  1524 
  1505                         .changeParameterType(1, int.class);
  1535                         .changeParameterType(1, int.class);
  1506                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
  1536                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
  1507             }
  1537             }
  1508 
  1538 
  1509             // Fold in byte[] instantiation at argument 0.
  1539             // Fold in byte[] instantiation at argument 0.
  1510             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList);
  1540             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypes);
  1511             mh = MethodHandles.foldArguments(mh, combiner);
  1541             mh = MethodHandles.foldArguments(mh, combiner);
  1512 
  1542 
  1513             // Start combining length and coder mixers.
  1543             // Start combining length and coder mixers.
  1514             //
  1544             //
  1515             // Length is easy: constant lengths can be computed on the spot, and all non-constant
  1545             // Length is easy: constant lengths can be computed on the spot, and all non-constant
  1524             // The method handle shape after all length and coder mixers is:
  1554             // The method handle shape after all length and coder mixers is:
  1525             //   (int, byte, <args>)String = ("index", "coder", <args>)
  1555             //   (int, byte, <args>)String = ("index", "coder", <args>)
  1526             byte initialCoder = INITIAL_CODER;
  1556             byte initialCoder = INITIAL_CODER;
  1527             int initialLen = 0;    // initial length, in characters
  1557             int initialLen = 0;    // initial length, in characters
  1528             for (RecipeElement el : recipe.getElements()) {
  1558             for (RecipeElement el : recipe.getElements()) {
  1529                 if (el.isConst()) {
  1559                 switch (el.getTag()) {
  1530                     Object constant = el.getValue();
  1560                     case TAG_CONST:
  1531                     String s = constant.toString();
  1561                         Object constant = el.getValue();
  1532                     initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
  1562                         String s = constant.toString();
  1533                     initialLen += s.length();
  1563                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
  1534                 } else {
  1564                         initialLen += s.length();
  1535                     int ac = el.getArgPos();
  1565                         break;
  1536 
  1566                     case TAG_ARG:
  1537                     Class<?> argClass = ptypesList.get(ac);
  1567                         int ac = el.getArgPos();
  1538                     MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
  1568 
  1539                     lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
  1569                         Class<?> argClass = ptypes[ac];
  1540                     lm = MethodHandles.dropArguments(lm, 2, byte.class);
  1570                         MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypes, ac);
  1541 
  1571                         lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
  1542                     MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
  1572                         lm = MethodHandles.dropArguments(lm, 2, byte.class);
  1543                     cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
  1573 
  1544 
  1574                         MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypes, ac);
  1545                     // Read this bottom up:
  1575                         cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
  1546 
  1576 
  1547                     // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
  1577                         // Read this bottom up:
  1548                     mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
  1578 
  1549 
  1579                         // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
  1550                     // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
  1580                         mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
  1551                     //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
  1581 
  1552                     mh = MethodHandles.foldArguments(mh, lm);
  1582                         // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
  1553 
  1583                         //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
  1554                     // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
  1584                         mh = MethodHandles.foldArguments(mh, lm);
  1555                     //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
  1585 
  1556                     mh = MethodHandles.foldArguments(mh, cm);
  1586                         // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
  1557 
  1587                         //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
  1558                     // 1. The mh shape here is ("old-index", "old-coder", <args>)
  1588                         mh = MethodHandles.foldArguments(mh, cm);
       
  1589 
       
  1590                         // 1. The mh shape here is ("old-index", "old-coder", <args>)
       
  1591                         break;
       
  1592                     default:
       
  1593                         throw new StringConcatException("Unhandled tag: " + el.getTag());
  1559                 }
  1594                 }
  1560             }
  1595             }
  1561 
  1596 
  1562             // Insert initial lengths and coders here.
  1597             // Insert initial lengths and coders here.
  1563             // The method handle shape here is (<args>).
  1598             // The method handle shape here is (<args>).
  1580             }
  1615             }
  1581             return perm;
  1616             return perm;
  1582         }
  1617         }
  1583 
  1618 
  1584         // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
  1619         // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
  1585         private static MethodHandle selectArgument(MethodHandle mh, int prefix, List<Class<?>> ptypes, int pos) {
  1620         private static MethodHandle selectArgument(MethodHandle mh, int prefix, Class<?>[] ptypes, int pos) {
  1586             if (pos == 0) {
  1621             if (pos == 0) {
  1587                 return MethodHandles.dropArguments(mh, prefix + 1, ptypes.subList(1, ptypes.size()));
  1622                 return MethodHandles.dropArguments(mh, prefix + 1, Arrays.copyOfRange(ptypes, 1, ptypes.length));
  1588             } else if (pos == ptypes.size() - 1) {
  1623             } else if (pos == ptypes.length - 1) {
  1589                 return MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, ptypes.size() - 1));
  1624                 return MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, ptypes.length - 1));
  1590             } else { // 0 < pos < ptypes.size() - 1
  1625             } else { // 0 < pos < ptypes.size() - 1
  1591                 MethodHandle t = MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, pos));
  1626                 MethodHandle t = MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, pos));
  1592                 return MethodHandles.dropArguments(t, prefix + 1 + pos, ptypes.subList(pos + 1, ptypes.size()));
  1627                 return MethodHandles.dropArguments(t, prefix + 1 + pos, Arrays.copyOfRange(ptypes, pos + 1, ptypes.length));
  1593             }
  1628             }
  1594         }
  1629         }
  1595 
  1630 
  1596         @ForceInline
  1631         @ForceInline
  1597         private static byte[] newArray(int length, byte coder) {
  1632         private static byte[] newArray(int length, byte coder) {
  1646         private static final MethodHandle CHECK_INDEX;
  1681         private static final MethodHandle CHECK_INDEX;
  1647         private static final MethodHandle NEW_ARRAY;
  1682         private static final MethodHandle NEW_ARRAY;
  1648         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
  1683         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
  1649         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
  1684         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
  1650         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
  1685         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
  1651         private static final Class<?> STRING_HELPER;
       
  1652         private static final byte INITIAL_CODER;
  1686         private static final byte INITIAL_CODER;
       
  1687         static final Class<?> STRING_HELPER;
  1653 
  1688 
  1654         static {
  1689         static {
  1655             try {
  1690             try {
  1656                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
  1691                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
  1657                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
  1692                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
  1749         }
  1784         }
  1750     }
  1785     }
  1751 
  1786 
  1752     /* ------------------------------- Common utilities ------------------------------------ */
  1787     /* ------------------------------- Common utilities ------------------------------------ */
  1753 
  1788 
  1754     private static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
  1789     static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
  1755         try {
  1790         try {
  1756             return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
  1791             return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
  1757         } catch (NoSuchMethodException | IllegalAccessException e) {
  1792         } catch (NoSuchMethodException | IllegalAccessException e) {
  1758             throw new AssertionError(e);
  1793             throw new AssertionError(e);
  1759         }
  1794         }
  1760     }
  1795     }
  1761 
  1796 
  1762     private static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
  1797     static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
  1763         try {
  1798         try {
  1764             return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
  1799             return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
  1765         } catch (NoSuchMethodException | IllegalAccessException e) {
  1800         } catch (NoSuchMethodException | IllegalAccessException e) {
  1766             throw new AssertionError(e);
  1801             throw new AssertionError(e);
  1767         }
  1802         }
  1768     }
  1803     }
  1769 
  1804 
  1770     private static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
  1805     static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
  1771         try {
  1806         try {
  1772             return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
  1807             return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
  1773         } catch (NoSuchMethodException | IllegalAccessException e) {
  1808         } catch (NoSuchMethodException | IllegalAccessException e) {
  1774             throw new AssertionError(e);
  1809             throw new AssertionError(e);
  1775         }
  1810         }
  1776     }
  1811     }
  1777 
  1812 
  1778     private static int estimateSize(Class<?> cl) {
  1813     static int estimateSize(Class<?> cl) {
  1779         if (cl == Integer.TYPE) {
  1814         if (cl == Integer.TYPE) {
  1780             return 11; // "-2147483648"
  1815             return 11; // "-2147483648"
  1781         } else if (cl == Boolean.TYPE) {
  1816         } else if (cl == Boolean.TYPE) {
  1782             return 5; // "false"
  1817             return 5; // "false"
  1783         } else if (cl == Byte.TYPE) {
  1818         } else if (cl == Byte.TYPE) {
  1795         } else {
  1830         } else {
  1796             throw new IllegalArgumentException("Cannot estimate the size for " + cl);
  1831             throw new IllegalArgumentException("Cannot estimate the size for " + cl);
  1797         }
  1832         }
  1798     }
  1833     }
  1799 
  1834 
  1800     private static Class<?> adaptToStringBuilder(Class<?> c) {
  1835     static Class<?> adaptToStringBuilder(Class<?> c) {
  1801         if (c.isPrimitive()) {
  1836         if (c.isPrimitive()) {
  1802             if (c == Byte.TYPE || c == Short.TYPE) {
  1837             if (c == Byte.TYPE || c == Short.TYPE) {
  1803                 return int.class;
  1838                 return int.class;
  1804             }
  1839             }
  1805         } else {
  1840         } else {