mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
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:
parent
642041adbc
commit
7cc1371059
198 changed files with 9526 additions and 1575 deletions
|
@ -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 =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue