14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import jdk.internal.misc.Unsafe; 29 import jdk.internal.misc.VM; 30 import jdk.internal.org.objectweb.asm.ClassWriter; 31 import jdk.internal.org.objectweb.asm.Label; 32 import jdk.internal.org.objectweb.asm.MethodVisitor; 33 import jdk.internal.org.objectweb.asm.Opcodes; 34 import jdk.internal.vm.annotation.ForceInline; 35 import sun.invoke.util.Wrapper; 36 import sun.security.action.GetPropertyAction; 37 38 import java.lang.invoke.MethodHandles.Lookup; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.List; 42 import java.util.Objects; 43 import java.util.Properties; 44 import java.util.concurrent.ConcurrentHashMap; 45 import java.util.concurrent.ConcurrentMap; 46 import java.util.function.Function; 47 48 import static jdk.internal.org.objectweb.asm.Opcodes.*; 49 50 /** 51 * <p>Methods to facilitate the creation of String concatenation methods, that 52 * can be used to efficiently concatenate a known number of arguments of known 53 * types, possibly after type adaptation and partial evaluation of arguments. 54 * These methods are typically used as <em>bootstrap methods</em> for {@code 55 * invokedynamic} call sites, to support the <em>string concatenation</em> 56 * feature of the Java Programming Language. 57 * 58 * <p>Indirect access to the behavior specified by the provided {@code 59 * MethodHandle} proceeds in order through two phases: 60 * 61 * <ol> 62 * <li><em>Linkage</em> occurs when the methods in this class are invoked. 63 * They take as arguments a method type describing the concatenated arguments 1515 * the character length of the integers, and the private String constructor 1516 * that accepts byte[] arrays without copying. While this strategy assumes a 1517 * particular implementation details for String, this opens the door for 1518 * building a very optimal concatenation sequence. This is the only strategy 1519 * that requires porting if there are private JDK changes occur. 1520 */ 1521 private static final class MethodHandleInlineCopyStrategy { 1522 static final Unsafe UNSAFE = Unsafe.getUnsafe(); 1523 1524 private MethodHandleInlineCopyStrategy() { 1525 // no instantiation 1526 } 1527 1528 static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { 1529 1530 // Fast-path two-argument Object + Object concatenations 1531 if (recipe.getElements().size() == 2) { 1532 // Two object arguments 1533 if (mt.parameterCount() == 2 && 1534 !mt.parameterType(0).isPrimitive() && 1535 !mt.parameterType(1).isPrimitive()) { 1536 return SIMPLE; 1537 } 1538 // One element is a constant 1539 if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) { 1540 MethodHandle mh = SIMPLE; 1541 // Insert constant element 1542 1543 // First recipe element is a constant 1544 if (recipe.getElements().get(0).getTag() == TAG_CONST && 1545 recipe.getElements().get(1).getTag() != TAG_CONST) { 1546 return MethodHandles.insertArguments(mh, 0, 1547 recipe.getElements().get(0).getValue()); 1548 } else if (recipe.getElements().get(1).getTag() == TAG_CONST && 1549 recipe.getElements().get(0).getTag() != TAG_CONST) { 1550 return MethodHandles.insertArguments(mh, 1, 1551 recipe.getElements().get(1).getValue()); 1552 } 1553 // else... fall-through to slow-path 1554 } 1555 } 1556 1557 // Create filters and obtain filtered parameter types. Filters would be used in the beginning 1558 // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). 1559 // The filtered argument type list is used all over in the combinators below. 1560 Class<?>[] ptypes = mt.parameterArray(); 1561 MethodHandle[] filters = null; 1562 for (int i = 0; i < ptypes.length; i++) { 1563 MethodHandle filter = Stringifiers.forMost(ptypes[i]); 1564 if (filter != null) { 1565 if (filters == null) { 1566 filters = new MethodHandle[ptypes.length]; 1567 } 1568 filters[i] = filter; 1569 ptypes[i] = filter.type().returnType(); 1570 } 1571 } 1572 1573 // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes" 1574 // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are 1575 // assembled bottom-up, which makes the code arguably hard to read. 1576 1577 // Drop all remaining parameter types, leave only helper arguments: 1578 MethodHandle mh; 1579 1580 mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); 1581 1582 // Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already 1583 // known from the combinators below. We are assembling the string backwards, so the index coded 1584 // into indexCoder is the *ending* index. 1585 for (RecipeElement el : recipe.getElements()) { 1586 // Do the prepend, and put "new" index at index 1 1587 switch (el.getTag()) { 1588 case TAG_CONST: { 1589 MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue()); 1590 mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, 1591 1, 0 // indexCoder, storage 1592 ); 1593 break; 1594 } 1595 case TAG_ARG: { 1596 int pos = el.getArgPos(); 1597 MethodHandle prepender = prepender(ptypes[pos]); 1598 mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender, 1599 1, 0, // indexCoder, storage 1600 2 + pos // selected argument 1601 ); 1602 break; 1603 } 1604 default: 1605 throw new StringConcatException("Unhandled tag: " + el.getTag()); 1606 } 1607 } 1608 1609 // Fold in byte[] instantiation at argument 0 1610 mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY, 1611 1 // index 1612 ); 1613 1614 // Start combining length and coder mixers. 1615 // 1616 // Length is easy: constant lengths can be computed on the spot, and all non-constant 1617 // shapes have been either converted to Strings, or explicit methods for getting the 1618 // string length out of primitives are provided. 1619 // 1620 // Coders are more interesting. Only Object, String and char arguments (and constants) 1621 // can have non-Latin1 encoding. It is easier to blindly convert constants to String, 1622 // and deduce the coder from there. Arguments would be either converted to Strings 1623 // during the initial filtering, or handled by specializations in MIXERS. 1624 // 1625 // The method handle shape before and after all mixers are combined in is: 1626 // (long, <args>)String = ("indexCoder", <args>) 1627 long initialLengthCoder = INITIAL_CODER; 1628 for (RecipeElement el : recipe.getElements()) { 1629 switch (el.getTag()) { 1630 case TAG_CONST: 1631 String constant = el.getValue(); 1632 initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant); 1633 break; 1634 case TAG_ARG: 1635 int ac = el.getArgPos(); 1636 1637 Class<?> argClass = ptypes[ac]; 1638 MethodHandle mix = mixer(argClass); 1639 1640 // Compute new "index" in-place using old value plus the appropriate argument. 1641 mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix, 1642 0, // old-index 1643 1 + ac // selected argument 1644 ); 1645 1646 break; 1647 default: 1648 throw new StringConcatException("Unhandled tag: " + el.getTag()); 1649 } 1650 } 1651 1652 // Insert initial length and coder value here. 1653 // The method handle shape here is (<args>). 1654 mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder); 1655 1656 // Apply filters, converting the arguments: 1657 if (filters != null) { 1658 mh = MethodHandles.filterArguments(mh, 0, filters); 1659 } 1660 1661 return mh; 1662 } 1663 1664 private static MethodHandle prepender(Class<?> cl) { 1665 return PREPENDERS.computeIfAbsent(cl, PREPEND); 1666 } 1667 1668 private static MethodHandle mixer(Class<?> cl) { 1669 return MIXERS.computeIfAbsent(cl, MIX); 1670 } 1671 1672 // This one is deliberately non-lambdified to optimize startup time: 1673 private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() { 1674 @Override 1675 public MethodHandle apply(Class<?> c) { 1676 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class, 1677 Wrapper.asPrimitiveType(c)); 1678 } 1679 }; 1680 1681 // This one is deliberately non-lambdified to optimize startup time: 1682 private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() { 1683 @Override 1684 public MethodHandle apply(Class<?> c) { 1685 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, 1686 Wrapper.asPrimitiveType(c)); 1687 } 1688 }; 1689 1690 private static final MethodHandle SIMPLE; 1691 private static final MethodHandle NEW_STRING; 1692 private static final MethodHandle NEW_ARRAY; 1693 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; 1694 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; 1695 private static final long INITIAL_CODER; 1696 1697 static { 1698 try { 1699 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); | 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import jdk.internal.misc.Unsafe; 29 import jdk.internal.misc.VM; 30 import jdk.internal.org.objectweb.asm.ClassWriter; 31 import jdk.internal.org.objectweb.asm.Label; 32 import jdk.internal.org.objectweb.asm.MethodVisitor; 33 import jdk.internal.org.objectweb.asm.Opcodes; 34 import sun.invoke.util.Wrapper; 35 36 import java.lang.invoke.MethodHandles.Lookup; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 import java.util.Objects; 41 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentMap; 43 import java.util.function.Function; 44 45 import static jdk.internal.org.objectweb.asm.Opcodes.*; 46 47 /** 48 * <p>Methods to facilitate the creation of String concatenation methods, that 49 * can be used to efficiently concatenate a known number of arguments of known 50 * types, possibly after type adaptation and partial evaluation of arguments. 51 * These methods are typically used as <em>bootstrap methods</em> for {@code 52 * invokedynamic} call sites, to support the <em>string concatenation</em> 53 * feature of the Java Programming Language. 54 * 55 * <p>Indirect access to the behavior specified by the provided {@code 56 * MethodHandle} proceeds in order through two phases: 57 * 58 * <ol> 59 * <li><em>Linkage</em> occurs when the methods in this class are invoked. 60 * They take as arguments a method type describing the concatenated arguments 1512 * the character length of the integers, and the private String constructor 1513 * that accepts byte[] arrays without copying. While this strategy assumes a 1514 * particular implementation details for String, this opens the door for 1515 * building a very optimal concatenation sequence. This is the only strategy 1516 * that requires porting if there are private JDK changes occur. 1517 */ 1518 private static final class MethodHandleInlineCopyStrategy { 1519 static final Unsafe UNSAFE = Unsafe.getUnsafe(); 1520 1521 private MethodHandleInlineCopyStrategy() { 1522 // no instantiation 1523 } 1524 1525 static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { 1526 1527 // Fast-path two-argument Object + Object concatenations 1528 if (recipe.getElements().size() == 2) { 1529 // Two object arguments 1530 if (mt.parameterCount() == 2 && 1531 !mt.parameterType(0).isPrimitive() && 1532 !mt.parameterType(1).isPrimitive() && 1533 recipe.getElements().get(0).getTag() == TAG_ARG && 1534 recipe.getElements().get(1).getTag() == TAG_ARG) { 1535 1536 return SIMPLE; 1537 1538 } else if (mt.parameterCount() == 1 && 1539 !mt.parameterType(0).isPrimitive()) { 1540 // One Object argument, one constant 1541 MethodHandle mh = SIMPLE; 1542 1543 if (recipe.getElements().get(0).getTag() == TAG_CONST && 1544 recipe.getElements().get(1).getTag() == TAG_ARG) { 1545 // First recipe element is a constant 1546 return MethodHandles.insertArguments(mh, 0, 1547 recipe.getElements().get(0).getValue()); 1548 1549 } else if (recipe.getElements().get(1).getTag() == TAG_CONST && 1550 recipe.getElements().get(0).getTag() == TAG_ARG) { 1551 // Second recipe element is a constant 1552 return MethodHandles.insertArguments(mh, 1, 1553 recipe.getElements().get(1).getValue()); 1554 1555 } 1556 } 1557 // else... fall-through to slow-path 1558 } 1559 1560 // Create filters and obtain filtered parameter types. Filters would be used in the beginning 1561 // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings). 1562 // The filtered argument type list is used all over in the combinators below. 1563 Class<?>[] ptypes = mt.parameterArray(); 1564 MethodHandle[] filters = null; 1565 for (int i = 0; i < ptypes.length; i++) { 1566 MethodHandle filter = Stringifiers.forMost(ptypes[i]); 1567 if (filter != null) { 1568 if (filters == null) { 1569 filters = new MethodHandle[ptypes.length]; 1570 } 1571 filters[i] = filter; 1572 ptypes[i] = filter.type().returnType(); 1573 } 1574 } 1575 1576 // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes" 1577 // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are 1578 // assembled bottom-up, which makes the code arguably hard to read. 1579 1580 // Drop all remaining parameter types, leave only helper arguments: 1581 MethodHandle mh; 1582 1583 mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); 1584 1585 long initialLengthCoder = INITIAL_CODER; 1586 1587 // Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already 1588 // known from the combinators below. We are assembling the string backwards, so the index coded 1589 // into indexCoder is the *ending* index. 1590 String prefixConstant = null, suffixConstant = null; 1591 int pos = -1; 1592 for (RecipeElement el : recipe.getElements()) { 1593 // Do the prepend, and put "new" index at index 1 1594 switch (el.getTag()) { 1595 case TAG_CONST: { 1596 String constantValue = el.getValue(); 1597 1598 // Eagerly update the initialLengthCoder value 1599 initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constantValue); 1600 1601 if (pos < 0) { 1602 // Collecting into prefixConstant 1603 prefixConstant = prefixConstant == null ? constantValue : prefixConstant + constantValue; 1604 } else { 1605 // Collecting into suffixConstant 1606 suffixConstant = suffixConstant == null ? constantValue : suffixConstant + constantValue; 1607 } 1608 break; 1609 } 1610 case TAG_ARG: { 1611 1612 if (pos >= 0) { 1613 // Flush the previous non-constant arg with any prefix/suffix constant 1614 mh = MethodHandles.filterArgumentsWithCombiner( 1615 mh, 1, 1616 prepender(prefixConstant, ptypes[pos], suffixConstant), 1617 1, 0, // indexCoder, storage 1618 2 + pos // selected argument 1619 ); 1620 prefixConstant = suffixConstant = null; 1621 } 1622 // Mark the pos of next non-constant arg 1623 pos = el.getArgPos(); 1624 break; 1625 } 1626 default: 1627 throw new StringConcatException("Unhandled tag: " + el.getTag()); 1628 } 1629 } 1630 1631 // Insert any trailing args, constants 1632 if (pos >= 0) { 1633 mh = MethodHandles.filterArgumentsWithCombiner( 1634 mh, 1, 1635 prepender(prefixConstant, ptypes[pos], suffixConstant), 1636 1, 0, // indexCoder, storage 1637 2 + pos // selected argument 1638 ); 1639 } else if (prefixConstant != null) { 1640 // assert suffixConstant == null; 1641 // Sole prefixConstant can only happen if there were no non-constant arguments 1642 mh = MethodHandles.filterArgumentsWithCombiner( 1643 mh, 1, 1644 MethodHandles.insertArguments(prepender(null, String.class, null), 2, prefixConstant), 1645 1, 0 // indexCoder, storage 1646 ); 1647 } 1648 1649 // Fold in byte[] instantiation at argument 0 1650 mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY, 1651 1 // index 1652 ); 1653 1654 // Start combining length and coder mixers. 1655 // 1656 // Length is easy: constant lengths can be computed on the spot, and all non-constant 1657 // shapes have been either converted to Strings, or explicit methods for getting the 1658 // string length out of primitives are provided. 1659 // 1660 // Coders are more interesting. Only Object, String and char arguments (and constants) 1661 // can have non-Latin1 encoding. It is easier to blindly convert constants to String, 1662 // and deduce the coder from there. Arguments would be either converted to Strings 1663 // during the initial filtering, or handled by specializations in MIXERS. 1664 // 1665 // The method handle shape before and after all mixers are combined in is: 1666 // (long, <args>)String = ("indexCoder", <args>) 1667 1668 for (RecipeElement el : recipe.getElements()) { 1669 switch (el.getTag()) { 1670 case TAG_CONST: 1671 // Constants already handled in the code above 1672 break; 1673 case TAG_ARG: 1674 int ac = el.getArgPos(); 1675 1676 Class<?> argClass = ptypes[ac]; 1677 MethodHandle mix = mixer(argClass); 1678 1679 // Compute new "index" in-place using old value plus the appropriate argument. 1680 mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix, 1681 0, // old-index 1682 1 + ac // selected argument 1683 ); 1684 1685 break; 1686 default: 1687 throw new StringConcatException("Unhandled tag: " + el.getTag()); 1688 } 1689 } 1690 1691 // Insert initial length and coder value here. 1692 // The method handle shape here is (<args>). 1693 mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder); 1694 1695 // Apply filters, converting the arguments: 1696 if (filters != null) { 1697 mh = MethodHandles.filterArguments(mh, 0, filters); 1698 } 1699 1700 return mh; 1701 } 1702 1703 private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) { 1704 return MethodHandles.insertArguments( 1705 MethodHandles.insertArguments( 1706 PREPENDERS.computeIfAbsent(cl, 1707 c -> lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, 1708 "prepend", long.class, long.class, byte[].class, 1709 String.class, Wrapper.asPrimitiveType(c), String.class)), 1710 2, prefix), 3, suffix); 1711 } 1712 1713 private static MethodHandle mixer(Class<?> cl) { 1714 return MIXERS.computeIfAbsent(cl, MIX); 1715 } 1716 1717 1718 // This one is deliberately non-lambdified to optimize startup time: 1719 private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() { 1720 @Override 1721 public MethodHandle apply(Class<?> c) { 1722 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, 1723 Wrapper.asPrimitiveType(c)); 1724 } 1725 }; 1726 1727 private static final MethodHandle SIMPLE; 1728 private static final MethodHandle NEW_STRING; 1729 private static final MethodHandle NEW_ARRAY; 1730 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; 1731 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; 1732 private static final long INITIAL_CODER; 1733 1734 static { 1735 try { 1736 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); |