8164858: Enable build-time use of java.lang.invoke resolve tracing

Reviewed-by: erikj, vlivanov
This commit is contained in:
Claes Redestad 2016-08-31 14:20:02 +02:00
parent 3f2262b347
commit 5afec5d3d6
4 changed files with 73 additions and 51 deletions

View File

@ -50,6 +50,8 @@ TARGETS += $(CLASSLIST_JAR)
CLASSLIST_FILE := $(SUPPORT_OUTPUTDIR)/classlist/classlist CLASSLIST_FILE := $(SUPPORT_OUTPUTDIR)/classlist/classlist
JLI_TRACE_FILE := $(SUPPORT_OUTPUTDIR)/classlist/jli_trace.out
# If an external buildjdk has been supplied, we don't build a separate interim # If an external buildjdk has been supplied, we don't build a separate interim
# image, so just use the external build jdk instead. # image, so just use the external build jdk instead.
ifeq ($(EXTERNAL_BUILDJDK), true) ifeq ($(EXTERNAL_BUILDJDK), true)
@ -59,13 +61,11 @@ endif
$(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXE_SUFFIX) $(CLASSLIST_JAR) $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXE_SUFFIX) $(CLASSLIST_JAR)
$(call MakeDir, $(@D)) $(call MakeDir, $(@D))
$(call LogInfo, Generating lib/classlist) $(call LogInfo, Generating lib/classlist)
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.tmp \
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist $(LOG_DEBUG) 2>&1
# Filter out generated classes, remove after JDK-8149977
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@ \ $(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@ \
-Xshare:dump -XX:SharedClassListFile=$@.tmp $(LOG_DEBUG) 2>&1 -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \
$(RM) $@.tmp -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist \
$(LOG_DEBUG) 2>&1 > $(JLI_TRACE_FILE)
TARGETS += $(CLASSLIST_FILE) TARGETS += $(CLASSLIST_FILE)

View File

@ -29,12 +29,12 @@ import java.io.IOException;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.SharedSecrets;
@ -69,11 +69,11 @@ public final class GenerateJLIClassesPlugin implements Plugin {
private static final JavaLangInvokeAccess JLIA private static final JavaLangInvokeAccess JLIA
= SharedSecrets.getJavaLangInvokeAccess(); = SharedSecrets.getJavaLangInvokeAccess();
List<String> speciesTypes; Set<String> speciesTypes;
List<String> invokerTypes; Set<String> invokerTypes;
Map<String, List<String>> dmhMethods; Map<String, Set<String>> dmhMethods;
public GenerateJLIClassesPlugin() { public GenerateJLIClassesPlugin() {
} }
@ -110,8 +110,8 @@ public final class GenerateJLIClassesPlugin implements Plugin {
* A better long-term solution is to define and run a set of quick * 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. * generators and extracting this list as a step in the build process.
*/ */
public static List<String> defaultSpecies() { public static Set<String> defaultSpecies() {
return List.of("LL", "L3", "L4", "L5", "L6", "L7", "L7I", return Set.of("LL", "L3", "L4", "L5", "L6", "L7", "L7I",
"L7II", "L7IIL", "L8", "L9", "L10", "L10I", "L10II", "L10IIL", "L7II", "L7IIL", "L8", "L9", "L10", "L10I", "L10II", "L10IIL",
"L11", "L12", "L13", "LI", "D", "L3I", "LIL", "LLI", "LLIL", "L11", "L12", "L13", "LI", "D", "L3I", "LIL", "LLI", "LLIL",
"LILL", "I", "LLILL"); "LILL", "I", "LLILL");
@ -120,23 +120,23 @@ public final class GenerateJLIClassesPlugin implements Plugin {
/** /**
* @return the default invoker forms to generate. * @return the default invoker forms to generate.
*/ */
private static List<String> defaultInvokers() { private static Set<String> defaultInvokers() {
return List.of("LL_L", "LL_I", "LILL_I", "L6_L"); return Set.of("LL_L", "LL_I", "LILL_I", "L6_L");
} }
/** /**
* @return the list of default DirectMethodHandle methods to generate. * @return the list of default DirectMethodHandle methods to generate.
*/ */
private static Map<String, List<String>> defaultDMHMethods() { private static Map<String, Set<String>> defaultDMHMethods() {
return Map.of( return Map.of(
DMH_INVOKE_VIRTUAL, List.of("L_L", "LL_L", "LLI_I", "L3_V"), DMH_INVOKE_VIRTUAL, Set.of("L_L", "LL_L", "LLI_I", "L3_V"),
DMH_INVOKE_SPECIAL, List.of("LL_I", "LL_L", "LLF_L", "LLD_L", "L3_L", DMH_INVOKE_SPECIAL, Set.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", "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", "LLIL_I", "LLII_I", "LLII_L", "L3I_L", "L3I_I", "LLILI_I",
"LLIIL_L", "LLIILL_L", "LLIILL_I", "LLIIL_I", "LLILIL_I", "LLIIL_L", "LLIILL_L", "LLIILL_I", "LLIIL_I", "LLILIL_I",
"LLILILL_I", "LLILII_I", "LLI3_I", "LLI3L_I", "LLI3LL_I", "LLILILL_I", "LLILII_I", "LLI3_I", "LLI3L_I", "LLI3LL_I",
"LLI3_L", "LLI4_I"), "LLI3_L", "LLI4_I"),
DMH_INVOKE_STATIC, List.of("LII_I", "LIL_I", "LILIL_I", "LILII_I", DMH_INVOKE_STATIC, Set.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", "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", "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", "L8_L", "L9_L", "L10_L", "L10I_L", "L10II_L", "L10IIL_L",
@ -159,15 +159,48 @@ public final class GenerateJLIClassesPlugin implements Plugin {
public void configure(Map<String, String> config) { public void configure(Map<String, String> config) {
String mainArgument = config.get(NAME); String mainArgument = config.get(NAME);
if (mainArgument != null && mainArgument.startsWith("@")) { if ("none".equals(mainArgument)) {
speciesTypes = Set.of();
invokerTypes = Set.of();
dmhMethods = Map.of();
return;
}
// Start with the default configuration
Set<String> defaultBMHSpecies = defaultSpecies();
// Expand BMH species signatures
defaultBMHSpecies = defaultBMHSpecies.stream()
.map(type -> expandSignature(type))
.collect(Collectors.toSet());
Set<String> defaultInvokerTypes = defaultInvokers();
validateMethodTypes(defaultInvokerTypes);
Map<String, Set<String>> defaultDmhMethods = defaultDMHMethods();
for (Set<String> dmhMethodTypes : defaultDmhMethods.values()) {
validateMethodTypes(dmhMethodTypes);
}
// Extend the default configuration with the contents in the supplied
// input file
if (mainArgument == null || !mainArgument.startsWith("@")) {
speciesTypes = defaultBMHSpecies;
invokerTypes = defaultInvokerTypes;
dmhMethods = defaultDmhMethods;
} else {
File file = new File(mainArgument.substring(1)); File file = new File(mainArgument.substring(1));
if (file.exists()) { if (file.exists()) {
speciesTypes = new ArrayList<>(); // Use TreeSet/TreeMap to keep things sorted in a deterministic
invokerTypes = new ArrayList<>(); // order to avoid scrambling the layout on small changes and to
dmhMethods = new HashMap<>(); // ease finding methods in the generated code
Stream<String> lines = fileLines(file); speciesTypes = new TreeSet<>(defaultBMHSpecies);
invokerTypes = new TreeSet<>(defaultInvokerTypes);
lines.map(line -> line.split(" ")) dmhMethods = new TreeMap<>();
for (Map.Entry<String, Set<String>> entry : defaultDmhMethods.entrySet()) {
dmhMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
}
fileLines(file)
.map(line -> line.split(" "))
.forEach(parts -> { .forEach(parts -> {
switch (parts[0]) { switch (parts[0]) {
case "[BMH_RESOLVE]": case "[BMH_RESOLVE]":
@ -191,28 +224,14 @@ public final class GenerateJLIClassesPlugin implements Plugin {
} }
}); });
} }
} else {
List<String> bmhSpecies = defaultSpecies();
// Expand BMH species signatures
speciesTypes = bmhSpecies.stream()
.map(type -> expandSignature(type))
.collect(Collectors.toList());
invokerTypes = defaultInvokers();
validateMethodTypes(invokerTypes);
dmhMethods = defaultDMHMethods();
for (List<String> dmhMethodTypes : dmhMethods.values()) {
validateMethodTypes(dmhMethodTypes);
}
} }
} }
private void addDMHMethodType(String dmh, String methodType) { private void addDMHMethodType(String dmh, String methodType) {
validateMethodType(methodType); validateMethodType(methodType);
List<String> methodTypes = dmhMethods.get(dmh); Set<String> methodTypes = dmhMethods.get(dmh);
if (methodTypes == null) { if (methodTypes == null) {
methodTypes = new ArrayList<>(); methodTypes = new TreeSet<>();
dmhMethods.put(dmh, methodTypes); dmhMethods.put(dmh, methodTypes);
} }
methodTypes.add(methodType); methodTypes.add(methodType);
@ -226,7 +245,7 @@ public final class GenerateJLIClassesPlugin implements Plugin {
} }
} }
private void validateMethodTypes(List<String> dmhMethodTypes) { private void validateMethodTypes(Set<String> dmhMethodTypes) {
for (String type : dmhMethodTypes) { for (String type : dmhMethodTypes) {
validateMethodType(type); validateMethodType(type);
} }
@ -291,13 +310,13 @@ public final class GenerateJLIClassesPlugin implements Plugin {
private void generateHolderClasses(ResourcePoolBuilder out) { private void generateHolderClasses(ResourcePoolBuilder out) {
int count = 0; int count = 0;
for (List<String> entry : dmhMethods.values()) { for (Set<String> entry : dmhMethods.values()) {
count += entry.size(); count += entry.size();
} }
MethodType[] directMethodTypes = new MethodType[count]; MethodType[] directMethodTypes = new MethodType[count];
int[] dmhTypes = new int[count]; int[] dmhTypes = new int[count];
int index = 0; int index = 0;
for (Map.Entry<String, List<String>> entry : dmhMethods.entrySet()) { for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
String dmhType = entry.getKey(); String dmhType = entry.getKey();
for (String type : entry.getValue()) { for (String type : entry.getValue()) {
// The DMH type to actually ask for is retrieved by removing // The DMH type to actually ask for is retrieved by removing
@ -314,10 +333,11 @@ public final class GenerateJLIClassesPlugin implements Plugin {
} }
} }
MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()]; MethodType[] invokerMethodTypes = new MethodType[this.invokerTypes.size()];
for (int i = 0; i < invokerTypes.size(); i++) { int i = 0;
for (String invokerType : invokerTypes) {
// The invoker type to ask for is retrieved by removing the first // The invoker type to ask for is retrieved by removing the first
// and the last argument, which needs to be of Object.class // and the last argument, which needs to be of Object.class
MethodType mt = asMethodType(invokerTypes.get(i)); MethodType mt = asMethodType(invokerType);
final int lastParam = mt.parameterCount() - 1; final int lastParam = mt.parameterCount() - 1;
if (mt.parameterCount() < 2 || if (mt.parameterCount() < 2 ||
mt.parameterType(0) != Object.class || mt.parameterType(0) != Object.class ||
@ -327,6 +347,7 @@ public final class GenerateJLIClassesPlugin implements Plugin {
} }
mt = mt.dropParameterTypes(lastParam, lastParam + 1); mt = mt.dropParameterTypes(lastParam, lastParam + 1);
invokerMethodTypes[i] = mt.dropParameterTypes(0, 1); invokerMethodTypes[i] = mt.dropParameterTypes(0, 1);
i++;
} }
try { try {
byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes( byte[] bytes = JLIA.generateDirectMethodHandleHolderClassBytes(

View File

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

View File

@ -22,6 +22,7 @@
*/ */
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -75,7 +76,7 @@ public class GenerateJLIClassesPluginTest {
} }
private static List<String> classFilesForSpecies(List<String> species) { private static List<String> classFilesForSpecies(Collection<String> species) {
return species.stream() return species.stream()
.map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class") .map(s -> "/java.base/java/lang/invoke/BoundMethodHandle$Species_" + s + ".class")
.collect(Collectors.toList()); .collect(Collectors.toList());