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: |
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) { |