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 & 0x7FFFFFFF);
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
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 StringifierMost extends ClassValue<MethodHandle> {
1696 @Override
1697 protected MethodHandle computeValue(Class<?> cl) {
1698 if (cl == String.class) {
1699 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1700 } else if (cl == float.class) {
1701 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1702 } else if (cl == double.class) {
1703 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1704 } else if (!cl.isPrimitive()) {
1705 MethodHandle mhObject = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1706
1707 // We need the additional conversion here, because String.valueOf(Object) may return null.
1708 // String conversion rules in Java state we need to produce "null" String in this case.
1709 // It can be easily done with applying valueOf the second time.
1710 return MethodHandles.filterReturnValue(mhObject,
1711 mhObject.asType(MethodType.methodType(String.class, String.class)));
1712 }
1713
1714 return null;
1715 }
1716 }
1717
1718 private static class StringifierAny extends ClassValue<MethodHandle> {
1719 @Override
1720 protected MethodHandle computeValue(Class<?> cl) {
1721 if (cl == byte.class || cl == short.class || cl == int.class) {
1722 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1723 } else if (cl == boolean.class) {
1724 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1725 } else if (cl == char.class) {
1726 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class);
1727 } else if (cl == long.class) {
1728 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
1729 } else {
1730 MethodHandle mh = STRINGIFIERS_MOST.get(cl);
1731 if (mh != null) {
1732 return mh;
1733 } else {
1734 throw new IllegalStateException("Unknown class: " + cl);
1735 }
1736 }
1737 }
1738 }
1739
1740 private static final ClassValue<MethodHandle> STRINGIFIERS_MOST = new StringifierMost();
1741 private static final ClassValue<MethodHandle> STRINGIFIERS_ANY = new StringifierAny();
1742
1743 /**
1744 * Returns a stringifier for references and floats/doubles only.
1745 * Always returns null for other primitives.
1746 *
1747 * @param t class to stringify
1748 * @return stringifier; null, if not available
1749 */
1750 static MethodHandle forMost(Class<?> t) {
1751 return STRINGIFIERS_MOST.get(t);
1752 }
1753
1754 /**
1755 * Returns a stringifier for any type. Never returns null.
1756 *
1757 * @param t class to stringify
1758 * @return stringifier
1759 */
1760 static MethodHandle forAny(Class<?> t) {
1761 return STRINGIFIERS_ANY.get(t);
1762 }
1763 }
1764
1765 /* ------------------------------- Common utilities ------------------------------------ */
1766
1767 static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1768 try {
1769 return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
1770 } catch (NoSuchMethodException | IllegalAccessException e) {
1771 throw new AssertionError(e);
1772 }
1773 }
1774
1775 static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1776 try {
1777 return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
1778 } catch (NoSuchMethodException | IllegalAccessException e) {
1779 throw new AssertionError(e);
1780 }
1781 }
|
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
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 StringStringifier {
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, StringStringifier.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) {
1731 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class);
1732 } else if (cl == long.class) {
1733 return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.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 StringStringifier.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 ------------------------------------ */
1775
1776 static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1777 try {
1778 return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
1779 } catch (NoSuchMethodException | IllegalAccessException e) {
1780 throw new AssertionError(e);
1781 }
1782 }
1783
1784 static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1785 try {
1786 return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
1787 } catch (NoSuchMethodException | IllegalAccessException e) {
1788 throw new AssertionError(e);
1789 }
1790 }
|