1 /* |
1 /* |
2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. |
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. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
24 */ |
24 */ |
25 |
25 |
26 package java.lang.invoke; |
26 package java.lang.invoke; |
27 |
27 |
28 import jdk.internal.misc.Unsafe; |
28 import jdk.internal.misc.Unsafe; |
|
29 import jdk.internal.misc.VM; |
29 import jdk.internal.org.objectweb.asm.ClassWriter; |
30 import jdk.internal.org.objectweb.asm.ClassWriter; |
30 import jdk.internal.org.objectweb.asm.Label; |
31 import jdk.internal.org.objectweb.asm.Label; |
31 import jdk.internal.org.objectweb.asm.MethodVisitor; |
32 import jdk.internal.org.objectweb.asm.MethodVisitor; |
32 import jdk.internal.org.objectweb.asm.Opcodes; |
33 import jdk.internal.org.objectweb.asm.Opcodes; |
33 import jdk.internal.vm.annotation.ForceInline; |
34 import jdk.internal.vm.annotation.ForceInline; |
189 /** |
190 /** |
190 * Dump generated classes to disk, for debugging purposes. |
191 * Dump generated classes to disk, for debugging purposes. |
191 */ |
192 */ |
192 private static final ProxyClassesDumper DUMPER; |
193 private static final ProxyClassesDumper DUMPER; |
193 |
194 |
|
195 private static final Class<?> STRING_HELPER; |
|
196 |
194 static { |
197 static { |
195 // In case we need to double-back onto the StringConcatFactory during this |
198 // In case we need to double-back onto the StringConcatFactory during this |
196 // static initialization, make sure we have the reasonable defaults to complete |
199 // static initialization, make sure we have the reasonable defaults to complete |
197 // the static initialization properly. After that, actual users would use |
200 // the static initialization properly. After that, actual users would use |
198 // the proper values we have read from the properties. |
201 // the proper values we have read from the properties. |
200 // CACHE_ENABLE = false; // implied |
203 // CACHE_ENABLE = false; // implied |
201 // CACHE = null; // implied |
204 // CACHE = null; // implied |
202 // DEBUG = false; // implied |
205 // DEBUG = false; // implied |
203 // DUMPER = null; // implied |
206 // DUMPER = null; // implied |
204 |
207 |
205 Properties props = GetPropertyAction.privilegedGetProperties(); |
208 try { |
|
209 STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); |
|
210 } catch (Throwable e) { |
|
211 throw new AssertionError(e); |
|
212 } |
|
213 |
206 final String strategy = |
214 final String strategy = |
207 props.getProperty("java.lang.invoke.stringConcat"); |
215 VM.getSavedProperty("java.lang.invoke.stringConcat"); |
208 CACHE_ENABLE = Boolean.parseBoolean( |
216 CACHE_ENABLE = Boolean.parseBoolean( |
209 props.getProperty("java.lang.invoke.stringConcat.cache")); |
217 VM.getSavedProperty("java.lang.invoke.stringConcat.cache")); |
210 DEBUG = Boolean.parseBoolean( |
218 DEBUG = Boolean.parseBoolean( |
211 props.getProperty("java.lang.invoke.stringConcat.debug")); |
219 VM.getSavedProperty("java.lang.invoke.stringConcat.debug")); |
212 final String dumpPath = |
220 final String dumpPath = |
213 props.getProperty("java.lang.invoke.stringConcat.dumpClasses"); |
221 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses"); |
214 |
222 |
215 STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy); |
223 STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy); |
216 CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null; |
224 CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null; |
217 DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath); |
225 DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath); |
218 } |
226 } |
1517 // no instantiation |
1525 // no instantiation |
1518 } |
1526 } |
1519 |
1527 |
1520 static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { |
1528 static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { |
1521 |
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 |
1522 // Create filters and obtain filtered parameter types. Filters would be used in the beginning |
1557 // 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). |
1558 // 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. |
1559 // The filtered argument type list is used all over in the combinators below. |
1525 Class<?>[] ptypes = mt.parameterArray(); |
1560 Class<?>[] ptypes = mt.parameterArray(); |
1526 MethodHandle[] filters = null; |
1561 MethodHandle[] filters = null; |
1657 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, |
1685 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, |
1658 Wrapper.asPrimitiveType(c)); |
1686 Wrapper.asPrimitiveType(c)); |
1659 } |
1687 } |
1660 }; |
1688 }; |
1661 |
1689 |
|
1690 private static final MethodHandle SIMPLE; |
1662 private static final MethodHandle NEW_STRING; |
1691 private static final MethodHandle NEW_STRING; |
1663 private static final MethodHandle NEW_ARRAY; |
1692 private static final MethodHandle NEW_ARRAY; |
1664 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; |
1693 private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS; |
1665 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; |
1694 private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; |
1666 private static final long INITIAL_CODER; |
1695 private static final long INITIAL_CODER; |
1667 static final Class<?> STRING_HELPER; |
|
1668 |
1696 |
1669 static { |
1697 static { |
1670 try { |
1698 try { |
1671 STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); |
|
1672 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); |
1699 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); |
1673 INITIAL_CODER = (long) initCoder.invoke(); |
1700 INITIAL_CODER = (long) initCoder.invoke(); |
1674 } catch (Throwable e) { |
1701 } catch (Throwable e) { |
1675 throw new AssertionError(e); |
1702 throw new AssertionError(e); |
1676 } |
1703 } |
1677 |
1704 |
1678 PREPENDERS = new ConcurrentHashMap<>(); |
1705 PREPENDERS = new ConcurrentHashMap<>(); |
1679 MIXERS = new ConcurrentHashMap<>(); |
1706 MIXERS = new ConcurrentHashMap<>(); |
1680 |
1707 |
|
1708 SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class); |
1681 NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class); |
1709 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); |
1710 NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class); |
1683 } |
1711 } |
1684 } |
1712 } |
1685 |
1713 |
1686 /** |
1714 /** |
1687 * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally |
1715 * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally |
1690 private static final class Stringifiers { |
1718 private static final class Stringifiers { |
1691 private Stringifiers() { |
1719 private Stringifiers() { |
1692 // no instantiation |
1720 // no instantiation |
1693 } |
1721 } |
1694 |
1722 |
1695 private static class ObjectStringifier { |
1723 private static final MethodHandle OBJECT_INSTANCE = |
1696 |
1724 lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class); |
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 |
1725 |
1712 private static class FloatStringifiers { |
1726 private static class FloatStringifiers { |
1713 private static final MethodHandle FLOAT_INSTANCE = |
1727 private static final MethodHandle FLOAT_INSTANCE = |
1714 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class); |
1728 lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class); |
1715 |
1729 |
1749 * @param t class to stringify |
1763 * @param t class to stringify |
1750 * @return stringifier; null, if not available |
1764 * @return stringifier; null, if not available |
1751 */ |
1765 */ |
1752 static MethodHandle forMost(Class<?> t) { |
1766 static MethodHandle forMost(Class<?> t) { |
1753 if (!t.isPrimitive()) { |
1767 if (!t.isPrimitive()) { |
1754 return ObjectStringifier.INSTANCE; |
1768 return OBJECT_INSTANCE; |
1755 } else if (t == float.class) { |
1769 } else if (t == float.class) { |
1756 return FloatStringifiers.FLOAT_INSTANCE; |
1770 return FloatStringifiers.FLOAT_INSTANCE; |
1757 } else if (t == double.class) { |
1771 } else if (t == double.class) { |
1758 return FloatStringifiers.DOUBLE_INSTANCE; |
1772 return FloatStringifiers.DOUBLE_INSTANCE; |
1759 } |
1773 } |