8238358: Implementation of JEP 371: Hidden Classes

Co-authored-by: Lois Foltan <lois.foltan@oracle.com>
Co-authored-by: David Holmes <david.holmes@oracle.com>
Co-authored-by: Harold Seigel <harold.seigel@oracle.com>
Co-authored-by: Serguei Spitsyn <serguei.spitsyn@oracle.com>
Co-authored-by: Alex Buckley <alex.buckley@oracle.com>
Co-authored-by: Jamsheed Mohammed C M <jamsheed.c.m@oracle.com>
Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com>
Co-authored-by: Amy Lu <amy.lu@oracle.com>
Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, sspitsyn, vromero
This commit is contained in:
Mandy Chung 2020-04-21 06:55:38 -07:00
parent 642041adbc
commit 7cc1371059
198 changed files with 9526 additions and 1575 deletions

View file

@ -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.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.MethodHandles.Lookup.ClassOption.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
@ -133,6 +137,8 @@ public final class StringConcatFactory {
*/
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 @@ public final class StringConcatFactory {
*/
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 @@ public final class StringConcatFactory {
// 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(
@ -718,25 +716,10 @@ public final class StringConcatFactory {
private static String getClassName(Class<?> hostClass) throws StringConcatException {
/*
The generated class is in the same package as the host class as
it's the implementation of the string concatenation for the host class.
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.
But the call stack traces would be extremely puzzling to unsuspecting users
and profiling tools: whatever stub wins the race, would be linked in all
similar callsites.
Therefore, we set the class prefix to match the host class package, and use
the prefix as the cache key too. This only affects BC_* strategies, and only when
cache is enabled.
*/
switch (STRATEGY) {
@ -745,9 +728,11 @@ public final class StringConcatFactory {
case BC_SB_SIZED_EXACT: {
if (CACHE_ENABLE) {
String pkgName = hostClass.getPackageName();
return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
return (!pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
} else {
return hostClass.getName().replace('.', '/') + "$$StringConcat";
String name = hostClass.isHidden() ? hostClass.getName().replace('/', '_')
: hostClass.getName();
return name.replace('.', '/') + "$$StringConcat";
}
}
case MH_SB_SIZED:
@ -819,7 +804,7 @@ public final class StringConcatFactory {
* 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 +833,6 @@ public final class StringConcatFactory {
* 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 +845,7 @@ public final class StringConcatFactory {
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 +858,7 @@ public final class StringConcatFactory {
null,
null);
// use of @ForceInline no longer has any effect
mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
mv.visitCode();
@ -1143,11 +1128,9 @@ public final class StringConcatFactory {
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, STRONG).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 +1253,8 @@ public final class StringConcatFactory {
* 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.
*
* <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
* sized exactly".</b>
@ -1283,7 +1266,6 @@ public final class StringConcatFactory {
* private String API.
*/
private static final class MethodHandleStringBuilderStrategy {
private MethodHandleStringBuilderStrategy() {
// no instantiation
}
@ -1461,6 +1443,8 @@ public final class StringConcatFactory {
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:
@ -1474,9 +1458,9 @@ public final class StringConcatFactory {
// 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 +1475,8 @@ public final class StringConcatFactory {
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 +1500,6 @@ public final class StringConcatFactory {
* 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 +1718,9 @@ public final class StringConcatFactory {
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);
return JLA.stringConcatHelper("prepend",
methodType(long.class, long.class, byte[].class,
String.class, Wrapper.asPrimitiveType(c), String.class));
}
};
@ -1745,8 +1728,7 @@ public final class StringConcatFactory {
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));
return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));
}
};
@ -1759,7 +1741,7 @@ public final class StringConcatFactory {
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 +1750,9 @@ public final class StringConcatFactory {
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 +1766,7 @@ public final class StringConcatFactory {
}
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 =