8164044: Generate corresponding simple DelegatingMethodHandles when generating a DirectMethodHandle at link time
Reviewed-by: vlivanov, mhaupt, shade
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Thu Aug 18 19:00:39 2016 +0200
@@ -36,7 +36,6 @@
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 @@
/*non-public*/ char fieldTypeChar(int i) {
return typeChars.charAt(i);
}
- Object fieldSignature() {
+ String fieldSignature() {
return typeChars;
}
public Class<? extends BoundMethodHandle> fieldHolder() {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java Thu Aug 18 19:00:39 2016 +0200
@@ -27,6 +27,7 @@
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 @@
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 @@
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 @@
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 @@
} 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 {}
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Aug 18 19:00:39 2016 +0200
@@ -38,6 +38,7 @@
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 @@
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 @@
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 @@
}
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 @@
}
/* Placeholder class for DirectMethodHandles generated ahead of time */
- private final class Holder {}
+ final class Holder {}
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Thu Aug 18 19:00:39 2016 +0200
@@ -29,21 +29,56 @@
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 @@
* 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()) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Thu Aug 18 19:00:39 2016 +0200
@@ -46,6 +46,7 @@
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 @@
}
/** 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 @@
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());
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Thu Aug 18 19:00:39 2016 +0200
@@ -41,6 +41,7 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
}
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 @@
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 @@
/** 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 @@
}
}
- /**
- * 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)) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Aug 18 19:00:39 2016 +0200
@@ -1718,10 +1718,19 @@
}
@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
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java Thu Aug 18 19:00:39 2016 +0200
@@ -51,8 +51,17 @@
* 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
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Thu Aug 18 19:00:39 2016 +0200
@@ -55,7 +55,7 @@
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 @@
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 @@
@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 @@
}
}
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 @@
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;
--- a/jdk/test/java/lang/StackWalker/VerifyStackTrace.java Thu Aug 18 09:52:43 2016 -0700
+++ b/jdk/test/java/lang/StackWalker/VerifyStackTrace.java Thu Aug 18 19:00:39 2016 +0200
@@ -205,8 +205,12 @@
.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;