< prev index next >

src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java

Print this page
rev 15544 : imported patch fold_select
rev 15545 : imported patch noperm


 546 
 547         return doStringConcat(lookup, name, concatType, false, recipe, constants);
 548     }
 549 
 550     private static CallSite doStringConcat(MethodHandles.Lookup lookup,
 551                                            String name,
 552                                            MethodType concatType,
 553                                            boolean generateRecipe,
 554                                            String recipe,
 555                                            Object... constants) throws StringConcatException {
 556         Objects.requireNonNull(lookup, "Lookup is null");
 557         Objects.requireNonNull(name, "Name is null");
 558         Objects.requireNonNull(concatType, "Concat type is null");
 559         Objects.requireNonNull(constants, "Constants are null");
 560 
 561         for (Object o : constants) {
 562             Objects.requireNonNull(o, "Cannot accept null constants");
 563         }
 564 
 565         if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
 566             throw new StringConcatException(String.format(
 567                     "Invalid caller: %s",
 568                     lookup.lookupClass().getName()));
 569         }
 570 
 571         int cCount = 0;
 572         int oCount = 0;
 573         if (generateRecipe) {
 574             // Mock the recipe to reuse the concat generator code
 575             char[] value = new char[concatType.parameterCount()];
 576             Arrays.fill(value, TAG_ARG);
 577             recipe = new String(value);
 578             oCount = concatType.parameterCount();
 579         } else {
 580             Objects.requireNonNull(recipe, "Recipe is null");
 581 
 582             for (int i = 0; i < recipe.length(); i++) {
 583                 char c = recipe.charAt(i);
 584                 if (c == TAG_CONST) cCount++;
 585                 if (c == TAG_ARG)   oCount++;
 586             }
 587         }
 588 


1478             MethodHandle[] filters = null;
1479             for (int i = 0; i < ptypes.length; i++) {
1480                 MethodHandle filter = Stringifiers.forMost(ptypes[i]);
1481                 if (filter != null) {
1482                     if (filters == null) {
1483                         filters = new MethodHandle[ptypes.length];
1484                     }
1485                     filters[i] = filter;
1486                     ptypes[i] = filter.type().returnType();
1487                 }
1488             }
1489 
1490             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
1491             // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
1492             // which makes the code arguably hard to read.
1493 
1494             // Drop all remaining parameter types, leave only helper arguments:
1495             MethodHandle mh;
1496 
1497             mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
1498             mh = MethodHandles.dropArguments(mh, 0, int.class);
1499 
1500             // Safety: check that remaining index is zero -- that would mean the storage is completely
1501             // overwritten, and no leakage of uninitialized data occurred.
1502             mh = MethodHandles.filterArgument(mh, 0, CHECK_INDEX);
1503 
1504             // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already
1505             // known from the combinators below. We are assembling the string backwards, so "index" is the
1506             // *ending* index.
1507             for (RecipeElement el : recipe.getElements()) {
1508                 MethodHandle prepender;

1509                 switch (el.getTag()) {
1510                     case TAG_CONST:
1511                         Object cnst = el.getValue();
1512                         prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);



1513                         break;
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());
1520                 }
1521 
1522                 // Remove "old" index from arguments
1523                 mh = MethodHandles.dropArguments(mh, 1, int.class);
1524 
1525                 // Do the prepend, and put "new" index at index 0
1526                 mh = MethodHandles.foldArguments(mh, prepender);
1527             }
1528 
1529             // Prepare the argument list for prepending. The tree below would instantiate
1530             // the storage byte[] into argument 0, so we need to swap "storage" and "index".
1531             // The index at this point equals to "size", and resides at argument 1.
1532             {
1533                 MethodType nmt = mh.type()
1534                         .changeParameterType(0, byte[].class)
1535                         .changeParameterType(1, int.class);
1536                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
1537             }
1538 
1539             // Fold in byte[] instantiation at argument 0.
1540             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypes);
1541             mh = MethodHandles.foldArguments(mh, combiner);

1542 
1543             // Start combining length and coder mixers.
1544             //
1545             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1546             // shapes have been either converted to Strings, or explicit methods for getting the
1547             // string length out of primitives are provided.
1548             //
1549             // Coders are more interesting. Only Object, String and char arguments (and constants)
1550             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1551             // and deduce the coder from there. Arguments would be either converted to Strings
1552             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1553             //
1554             // The method handle shape after all length and coder mixers is:
1555             //   (int, byte, <args>)String = ("index", "coder", <args>)
1556             byte initialCoder = INITIAL_CODER;
1557             int initialLen = 0;    // initial length, in characters
1558             for (RecipeElement el : recipe.getElements()) {
1559                 switch (el.getTag()) {
1560                     case TAG_CONST:
1561                         Object constant = el.getValue();
1562                         String s = constant.toString();
1563                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1564                         initialLen += s.length();
1565                         break;
1566                     case TAG_ARG:
1567                         int ac = el.getArgPos();
1568 
1569                         Class<?> argClass = ptypes[ac];
1570                         MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypes, ac);
1571                         lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
1572                         lm = MethodHandles.dropArguments(lm, 2, byte.class);
1573 
1574                         MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypes, ac);
1575                         cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
1576 
1577                         // Read this bottom up:
1578 
1579                         // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
1580                         mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
1581 
1582                         // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
1583                         //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
1584                         mh = MethodHandles.foldArguments(mh, lm);



1585 
1586                         // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
1587                         //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
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());
1594                 }
1595             }
1596 
1597             // Insert initial lengths and coders here.
1598             // The method handle shape here is (<args>).
1599             mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
1600 
1601             // Apply filters, converting the arguments:
1602             if (filters != null) {
1603                 mh = MethodHandles.filterArguments(mh, 0, filters);
1604             }
1605 
1606             return mh;
1607         }
1608 
1609         private static int[] swap10(int count) {
1610             int[] perm = new int[count];
1611             perm[0] = 1;
1612             perm[1] = 0;
1613             for (int i = 2; i < count; i++) {
1614                 perm[i] = i;
1615             }
1616             return perm;
1617         }
1618 
1619         // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
1620         private static MethodHandle selectArgument(MethodHandle mh, int prefix, Class<?>[] ptypes, int pos) {
1621             if (pos == 0) {
1622                 return MethodHandles.dropArguments(mh, prefix + 1, Arrays.copyOfRange(ptypes, 1, ptypes.length));
1623             } else if (pos == ptypes.length - 1) {
1624                 return MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, ptypes.length - 1));
1625             } else { // 0 < pos < ptypes.size() - 1
1626                 MethodHandle t = MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, pos));
1627                 return MethodHandles.dropArguments(t, prefix + 1 + pos, Arrays.copyOfRange(ptypes, pos + 1, ptypes.length));
1628             }
1629         }
1630 
1631         @ForceInline
1632         private static byte[] newArray(int length, byte coder) {
1633             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1634         }
1635 
1636         @ForceInline
1637         private static int checkIndex(int index) {
1638             if (index != 0) {
1639                 throw new IllegalStateException("Storage is not completely initialized, " + index + " bytes left");
1640             }
1641             return index;
1642         }
1643 
1644         private static MethodHandle prepender(Class<?> cl) {
1645             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1646         }
1647 
1648         private static MethodHandle coderMixer(Class<?> cl) {




 546 
 547         return doStringConcat(lookup, name, concatType, false, recipe, constants);
 548     }
 549 
 550     private static CallSite doStringConcat(MethodHandles.Lookup lookup,
 551                                            String name,
 552                                            MethodType concatType,
 553                                            boolean generateRecipe,
 554                                            String recipe,
 555                                            Object... constants) throws StringConcatException {
 556         Objects.requireNonNull(lookup, "Lookup is null");
 557         Objects.requireNonNull(name, "Name is null");
 558         Objects.requireNonNull(concatType, "Concat type is null");
 559         Objects.requireNonNull(constants, "Constants are null");
 560 
 561         for (Object o : constants) {
 562             Objects.requireNonNull(o, "Cannot accept null constants");
 563         }
 564 
 565         if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
 566             throw new StringConcatException("Invalid caller: " +
 567                     lookup.lookupClass().getName());

 568         }
 569 
 570         int cCount = 0;
 571         int oCount = 0;
 572         if (generateRecipe) {
 573             // Mock the recipe to reuse the concat generator code
 574             char[] value = new char[concatType.parameterCount()];
 575             Arrays.fill(value, TAG_ARG);
 576             recipe = new String(value);
 577             oCount = concatType.parameterCount();
 578         } else {
 579             Objects.requireNonNull(recipe, "Recipe is null");
 580 
 581             for (int i = 0; i < recipe.length(); i++) {
 582                 char c = recipe.charAt(i);
 583                 if (c == TAG_CONST) cCount++;
 584                 if (c == TAG_ARG)   oCount++;
 585             }
 586         }
 587 


1477             MethodHandle[] filters = null;
1478             for (int i = 0; i < ptypes.length; i++) {
1479                 MethodHandle filter = Stringifiers.forMost(ptypes[i]);
1480                 if (filter != null) {
1481                     if (filters == null) {
1482                         filters = new MethodHandle[ptypes.length];
1483                     }
1484                     filters[i] = filter;
1485                     ptypes[i] = filter.type().returnType();
1486                 }
1487             }
1488 
1489             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
1490             // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
1491             // which makes the code arguably hard to read.
1492 
1493             // Drop all remaining parameter types, leave only helper arguments:
1494             MethodHandle mh;
1495 
1496             mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
1497             mh = MethodHandles.dropArguments(mh, 1, int.class);
1498 
1499             // Safety: check that remaining index is zero -- that would mean the storage is completely
1500             // overwritten, and no leakage of uninitialized data occurred.
1501             mh = MethodHandles.filterArgument(mh, 1, CHECK_INDEX);
1502 
1503             // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
1504             // known from the combinators below. We are assembling the string backwards, so "index" is the
1505             // *ending* index.
1506             for (RecipeElement el : recipe.getElements()) {
1507                 // Do the prepend, and put "new" index at index 1
1508                 mh = MethodHandles.dropArguments(mh, 2, int.class);
1509                 switch (el.getTag()) {
1510                     case TAG_CONST: {
1511                         Object cnst = el.getValue();
1512                         MethodHandle prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
1513                         mh = MethodHandles.foldArguments(mh, 1, prepender,
1514                                 2, 0, 3 // index, storage, coder
1515                         );
1516                         break;
1517                     }
1518                     case TAG_ARG: {
1519                         int pos = el.getArgPos();
1520                         MethodHandle prepender = prepender(ptypes[pos]);
1521                         mh = MethodHandles.foldArguments(mh, 1, prepender,
1522                                 2, 0, 3, // index, storage, coder
1523                                 4 + pos  // selected argument
1524                         );
1525                         break;
1526                     }
1527                     default:
1528                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1529                 }
















1530             }
1531 
1532             // Fold in byte[] instantiation at argument 0
1533             mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY,
1534                     1, 2 // index, coder
1535             );
1536 
1537             // Start combining length and coder mixers.
1538             //
1539             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1540             // shapes have been either converted to Strings, or explicit methods for getting the
1541             // string length out of primitives are provided.
1542             //
1543             // Coders are more interesting. Only Object, String and char arguments (and constants)
1544             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1545             // and deduce the coder from there. Arguments would be either converted to Strings
1546             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1547             //
1548             // The method handle shape after all length and coder mixers is:
1549             //   (int, byte, <args>)String = ("index", "coder", <args>)
1550             byte initialCoder = INITIAL_CODER;
1551             int initialLen = 0;    // initial length, in characters
1552             for (RecipeElement el : recipe.getElements()) {
1553                 switch (el.getTag()) {
1554                     case TAG_CONST:
1555                         Object constant = el.getValue();
1556                         String s = constant.toString();
1557                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1558                         initialLen += s.length();
1559                         break;
1560                     case TAG_ARG:
1561                         int ac = el.getArgPos();
1562 
1563                         Class<?> argClass = ptypes[ac];
1564                         MethodHandle lm = lengthMixer(argClass);
1565                         MethodHandle cm = coderMixer(argClass);




1566 
1567                         // Read this bottom up:
1568 
1569                         // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
1570                         mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
1571 
1572                         // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
1573                         //    Length mixer needs old index, plus the appropriate argument
1574                         mh = MethodHandles.foldArguments(mh, 0, lm,
1575                                 2, // old-index
1576                                 4 + ac // selected argument
1577                         );
1578 
1579                         // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
1580                         //    Coder mixer needs old coder, plus the appropriate argument.
1581                         mh = MethodHandles.foldArguments(mh, 0, cm,
1582                                 2, // old-coder
1583                                 3 + ac // selected argument
1584                         );
1585 
1586                         // 1. The mh shape here is ("old-index", "old-coder", <args>)
1587                         break;
1588                     default:
1589                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1590                 }
1591             }
1592 
1593             // Insert initial lengths and coders here.
1594             // The method handle shape here is (<args>).
1595             mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
1596 
1597             // Apply filters, converting the arguments:
1598             if (filters != null) {
1599                 mh = MethodHandles.filterArguments(mh, 0, filters);
1600             }
1601 
1602             return mh;






















1603         }
1604 
1605         @ForceInline
1606         private static byte[] newArray(int length, byte coder) {
1607             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1608         }
1609 
1610         @ForceInline
1611         private static int checkIndex(int index) {
1612             if (index != 0) {
1613                 throw new IllegalStateException("Storage is not completely initialized, " + index + " bytes left");
1614             }
1615             return index;
1616         }
1617 
1618         private static MethodHandle prepender(Class<?> cl) {
1619             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1620         }
1621 
1622         private static MethodHandle coderMixer(Class<?> cl) {


< prev index next >