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);
1700 INITIAL_CODER = (long) initCoder.invoke();
1701 } catch (Throwable e) {
1702 throw new AssertionError(e);
|
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
1591 // We need one prepender per argument, but also need to fold in constants. We do so by greedily
1592 // create prependers that fold in surrounding constants into the argument prepender. This reduces
1593 // the number of unique MH combinator tree shapes we'll create in an application.
1594 String prefixConstant = null, suffixConstant = null;
1595 int pos = -1;
1596 for (RecipeElement el : recipe.getElements()) {
1597 // Do the prepend, and put "new" index at index 1
1598 switch (el.getTag()) {
1599 case TAG_CONST: {
1600 String constantValue = el.getValue();
1601
1602 // Eagerly update the initialLengthCoder value
1603 initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constantValue);
1604
1605 if (pos < 0) {
1606 // Collecting into prefixConstant
1607 prefixConstant = prefixConstant == null ? constantValue : prefixConstant + constantValue;
1608 } else {
1609 // Collecting into suffixConstant
1610 suffixConstant = suffixConstant == null ? constantValue : suffixConstant + constantValue;
1611 }
1612 break;
1613 }
1614 case TAG_ARG: {
1615
1616 if (pos >= 0) {
1617 // Flush the previous non-constant arg with any prefix/suffix constant
1618 mh = MethodHandles.filterArgumentsWithCombiner(
1619 mh, 1,
1620 prepender(prefixConstant, ptypes[pos], suffixConstant),
1621 1, 0, // indexCoder, storage
1622 2 + pos // selected argument
1623 );
1624 prefixConstant = suffixConstant = null;
1625 }
1626 // Mark the pos of next non-constant arg
1627 pos = el.getArgPos();
1628 break;
1629 }
1630 default:
1631 throw new StringConcatException("Unhandled tag: " + el.getTag());
1632 }
1633 }
1634
1635 // Insert any trailing args, constants
1636 if (pos >= 0) {
1637 mh = MethodHandles.filterArgumentsWithCombiner(
1638 mh, 1,
1639 prepender(prefixConstant, ptypes[pos], suffixConstant),
1640 1, 0, // indexCoder, storage
1641 2 + pos // selected argument
1642 );
1643 } else if (prefixConstant != null) {
1644 assert (suffixConstant == null);
1645 // Sole prefixConstant can only happen if there were no non-constant arguments
1646 mh = MethodHandles.filterArgumentsWithCombiner(
1647 mh, 1,
1648 MethodHandles.insertArguments(prepender(null, String.class, null), 2, prefixConstant),
1649 1, 0 // indexCoder, storage
1650 );
1651 }
1652
1653 // Fold in byte[] instantiation at argument 0
1654 mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
1655 1 // index
1656 );
1657
1658 // Start combining length and coder mixers.
1659 //
1660 // Length is easy: constant lengths can be computed on the spot, and all non-constant
1661 // shapes have been either converted to Strings, or explicit methods for getting the
1662 // string length out of primitives are provided.
1663 //
1664 // Coders are more interesting. Only Object, String and char arguments (and constants)
1665 // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1666 // and deduce the coder from there. Arguments would be either converted to Strings
1667 // during the initial filtering, or handled by specializations in MIXERS.
1668 //
1669 // The method handle shape before and after all mixers are combined in is:
1670 // (long, <args>)String = ("indexCoder", <args>)
1671
1672 for (RecipeElement el : recipe.getElements()) {
1673 switch (el.getTag()) {
1674 case TAG_CONST:
1675 // Constants already handled in the code above
1676 break;
1677 case TAG_ARG:
1678 int ac = el.getArgPos();
1679
1680 Class<?> argClass = ptypes[ac];
1681 MethodHandle mix = mixer(argClass);
1682
1683 // Compute new "index" in-place using old value plus the appropriate argument.
1684 mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix,
1685 0, // old-index
1686 1 + ac // selected argument
1687 );
1688
1689 break;
1690 default:
1691 throw new StringConcatException("Unhandled tag: " + el.getTag());
1692 }
1693 }
1694
1695 // Insert initial length and coder value here.
1696 // The method handle shape here is (<args>).
1697 mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
1698
1699 // Apply filters, converting the arguments:
1700 if (filters != null) {
1701 mh = MethodHandles.filterArguments(mh, 0, filters);
1702 }
1703
1704 return mh;
1705 }
1706
1707 private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
1708 return MethodHandles.insertArguments(
1709 MethodHandles.insertArguments(
1710 PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
1711 }
1712
1713 private static MethodHandle mixer(Class<?> cl) {
1714 return MIXERS.computeIfAbsent(cl, MIX);
1715 }
1716
1717 // This one is deliberately non-lambdified to optimize startup time:
1718 private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
1719 @Override
1720 public MethodHandle apply(Class<?> c) {
1721 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
1722 String.class, Wrapper.asPrimitiveType(c), String.class);
1723 }
1724 };
1725
1726 // This one is deliberately non-lambdified to optimize startup time:
1727 private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
1728 @Override
1729 public MethodHandle apply(Class<?> c) {
1730 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
1731 Wrapper.asPrimitiveType(c));
1732 }
1733 };
1734
1735 private static final MethodHandle SIMPLE;
1736 private static final MethodHandle NEW_STRING;
1737 private static final MethodHandle NEW_ARRAY;
1738 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1739 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1740 private static final long INITIAL_CODER;
1741
1742 static {
1743 try {
1744 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
1745 INITIAL_CODER = (long) initCoder.invoke();
1746 } catch (Throwable e) {
1747 throw new AssertionError(e);
|