< prev index next >

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

Print this page
rev 17632 : 8186500: StringConcatFactory.makeConcatWithConstants throws AssertionError when recipe contains non-String constants
Reviewed-by: shade, psandoz


   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.org.objectweb.asm.ClassWriter;
  29 import jdk.internal.org.objectweb.asm.Label;
  30 import jdk.internal.org.objectweb.asm.MethodVisitor;
  31 import jdk.internal.org.objectweb.asm.Opcodes;
  32 import jdk.internal.vm.annotation.ForceInline;
  33 import jdk.internal.misc.Unsafe;

  34 
  35 import java.lang.invoke.MethodHandles.Lookup;
  36 import java.util.*;




  37 import java.util.concurrent.ConcurrentHashMap;
  38 import java.util.concurrent.ConcurrentMap;
  39 import java.util.function.Function;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  43 
  44 /**
  45  * <p>Methods to facilitate the creation of String concatenation methods, that
  46  * can be used to efficiently concatenate a known number of arguments of known
  47  * types, possibly after type adaptation and partial evaluation of arguments.
  48  * These methods are typically used as <em>bootstrap methods</em> for {@code
  49  * invokedynamic} call sites, to support the <em>string concatenation</em>
  50  * feature of the Java Programming Language.
  51  *
  52  * <p>Indirect access to the behavior specified by the provided {@code
  53  * MethodHandle} proceeds in order through two phases:
  54  *
  55  * <ol>
  56  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  57  * They take as arguments a method type describing the concatenated arguments
  58  * count and types, and optionally the String <em>recipe</em>, plus the
  59  * constants that participate in the String concatenation. The details on
  60  * accepted recipe shapes are described further below. Linkage may involve


 907 
 908             // Prepare StringBuilder instance
 909             mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
 910             mv.visitInsn(DUP);
 911 
 912             if (mode.isSized()) {
 913                 /*
 914                     Sized mode requires us to walk through the arguments, and estimate the final length.
 915                     In exact mode, this will operate on Strings only. This code would accumulate the
 916                     final length on stack.
 917                  */
 918                 int len = 0;
 919                 int off = 0;
 920 
 921                 mv.visitInsn(ICONST_0);
 922 
 923                 for (RecipeElement el : recipe.getElements()) {
 924                     switch (el.getTag()) {
 925                         case TAG_CONST:
 926                             Object cnst = el.getValue();

 927                             len += cnst.toString().length();
 928                             break;
 929                         case TAG_ARG:
 930                             /*
 931                                 If an argument is String, then we can call .length() on it. Sized/Exact modes have
 932                                 converted arguments for us. If an argument is primitive, we can provide a guess
 933                                 for its String representation size.
 934                             */
 935                             Class<?> cl = arr[el.getArgPos()];
 936                             if (cl == String.class) {
 937                                 mv.visitIntInsn(ALOAD, off);
 938                                 mv.visitMethodInsn(
 939                                         INVOKEVIRTUAL,
 940                                         "java/lang/String",
 941                                         "length",
 942                                         "()I",
 943                                         false
 944                                 );
 945                                 mv.visitInsn(IADD);
 946                             } else if (cl.isPrimitive()) {


 967                         false
 968                 );
 969             } else {
 970                 mv.visitMethodInsn(
 971                         INVOKESPECIAL,
 972                         "java/lang/StringBuilder",
 973                         "<init>",
 974                         "()V",
 975                         false
 976                 );
 977             }
 978 
 979             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
 980             {
 981                 int off = 0;
 982                 for (RecipeElement el : recipe.getElements()) {
 983                     String desc;
 984                     switch (el.getTag()) {
 985                         case TAG_CONST:
 986                             Object cnst = el.getValue();
 987                             mv.visitLdcInsn(cnst);
 988                             desc = getSBAppendDesc(cnst.getClass());

 989                             break;
 990                         case TAG_ARG:
 991                             Class<?> cl = arr[el.getArgPos()];
 992                             mv.visitVarInsn(getLoadOpcode(cl), off);
 993                             off += getParameterSize(cl);
 994                             desc = getSBAppendDesc(cl);
 995                             break;
 996                         default:
 997                             throw new StringConcatException("Unhandled tag: " + el.getTag());
 998                     }
 999 
1000                     mv.visitMethodInsn(
1001                             INVOKEVIRTUAL,
1002                             "java/lang/StringBuilder",
1003                             "append",
1004                             desc,
1005                             false
1006                     );
1007                 }
1008             }


1604         private static byte[] newArray(int length, byte coder) {
1605             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1606         }
1607 
1608         private static MethodHandle prepender(Class<?> cl) {
1609             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1610         }
1611 
1612         private static MethodHandle coderMixer(Class<?> cl) {
1613             return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
1614         }
1615 
1616         private static MethodHandle lengthMixer(Class<?> cl) {
1617             return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
1618         }
1619 
1620         // This one is deliberately non-lambdified to optimize startup time:
1621         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
1622             @Override
1623             public MethodHandle apply(Class<?> c) {
1624                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, c);

1625             }
1626         };
1627 
1628         // This one is deliberately non-lambdified to optimize startup time:
1629         private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
1630             @Override
1631             public MethodHandle apply(Class<?> c) {
1632                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c);

1633             }
1634         };
1635 
1636         // This one is deliberately non-lambdified to optimize startup time:
1637         private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1638             @Override
1639             public MethodHandle apply(Class<?> c) {
1640                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c);

1641             }
1642         };
1643 
1644         private static final MethodHandle NEW_STRING;
1645         private static final MethodHandle NEW_ARRAY;
1646         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1647         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1648         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1649         private static final byte INITIAL_CODER;
1650         static final Class<?> STRING_HELPER;
1651 
1652         static {
1653             try {
1654                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1655                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
1656                 INITIAL_CODER = (byte) initCoder.invoke();
1657             } catch (Throwable e) {
1658                 throw new AssertionError(e);
1659             }
1660 




   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 
  49 /**
  50  * <p>Methods to facilitate the creation of String concatenation methods, that
  51  * can be used to efficiently concatenate a known number of arguments of known
  52  * types, possibly after type adaptation and partial evaluation of arguments.
  53  * These methods are typically used as <em>bootstrap methods</em> for {@code
  54  * invokedynamic} call sites, to support the <em>string concatenation</em>
  55  * feature of the Java Programming Language.
  56  *
  57  * <p>Indirect access to the behavior specified by the provided {@code
  58  * MethodHandle} proceeds in order through two phases:
  59  *
  60  * <ol>
  61  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  62  * They take as arguments a method type describing the concatenated arguments
  63  * count and types, and optionally the String <em>recipe</em>, plus the
  64  * constants that participate in the String concatenation. The details on
  65  * accepted recipe shapes are described further below. Linkage may involve


 912 
 913             // Prepare StringBuilder instance
 914             mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
 915             mv.visitInsn(DUP);
 916 
 917             if (mode.isSized()) {
 918                 /*
 919                     Sized mode requires us to walk through the arguments, and estimate the final length.
 920                     In exact mode, this will operate on Strings only. This code would accumulate the
 921                     final length on stack.
 922                  */
 923                 int len = 0;
 924                 int off = 0;
 925 
 926                 mv.visitInsn(ICONST_0);
 927 
 928                 for (RecipeElement el : recipe.getElements()) {
 929                     switch (el.getTag()) {
 930                         case TAG_CONST:
 931                             Object cnst = el.getValue();
 932                             String s = (cnst != null) ? cnst.toString() : "null";
 933                             len += cnst.toString().length();
 934                             break;
 935                         case TAG_ARG:
 936                             /*
 937                                 If an argument is String, then we can call .length() on it. Sized/Exact modes have
 938                                 converted arguments for us. If an argument is primitive, we can provide a guess
 939                                 for its String representation size.
 940                             */
 941                             Class<?> cl = arr[el.getArgPos()];
 942                             if (cl == String.class) {
 943                                 mv.visitIntInsn(ALOAD, off);
 944                                 mv.visitMethodInsn(
 945                                         INVOKEVIRTUAL,
 946                                         "java/lang/String",
 947                                         "length",
 948                                         "()I",
 949                                         false
 950                                 );
 951                                 mv.visitInsn(IADD);
 952                             } else if (cl.isPrimitive()) {


 973                         false
 974                 );
 975             } else {
 976                 mv.visitMethodInsn(
 977                         INVOKESPECIAL,
 978                         "java/lang/StringBuilder",
 979                         "<init>",
 980                         "()V",
 981                         false
 982                 );
 983             }
 984 
 985             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
 986             {
 987                 int off = 0;
 988                 for (RecipeElement el : recipe.getElements()) {
 989                     String desc;
 990                     switch (el.getTag()) {
 991                         case TAG_CONST:
 992                             Object cnst = el.getValue();
 993                             String s = (cnst != null) ? cnst.toString() : "null";
 994                             mv.visitLdcInsn(s);
 995                             desc = getSBAppendDesc(String.class);
 996                             break;
 997                         case TAG_ARG:
 998                             Class<?> cl = arr[el.getArgPos()];
 999                             mv.visitVarInsn(getLoadOpcode(cl), off);
1000                             off += getParameterSize(cl);
1001                             desc = getSBAppendDesc(cl);
1002                             break;
1003                         default:
1004                             throw new StringConcatException("Unhandled tag: " + el.getTag());
1005                     }
1006 
1007                     mv.visitMethodInsn(
1008                             INVOKEVIRTUAL,
1009                             "java/lang/StringBuilder",
1010                             "append",
1011                             desc,
1012                             false
1013                     );
1014                 }
1015             }


1611         private static byte[] newArray(int length, byte coder) {
1612             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1613         }
1614 
1615         private static MethodHandle prepender(Class<?> cl) {
1616             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1617         }
1618 
1619         private static MethodHandle coderMixer(Class<?> cl) {
1620             return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
1621         }
1622 
1623         private static MethodHandle lengthMixer(Class<?> cl) {
1624             return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
1625         }
1626 
1627         // This one is deliberately non-lambdified to optimize startup time:
1628         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
1629             @Override
1630             public MethodHandle apply(Class<?> c) {
1631                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class,
1632                         Wrapper.asPrimitiveType(c));
1633             }
1634         };
1635 
1636         // This one is deliberately non-lambdified to optimize startup time:
1637         private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
1638             @Override
1639             public MethodHandle apply(Class<?> c) {
1640                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class,
1641                         Wrapper.asPrimitiveType(c));
1642             }
1643         };
1644 
1645         // This one is deliberately non-lambdified to optimize startup time:
1646         private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1647             @Override
1648             public MethodHandle apply(Class<?> c) {
1649                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class,
1650                         Wrapper.asPrimitiveType(c));
1651             }
1652         };
1653 
1654         private static final MethodHandle NEW_STRING;
1655         private static final MethodHandle NEW_ARRAY;
1656         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1657         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1658         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1659         private static final byte INITIAL_CODER;
1660         static final Class<?> STRING_HELPER;
1661 
1662         static {
1663             try {
1664                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1665                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
1666                 INITIAL_CODER = (byte) initCoder.invoke();
1667             } catch (Throwable e) {
1668                 throw new AssertionError(e);
1669             }
1670 


< prev index next >