< prev index next >

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

Print this page




  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);


< prev index next >