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 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
61 * count and types, and optionally the String <em>recipe</em>, plus the
62 * constants that participate in the String concatenation. The details on
63 * accepted recipe shapes are described further below. Linkage may involve
64 * dynamically loading a new class that implements the expected concatenation
116 /**
117 * Maximum number of argument slots in String Concat call.
118 *
119 * While the maximum number of argument slots that indy call can handle is 253,
120 * we do not use all those slots, to let the strategies with MethodHandle
121 * combinators to use some arguments.
122 */
123 private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
124
125 /**
126 * Concatenation strategy to use. See {@link Strategy} for possible options.
127 * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
128 */
129 private static Strategy STRATEGY;
130
131 /**
132 * Default strategy to use for concatenation.
133 */
134 private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
135
136 private enum Strategy {
137 /**
138 * Bytecode generator, calling into {@link java.lang.StringBuilder}.
139 */
140 BC_SB,
141
142 /**
143 * Bytecode generator, calling into {@link java.lang.StringBuilder};
144 * but trying to estimate the required storage.
145 */
146 BC_SB_SIZED,
147
148 /**
149 * Bytecode generator, calling into {@link java.lang.StringBuilder};
150 * but computing the required storage exactly.
151 */
152 BC_SB_SIZED_EXACT,
153
154 /**
155 * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
172
173 /**
174 * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
175 * checks, etc.
176 */
177 private static final boolean DEBUG;
178
179 /**
180 * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
181 * code, at the expense of contaminating the profiles.
182 */
183 private static final boolean CACHE_ENABLE;
184
185 private static final ConcurrentMap<Key, MethodHandle> CACHE;
186
187 /**
188 * Dump generated classes to disk, for debugging purposes.
189 */
190 private static final ProxyClassesDumper DUMPER;
191
192 private static final Class<?> STRING_HELPER;
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 try {
206 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
207 } catch (Throwable e) {
208 throw new AssertionError(e);
209 }
210
211 final String strategy =
212 VM.getSavedProperty("java.lang.invoke.stringConcat");
213 CACHE_ENABLE = Boolean.parseBoolean(
214 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
215 DEBUG = Boolean.parseBoolean(
216 VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
217 final String dumpPath =
218 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
219
220 STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
221 CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
222 DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
223 }
224
225 /**
226 * Cache key is a composite of:
227 * - class name, that lets to disambiguate stubs, to avoid excess sharing
228 * - method type, describing the dynamic arguments for concatenation
229 * - concat recipe, describing the constants and concat shape
230 */
706 ptype != Object.class) { // truncate to Object
707 if (ptypes == null) {
708 ptypes = args.parameterArray();
709 }
710 ptypes[i] = Object.class;
711 }
712 // else other primitives or String or Object (unchanged)
713 }
714 return (ptypes != null)
715 ? MethodType.methodType(args.returnType(), ptypes)
716 : args;
717 }
718
719 private static String getClassName(Class<?> hostClass) throws StringConcatException {
720 /*
721 When cache is enabled, we want to cache as much as we can.
722
723 However, there are two peculiarities:
724
725 a) The generated class should stay within the same package as the
726 host class, to allow Unsafe.defineAnonymousClass access controls
727 to work properly. JDK may choose to fail with IllegalAccessException
728 when accessing a VM anonymous class with non-privileged callers,
729 see JDK-8058575.
730
731 b) If we mark the stub with some prefix, say, derived from the package
732 name because of (a), we can technically use that stub in other packages.
733 But the call stack traces would be extremely puzzling to unsuspecting users
734 and profiling tools: whatever stub wins the race, would be linked in all
735 similar callsites.
736
737 Therefore, we set the class prefix to match the host class package, and use
738 the prefix as the cache key too. This only affects BC_* strategies, and only when
739 cache is enabled.
740 */
741
742 switch (STRATEGY) {
743 case BC_SB:
744 case BC_SB_SIZED:
745 case BC_SB_SIZED_EXACT: {
746 if (CACHE_ENABLE) {
747 String pkgName = hostClass.getPackageName();
748 return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
749 } else {
750 return hostClass.getName().replace('.', '/') + "$$StringConcat";
751 }
752 }
753 case MH_SB_SIZED:
754 case MH_SB_SIZED_EXACT:
755 case MH_INLINE_SIZED_EXACT:
756 // MethodHandle strategies do not need a class name.
757 return "";
758 default:
759 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
760 }
761 }
762
763 private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
764 try {
765 switch (STRATEGY) {
766 case BC_SB:
767 return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
768 case BC_SB_SIZED:
769 return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
770 case BC_SB_SIZED_EXACT:
802 boolean isSized() {
803 return sized;
804 }
805
806 boolean isExact() {
807 return exact;
808 }
809 }
810
811 /**
812 * Bytecode StringBuilder strategy.
813 *
814 * <p>This strategy operates in three modes, gated by {@link Mode}.
815 *
816 * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
817 *
818 * <p>This strategy spins up the bytecode that has the same StringBuilder
819 * chain javac would otherwise emit. This strategy uses only the public API,
820 * and comes as the baseline for the current JDK behavior. On other words,
821 * this strategy moves the javac generated bytecode to runtime. The
822 * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
823 * the caller class coming from the BSM -- in other words, the protection
824 * guarantees are inherited from the method where invokedynamic was
825 * originally called. This means, among other things, that the bytecode is
826 * verified for all non-JDK uses.
827 *
828 * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
829 * sized".</b>
830 *
831 * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
832 * tries to guess the capacity required for StringBuilder to accept all
833 * arguments without resizing. This strategy only makes an educated guess:
834 * it only guesses the space required for known types (e.g. primitives and
835 * Strings), but does not otherwise convert arguments. Therefore, the
836 * capacity estimate may be wrong, and StringBuilder may have to
837 * transparently resize or trim when doing the actual concatenation. While
838 * this does not constitute a correctness issue (in the end, that what BC_SB
839 * has to do anyway), this does pose a potential performance problem.
840 *
841 * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
842 * sized exactly".</b>
843 *
844 * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
845 * converting all arguments to String in order to get the exact capacity
846 * StringBuilder should have. The conversion is done via the public
847 * String.valueOf and/or Object.toString methods, and does not touch any
848 * private String API.
849 */
850 private static final class BytecodeStringBuilderStrategy {
851 static final Unsafe UNSAFE = Unsafe.getUnsafe();
852 static final int CLASSFILE_VERSION = 52;
853 static final String METHOD_NAME = "concat";
854
855 private BytecodeStringBuilderStrategy() {
856 // no instantiation
857 }
858
859 private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
860 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
861
862 cw.visit(CLASSFILE_VERSION,
863 ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
864 className, // Unsafe.defineAnonymousClass would append an unique ID
865 null,
866 "java/lang/Object",
867 null
868 );
869
870 MethodVisitor mv = cw.visitMethod(
871 ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
872 METHOD_NAME,
873 args.toMethodDescriptorString(),
874 null,
875 null);
876
877 mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
878 mv.visitCode();
879
880 Class<?>[] arr = args.parameterArray();
881 boolean[] guaranteedNonNull = new boolean[arr.length];
882
883 if (mode.isExact()) {
884 /*
885 In exact mode, we need to convert all arguments to their String representations,
886 as this allows to compute their String sizes exactly. We cannot use private
887 methods for primitives in here, therefore we need to convert those as well.
888
889 We also record what arguments are guaranteed to be non-null as the result
890 of the conversion. String.valueOf does the null checks for us. The only
891 corner case to take care of is String.valueOf(Object) returning null itself.
892
893 Also, if any conversion happened, then the slot indices in the incoming
894 arguments are not equal to the final local maps. The only case this may break
895 is when converting 2-slot long/double argument to 1-slot String. Therefore,
896 we get away with tracking modified offset, since no conversion can overwrite
1126
1127 mv.visitLabel(l0);
1128 }
1129
1130 mv.visitMethodInsn(
1131 INVOKEVIRTUAL,
1132 "java/lang/StringBuilder",
1133 "toString",
1134 "()Ljava/lang/String;",
1135 false
1136 );
1137
1138 mv.visitInsn(ARETURN);
1139
1140 mv.visitMaxs(-1, -1);
1141 mv.visitEnd();
1142 cw.visitEnd();
1143
1144 byte[] classBytes = cw.toByteArray();
1145 try {
1146 Class<?> hostClass = lookup.lookupClass();
1147 Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
1148 UNSAFE.ensureClassInitialized(innerClass);
1149 dumpIfEnabled(innerClass.getName(), classBytes);
1150 return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
1151 } catch (Exception e) {
1152 dumpIfEnabled(className + "$$FAILED", classBytes);
1153 throw new StringConcatException("Exception while spinning the class", e);
1154 }
1155 }
1156
1157 private static void dumpIfEnabled(String name, byte[] bytes) {
1158 if (DUMPER != null) {
1159 DUMPER.dumpClass(name, bytes);
1160 }
1161 }
1162
1163 private static String getSBAppendDesc(Class<?> cl) {
1164 if (cl.isPrimitive()) {
1165 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1166 return "(I)Ljava/lang/StringBuilder;";
1167 } else if (cl == Boolean.TYPE) {
1168 return "(Z)Ljava/lang/StringBuilder;";
1169 } else if (cl == Character.TYPE) {
1170 return "(C)Ljava/lang/StringBuilder;";
1253 return 0;
1254 } else if (c == Long.TYPE || c == Double.TYPE) {
1255 return 2;
1256 }
1257 return 1;
1258 }
1259 }
1260
1261 /**
1262 * MethodHandle StringBuilder strategy.
1263 *
1264 * <p>This strategy operates in two modes, gated by {@link Mode}.
1265 *
1266 * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1267 * sized".</b>
1268 *
1269 * <p>This strategy avoids spinning up the bytecode by building the
1270 * computation on MethodHandle combinators. The computation is built with
1271 * public MethodHandle APIs, resolved from a public Lookup sequence, and
1272 * ends up calling the public StringBuilder API. Therefore, this strategy
1273 * does not use any private API at all, even the Unsafe.defineAnonymousClass,
1274 * since everything is handled under cover by java.lang.invoke APIs.
1275 *
1276 * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1277 * sized exactly".</b>
1278 *
1279 * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1280 * converting all arguments to String in order to get the exact capacity
1281 * StringBuilder should have. The conversion is done via the public
1282 * String.valueOf and/or Object.toString methods, and does not touch any
1283 * private String API.
1284 */
1285 private static final class MethodHandleStringBuilderStrategy {
1286
1287 private MethodHandleStringBuilderStrategy() {
1288 // no instantiation
1289 }
1290
1291 private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1292 int pc = mt.parameterCount();
1293
1294 Class<?>[] ptypes = mt.parameterArray();
1295 MethodHandle[] filters = new MethodHandle[ptypes.length];
1296 for (int i = 0; i < ptypes.length; i++) {
1297 MethodHandle filter;
1298 switch (mode) {
1299 case SIZED:
1300 // In sized mode, we convert all references and floats/doubles
1301 // to String: there is no specialization for different
1302 // classes in StringBuilder API, and it will convert to
1303 // String internally anyhow.
1304 filter = Stringifiers.forMost(ptypes[i]);
1305 break;
1306 case SIZED_EXACT:
1444 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1445 return v1 + v2 + v3 + v4 + v5 + v6;
1446 }
1447
1448 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1449 return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1450 }
1451
1452 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1453 return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1454 }
1455
1456 private static int sum(int initial, int[] vs) {
1457 int sum = initial;
1458 for (int v : vs) {
1459 sum += v;
1460 }
1461 return sum;
1462 }
1463
1464 private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1465
1466 // This one is deliberately non-lambdified to optimize startup time:
1467 private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1468 @Override
1469 public MethodHandle apply(Integer cnt) {
1470 if (cnt == 1) {
1471 return MethodHandles.identity(int.class);
1472 } else if (cnt <= 8) {
1473 // Variable-arity collectors are not as efficient as small-count methods,
1474 // unroll some initial sizes.
1475 Class<?>[] cls = new Class<?>[cnt];
1476 Arrays.fill(cls, int.class);
1477 return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1478 } else {
1479 return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1480 .asCollector(int[].class, cnt - 1);
1481 }
1482 }
1483 };
1484
1485 private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1486
1487 static {
1488 SUMMERS = new ConcurrentHashMap<>();
1489 Lookup publicLookup = MethodHandles.publicLookup();
1490 NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1491 STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1492 BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1493 if (DEBUG) {
1494 BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
1495 MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
1496 } else {
1497 BUILDER_TO_STRING_CHECKED = null;
1498 }
1499 }
1500
1501 }
1502
1503
1504 /**
1505 * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1506 * sized exactly".</b>
1507 *
1508 * <p>This strategy replicates what StringBuilders are doing: it builds the
1509 * byte[] array on its own and passes that byte[] array to String
1510 * constructor. This strategy requires access to some private APIs in JDK,
1511 * most notably, the read-only Integer/Long.stringSize methods that measure
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
1719 mh = MethodHandles.filterArguments(mh, 0, filters);
1720 }
1721
1722 return mh;
1723 }
1724
1725 private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
1726 return MethodHandles.insertArguments(
1727 MethodHandles.insertArguments(
1728 PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
1729 }
1730
1731 private static MethodHandle mixer(Class<?> cl) {
1732 return MIXERS.computeIfAbsent(cl, MIX);
1733 }
1734
1735 // This one is deliberately non-lambdified to optimize startup time:
1736 private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
1737 @Override
1738 public MethodHandle apply(Class<?> c) {
1739 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
1740 String.class, Wrapper.asPrimitiveType(c), String.class);
1741 }
1742 };
1743
1744 // This one is deliberately non-lambdified to optimize startup time:
1745 private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
1746 @Override
1747 public MethodHandle apply(Class<?> c) {
1748 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
1749 Wrapper.asPrimitiveType(c));
1750 }
1751 };
1752
1753 private static final MethodHandle SIMPLE;
1754 private static final MethodHandle NEW_STRING;
1755 private static final MethodHandle NEW_ARRAY;
1756 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1757 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1758 private static final long INITIAL_CODER;
1759
1760 static {
1761 try {
1762 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
1763 INITIAL_CODER = (long) initCoder.invoke();
1764 } catch (Throwable e) {
1765 throw new AssertionError(e);
1766 }
1767
1768 PREPENDERS = new ConcurrentHashMap<>();
1769 MIXERS = new ConcurrentHashMap<>();
1770
1771 SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
1772 NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
1773 NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
1774 }
1775 }
1776
1777 /**
1778 * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1779 * delegate to {@code String.valueOf}, depending on argument's type.
1780 */
1781 private static final class Stringifiers {
1782 private Stringifiers() {
1783 // no instantiation
1784 }
1785
1786 private static final MethodHandle OBJECT_INSTANCE =
1787 lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
1788
1789 private static class FloatStringifiers {
1790 private static final MethodHandle FLOAT_INSTANCE =
1791 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1792
1793 private static final MethodHandle DOUBLE_INSTANCE =
1794 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1795 }
1796
1797 private static class StringifierAny extends ClassValue<MethodHandle> {
1798
1799 private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1800
1801 @Override
1802 protected MethodHandle computeValue(Class<?> cl) {
1803 if (cl == byte.class || cl == short.class || cl == int.class) {
1804 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1805 } else if (cl == boolean.class) {
1806 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1807 } else if (cl == char.class) {
|
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.access.JavaLangAccess;
29 import jdk.internal.access.SharedSecrets;
30 import jdk.internal.misc.VM;
31 import jdk.internal.org.objectweb.asm.ClassWriter;
32 import jdk.internal.org.objectweb.asm.Label;
33 import jdk.internal.org.objectweb.asm.MethodVisitor;
34 import jdk.internal.org.objectweb.asm.Opcodes;
35 import sun.invoke.util.Wrapper;
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.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ConcurrentMap;
44 import java.util.function.Function;
45
46 import static java.lang.invoke.MethodHandles.lookup;
47 import static java.lang.invoke.MethodType.methodType;
48 import static java.lang.invoke.MethodHandleNatives.Constants.*;
49 import static jdk.internal.org.objectweb.asm.Opcodes.*;
50
51 /**
52 * <p>Methods to facilitate the creation of String concatenation methods, that
53 * can be used to efficiently concatenate a known number of arguments of known
54 * types, possibly after type adaptation and partial evaluation of arguments.
55 * These methods are typically used as <em>bootstrap methods</em> for {@code
56 * invokedynamic} call sites, to support the <em>string concatenation</em>
57 * feature of the Java Programming Language.
58 *
59 * <p>Indirect access to the behavior specified by the provided {@code
60 * MethodHandle} proceeds in order through two phases:
61 *
62 * <ol>
63 * <li><em>Linkage</em> occurs when the methods in this class are invoked.
64 * They take as arguments a method type describing the concatenated arguments
65 * count and types, and optionally the String <em>recipe</em>, plus the
66 * constants that participate in the String concatenation. The details on
67 * accepted recipe shapes are described further below. Linkage may involve
68 * dynamically loading a new class that implements the expected concatenation
120 /**
121 * Maximum number of argument slots in String Concat call.
122 *
123 * While the maximum number of argument slots that indy call can handle is 253,
124 * we do not use all those slots, to let the strategies with MethodHandle
125 * combinators to use some arguments.
126 */
127 private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
128
129 /**
130 * Concatenation strategy to use. See {@link Strategy} for possible options.
131 * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
132 */
133 private static Strategy STRATEGY;
134
135 /**
136 * Default strategy to use for concatenation.
137 */
138 private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
139
140 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
141
142 private enum Strategy {
143 /**
144 * Bytecode generator, calling into {@link java.lang.StringBuilder}.
145 */
146 BC_SB,
147
148 /**
149 * Bytecode generator, calling into {@link java.lang.StringBuilder};
150 * but trying to estimate the required storage.
151 */
152 BC_SB_SIZED,
153
154 /**
155 * Bytecode generator, calling into {@link java.lang.StringBuilder};
156 * but computing the required storage exactly.
157 */
158 BC_SB_SIZED_EXACT,
159
160 /**
161 * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
178
179 /**
180 * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
181 * checks, etc.
182 */
183 private static final boolean DEBUG;
184
185 /**
186 * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
187 * code, at the expense of contaminating the profiles.
188 */
189 private static final boolean CACHE_ENABLE;
190
191 private static final ConcurrentMap<Key, MethodHandle> CACHE;
192
193 /**
194 * Dump generated classes to disk, for debugging purposes.
195 */
196 private static final ProxyClassesDumper DUMPER;
197
198 static {
199 // In case we need to double-back onto the StringConcatFactory during this
200 // static initialization, make sure we have the reasonable defaults to complete
201 // the static initialization properly. After that, actual users would use
202 // the proper values we have read from the properties.
203 STRATEGY = DEFAULT_STRATEGY;
204 // CACHE_ENABLE = false; // implied
205 // CACHE = null; // implied
206 // DEBUG = false; // implied
207 // DUMPER = null; // implied
208
209 final String strategy =
210 VM.getSavedProperty("java.lang.invoke.stringConcat");
211 CACHE_ENABLE = Boolean.parseBoolean(
212 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
213 DEBUG = Boolean.parseBoolean(
214 VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
215 final String dumpPath =
216 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
217
218 STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
219 CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
220 DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
221 }
222
223 /**
224 * Cache key is a composite of:
225 * - class name, that lets to disambiguate stubs, to avoid excess sharing
226 * - method type, describing the dynamic arguments for concatenation
227 * - concat recipe, describing the constants and concat shape
228 */
704 ptype != Object.class) { // truncate to Object
705 if (ptypes == null) {
706 ptypes = args.parameterArray();
707 }
708 ptypes[i] = Object.class;
709 }
710 // else other primitives or String or Object (unchanged)
711 }
712 return (ptypes != null)
713 ? MethodType.methodType(args.returnType(), ptypes)
714 : args;
715 }
716
717 private static String getClassName(Class<?> hostClass) throws StringConcatException {
718 /*
719 When cache is enabled, we want to cache as much as we can.
720
721 However, there are two peculiarities:
722
723 a) The generated class should stay within the same package as the
724 host class. JDK may choose to fail with IllegalAccessException
725 when accessing a VM anonymous class with non-privileged callers,
726 see JDK-8058575.
727
728 b) If we mark the stub with some prefix, say, derived from the package
729 name because of (a), we can technically use that stub in other packages.
730 But the call stack traces would be extremely puzzling to unsuspecting users
731 and profiling tools: whatever stub wins the race, would be linked in all
732 similar callsites.
733
734 Therefore, we set the class prefix to match the host class package, and use
735 the prefix as the cache key too. This only affects BC_* strategies, and only when
736 cache is enabled.
737 */
738
739 switch (STRATEGY) {
740 case BC_SB:
741 case BC_SB_SIZED:
742 case BC_SB_SIZED_EXACT: {
743 if (CACHE_ENABLE) {
744 String pkgName = hostClass.getPackageName();
745 return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
746 } else {
747 String name = hostClass.isHiddenClass() ? hostClass.getName().replace('/', '_')
748 : hostClass.getName();
749 return name.replace('.', '/') + "$$StringConcat";
750 }
751 }
752 case MH_SB_SIZED:
753 case MH_SB_SIZED_EXACT:
754 case MH_INLINE_SIZED_EXACT:
755 // MethodHandle strategies do not need a class name.
756 return "";
757 default:
758 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
759 }
760 }
761
762 private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
763 try {
764 switch (STRATEGY) {
765 case BC_SB:
766 return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
767 case BC_SB_SIZED:
768 return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
769 case BC_SB_SIZED_EXACT:
801 boolean isSized() {
802 return sized;
803 }
804
805 boolean isExact() {
806 return exact;
807 }
808 }
809
810 /**
811 * Bytecode StringBuilder strategy.
812 *
813 * <p>This strategy operates in three modes, gated by {@link Mode}.
814 *
815 * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
816 *
817 * <p>This strategy spins up the bytecode that has the same StringBuilder
818 * chain javac would otherwise emit. This strategy uses only the public API,
819 * and comes as the baseline for the current JDK behavior. On other words,
820 * this strategy moves the javac generated bytecode to runtime. The
821 * generated bytecode is loaded via Lookup::defineClass, but with
822 * the caller class coming from the BSM -- in other words, the protection
823 * guarantees are inherited from the method where invokedynamic was
824 * originally called. This means, among other things, that the bytecode is
825 * verified for all non-JDK uses.
826 *
827 * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
828 * sized".</b>
829 *
830 * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
831 * tries to guess the capacity required for StringBuilder to accept all
832 * arguments without resizing. This strategy only makes an educated guess:
833 * it only guesses the space required for known types (e.g. primitives and
834 * Strings), but does not otherwise convert arguments. Therefore, the
835 * capacity estimate may be wrong, and StringBuilder may have to
836 * transparently resize or trim when doing the actual concatenation. While
837 * this does not constitute a correctness issue (in the end, that what BC_SB
838 * has to do anyway), this does pose a potential performance problem.
839 *
840 * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
841 * sized exactly".</b>
842 *
843 * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
844 * converting all arguments to String in order to get the exact capacity
845 * StringBuilder should have. The conversion is done via the public
846 * String.valueOf and/or Object.toString methods, and does not touch any
847 * private String API.
848 */
849 private static final class BytecodeStringBuilderStrategy {
850 static final int CLASSFILE_VERSION = 52;
851 static final String METHOD_NAME = "concat";
852
853 private BytecodeStringBuilderStrategy() {
854 // no instantiation
855 }
856
857 private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
858 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
859
860 cw.visit(CLASSFILE_VERSION,
861 ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
862 className,
863 null,
864 "java/lang/Object",
865 null
866 );
867
868 MethodVisitor mv = cw.visitMethod(
869 ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
870 METHOD_NAME,
871 args.toMethodDescriptorString(),
872 null,
873 null);
874
875 // use of @ForceInline no longer has any effect
876 mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
877 mv.visitCode();
878
879 Class<?>[] arr = args.parameterArray();
880 boolean[] guaranteedNonNull = new boolean[arr.length];
881
882 if (mode.isExact()) {
883 /*
884 In exact mode, we need to convert all arguments to their String representations,
885 as this allows to compute their String sizes exactly. We cannot use private
886 methods for primitives in here, therefore we need to convert those as well.
887
888 We also record what arguments are guaranteed to be non-null as the result
889 of the conversion. String.valueOf does the null checks for us. The only
890 corner case to take care of is String.valueOf(Object) returning null itself.
891
892 Also, if any conversion happened, then the slot indices in the incoming
893 arguments are not equal to the final local maps. The only case this may break
894 is when converting 2-slot long/double argument to 1-slot String. Therefore,
895 we get away with tracking modified offset, since no conversion can overwrite
1125
1126 mv.visitLabel(l0);
1127 }
1128
1129 mv.visitMethodInsn(
1130 INVOKEVIRTUAL,
1131 "java/lang/StringBuilder",
1132 "toString",
1133 "()Ljava/lang/String;",
1134 false
1135 );
1136
1137 mv.visitInsn(ARETURN);
1138
1139 mv.visitMaxs(-1, -1);
1140 mv.visitEnd();
1141 cw.visitEnd();
1142
1143 byte[] classBytes = cw.toByteArray();
1144 try {
1145 Class<?> innerClass = lookup.defineHiddenClass(classBytes,true).lookupClass();
1146 dumpIfEnabled(className, classBytes);
1147 return lookup.findStatic(innerClass, METHOD_NAME, args);
1148 } catch (Exception e) {
1149 dumpIfEnabled(className + "$$FAILED", classBytes);
1150 throw new StringConcatException("Exception while spinning the class", e);
1151 }
1152 }
1153
1154 private static void dumpIfEnabled(String name, byte[] bytes) {
1155 if (DUMPER != null) {
1156 DUMPER.dumpClass(name, bytes);
1157 }
1158 }
1159
1160 private static String getSBAppendDesc(Class<?> cl) {
1161 if (cl.isPrimitive()) {
1162 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1163 return "(I)Ljava/lang/StringBuilder;";
1164 } else if (cl == Boolean.TYPE) {
1165 return "(Z)Ljava/lang/StringBuilder;";
1166 } else if (cl == Character.TYPE) {
1167 return "(C)Ljava/lang/StringBuilder;";
1250 return 0;
1251 } else if (c == Long.TYPE || c == Double.TYPE) {
1252 return 2;
1253 }
1254 return 1;
1255 }
1256 }
1257
1258 /**
1259 * MethodHandle StringBuilder strategy.
1260 *
1261 * <p>This strategy operates in two modes, gated by {@link Mode}.
1262 *
1263 * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1264 * sized".</b>
1265 *
1266 * <p>This strategy avoids spinning up the bytecode by building the
1267 * computation on MethodHandle combinators. The computation is built with
1268 * public MethodHandle APIs, resolved from a public Lookup sequence, and
1269 * ends up calling the public StringBuilder API. Therefore, this strategy
1270 * does not use any private API at all since everything is handled under
1271 * cover by java.lang.invoke APIs.
1272 *
1273 * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1274 * sized exactly".</b>
1275 *
1276 * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1277 * converting all arguments to String in order to get the exact capacity
1278 * StringBuilder should have. The conversion is done via the public
1279 * String.valueOf and/or Object.toString methods, and does not touch any
1280 * private String API.
1281 */
1282 private static final class MethodHandleStringBuilderStrategy {
1283 private MethodHandleStringBuilderStrategy() {
1284 // no instantiation
1285 }
1286
1287 private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1288 int pc = mt.parameterCount();
1289
1290 Class<?>[] ptypes = mt.parameterArray();
1291 MethodHandle[] filters = new MethodHandle[ptypes.length];
1292 for (int i = 0; i < ptypes.length; i++) {
1293 MethodHandle filter;
1294 switch (mode) {
1295 case SIZED:
1296 // In sized mode, we convert all references and floats/doubles
1297 // to String: there is no specialization for different
1298 // classes in StringBuilder API, and it will convert to
1299 // String internally anyhow.
1300 filter = Stringifiers.forMost(ptypes[i]);
1301 break;
1302 case SIZED_EXACT:
1440 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1441 return v1 + v2 + v3 + v4 + v5 + v6;
1442 }
1443
1444 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1445 return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1446 }
1447
1448 private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1449 return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1450 }
1451
1452 private static int sum(int initial, int[] vs) {
1453 int sum = initial;
1454 for (int v : vs) {
1455 sum += v;
1456 }
1457 return sum;
1458 }
1459
1460 private static final Lookup MHSBS_LOOKUP = lookup();
1461
1462 private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1463
1464 // This one is deliberately non-lambdified to optimize startup time:
1465 private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1466 @Override
1467 public MethodHandle apply(Integer cnt) {
1468 if (cnt == 1) {
1469 return MethodHandles.identity(int.class);
1470 } else if (cnt <= 8) {
1471 // Variable-arity collectors are not as efficient as small-count methods,
1472 // unroll some initial sizes.
1473 Class<?>[] cls = new Class<?>[cnt];
1474 Arrays.fill(cls, int.class);
1475 return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1476 } else {
1477 return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1478 .asCollector(int[].class, cnt - 1);
1479 }
1480 }
1481 };
1482
1483 private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1484
1485 static {
1486 SUMMERS = new ConcurrentHashMap<>();
1487 Lookup publicLookup = MethodHandles.publicLookup();
1488 NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1489 STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1490 BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1491 if (DEBUG) {
1492 BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class,
1493 "toStringChecked", String.class, StringBuilder.class);
1494 } else {
1495 BUILDER_TO_STRING_CHECKED = null;
1496 }
1497 }
1498
1499 }
1500
1501
1502 /**
1503 * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1504 * sized exactly".</b>
1505 *
1506 * <p>This strategy replicates what StringBuilders are doing: it builds the
1507 * byte[] array on its own and passes that byte[] array to String
1508 * constructor. This strategy requires access to some private APIs in JDK,
1509 * most notably, the read-only Integer/Long.stringSize methods that measure
1510 * the character length of the integers, and the private String constructor
1511 * that accepts byte[] arrays without copying. While this strategy assumes a
1512 * particular implementation details for String, this opens the door for
1513 * building a very optimal concatenation sequence. This is the only strategy
1514 * that requires porting if there are private JDK changes occur.
1515 */
1516 private static final class MethodHandleInlineCopyStrategy {
1517 private MethodHandleInlineCopyStrategy() {
1518 // no instantiation
1519 }
1520
1521 static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1522
1523 // Fast-path two-argument Object + Object concatenations
1524 if (recipe.getElements().size() == 2) {
1525 // Two object arguments
1526 if (mt.parameterCount() == 2 &&
1527 !mt.parameterType(0).isPrimitive() &&
1528 !mt.parameterType(1).isPrimitive() &&
1529 recipe.getElements().get(0).getTag() == TAG_ARG &&
1530 recipe.getElements().get(1).getTag() == TAG_ARG) {
1531
1532 return SIMPLE;
1533
1534 } else if (mt.parameterCount() == 1 &&
1535 !mt.parameterType(0).isPrimitive()) {
1536 // One Object argument, one constant
1715 mh = MethodHandles.filterArguments(mh, 0, filters);
1716 }
1717
1718 return mh;
1719 }
1720
1721 private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
1722 return MethodHandles.insertArguments(
1723 MethodHandles.insertArguments(
1724 PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
1725 }
1726
1727 private static MethodHandle mixer(Class<?> cl) {
1728 return MIXERS.computeIfAbsent(cl, MIX);
1729 }
1730
1731 // This one is deliberately non-lambdified to optimize startup time:
1732 private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
1733 @Override
1734 public MethodHandle apply(Class<?> c) {
1735 return JLA.stringConcatHelper("prepend",
1736 methodType(long.class, long.class, byte[].class,
1737 String.class, Wrapper.asPrimitiveType(c), String.class));
1738 }
1739 };
1740
1741 // This one is deliberately non-lambdified to optimize startup time:
1742 private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
1743 @Override
1744 public MethodHandle apply(Class<?> c) {
1745 return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));
1746 }
1747 };
1748
1749 private static final MethodHandle SIMPLE;
1750 private static final MethodHandle NEW_STRING;
1751 private static final MethodHandle NEW_ARRAY;
1752 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1753 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1754 private static final long INITIAL_CODER;
1755
1756 static {
1757 try {
1758 MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class));
1759 INITIAL_CODER = (long) initCoder.invoke();
1760 } catch (Throwable e) {
1761 throw new AssertionError(e);
1762 }
1763
1764 PREPENDERS = new ConcurrentHashMap<>();
1765 MIXERS = new ConcurrentHashMap<>();
1766
1767 SIMPLE = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class));
1768 NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class));
1769 NEW_ARRAY = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class));
1770 }
1771 }
1772
1773 /**
1774 * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1775 * delegate to {@code String.valueOf}, depending on argument's type.
1776 */
1777 private static final class Stringifiers {
1778 private Stringifiers() {
1779 // no instantiation
1780 }
1781
1782 private static final MethodHandle OBJECT_INSTANCE =
1783 JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class));
1784
1785 private static class FloatStringifiers {
1786 private static final MethodHandle FLOAT_INSTANCE =
1787 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1788
1789 private static final MethodHandle DOUBLE_INSTANCE =
1790 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1791 }
1792
1793 private static class StringifierAny extends ClassValue<MethodHandle> {
1794
1795 private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1796
1797 @Override
1798 protected MethodHandle computeValue(Class<?> cl) {
1799 if (cl == byte.class || cl == short.class || cl == int.class) {
1800 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1801 } else if (cl == boolean.class) {
1802 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1803 } else if (cl == char.class) {
|