--- old/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java 2019-12-03 19:37:29.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java 2019-12-03 19:37:29.000000000 -0800 @@ -25,7 +25,8 @@ package java.lang.invoke; -import jdk.internal.misc.Unsafe; +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; @@ -42,6 +43,9 @@ 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.*; /** @@ -133,6 +137,8 @@ */ 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}. @@ -189,8 +195,6 @@ */ 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 @@ -202,12 +206,6 @@ // 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( @@ -723,8 +721,7 @@ 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 + host class. JDK may choose to fail with IllegalAccessException when accessing a VM anonymous class with non-privileged callers, see JDK-8058575. @@ -747,7 +744,9 @@ String pkgName = hostClass.getPackageName(); return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat"; } else { - return hostClass.getName().replace('.', '/') + "$$StringConcat"; + String name = hostClass.isHiddenClass() ? hostClass.getName().replace('/', '_') + : hostClass.getName(); + return name.replace('.', '/') + "$$StringConcat"; } } case MH_SB_SIZED: @@ -819,7 +818,7 @@ * 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 + * 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 @@ -848,7 +847,6 @@ * 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"; @@ -861,7 +859,7 @@ cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, - className, // Unsafe.defineAnonymousClass would append an unique ID + className, null, "java/lang/Object", null @@ -874,6 +872,7 @@ null, null); + // use of @ForceInline no longer has any effect mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true); mv.visitCode(); @@ -1143,11 +1142,9 @@ 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); + 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); @@ -1270,8 +1267,8 @@ * 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. + * does not use any private API at all since everything is handled under + * cover by java.lang.invoke APIs. * *

{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder, * sized exactly". @@ -1283,7 +1280,6 @@ * private String API. */ private static final class MethodHandleStringBuilderStrategy { - private MethodHandleStringBuilderStrategy() { // no instantiation } @@ -1461,6 +1457,8 @@ return sum; } + private static final Lookup MHSBS_LOOKUP = lookup(); + private static final ConcurrentMap SUMMERS; // This one is deliberately non-lambdified to optimize startup time: @@ -1474,9 +1472,9 @@ // 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); + return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls); } else { - return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) + return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class) .asCollector(int[].class, cnt - 1); } } @@ -1491,8 +1489,8 @@ 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); + BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, + "toStringChecked", String.class, StringBuilder.class); } else { BUILDER_TO_STRING_CHECKED = null; } @@ -1516,8 +1514,6 @@ * 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 } @@ -1736,8 +1732,9 @@ private static final Function, 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); + return JLA.stringConcatHelper("prepend", + methodType(long.class, long.class, byte[].class, + String.class, Wrapper.asPrimitiveType(c), String.class)); } }; @@ -1745,8 +1742,7 @@ private static final Function, 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)); + return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c))); } }; @@ -1759,7 +1755,7 @@ static { try { - MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class); + MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class)); INITIAL_CODER = (long) initCoder.invoke(); } catch (Throwable e) { throw new AssertionError(e); @@ -1768,9 +1764,9 @@ 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); + 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)); } } @@ -1784,7 +1780,7 @@ } private static final MethodHandle OBJECT_INSTANCE = - lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class); + JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class)); private static class FloatStringifiers { private static final MethodHandle FLOAT_INSTANCE =