8163371: Enable tracing which JLI classes can be pre-generated

Reviewed-by: vlivanov
This commit is contained in:
Claes Redestad 2016-08-26 16:16:09 +02:00
parent f78a742b53
commit 1a404080ee
5 changed files with 127 additions and 100 deletions

View File

@ -497,6 +497,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
String shortTypes = LambdaForm.shortenSignature(types);
String className = SPECIES_CLASS_PREFIX + shortTypes;
Class<?> c = BootLoader.loadClassOrNull(className);
if (TRACE_RESOLVE) {
System.out.println("[BMH_RESOLVE] " + shortTypes +
(c != null ? " (success)" : " (fail)") );
}
if (c != null) {
return c.asSubclass(BoundMethodHandle.class);
} else {

View File

@ -607,7 +607,10 @@ class InvokerBytecodeGenerator {
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);
if (TRACE_RESOLVE) {
System.out.println("[LF_RESOLVE] " + holder.getName() + " " + name + " " +
shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)") );
}
return resolvedMember;
}

View File

@ -46,6 +46,7 @@ import java.util.Properties;
static final boolean DUMP_CLASS_FILES;
static final boolean TRACE_INTERPRETER;
static final boolean TRACE_METHOD_LINKAGE;
static final boolean TRACE_RESOLVE;
static final int COMPILE_THRESHOLD;
static final boolean LOG_LF_COMPILATION_FAILURE;
static final int DONT_INLINE_THRESHOLD;
@ -65,6 +66,8 @@ import java.util.Properties;
props.getProperty("java.lang.invoke.MethodHandle.TRACE_INTERPRETER"));
TRACE_METHOD_LINKAGE = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"));
TRACE_RESOLVE = Boolean.parseBoolean(
props.getProperty("java.lang.invoke.MethodHandle.TRACE_RESOLVE"));
COMPILE_THRESHOLD = Integer.parseInt(
props.getProperty("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", "0"));
LOG_LF_COMPILATION_FAILURE = Boolean.parseBoolean(

View File

@ -24,7 +24,11 @@
*/
package jdk.tools.jlink.internal.plugins;
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
@ -32,6 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaLangInvokeAccess;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
@ -47,12 +52,8 @@ public final class GenerateJLIClassesPlugin implements Plugin {
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 DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
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";
@ -61,8 +62,6 @@ 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 INVOKERS_PARAM = "invokers";
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";
@ -76,7 +75,6 @@ public final class GenerateJLIClassesPlugin implements Plugin {
Map<String, List<String>> dmhMethods;
public GenerateJLIClassesPlugin() {
}
@ -112,7 +110,7 @@ public final class GenerateJLIClassesPlugin implements Plugin {
* A better long-term solution is to define and run a set of quick
* generators and extracting this list as a step in the build process.
*/
public static List<String> defaultSpecies() {
private static List<String> defaultSpecies() {
return List.of("LL", "L3", "L4", "L5", "L6", "L7", "L7I",
"L7II", "L7IIL", "L8", "L9", "L10", "L10I", "L10II", "L10IIL",
"L11", "L12", "L13", "LI", "D", "L3I", "LIL", "LLI", "LLIL",
@ -122,26 +120,27 @@ public final class GenerateJLIClassesPlugin implements Plugin {
/**
* @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");
private static List<String> defaultInvokers() {
return List.of("LL_L", "LL_I", "LILL_I", "L6_L");
}
/**
* @return the list of default DirectMethodHandle methods to generate.
*/
public static Map<String, List<String>> defaultDMHMethods() {
private static Map<String, List<String>> defaultDMHMethods() {
return Map.of(
DMH_INVOKE_VIRTUAL, List.of("_L", "L_L", "LI_I", "LL_V"),
DMH_INVOKE_SPECIAL, List.of("L_I", "L_L", "LF_L", "LD_L", "LL_L",
"L3_L", "L4_L", "L5_L", "L6_L", "L7_L", "LI_I", "LI_L", "LIL_I",
"LII_I", "LII_L", "LLI_L", "LLI_I", "LILI_I", "LIIL_L",
"LIILL_L", "LIILL_I", "LIIL_I", "LILIL_I", "LILILL_I",
"LILII_I", "LI3_I", "LI3L_I", "LI3LL_I", "LI3_L", "LI4_I"),
DMH_INVOKE_STATIC, List.of("II_I", "IL_I", "ILIL_I", "ILII_I",
"_I", "_L", "_V", "D_L", "F_L", "I_I", "II_L", "LI_L",
"L_V", "L_L", "LL_L", "L3_L", "L4_L", "L5_L", "L6_L",
"L7_L", "L8_L", "L9_L", "L9I_L", "L9II_L", "L9IIL_L",
"L10_L", "L11_L", "L12_L", "L13_L", "L13I_L", "L13II_L")
DMH_INVOKE_VIRTUAL, List.of("L_L", "LL_L", "LLI_I", "L3_V"),
DMH_INVOKE_SPECIAL, List.of("LL_I", "LL_L", "LLF_L", "LLD_L", "L3_L",
"L4_L", "L5_L", "L6_L", "L7_L", "L8_L", "LLI_I", "LLI_L",
"LLIL_I", "LLII_I", "LLII_L", "L3I_L", "L3I_I", "LLILI_I",
"LLIIL_L", "LLIILL_L", "LLIILL_I", "LLIIL_I", "LLILIL_I",
"LLILILL_I", "LLILII_I", "LLI3_I", "LLI3L_I", "LLI3LL_I",
"LLI3_L", "LLI4_I"),
DMH_INVOKE_STATIC, List.of("LII_I", "LIL_I", "LILIL_I", "LILII_I",
"L_I", "L_L", "L_V", "LD_L", "LF_L", "LI_I", "LII_L", "LLI_L",
"LL_V", "LL_L", "L3_L", "L4_L", "L5_L", "L6_L", "L7_L",
"L8_L", "L9_L", "L10_L", "L10I_L", "L10II_L", "L10IIL_L",
"L11_L", "L12_L", "L13_L", "L14_L", "L14I_L", "L14II_L")
);
}
@ -160,92 +159,89 @@ public final class GenerateJLIClassesPlugin implements Plugin {
public void configure(Map<String, String> config) {
String mainArgument = config.get(NAME);
// 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)) {
bmhEnabled = false;
}
if (!args.contains(DMH_PARAM)) {
dmhEnabled = false;
}
if (!args.contains(INVOKERS_PARAM)) {
dmhEnabled = false;
}
}
if (mainArgument != null && mainArgument.startsWith("@")) {
File file = new File(mainArgument.substring(1));
if (file.exists()) {
speciesTypes = new ArrayList<>();
invokerTypes = new ArrayList<>();
dmhMethods = new HashMap<>();
Stream<String> lines = fileLines(file);
if (!bmhEnabled) {
speciesTypes = List.of();
lines.map(line -> line.split(" "))
.forEach(parts -> {
switch (parts[0]) {
case "[BMH_RESOLVE]":
speciesTypes.add(expandSignature(parts[1]));
break;
case "[LF_RESOLVE]":
String methodType = parts[3];
validateMethodType(methodType);
if (parts[1].contains("Invokers")) {
invokerTypes.add(methodType);
} else if (parts[1].contains("DirectMethodHandle")) {
String dmh = parts[2];
// ignore getObject etc for now (generated
// by default)
if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
addDMHMethodType(dmh, methodType);
}
}
break;
default: break; // ignore
}
});
}
} else {
String args = config.get(BMH_SPECIES_PARAM);
List<String> bmhSpecies;
if (args != null && !args.isEmpty()) {
bmhSpecies = Arrays.stream(args.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
} else {
bmhSpecies = defaultSpecies();
}
List<String> bmhSpecies = defaultSpecies();
// Expand BMH species signatures
speciesTypes = bmhSpecies.stream()
.map(type -> expandSignature(type))
.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();
}
invokerTypes = defaultInvokers();
validateMethodTypes(invokerTypes);
}
// DirectMethodHandles
if (!dmhEnabled) {
dmhMethods = Map.of();
} else {
dmhMethods = new HashMap<>();
for (String dmhParam : DMH_METHOD_TYPE_MAP.keySet()) {
String args = config.get(dmhParam);
if (args != null && !args.isEmpty()) {
List<String> dmhMethodTypes = Arrays.stream(args.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
dmhMethods.put(dmhParam, dmhMethodTypes);
validateMethodTypes(dmhMethodTypes);
}
}
if (dmhMethods.isEmpty()) {
dmhMethods = defaultDMHMethods();
dmhMethods = defaultDMHMethods();
for (List<String> dmhMethodTypes : dmhMethods.values()) {
validateMethodTypes(dmhMethodTypes);
}
}
}
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 void addDMHMethodType(String dmh, String methodType) {
validateMethodType(methodType);
List<String> methodTypes = dmhMethods.get(dmh);
if (methodTypes == null) {
methodTypes = new ArrayList<>();
dmhMethods.put(dmh, methodTypes);
}
methodTypes.add(methodType);
}
private Stream<String> fileLines(File file) {
try {
return Files.lines(file.toPath());
} catch (IOException io) {
throw new PluginException("Couldn't read file");
}
}
private void validateMethodTypes(List<String> dmhMethodTypes) {
for (String type : dmhMethodTypes) {
validateMethodType(type);
}
}
private void validateMethodType(String type) {
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) {
@ -304,14 +300,33 @@ public final class GenerateJLIClassesPlugin implements Plugin {
for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) {
String dmhType = entry.getKey();
for (String type : entry.getValue()) {
directMethodTypes[index] = asMethodType(type);
// The DMH type to actually ask for is retrieved by removing
// the first argument, which needs to be of Object.class
MethodType mt = asMethodType(type);
if (mt.parameterCount() < 1 ||
mt.parameterType(0) != Object.class) {
throw new PluginException(
"DMH type parameter must start with L");
}
directMethodTypes[index] = mt.dropParameterTypes(0, 1);
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));
// The invoker type to ask for is retrieved by removing the first
// and the last argument, which needs to be of Object.class
MethodType mt = asMethodType(invokerTypes.get(i));
final int lastParam = mt.parameterCount() - 1;
if (mt.parameterCount() < 2 ||
mt.parameterType(0) != Object.class ||
mt.parameterType(lastParam) != Object.class) {
throw new PluginException(
"Invoker type parameter must start and end with L");
}
mt = mt.dropParameterTypes(lastParam, lastParam + 1);
invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
}
try {
byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(

View File

@ -68,10 +68,12 @@ exclude-resources.argument=<pattern-list> resources to exclude
exclude-resources.description=\
Specify resources to exclude. e.g.: **.jcov,glob:**/META-INF/**
generate-jli-classes.argument=<bmh[:bmh-species=LL,L3,...]>
generate-jli-classes.argument=<@filename>
generate-jli-classes.description=\
Concrete java.lang.invoke classes to generate
Takes a file hinting to jlink what java.lang.invoke classes to pre-generate. If\n\
this flag is not specified a default set of classes will be generated, so to \n\
disable pre-generation supply the name of an empty or non-existing file
installed-modules.description=Fast loading of module descriptors (always enabled)