< prev index next >

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

Print this page


   1 /*
   2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  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 


 175 
 176     /**
 177      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 178      * checks, etc.
 179      */
 180     private static final boolean DEBUG;
 181 
 182     /**
 183      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 184      * code, at the expense of contaminating the profiles.
 185      */
 186     private static final boolean CACHE_ENABLE;
 187 
 188     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 189 
 190     /**
 191      * Dump generated classes to disk, for debugging purposes.
 192      */
 193     private static final ProxyClassesDumper DUMPER;
 194 
 195     private static final Class<?> STRING_HELPER;
 196 
 197     static {
 198         // In case we need to double-back onto the StringConcatFactory during this
 199         // static initialization, make sure we have the reasonable defaults to complete
 200         // the static initialization properly. After that, actual users would use
 201         // the proper values we have read from the properties.
 202         STRATEGY = DEFAULT_STRATEGY;
 203         // CACHE_ENABLE = false; // implied
 204         // CACHE = null;         // implied
 205         // DEBUG = false;        // implied
 206         // DUMPER = null;        // implied
 207 
 208         try {
 209             STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
 210         } catch (Throwable e) {
 211             throw new AssertionError(e);
 212         }
 213 
 214         final String strategy =
 215                 VM.getSavedProperty("java.lang.invoke.stringConcat");
 216         CACHE_ENABLE = Boolean.parseBoolean(
 217                 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
 218         DEBUG = Boolean.parseBoolean(
 219                 VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
 220         final String dumpPath =
 221                 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
 222 
 223         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 224         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 225         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 226     }
 227 
 228     /**
 229      * Cache key is a composite of:
 230      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 231      *   - method type, describing the dynamic arguments for concatenation
 232      *   - concat recipe, describing the constants and concat shape
 233      */
 234     private static final class Key {
 235         final String className;
 236         final MethodType mt;
 237         final Recipe recipe;
 238 
 239         public Key(String className, MethodType mt, Recipe recipe) {
 240             this.className = className;
 241             this.mt = mt;


1510      *
1511      * <p>This strategy replicates what StringBuilders are doing: it builds the
1512      * byte[] array on its own and passes that byte[] array to String
1513      * constructor. This strategy requires access to some private APIs in JDK,
1514      * most notably, the read-only Integer/Long.stringSize methods that measure
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 


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);
1703             }
1704 
1705             PREPENDERS = new ConcurrentHashMap<>();
1706             MIXERS = new ConcurrentHashMap<>();
1707 
1708             SIMPLE     = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
1709             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
1710             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
1711         }
1712     }
1713 
1714     /**
1715      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1716      * delegate to {@code String.valueOf}, depending on argument's type.
1717      */
1718     private static final class Stringifiers {
1719         private Stringifiers() {
1720             // no instantiation
1721         }
1722 
1723         private static final MethodHandle OBJECT_INSTANCE =
1724             lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);














1725 
1726         private static class FloatStringifiers {
1727             private static final MethodHandle FLOAT_INSTANCE =
1728                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1729 
1730             private static final MethodHandle DOUBLE_INSTANCE =
1731                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1732         }
1733 
1734         private static class StringifierAny extends ClassValue<MethodHandle> {
1735 
1736             private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1737 
1738             @Override
1739             protected MethodHandle computeValue(Class<?> cl) {
1740                 if (cl == byte.class || cl == short.class || cl == int.class) {
1741                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1742                 } else if (cl == boolean.class) {
1743                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1744                 } else if (cl == char.class) {


1748                 } else {
1749                     MethodHandle mh = forMost(cl);
1750                     if (mh != null) {
1751                         return mh;
1752                     } else {
1753                         throw new IllegalStateException("Unknown class: " + cl);
1754                     }
1755                 }
1756             }
1757         }
1758 
1759         /**
1760          * Returns a stringifier for references and floats/doubles only.
1761          * Always returns null for other primitives.
1762          *
1763          * @param t class to stringify
1764          * @return stringifier; null, if not available
1765          */
1766         static MethodHandle forMost(Class<?> t) {
1767             if (!t.isPrimitive()) {
1768                 return OBJECT_INSTANCE;
1769             } else if (t == float.class) {
1770                 return FloatStringifiers.FLOAT_INSTANCE;
1771             } else if (t == double.class) {
1772                 return FloatStringifiers.DOUBLE_INSTANCE;
1773             }
1774             return null;
1775         }
1776 
1777         /**
1778          * Returns a stringifier for any type. Never returns null.
1779          *
1780          * @param t class to stringify
1781          * @return stringifier
1782          */
1783         static MethodHandle forAny(Class<?> t) {
1784             return StringifierAny.INSTANCE.get(t);
1785         }
1786     }
1787 
1788     /* ------------------------------- Common utilities ------------------------------------ */


   1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  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.org.objectweb.asm.ClassWriter;
  30 import jdk.internal.org.objectweb.asm.Label;
  31 import jdk.internal.org.objectweb.asm.MethodVisitor;
  32 import jdk.internal.org.objectweb.asm.Opcodes;
  33 import jdk.internal.vm.annotation.ForceInline;
  34 import sun.invoke.util.Wrapper;
  35 import sun.security.action.GetPropertyAction;
  36 
  37 import java.lang.invoke.MethodHandles.Lookup;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.List;
  41 import java.util.Objects;
  42 import java.util.Properties;
  43 import java.util.concurrent.ConcurrentHashMap;
  44 import java.util.concurrent.ConcurrentMap;
  45 import java.util.function.Function;
  46 
  47 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  48 


 174 
 175     /**
 176      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 177      * checks, etc.
 178      */
 179     private static final boolean DEBUG;
 180 
 181     /**
 182      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 183      * code, at the expense of contaminating the profiles.
 184      */
 185     private static final boolean CACHE_ENABLE;
 186 
 187     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 188 
 189     /**
 190      * Dump generated classes to disk, for debugging purposes.
 191      */
 192     private static final ProxyClassesDumper DUMPER;
 193 


 194     static {
 195         // In case we need to double-back onto the StringConcatFactory during this
 196         // static initialization, make sure we have the reasonable defaults to complete
 197         // the static initialization properly. After that, actual users would use
 198         // the proper values we have read from the properties.
 199         STRATEGY = DEFAULT_STRATEGY;
 200         // CACHE_ENABLE = false; // implied
 201         // CACHE = null;         // implied
 202         // DEBUG = false;        // implied
 203         // DUMPER = null;        // implied
 204 
 205         Properties props = GetPropertyAction.privilegedGetProperties();





 206         final String strategy =
 207                 props.getProperty("java.lang.invoke.stringConcat");
 208         CACHE_ENABLE = Boolean.parseBoolean(
 209                 props.getProperty("java.lang.invoke.stringConcat.cache"));
 210         DEBUG = Boolean.parseBoolean(
 211                 props.getProperty("java.lang.invoke.stringConcat.debug"));
 212         final String dumpPath =
 213                 props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
 214 
 215         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 216         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 217         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 218     }
 219 
 220     /**
 221      * Cache key is a composite of:
 222      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 223      *   - method type, describing the dynamic arguments for concatenation
 224      *   - concat recipe, describing the constants and concat shape
 225      */
 226     private static final class Key {
 227         final String className;
 228         final MethodType mt;
 229         final Recipe recipe;
 230 
 231         public Key(String className, MethodType mt, Recipe recipe) {
 232             this.className = className;
 233             this.mt = mt;


1502      *
1503      * <p>This strategy replicates what StringBuilders are doing: it builds the
1504      * byte[] array on its own and passes that byte[] array to String
1505      * constructor. This strategy requires access to some private APIs in JDK,
1506      * most notably, the read-only Integer/Long.stringSize methods that measure
1507      * the character length of the integers, and the private String constructor
1508      * that accepts byte[] arrays without copying. While this strategy assumes a
1509      * particular implementation details for String, this opens the door for
1510      * building a very optimal concatenation sequence. This is the only strategy
1511      * that requires porting if there are private JDK changes occur.
1512      */
1513     private static final class MethodHandleInlineCopyStrategy {
1514         static final Unsafe UNSAFE = Unsafe.getUnsafe();
1515 
1516         private MethodHandleInlineCopyStrategy() {
1517             // no instantiation
1518         }
1519 
1520         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1521 



























1522             // Create filters and obtain filtered parameter types. Filters would be used in the beginning
1523             // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
1524             // The filtered argument type list is used all over in the combinators below.
1525             Class<?>[] ptypes = mt.parameterArray();
1526             MethodHandle[] filters = null;
1527             for (int i = 0; i < ptypes.length; i++) {
1528                 MethodHandle filter = Stringifiers.forMost(ptypes[i]);
1529                 if (filter != null) {
1530                     if (filters == null) {
1531                         filters = new MethodHandle[ptypes.length];
1532                     }
1533                     filters[i] = filter;
1534                     ptypes[i] = filter.type().returnType();
1535                 }
1536             }
1537 
1538             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
1539             // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are
1540             // assembled bottom-up, which makes the code arguably hard to read.
1541 


1609                         );
1610 
1611                         break;
1612                     default:
1613                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1614                 }
1615             }
1616 
1617             // Insert initial length and coder value here.
1618             // The method handle shape here is (<args>).
1619             mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
1620 
1621             // Apply filters, converting the arguments:
1622             if (filters != null) {
1623                 mh = MethodHandles.filterArguments(mh, 0, filters);
1624             }
1625 
1626             return mh;
1627         }
1628 
1629         @ForceInline
1630         private static byte[] newArray(long indexCoder) {
1631             byte coder = (byte)(indexCoder >> 32);
1632             int index = (int)indexCoder;
1633             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
1634         }
1635 
1636         private static MethodHandle prepender(Class<?> cl) {
1637             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1638         }
1639 
1640         private static MethodHandle mixer(Class<?> cl) {
1641             return MIXERS.computeIfAbsent(cl, MIX);
1642         }
1643 
1644         // This one is deliberately non-lambdified to optimize startup time:
1645         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
1646             @Override
1647             public MethodHandle apply(Class<?> c) {
1648                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
1649                         Wrapper.asPrimitiveType(c));
1650             }
1651         };
1652 
1653         // This one is deliberately non-lambdified to optimize startup time:
1654         private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
1655             @Override
1656             public MethodHandle apply(Class<?> c) {
1657                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
1658                         Wrapper.asPrimitiveType(c));
1659             }
1660         };
1661 

1662         private static final MethodHandle NEW_STRING;
1663         private static final MethodHandle NEW_ARRAY;
1664         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1665         private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1666         private static final long INITIAL_CODER;
1667         static final Class<?> STRING_HELPER;
1668 
1669         static {
1670             try {
1671                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1672                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
1673                 INITIAL_CODER = (long) initCoder.invoke();
1674             } catch (Throwable e) {
1675                 throw new AssertionError(e);
1676             }
1677 
1678             PREPENDERS = new ConcurrentHashMap<>();
1679             MIXERS = new ConcurrentHashMap<>();
1680 

1681             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
1682             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
1683         }
1684     }
1685 
1686     /**
1687      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1688      * delegate to {@code String.valueOf}, depending on argument's type.
1689      */
1690     private static final class Stringifiers {
1691         private Stringifiers() {
1692             // no instantiation
1693         }
1694 
1695         private static class ObjectStringifier {
1696 
1697             // We need some additional conversion for Objects in general, because String.valueOf(Object)
1698             // may return null. String conversion rules in Java state we need to produce "null" String
1699             // in this case, so we provide a customized version that deals with this problematic corner case.
1700             private static String valueOf(Object value) {
1701                 String s;
1702                 return (value == null || (s = value.toString()) == null) ? "null" : s;
1703             }
1704 
1705             // Could have used MethodHandles.lookup() instead of Lookup.IMPL_LOOKUP, if not for the fact
1706             // java.lang.invoke Lookups are explicitly forbidden to be retrieved using that API.
1707             private static final MethodHandle INSTANCE =
1708                     lookupStatic(Lookup.IMPL_LOOKUP, ObjectStringifier.class, "valueOf", String.class, Object.class);
1709 
1710         }
1711 
1712         private static class FloatStringifiers {
1713             private static final MethodHandle FLOAT_INSTANCE =
1714                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1715 
1716             private static final MethodHandle DOUBLE_INSTANCE =
1717                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1718         }
1719 
1720         private static class StringifierAny extends ClassValue<MethodHandle> {
1721 
1722             private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1723 
1724             @Override
1725             protected MethodHandle computeValue(Class<?> cl) {
1726                 if (cl == byte.class || cl == short.class || cl == int.class) {
1727                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1728                 } else if (cl == boolean.class) {
1729                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1730                 } else if (cl == char.class) {


1734                 } else {
1735                     MethodHandle mh = forMost(cl);
1736                     if (mh != null) {
1737                         return mh;
1738                     } else {
1739                         throw new IllegalStateException("Unknown class: " + cl);
1740                     }
1741                 }
1742             }
1743         }
1744 
1745         /**
1746          * Returns a stringifier for references and floats/doubles only.
1747          * Always returns null for other primitives.
1748          *
1749          * @param t class to stringify
1750          * @return stringifier; null, if not available
1751          */
1752         static MethodHandle forMost(Class<?> t) {
1753             if (!t.isPrimitive()) {
1754                 return ObjectStringifier.INSTANCE;
1755             } else if (t == float.class) {
1756                 return FloatStringifiers.FLOAT_INSTANCE;
1757             } else if (t == double.class) {
1758                 return FloatStringifiers.DOUBLE_INSTANCE;
1759             }
1760             return null;
1761         }
1762 
1763         /**
1764          * Returns a stringifier for any type. Never returns null.
1765          *
1766          * @param t class to stringify
1767          * @return stringifier
1768          */
1769         static MethodHandle forAny(Class<?> t) {
1770             return StringifierAny.INSTANCE.get(t);
1771         }
1772     }
1773 
1774     /* ------------------------------- Common utilities ------------------------------------ */


< prev index next >