8164569: Generate non-customized invoker forms at link time
Reviewed-by: vlivanov
--- a/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java Wed Aug 24 16:11:21 2016 +0200
@@ -133,6 +133,34 @@
forms.toArray(new LambdaForm[0]));
}
+ static byte[] generateInvokersHolderClassBytes(String className,
+ MethodType[] methodTypes) {
+
+ HashSet<MethodType> dedupSet = new HashSet<>();
+ ArrayList<LambdaForm> forms = new ArrayList<>();
+ ArrayList<String> names = new ArrayList<>();
+ int[] types = {
+ MethodTypeForm.LF_EX_LINKER,
+ MethodTypeForm.LF_EX_INVOKER,
+ MethodTypeForm.LF_GEN_LINKER,
+ MethodTypeForm.LF_GEN_INVOKER
+ };
+ for (int i = 0; i < methodTypes.length; i++) {
+ // generate methods representing invokers of the specified type
+ if (dedupSet.add(methodTypes[i])) {
+ for (int type : types) {
+ LambdaForm invokerForm = Invokers.invokeHandleForm(methodTypes[i],
+ /*customized*/false, type);
+ forms.add(invokerForm);
+ names.add(invokerForm.kind.defaultLambdaName);
+ }
+ }
+ }
+ return generateCodeBytesForLFs(className,
+ names.toArray(new String[0]),
+ forms.toArray(new LambdaForm[0]));
+ }
+
/*
* Generate customized code for a set of LambdaForms of specified types into
* a class with a specified name.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Aug 24 16:11:21 2016 +0200
@@ -629,6 +629,10 @@
name = name + "_" + form.returnType().basicTypeChar();
return resolveFrom(name, invokerType, LambdaForm.Holder.class);
}
+ case EXACT_INVOKER: // fall-through
+ case EXACT_LINKER: // fall-through
+ case GENERIC_INVOKER: // fall-through
+ case GENERIC_LINKER: return resolveFrom(name, invokerType.basicType(), Invokers.Holder.class);
case GET_OBJECT: // fall-through
case GET_BOOLEAN: // fall-through
case GET_BYTE: // fall-through
--- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Aug 24 16:11:21 2016 +0200
@@ -36,6 +36,7 @@
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.LambdaForm.Kind.*;
/**
* Construction and caching of often-used invokers.
@@ -254,7 +255,7 @@
* @param which bit-encoded 0x01 whether it is a CP adapter ("linker") or MHs.invoker value ("invoker");
* 0x02 whether it is for invokeExact or generic invoke
*/
- private static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) {
+ static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) {
boolean isCached;
if (!customized) {
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
@@ -263,12 +264,12 @@
isCached = false; // maybe cache if mtype == mtype.basicType()
}
boolean isLinker, isGeneric;
- String debugName;
+ Kind kind;
switch (which) {
- case MethodTypeForm.LF_EX_LINKER: isLinker = true; isGeneric = false; debugName = "invokeExact_MT"; break;
- case MethodTypeForm.LF_EX_INVOKER: isLinker = false; isGeneric = false; debugName = "exactInvoker"; break;
- case MethodTypeForm.LF_GEN_LINKER: isLinker = true; isGeneric = true; debugName = "invoke_MT"; break;
- case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
+ case MethodTypeForm.LF_EX_LINKER: isLinker = true; isGeneric = false; kind = EXACT_LINKER; break;
+ case MethodTypeForm.LF_EX_INVOKER: isLinker = false; isGeneric = false; kind = EXACT_INVOKER; break;
+ case MethodTypeForm.LF_GEN_LINKER: isLinker = true; isGeneric = true; kind = GENERIC_LINKER; break;
+ case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; kind = GENERIC_INVOKER; break;
default: throw new InternalError();
}
LambdaForm lform;
@@ -323,7 +324,11 @@
names[CHECK_CUSTOM] = new Name(NF_checkCustomized, outArgs[0]);
}
names[LINKER_CALL] = new Name(outCallType, outArgs);
- lform = new LambdaForm(debugName, INARG_LIMIT, names);
+ if (customized) {
+ lform = new LambdaForm(kind.defaultLambdaName, INARG_LIMIT, names);
+ } else {
+ lform = new LambdaForm(kind.defaultLambdaName, INARG_LIMIT, names, kind);
+ }
if (isLinker)
lform.compileToBytecode(); // JVM needs a real methodOop
if (isCached)
@@ -614,4 +619,15 @@
}
}
}
+
+ static {
+ // The Holder class will contain pre-generated Invokers 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 Invokers generated ahead of time */
+ final class Holder {}
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Aug 24 16:11:21 2016 +0200
@@ -275,6 +275,10 @@
BOUND_REINVOKER("BMH.reinvoke"),
REINVOKER("MH.reinvoke"),
DELEGATE("MH.delegate"),
+ EXACT_LINKER("MH.invokeExact_MT"),
+ EXACT_INVOKER("MH.exactInvoker"),
+ GENERIC_LINKER("MH.invoke_MT"),
+ GENERIC_INVOKER("MH.invoker"),
DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual"),
DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial"),
DIRECT_INVOKE_STATIC("DMH.invokeStatic"),
@@ -366,6 +370,10 @@
this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null, Kind.GENERIC);
}
LambdaForm(String debugName,
+ int arity, Name[] names, Kind kind) {
+ this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null, kind);
+ }
+ LambdaForm(String debugName,
int arity, Name[] names, boolean forceInline) {
this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null, Kind.GENERIC);
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Aug 24 16:11:21 2016 +0200
@@ -1745,6 +1745,13 @@
return GenerateJLIClassesHelper
.generateBasicFormsClassBytes(className);
}
+
+ @Override
+ public byte[] generateInvokersHolderClassBytes(final String className,
+ MethodType[] methodTypes) {
+ return GenerateJLIClassesHelper
+ .generateInvokersHolderClassBytes(className, methodTypes);
+ }
});
}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java Wed Aug 24 16:11:21 2016 +0200
@@ -46,7 +46,7 @@
boolean isNative(Object mname);
/**
- * Returns a {@code byte[]} containing the bytecode for a class implementing
+ * Returns a {@code byte[]} representation of a class implementing
* DirectMethodHandle of each pairwise combination of {@code MethodType} and
* an {@code int} representing method type. Used by
* GenerateJLIClassesPlugin to generate such a class during the jlink phase.
@@ -55,7 +55,7 @@
MethodType[] methodTypes, int[] types);
/**
- * Returns a {@code byte[]} containing the bytecode for a class implementing
+ * Returns a {@code byte[]} representation of 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.
@@ -64,7 +64,7 @@
MethodType[] methodTypes);
/**
- * Returns a {@code byte[]} containing the bytecode for a BoundMethodHandle
+ * Returns a {@code byte[]} representation of {@code BoundMethodHandle}
* species class implementing the signature defined by {@code types}. Used
* by GenerateBMHClassesPlugin to enable generation of such classes during
* the jlink phase. Should do some added validation since this string may be
@@ -74,8 +74,15 @@
final String types);
/**
- * Returns a {@code byte[]} containing the bytecode for a class implementing
+ * Returns a {@code byte[]} representation of a class implementing
* the zero and identity forms of all {@code LambdaForm.BasicType}s.
*/
byte[] generateBasicFormsClassBytes(final String className);
+
+ /**
+ * Returns a {@code byte[]} representation of a class implementing
+ * the invoker forms for the set of supplied {@code methodTypes}.
+ */
+ byte[] generateInvokersHolderClassBytes(String className,
+ MethodType[] methodTypes);
}
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Wed Aug 24 16:11:21 2016 +0200
@@ -48,14 +48,12 @@
private static final String NAME = "generate-jli-classes";
private static final String BMH_PARAM = "bmh";
-
private static final String BMH_SPECIES_PARAM = "bmh-species";
- private static final String DMH_PARAM = "dmh";
-
private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
- private static final String DIRECT_METHOD_HANDLE = "java/lang/invoke/DirectMethodHandle$Holder";
+ private static final String DMH_PARAM = "dmh";
+ private static final String DIRECT_HOLDER = "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,17 +61,22 @@
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 String INVOKERS_PARAM = "invokers";
- private static final String BASIC_FORMS_HANDLE = "java/lang/invoke/LambdaForm$Holder";
+ private static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
+ private static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
+ private static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";
private static final JavaLangInvokeAccess JLIA
= SharedSecrets.getJavaLangInvokeAccess();
List<String> speciesTypes;
+ List<String> invokerTypes;
+
Map<String, List<String>> dmhMethods;
+
public GenerateJLIClassesPlugin() {
}
@@ -117,6 +120,13 @@
}
/**
+ * @return the default invoker forms to generate.
+ */
+ public static List<String> defaultInvokers() {
+ return List.of("_L", "_I", "I_I", "LI_I", "ILL_I", "LIL_I", "L_L", "LL_V", "LLLL_L");
+ }
+
+ /**
* @return the list of default DirectMethodHandle methods to generate.
*/
public static Map<String, List<String>> defaultDMHMethods() {
@@ -153,6 +163,7 @@
// Enable by default
boolean bmhEnabled = true;
boolean dmhEnabled = true;
+ boolean invokersEnabled = true;
if (mainArgument != null) {
List<String> args = Arrays.asList(mainArgument.split(","));
if (!args.contains(BMH_PARAM)) {
@@ -161,6 +172,9 @@
if (!args.contains(DMH_PARAM)) {
dmhEnabled = false;
}
+ if (!args.contains(INVOKERS_PARAM)) {
+ dmhEnabled = false;
+ }
}
if (!bmhEnabled) {
@@ -183,6 +197,21 @@
.collect(Collectors.toList());
}
+ if (!invokersEnabled) {
+ invokerTypes = List.of();
+ } else {
+ String args = config.get(INVOKERS_PARAM);
+ if (args != null && !args.isEmpty()) {
+ invokerTypes = Arrays.stream(args.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.toList());
+ validateMethodTypes(invokerTypes);
+ } else {
+ invokerTypes = defaultInvokers();
+ }
+
+ }
// DirectMethodHandles
if (!dmhEnabled) {
dmhMethods = Map.of();
@@ -196,18 +225,7 @@
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
dmhMethods.put(dmhParam, dmhMethodTypes);
- // Validation check
- for (String type : dmhMethodTypes) {
- String[] typeParts = type.split("_");
- // check return type (second part)
- if (typeParts.length != 2 || typeParts[1].length() != 1
- || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) {
- throw new PluginException(
- "Method type signature must be of form [LJIFD]*_[LJIFDV]");
- }
- // expand and check arguments (first part)
- expandSignature(typeParts[0]);
- }
+ validateMethodTypes(dmhMethodTypes);
}
}
if (dmhMethods.isEmpty()) {
@@ -216,6 +234,20 @@
}
}
+ void validateMethodTypes(List<String> dmhMethodTypes) {
+ for (String type : dmhMethodTypes) {
+ String[] typeParts = type.split("_");
+ // check return type (second part)
+ if (typeParts.length != 2 || typeParts[1].length() != 1
+ || "LJIFDV".indexOf(typeParts[1].charAt(0)) == -1) {
+ throw new PluginException(
+ "Method type signature must be of form [LJIFD]*_[LJIFDV]");
+ }
+ // expand and check arguments (first part)
+ expandSignature(typeParts[0]);
+ }
+ }
+
private static void requireBasicType(char c) {
if ("LIJFD".indexOf(c) < 0) {
throw new PluginException(
@@ -228,9 +260,10 @@
// Copy all but DMH_ENTRY to out
in.transformAndCopy(entry -> {
// filter out placeholder entries
- if (entry.path().equals(DIRECT_METHOD_HANDLE_ENTRY) ||
- entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY) ||
- entry.path().equals(BASIC_FORMS_HANDLE_ENTRY)) {
+ if (entry.path().equals(DIRECT_METHOD_HOLDER_ENTRY) ||
+ entry.path().equals(DELEGATING_METHOD_HOLDER_ENTRY) ||
+ entry.path().equals(INVOKERS_HOLDER_ENTRY) ||
+ entry.path().equals(BASIC_FORMS_HOLDER_ENTRY)) {
return null;
} else {
return entry;
@@ -265,42 +298,53 @@
for (List<String> entry : dmhMethods.values()) {
count += entry.size();
}
- MethodType[] methodTypes = new MethodType[count];
+ MethodType[] directMethodTypes = new MethodType[count];
int[] dmhTypes = new int[count];
int index = 0;
for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
String dmhType = entry.getKey();
for (String type : entry.getValue()) {
- methodTypes[index] = asMethodType(type);
+ directMethodTypes[index] = asMethodType(type);
dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
index++;
}
}
+ MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
+ for (int i = 0; i < invokerTypes.size(); i++) {
+ invokerMethodTypes[i] = asMethodType(invokerTypes.get(i));
+ }
try {
byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(
- DIRECT_METHOD_HANDLE, methodTypes, dmhTypes);
+ DIRECT_HOLDER, directMethodTypes, dmhTypes);
ResourcePoolEntry ndata = ResourcePoolEntry
- .create(DIRECT_METHOD_HANDLE_ENTRY, bytes);
+ .create(DIRECT_METHOD_HOLDER_ENTRY, bytes);
out.add(ndata);
bytes = JLIA.generateDelegatingMethodHandleHolderClassBytes(
- DELEGATING_METHOD_HANDLE, methodTypes);
- ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HANDLE_ENTRY, bytes);
+ DELEGATING_HOLDER, directMethodTypes);
+ ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HOLDER_ENTRY, bytes);
out.add(ndata);
- bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HANDLE);
- ndata = ResourcePoolEntry.create(BASIC_FORMS_HANDLE_ENTRY, bytes);
+ bytes = JLIA.generateInvokersHolderClassBytes(INVOKERS_HOLDER,
+ invokerMethodTypes);
+ ndata = ResourcePoolEntry.create(INVOKERS_HOLDER_ENTRY, bytes);
+ out.add(ndata);
+
+ bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HOLDER);
+ ndata = ResourcePoolEntry.create(BASIC_FORMS_HOLDER_ENTRY, bytes);
out.add(ndata);
} catch (Exception ex) {
throw new PluginException(ex);
}
}
- 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";
- private static final String BASIC_FORMS_HANDLE_ENTRY =
- "/java.base/" + BASIC_FORMS_HANDLE + ".class";
+ private static final String DIRECT_METHOD_HOLDER_ENTRY =
+ "/java.base/" + DIRECT_HOLDER + ".class";
+ private static final String DELEGATING_METHOD_HOLDER_ENTRY =
+ "/java.base/" + DELEGATING_HOLDER + ".class";
+ private static final String BASIC_FORMS_HOLDER_ENTRY =
+ "/java.base/" + BASIC_FORMS_HOLDER + ".class";
+ private static final String INVOKERS_HOLDER_ENTRY =
+ "/java.base/" + INVOKERS_HOLDER + ".class";
// Convert LL -> LL, L3 -> LLL
private static String expandSignature(String signature) {
--- a/jdk/test/java/lang/StackWalker/VerifyStackTrace.java Wed Aug 24 16:09:34 2016 +0200
+++ b/jdk/test/java/lang/StackWalker/VerifyStackTrace.java Wed Aug 24 16:11:21 2016 +0200
@@ -205,12 +205,13 @@
.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
+ // LFs may or may not be pre-generated, making frames differ
.replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH")
- .replaceAll("DMH\\.invoke", "DMH/xxxxxxxx.invoke")
+ .replaceAll("Invokers\\$Holder", "LambdaForm\\$MH")
+ .replaceAll("MH\\.invoke", "MH/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("xx\\.invoke([A-Za-z]*)_[A-Z_]+", "xx.invoke$1")
.replaceAll("\\$[0-9]+", "\\$??");
} else {
return produced;