src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
changeset 54550 5fa7fbddfe9d
parent 52497 34510f65fb58
child 54652 1cb0306f16d1
equal deleted inserted replaced
54549:02ef86858896 54550:5fa7fbddfe9d
     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;
  1624             }
  1659             }
  1625 
  1660 
  1626             return mh;
  1661             return mh;
  1627         }
  1662         }
  1628 
  1663 
  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) {
  1664         private static MethodHandle prepender(Class<?> cl) {
  1637             return PREPENDERS.computeIfAbsent(cl, PREPEND);
  1665             return PREPENDERS.computeIfAbsent(cl, PREPEND);
  1638         }
  1666         }
  1639 
  1667 
  1640         private static MethodHandle mixer(Class<?> cl) {
  1668         private static MethodHandle mixer(Class<?> cl) {
  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             }