8164044: Generate corresponding simple DelegatingMethodHandles when generating a DirectMethodHandle at link time
Reviewed-by: vlivanov, mhaupt, shade
This commit is contained in:
parent
f657bd2fa9
commit
9dcafe04f0
@ -36,7 +36,6 @@ import sun.invoke.util.Wrapper;
|
||||
import java.lang.invoke.LambdaForm.NamedFunction;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
@ -308,7 +307,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
/*non-public*/ char fieldTypeChar(int i) {
|
||||
return typeChars.charAt(i);
|
||||
}
|
||||
Object fieldSignature() {
|
||||
String fieldSignature() {
|
||||
return typeChars;
|
||||
}
|
||||
public Class<? extends BoundMethodHandle> fieldHolder() {
|
||||
|
@ -27,6 +27,7 @@ package java.lang.invoke;
|
||||
|
||||
import java.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
@ -96,14 +97,8 @@ abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
int whichCache,
|
||||
Object constraint,
|
||||
NamedFunction getTargetFn) {
|
||||
String debugString;
|
||||
switch(whichCache) {
|
||||
case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break;
|
||||
case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break;
|
||||
default: debugString = "MH.reinvoke"; break;
|
||||
}
|
||||
// No pre-action needed.
|
||||
return makeReinvokerForm(target, whichCache, constraint, debugString, true, getTargetFn, null);
|
||||
return makeReinvokerForm(target, whichCache, constraint, null, true, getTargetFn, null);
|
||||
}
|
||||
/** Create a LF which simply reinvokes a target of the given basic type. */
|
||||
static LambdaForm makeReinvokerForm(MethodHandle target,
|
||||
@ -114,6 +109,10 @@ abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
NamedFunction getTargetFn,
|
||||
NamedFunction preActionFn) {
|
||||
MethodType mtype = target.type().basicType();
|
||||
Kind kind = whichKind(whichCache);
|
||||
if (debugString == null) {
|
||||
debugString = kind.defaultLambdaName;
|
||||
}
|
||||
boolean customized = (whichCache < 0 ||
|
||||
mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY);
|
||||
boolean hasPreAction = (preActionFn != null);
|
||||
@ -145,13 +144,21 @@ abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
|
||||
names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs);
|
||||
}
|
||||
form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline);
|
||||
form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline, kind);
|
||||
if (!customized) {
|
||||
form = mtype.form().setCachedLambdaForm(whichCache, form);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
private static Kind whichKind(int whichCache) {
|
||||
switch(whichCache) {
|
||||
case MethodTypeForm.LF_REBIND: return BOUND_REINVOKER;
|
||||
case MethodTypeForm.LF_DELEGATE: return DELEGATE;
|
||||
default: return REINVOKER;
|
||||
}
|
||||
}
|
||||
|
||||
static final NamedFunction NF_getTarget;
|
||||
static {
|
||||
try {
|
||||
@ -160,5 +167,13 @@ abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
// The Holder class will contain pre-generated DelegatingMethodHandles resolved
|
||||
// speculatively using MemberName.getFactory().resolveOrNull. However, that
|
||||
// doesn't initialize the class, which subtly breaks inlining etc. By forcing
|
||||
// initialization of the Holder class we avoid these issues.
|
||||
UNSAFE.ensureClassInitialized(Holder.class);
|
||||
}
|
||||
|
||||
/* Placeholder class for DelegatingMethodHandles generated ahead of time */
|
||||
final class Holder {}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
||||
@ -189,14 +190,15 @@ class DirectMethodHandle extends MethodHandle {
|
||||
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
||||
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
||||
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
||||
String linkerName, lambdaName;
|
||||
String linkerName;
|
||||
LambdaForm.Kind kind;
|
||||
switch (which) {
|
||||
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; lambdaName = "DMH.invokeVirtual"; break;
|
||||
case LF_INVSTATIC: linkerName = "linkToStatic"; lambdaName = "DMH.invokeStatic"; break;
|
||||
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; lambdaName = "DMH.invokeStaticInit"; break;
|
||||
case LF_INVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.invokeSpecial"; break;
|
||||
case LF_INVINTERFACE: linkerName = "linkToInterface"; lambdaName = "DMH.invokeInterface"; break;
|
||||
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; lambdaName = "DMH.newInvokeSpecial"; break;
|
||||
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; kind = DIRECT_INVOKE_VIRTUAL; break;
|
||||
case LF_INVSTATIC: linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC; break;
|
||||
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC_INIT; break;
|
||||
case LF_INVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL; break;
|
||||
case LF_INVINTERFACE: linkerName = "linkToInterface"; kind = DIRECT_INVOKE_INTERFACE; break;
|
||||
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_NEW_INVOKE_SPECIAL; break;
|
||||
default: throw new InternalError("which="+which);
|
||||
}
|
||||
|
||||
@ -240,11 +242,11 @@ class DirectMethodHandle extends MethodHandle {
|
||||
result = NEW_OBJ;
|
||||
}
|
||||
names[LINKER_CALL] = new Name(linker, outArgs);
|
||||
lambdaName += "_" + shortenSignature(basicTypeSignature(mtype));
|
||||
LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result);
|
||||
String lambdaName = kind.defaultLambdaName + "_" + shortenSignature(basicTypeSignature(mtype));
|
||||
LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result, kind);
|
||||
|
||||
// This is a tricky bit of code. Don't send it through the LF interpreter.
|
||||
lform.compileToBytecode(Holder.class);
|
||||
lform.compileToBytecode();
|
||||
return lform;
|
||||
}
|
||||
|
||||
@ -705,7 +707,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
}
|
||||
|
||||
static {
|
||||
// The DMH class will contain pre-generated DirectMethodHandles resolved
|
||||
// The Holder class will contain pre-generated DirectMethodHandles resolved
|
||||
// speculatively using MemberName.getFactory().resolveOrNull. However, that
|
||||
// doesn't initialize the class, which subtly breaks inlining etc. By forcing
|
||||
// initialization of the Holder class we avoid these issues.
|
||||
@ -713,5 +715,5 @@ class DirectMethodHandle extends MethodHandle {
|
||||
}
|
||||
|
||||
/* Placeholder class for DirectMethodHandles generated ahead of time */
|
||||
private final class Holder {}
|
||||
final class Holder {}
|
||||
}
|
||||
|
@ -29,21 +29,56 @@ import java.util.Map;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Helper class to assist the GenerateJLIClassesPlugin to get access to
|
||||
* generate classes ahead of time.
|
||||
*/
|
||||
class GenerateJLIClassesHelper {
|
||||
|
||||
static byte[] generateDMHClassBytes(String className,
|
||||
static byte[] generateDirectMethodHandleHolderClassBytes(String className,
|
||||
MethodType[] methodTypes, int[] types) {
|
||||
LambdaForm[] forms = new LambdaForm[methodTypes.length];
|
||||
String[] names = new String[methodTypes.length];
|
||||
for (int i = 0; i < forms.length; i++) {
|
||||
forms[i] = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i],
|
||||
types[i]);
|
||||
methodTypes[i] = forms[i].methodType();
|
||||
names[i] = forms[i].kind.defaultLambdaName;
|
||||
}
|
||||
return generateCodeBytesForLFs(className, forms, methodTypes);
|
||||
return generateCodeBytesForLFs(className, names, forms);
|
||||
}
|
||||
|
||||
static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
|
||||
MethodType[] methodTypes) {
|
||||
|
||||
HashSet<MethodType> dedupSet = new HashSet<>();
|
||||
ArrayList<LambdaForm> forms = new ArrayList<>();
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
for (int i = 0; i < methodTypes.length; i++) {
|
||||
// generate methods representing the DelegatingMethodHandle
|
||||
if (dedupSet.add(methodTypes[i])) {
|
||||
// reinvokers are variant with the associated SpeciesData
|
||||
// and shape of the target LF, but we can easily pregenerate
|
||||
// the basic reinvokers associated with Species_L. Ultimately we
|
||||
// may want to consider pregenerating more of these, which will
|
||||
// require an even more complex naming scheme
|
||||
LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]);
|
||||
forms.add(reinvoker);
|
||||
String speciesSig = BoundMethodHandle
|
||||
.speciesData(reinvoker).fieldSignature();
|
||||
assert(speciesSig.equals("L"));
|
||||
names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig);
|
||||
|
||||
LambdaForm delegate = makeDelegateFor(methodTypes[i]);
|
||||
forms.add(delegate);
|
||||
names.add(delegate.kind.defaultLambdaName);
|
||||
}
|
||||
}
|
||||
return generateCodeBytesForLFs(className,
|
||||
names.toArray(new String[0]),
|
||||
forms.toArray(new LambdaForm[0]));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -51,22 +86,45 @@ class GenerateJLIClassesHelper {
|
||||
* a class with a specified name.
|
||||
*/
|
||||
private static byte[] generateCodeBytesForLFs(String className,
|
||||
LambdaForm[] forms, MethodType[] types) {
|
||||
assert(forms.length == types.length);
|
||||
String[] names, LambdaForm[] forms) {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
className, null, InvokerBytecodeGenerator.INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null);
|
||||
|
||||
for (int i = 0; i < forms.length; i++) {
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, forms[i], types[i]);
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
addMethod(className, names[i], forms[i],
|
||||
forms[i].methodType(), cw);
|
||||
}
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static void addMethod(String className, String methodName, LambdaForm form,
|
||||
MethodType type, ClassWriter cw) {
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, methodName, form, type);
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
}
|
||||
|
||||
private static LambdaForm makeReinvokerFor(MethodType type) {
|
||||
MethodHandle emptyHandle = MethodHandles.empty(type);
|
||||
return DelegatingMethodHandle.makeReinvokerForm(emptyHandle,
|
||||
MethodTypeForm.LF_REBIND,
|
||||
BoundMethodHandle.speciesData_L(),
|
||||
BoundMethodHandle.speciesData_L().getterFunction(0));
|
||||
}
|
||||
|
||||
private static LambdaForm makeDelegateFor(MethodType type) {
|
||||
MethodHandle handle = MethodHandles.empty(type);
|
||||
return DelegatingMethodHandle.makeReinvokerForm(
|
||||
handle,
|
||||
MethodTypeForm.LF_DELEGATE,
|
||||
DelegatingMethodHandle.class,
|
||||
DelegatingMethodHandle.NF_getTarget);
|
||||
}
|
||||
|
||||
static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
|
||||
final String types) {
|
||||
for (char c : types.toCharArray()) {
|
||||
|
@ -46,6 +46,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
@ -125,9 +126,15 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
/** For generating customized code for a single LambdaForm. */
|
||||
InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
|
||||
private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
|
||||
this(className, form.debugName, form, invokerType);
|
||||
}
|
||||
|
||||
/** For generating customized code for a single LambdaForm. */
|
||||
InvokerBytecodeGenerator(String className, String invokerName,
|
||||
LambdaForm form, MethodType invokerType) {
|
||||
this(form, form.names.length,
|
||||
className, form.debugName, invokerType);
|
||||
className, invokerName, invokerType);
|
||||
// Create an array to map name indexes to locals indexes.
|
||||
Name[] names = form.names;
|
||||
for (int i = 0, index = 0; i < localsMap.length; i++) {
|
||||
@ -597,10 +604,42 @@ class InvokerBytecodeGenerator {
|
||||
return c.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
private static MemberName resolveFrom(String name, MethodType type, Class<?> holder) {
|
||||
MemberName member = new MemberName(holder, name, type, REF_invokeStatic);
|
||||
MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder);
|
||||
|
||||
return resolvedMember;
|
||||
}
|
||||
|
||||
private static MemberName lookupPregenerated(LambdaForm form) {
|
||||
if (form.customized != null) {
|
||||
// No pre-generated version for customized LF
|
||||
return null;
|
||||
}
|
||||
MethodType invokerType = form.methodType();
|
||||
String name = form.kind.methodName;
|
||||
switch (form.kind) {
|
||||
case BOUND_REINVOKER: {
|
||||
name = name + "_" + BoundMethodHandle.speciesData(form).fieldSignature();
|
||||
return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
|
||||
}
|
||||
case DELEGATE: return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
|
||||
case DIRECT_INVOKE_INTERFACE: // fall-through
|
||||
case DIRECT_INVOKE_SPECIAL: // fall-through
|
||||
case DIRECT_INVOKE_STATIC: // fall-through
|
||||
case DIRECT_INVOKE_STATIC_INIT: // fall-through
|
||||
case DIRECT_INVOKE_VIRTUAL: return resolveFrom(name, invokerType, DirectMethodHandle.Holder.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate customized bytecode for a given LambdaForm.
|
||||
*/
|
||||
static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
|
||||
MemberName pregenerated = lookupPregenerated(form);
|
||||
if (pregenerated != null) return pregenerated; // pre-generated bytecode
|
||||
|
||||
InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
|
||||
return g.loadMethod(g.generateCustomizedCodeBytes());
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import java.util.HashMap;
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The symbolic, non-executable form of a method handle's invocation semantics.
|
||||
@ -127,6 +128,7 @@ class LambdaForm {
|
||||
final MethodHandle customized;
|
||||
@Stable final Name[] names;
|
||||
final String debugName;
|
||||
final Kind kind;
|
||||
MemberName vmentry; // low-level behavior, or null if not yet prepared
|
||||
private boolean isCompiled;
|
||||
|
||||
@ -266,12 +268,46 @@ class LambdaForm {
|
||||
}
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
GENERIC(""),
|
||||
BOUND_REINVOKER("BMH.reinvoke"),
|
||||
REINVOKER("MH.reinvoke"),
|
||||
DELEGATE("MH.delegate"),
|
||||
DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual"),
|
||||
DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial"),
|
||||
DIRECT_INVOKE_STATIC("DMH.invokeStatic"),
|
||||
DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial"),
|
||||
DIRECT_INVOKE_INTERFACE("DMH.invokeInterface"),
|
||||
DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit");
|
||||
|
||||
final String defaultLambdaName;
|
||||
final String methodName;
|
||||
|
||||
private Kind(String defaultLambdaName) {
|
||||
this.defaultLambdaName = defaultLambdaName;
|
||||
int p = defaultLambdaName.indexOf('.');
|
||||
if (p > -1) {
|
||||
this.methodName = defaultLambdaName.substring(p + 1);
|
||||
} else {
|
||||
this.methodName = defaultLambdaName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, int result) {
|
||||
this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null);
|
||||
this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null, Kind.GENERIC);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, int result, Kind kind) {
|
||||
this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null, kind);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, int result, boolean forceInline, MethodHandle customized) {
|
||||
this(debugName, arity, names, result, forceInline, customized, Kind.GENERIC);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, int result, boolean forceInline, MethodHandle customized, Kind kind) {
|
||||
assert(namesOK(arity, names));
|
||||
this.arity = arity;
|
||||
this.result = fixResult(result, names);
|
||||
@ -279,6 +315,7 @@ class LambdaForm {
|
||||
this.debugName = fixDebugName(debugName);
|
||||
this.forceInline = forceInline;
|
||||
this.customized = customized;
|
||||
this.kind = kind;
|
||||
int maxOutArity = normalize();
|
||||
if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
|
||||
// Cannot use LF interpreter on very high arity expressions.
|
||||
@ -288,11 +325,15 @@ class LambdaForm {
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names) {
|
||||
this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null);
|
||||
this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null, Kind.GENERIC);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, boolean forceInline) {
|
||||
this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null);
|
||||
this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null, Kind.GENERIC);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
int arity, Name[] names, boolean forceInline, Kind kind) {
|
||||
this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null, kind);
|
||||
}
|
||||
LambdaForm(String debugName,
|
||||
Name[] formals, Name[] temps, Name result) {
|
||||
@ -325,6 +366,7 @@ class LambdaForm {
|
||||
this.debugName = "LF.zero";
|
||||
this.forceInline = true;
|
||||
this.customized = null;
|
||||
this.kind = Kind.GENERIC;
|
||||
assert(nameRefsAreLegal());
|
||||
assert(isEmpty());
|
||||
String sig = null;
|
||||
@ -395,7 +437,7 @@ class LambdaForm {
|
||||
|
||||
/** Customize LambdaForm for a particular MethodHandle */
|
||||
LambdaForm customize(MethodHandle mh) {
|
||||
LambdaForm customForm = new LambdaForm(debugName, arity, names, result, forceInline, mh);
|
||||
LambdaForm customForm = new LambdaForm(debugName, arity, names, result, forceInline, mh, kind);
|
||||
if (COMPILE_THRESHOLD >= 0 && isCompiled) {
|
||||
// If shared LambdaForm has been compiled, compile customized version as well.
|
||||
customForm.compileToBytecode();
|
||||
@ -773,28 +815,6 @@ class LambdaForm {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate optimizable bytecode for this form after first looking for a
|
||||
* pregenerated version in a specified class.
|
||||
*/
|
||||
void compileToBytecode(Class<?> lookupClass) {
|
||||
if (vmentry != null && isCompiled) {
|
||||
return; // already compiled somehow
|
||||
}
|
||||
MethodType invokerType = methodType();
|
||||
assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
|
||||
int dot = debugName.indexOf('.');
|
||||
String methodName = (dot > 0) ? debugName.substring(dot + 1) : debugName;
|
||||
MemberName member = new MemberName(lookupClass, methodName, invokerType, REF_invokeStatic);
|
||||
MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, lookupClass);
|
||||
if (resolvedMember != null) {
|
||||
vmentry = resolvedMember;
|
||||
isCompiled = true;
|
||||
} else {
|
||||
compileToBytecode();
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeInitialPreparedForms() {
|
||||
// Find all predefined invokers and associate them with canonical empty lambda forms.
|
||||
for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) {
|
||||
|
@ -1718,10 +1718,19 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generateDMHClassBytes(String className,
|
||||
MethodType[] methodTypes, int[] types) {
|
||||
public byte[] generateDirectMethodHandleHolderClassBytes(
|
||||
String className, MethodType[] methodTypes, int[] types) {
|
||||
return GenerateJLIClassesHelper
|
||||
.generateDMHClassBytes(className, methodTypes, types);
|
||||
.generateDirectMethodHandleHolderClassBytes(
|
||||
className, methodTypes, types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generateDelegatingMethodHandleHolderClassBytes(
|
||||
String className, MethodType[] methodTypes) {
|
||||
return GenerateJLIClassesHelper
|
||||
.generateDelegatingMethodHandleHolderClassBytes(
|
||||
className, methodTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,8 +51,17 @@ public interface JavaLangInvokeAccess {
|
||||
* an {@code int} representing method type. Used by
|
||||
* GenerateJLIClassesPlugin to generate such a class during the jlink phase.
|
||||
*/
|
||||
byte[] generateDMHClassBytes(String className, MethodType[] methodTypes,
|
||||
int[] types);
|
||||
byte[] generateDirectMethodHandleHolderClassBytes(String className,
|
||||
MethodType[] methodTypes, int[] types);
|
||||
|
||||
/**
|
||||
* Returns a {@code byte[]} containing the bytecode for a class implementing
|
||||
* DelegatingMethodHandles of each {@code MethodType} kind in the
|
||||
* {@code methodTypes} argument. Used by GenerateJLIClassesPlugin to
|
||||
* generate such a class during the jlink phase.
|
||||
*/
|
||||
byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
|
||||
MethodType[] methodTypes);
|
||||
|
||||
/**
|
||||
* Returns a {@code byte[]} containing the bytecode for a BoundMethodHandle
|
||||
|
@ -55,7 +55,7 @@ public final class GenerateJLIClassesPlugin implements Plugin {
|
||||
|
||||
private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
|
||||
|
||||
private static final String DMH = "java/lang/invoke/DirectMethodHandle$Holder";
|
||||
private static final String DIRECT_METHOD_HANDLE = "java/lang/invoke/DirectMethodHandle$Holder";
|
||||
private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
|
||||
private static final String DMH_INVOKE_STATIC = "invokeStatic";
|
||||
private static final String DMH_INVOKE_SPECIAL = "invokeSpecial";
|
||||
@ -63,6 +63,8 @@ public final class GenerateJLIClassesPlugin implements Plugin {
|
||||
private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
|
||||
private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
|
||||
|
||||
private static final String DELEGATING_METHOD_HANDLE = "java/lang/invoke/DelegatingMethodHandle$Holder";
|
||||
|
||||
private static final JavaLangInvokeAccess JLIA
|
||||
= SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
@ -222,7 +224,15 @@ public final class GenerateJLIClassesPlugin implements Plugin {
|
||||
@Override
|
||||
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
|
||||
// Copy all but DMH_ENTRY to out
|
||||
in.transformAndCopy(entry -> entry.path().equals(DMH_ENTRY) ? null : entry, out);
|
||||
in.transformAndCopy(entry -> {
|
||||
// filter out placeholder entries
|
||||
if (entry.path().equals(DIRECT_METHOD_HANDLE_ENTRY) ||
|
||||
entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY)) {
|
||||
return null;
|
||||
} else {
|
||||
return entry;
|
||||
}
|
||||
}, out);
|
||||
speciesTypes.forEach(types -> generateBMHClass(types, out));
|
||||
generateDMHClass(out);
|
||||
return out.build();
|
||||
@ -264,15 +274,24 @@ public final class GenerateJLIClassesPlugin implements Plugin {
|
||||
}
|
||||
}
|
||||
try {
|
||||
byte[] bytes =
|
||||
JLIA.generateDMHClassBytes(DMH, methodTypes, dmhTypes);
|
||||
ResourcePoolEntry ndata = ResourcePoolEntry.create(DMH_ENTRY, bytes);
|
||||
byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
|
||||
DIRECT_METHOD_HANDLE, methodTypes, dmhTypes);
|
||||
ResourcePoolEntry ndata = ResourcePoolEntry
|
||||
.create(DIRECT_METHOD_HANDLE_ENTRY, bytes);
|
||||
out.add(ndata);
|
||||
|
||||
bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
|
||||
DELEGATING_METHOD_HANDLE, methodTypes);
|
||||
ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HANDLE_ENTRY, bytes);
|
||||
out.add(ndata);
|
||||
} catch (Exception ex) {
|
||||
throw new PluginException(ex);
|
||||
}
|
||||
}
|
||||
private static final String DMH_ENTRY = "/java.base/" + DMH + ".class";
|
||||
private static final String DIRECT_METHOD_HANDLE_ENTRY =
|
||||
"/java.base/" + DIRECT_METHOD_HANDLE + ".class";
|
||||
private static final String DELEGATING_METHOD_HANDLE_ENTRY =
|
||||
"/java.base/" + DELEGATING_METHOD_HANDLE + ".class";
|
||||
|
||||
// Convert LL -> LL, L3 -> LLL
|
||||
private static String expandSignature(String signature) {
|
||||
@ -310,15 +329,19 @@ public final class GenerateJLIClassesPlugin implements Plugin {
|
||||
assert(parts.length == 2);
|
||||
assert(parts[1].length() == 1);
|
||||
String parameters = expandSignature(parts[0]);
|
||||
Class<?> rtype = primitiveType(parts[1].charAt(0));
|
||||
Class<?>[] ptypes = new Class<?>[parameters.length()];
|
||||
for (int i = 0; i < ptypes.length; i++) {
|
||||
ptypes[i] = primitiveType(parameters.charAt(i));
|
||||
Class<?> rtype = simpleType(parts[1].charAt(0));
|
||||
if (parameters.isEmpty()) {
|
||||
return MethodType.methodType(rtype);
|
||||
} else {
|
||||
Class<?>[] ptypes = new Class<?>[parameters.length()];
|
||||
for (int i = 0; i < ptypes.length; i++) {
|
||||
ptypes[i] = simpleType(parameters.charAt(i));
|
||||
}
|
||||
return MethodType.methodType(rtype, ptypes);
|
||||
}
|
||||
return MethodType.methodType(rtype, ptypes);
|
||||
}
|
||||
|
||||
private static Class<?> primitiveType(char c) {
|
||||
private static Class<?> simpleType(char c) {
|
||||
switch (c) {
|
||||
case 'F':
|
||||
return float.class;
|
||||
|
@ -205,8 +205,12 @@ public class VerifyStackTrace {
|
||||
.replaceAll("java.base@(\\d+\\.){0,3}(\\d+)/", "java.base/")
|
||||
.replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run")
|
||||
.replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke")
|
||||
// DMHs may or may not be pre-generated, making frames differ
|
||||
.replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH")
|
||||
.replaceAll("DMH\\.invoke", "DMH/xxxxxxxx.invoke")
|
||||
// invoke frames may or may not have basic method type
|
||||
// information encoded for diagnostic purposes
|
||||
.replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z]+_[A-Z]", "xx.invoke$1")
|
||||
.replaceAll("\\$[0-9]+", "\\$??");
|
||||
} else {
|
||||
return produced;
|
||||
|
Loading…
Reference in New Issue
Block a user