< prev index next >

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

Print this page

        

*** 23,33 **** * questions. */ package java.lang.invoke; ! import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes; --- 23,34 ---- * questions. */ package java.lang.invoke; ! import jdk.internal.access.JavaLangAccess; ! import jdk.internal.access.SharedSecrets; import jdk.internal.misc.VM; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes;
*** 40,49 **** --- 41,53 ---- import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; + import static java.lang.invoke.MethodHandles.lookup; + import static java.lang.invoke.MethodType.methodType; + import static java.lang.invoke.MethodHandleNatives.Constants.*; import static jdk.internal.org.objectweb.asm.Opcodes.*; /** * <p>Methods to facilitate the creation of String concatenation methods, that * can be used to efficiently concatenate a known number of arguments of known
*** 131,140 **** --- 135,146 ---- /** * Default strategy to use for concatenation. */ private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT; + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private enum Strategy { /** * Bytecode generator, calling into {@link java.lang.StringBuilder}. */ BC_SB,
*** 187,198 **** /** * Dump generated classes to disk, for debugging purposes. */ private static final ProxyClassesDumper DUMPER; - private static final Class<?> STRING_HELPER; - static { // In case we need to double-back onto the StringConcatFactory during this // static initialization, make sure we have the reasonable defaults to complete // the static initialization properly. After that, actual users would use // the proper values we have read from the properties. --- 193,202 ----
*** 200,215 **** // CACHE_ENABLE = false; // implied // CACHE = null; // implied // DEBUG = false; // implied // DUMPER = null; // implied - try { - STRING_HELPER = Class.forName("java.lang.StringConcatHelper"); - } catch (Throwable e) { - throw new AssertionError(e); - } - final String strategy = VM.getSavedProperty("java.lang.invoke.stringConcat"); CACHE_ENABLE = Boolean.parseBoolean( VM.getSavedProperty("java.lang.invoke.stringConcat.cache")); DEBUG = Boolean.parseBoolean( --- 204,213 ----
*** 721,732 **** When cache is enabled, we want to cache as much as we can. However, there are two peculiarities: a) The generated class should stay within the same package as the ! host class, to allow Unsafe.defineAnonymousClass access controls ! to work properly. JDK may choose to fail with IllegalAccessException when accessing a VM anonymous class with non-privileged callers, see JDK-8058575. b) If we mark the stub with some prefix, say, derived from the package name because of (a), we can technically use that stub in other packages. --- 719,729 ---- When cache is enabled, we want to cache as much as we can. However, there are two peculiarities: a) The generated class should stay within the same package as the ! host class. JDK may choose to fail with IllegalAccessException when accessing a VM anonymous class with non-privileged callers, see JDK-8058575. b) If we mark the stub with some prefix, say, derived from the package name because of (a), we can technically use that stub in other packages.
*** 745,755 **** case BC_SB_SIZED_EXACT: { if (CACHE_ENABLE) { String pkgName = hostClass.getPackageName(); return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat"; } else { ! return hostClass.getName().replace('.', '/') + "$$StringConcat"; } } case MH_SB_SIZED: case MH_SB_SIZED_EXACT: case MH_INLINE_SIZED_EXACT: --- 742,754 ---- case BC_SB_SIZED_EXACT: { if (CACHE_ENABLE) { String pkgName = hostClass.getPackageName(); return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat"; } else { ! String name = hostClass.isHiddenClass() ? hostClass.getName().replace('/', '_') ! : hostClass.getName(); ! return name.replace('.', '/') + "$$StringConcat"; } } case MH_SB_SIZED: case MH_SB_SIZED_EXACT: case MH_INLINE_SIZED_EXACT:
*** 817,827 **** * * <p>This strategy spins up the bytecode that has the same StringBuilder * chain javac would otherwise emit. This strategy uses only the public API, * and comes as the baseline for the current JDK behavior. On other words, * this strategy moves the javac generated bytecode to runtime. The ! * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with * the caller class coming from the BSM -- in other words, the protection * guarantees are inherited from the method where invokedynamic was * originally called. This means, among other things, that the bytecode is * verified for all non-JDK uses. * --- 816,826 ---- * * <p>This strategy spins up the bytecode that has the same StringBuilder * chain javac would otherwise emit. This strategy uses only the public API, * and comes as the baseline for the current JDK behavior. On other words, * this strategy moves the javac generated bytecode to runtime. The ! * generated bytecode is loaded via Lookup::defineClass, but with * the caller class coming from the BSM -- in other words, the protection * guarantees are inherited from the method where invokedynamic was * originally called. This means, among other things, that the bytecode is * verified for all non-JDK uses. *
*** 846,856 **** * StringBuilder should have. The conversion is done via the public * String.valueOf and/or Object.toString methods, and does not touch any * private String API. */ private static final class BytecodeStringBuilderStrategy { - static final Unsafe UNSAFE = Unsafe.getUnsafe(); static final int CLASSFILE_VERSION = 52; static final String METHOD_NAME = "concat"; private BytecodeStringBuilderStrategy() { // no instantiation --- 845,854 ----
*** 859,869 **** private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, ! className, // Unsafe.defineAnonymousClass would append an unique ID null, "java/lang/Object", null ); --- 857,867 ---- private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, ! className, null, "java/lang/Object", null );
*** 872,881 **** --- 870,880 ---- METHOD_NAME, args.toMethodDescriptorString(), null, null); + // use of @ForceInline no longer has any effect mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true); mv.visitCode(); Class<?>[] arr = args.parameterArray(); boolean[] guaranteedNonNull = new boolean[arr.length];
*** 1141,1155 **** mv.visitEnd(); cw.visitEnd(); byte[] classBytes = cw.toByteArray(); try { ! Class<?> hostClass = lookup.lookupClass(); ! Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null); ! UNSAFE.ensureClassInitialized(innerClass); ! dumpIfEnabled(innerClass.getName(), classBytes); ! return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args); } catch (Exception e) { dumpIfEnabled(className + "$$FAILED", classBytes); throw new StringConcatException("Exception while spinning the class", e); } } --- 1140,1152 ---- mv.visitEnd(); cw.visitEnd(); byte[] classBytes = cw.toByteArray(); try { ! Class<?> innerClass = lookup.defineHiddenClass(classBytes,true).lookupClass(); ! dumpIfEnabled(className, classBytes); ! return lookup.findStatic(innerClass, METHOD_NAME, args); } catch (Exception e) { dumpIfEnabled(className + "$$FAILED", classBytes); throw new StringConcatException("Exception while spinning the class", e); } }
*** 1268,1279 **** * * <p>This strategy avoids spinning up the bytecode by building the * computation on MethodHandle combinators. The computation is built with * public MethodHandle APIs, resolved from a public Lookup sequence, and * ends up calling the public StringBuilder API. Therefore, this strategy ! * does not use any private API at all, even the Unsafe.defineAnonymousClass, ! * since everything is handled under cover by java.lang.invoke APIs. * * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, * sized exactly".</b> * * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first --- 1265,1276 ---- * * <p>This strategy avoids spinning up the bytecode by building the * computation on MethodHandle combinators. The computation is built with * public MethodHandle APIs, resolved from a public Lookup sequence, and * ends up calling the public StringBuilder API. Therefore, this strategy ! * does not use any private API at all since everything is handled under ! * cover by java.lang.invoke APIs. * * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, * sized exactly".</b> * * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
*** 1281,1291 **** * StringBuilder should have. The conversion is done via the public * String.valueOf and/or Object.toString methods, and does not touch any * private String API. */ private static final class MethodHandleStringBuilderStrategy { - private MethodHandleStringBuilderStrategy() { // no instantiation } private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception { --- 1278,1287 ----
*** 1459,1468 **** --- 1455,1466 ---- sum += v; } return sum; } + private static final Lookup MHSBS_LOOKUP = lookup(); + private static final ConcurrentMap<Integer, MethodHandle> SUMMERS; // This one is deliberately non-lambdified to optimize startup time: private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() { @Override
*** 1472,1484 **** } else if (cnt <= 8) { // Variable-arity collectors are not as efficient as small-count methods, // unroll some initial sizes. Class<?>[] cls = new Class<?>[cnt]; Arrays.fill(cls, int.class); ! return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); } else { ! return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) .asCollector(int[].class, cnt - 1); } } }; --- 1470,1482 ---- } else if (cnt <= 8) { // Variable-arity collectors are not as efficient as small-count methods, // unroll some initial sizes. Class<?>[] cls = new Class<?>[cnt]; Arrays.fill(cls, int.class); ! return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); } else { ! return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) .asCollector(int[].class, cnt - 1); } } };
*** 1489,1500 **** Lookup publicLookup = MethodHandles.publicLookup(); NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class); STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); if (DEBUG) { ! BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP, ! MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class); } else { BUILDER_TO_STRING_CHECKED = null; } } --- 1487,1498 ---- Lookup publicLookup = MethodHandles.publicLookup(); NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class); STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class); BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class); if (DEBUG) { ! BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, ! "toStringChecked", String.class, StringBuilder.class); } else { BUILDER_TO_STRING_CHECKED = null; } }
*** 1514,1525 **** * particular implementation details for String, this opens the door for * building a very optimal concatenation sequence. This is the only strategy * that requires porting if there are private JDK changes occur. */ private static final class MethodHandleInlineCopyStrategy { - static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private MethodHandleInlineCopyStrategy() { // no instantiation } static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable { --- 1512,1521 ----
*** 1734,1754 **** // This one is deliberately non-lambdified to optimize startup time: private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() { @Override public MethodHandle apply(Class<?> c) { ! return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class, ! String.class, Wrapper.asPrimitiveType(c), String.class); } }; // This one is deliberately non-lambdified to optimize startup time: private static final Function<Class<?>, MethodHandle> MIX = new Function<>() { @Override public MethodHandle apply(Class<?> c) { ! return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class, ! Wrapper.asPrimitiveType(c)); } }; private static final MethodHandle SIMPLE; private static final MethodHandle NEW_STRING; --- 1730,1750 ---- // This one is deliberately non-lambdified to optimize startup time: private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() { @Override public MethodHandle apply(Class<?> c) { ! return JLA.stringConcatHelper("prepend", ! methodType(long.class, long.class, byte[].class, ! String.class, Wrapper.asPrimitiveType(c), String.class)); } }; // This one is deliberately non-lambdified to optimize startup time: private static final Function<Class<?>, MethodHandle> MIX = new Function<>() { @Override public MethodHandle apply(Class<?> c) { ! return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c))); } }; private static final MethodHandle SIMPLE; private static final MethodHandle NEW_STRING;
*** 1757,1778 **** private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; private static final long INITIAL_CODER; static { try { ! MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); INITIAL_CODER = (long) initCoder.invoke(); } catch (Throwable e) { throw new AssertionError(e); } PREPENDERS = new ConcurrentHashMap<>(); MIXERS = new ConcurrentHashMap<>(); ! SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class); ! NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class); ! NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class); } } /** * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally --- 1753,1774 ---- private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS; private static final long INITIAL_CODER; static { try { ! MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class)); INITIAL_CODER = (long) initCoder.invoke(); } catch (Throwable e) { throw new AssertionError(e); } PREPENDERS = new ConcurrentHashMap<>(); MIXERS = new ConcurrentHashMap<>(); ! SIMPLE = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class)); ! NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class)); ! NEW_ARRAY = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class)); } } /** * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
*** 1782,1792 **** private Stringifiers() { // no instantiation } private static final MethodHandle OBJECT_INSTANCE = ! lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class); private static class FloatStringifiers { private static final MethodHandle FLOAT_INSTANCE = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class); --- 1778,1788 ---- private Stringifiers() { // no instantiation } private static final MethodHandle OBJECT_INSTANCE = ! JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class)); private static class FloatStringifiers { private static final MethodHandle FLOAT_INSTANCE = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
< prev index next >