8175026: Capture build-time parameters to --generate-jli-classes
Reviewed-by: mchung
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Thu Feb 16 13:55:49 2017 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java Fri Feb 17 18:12:55 2017 +0100
@@ -24,14 +24,17 @@
*/
package jdk.tools.jlink.internal.plugins;
+import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.lang.invoke.MethodType;
+import java.lang.module.ModuleDescriptor;
import java.nio.file.Files;
-import java.util.ArrayList;
import java.util.EnumSet;
-import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -51,8 +54,13 @@
public final class GenerateJLIClassesPlugin implements Plugin {
private static final String NAME = "generate-jli-classes";
+ private static final String IGNORE_VERSION = "ignore-version";
private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
+ private static final String IGNORE_VERSION_WARNING = NAME + ".ignore.version.warn";
+ private static final String VERSION_MISMATCH_WARNING = NAME + ".version.mismatch.warn";
+
+ private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
private static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
@@ -69,11 +77,15 @@
private static final JavaLangInvokeAccess JLIA
= SharedSecrets.getJavaLangInvokeAccess();
- Set<String> speciesTypes;
+ Set<String> speciesTypes = Set.of();
+
+ Set<String> invokerTypes = Set.of();
- Set<String> invokerTypes;
+ Map<String, Set<String>> dmhMethods = Map.of();
- Map<String, Set<String>> dmhMethods;
+ String mainArgument;
+
+ boolean ignoreVersion;
public GenerateJLIClassesPlugin() {
}
@@ -157,69 +169,101 @@
@Override
public void configure(Map<String, String> config) {
- String mainArgument = config.get(NAME);
+ mainArgument = config.get(NAME);
+ ignoreVersion = Boolean.parseBoolean(config.get(IGNORE_VERSION));
+ }
+ public void initialize(ResourcePool in) {
// Start with the default configuration
- Set<String> defaultBMHSpecies = defaultSpecies();
- // Expand BMH species signatures
- defaultBMHSpecies = defaultBMHSpecies.stream()
+ speciesTypes = defaultSpecies().stream()
.map(type -> expandSignature(type))
.collect(Collectors.toSet());
- Set<String> defaultInvokerTypes = defaultInvokers();
- validateMethodTypes(defaultInvokerTypes);
+ invokerTypes = defaultInvokers();
+ validateMethodTypes(invokerTypes);
- Map<String, Set<String>> defaultDmhMethods = defaultDMHMethods();
- for (Set<String> dmhMethodTypes : defaultDmhMethods.values()) {
+ dmhMethods = defaultDMHMethods();
+ for (Set<String> dmhMethodTypes : dmhMethods.values()) {
validateMethodTypes(dmhMethodTypes);
}
// Extend the default configuration with the contents in the supplied
- // input file
+ // input file - if none was supplied we look for the default file
if (mainArgument == null || !mainArgument.startsWith("@")) {
- speciesTypes = defaultBMHSpecies;
- invokerTypes = defaultInvokerTypes;
- dmhMethods = defaultDmhMethods;
+ try (InputStream traceFile =
+ this.getClass().getResourceAsStream(DEFAULT_TRACE_FILE)) {
+ if (traceFile != null) {
+ readTraceConfig(
+ new BufferedReader(
+ new InputStreamReader(traceFile)).lines());
+ }
+ } catch (Exception e) {
+ throw new PluginException("Couldn't read " + DEFAULT_TRACE_FILE, e);
+ }
} else {
File file = new File(mainArgument.substring(1));
if (file.exists()) {
- // Use TreeSet/TreeMap to keep things sorted in a deterministic
- // order to avoid scrambling the layout on small changes and to
- // ease finding methods in the generated code
- speciesTypes = new TreeSet<>(defaultBMHSpecies);
- invokerTypes = new TreeSet<>(defaultInvokerTypes);
- 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 -> {
- 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
- }
- });
+ readTraceConfig(fileLines(file));
}
}
}
+ private boolean checkVersion(Runtime.Version linkedVersion) {
+ Runtime.Version baseVersion = Runtime.version();
+ if (baseVersion.major() != linkedVersion.major() ||
+ baseVersion.minor() != linkedVersion.minor()) {
+ return false;
+ }
+ return true;
+ }
+
+ private Runtime.Version getLinkedVersion(ResourcePool in) {
+ ModuleDescriptor.Version version = in.moduleView()
+ .findModule("java.base")
+ .get()
+ .descriptor()
+ .version()
+ .orElseThrow(() -> new PluginException("No version defined in "
+ + "the java.base being linked"));
+ return Runtime.Version.parse(version.toString());
+ }
+
+ private void readTraceConfig(Stream<String> lines) {
+ // Use TreeSet/TreeMap to keep things sorted in a deterministic
+ // order to avoid scrambling the layout on small changes and to
+ // ease finding methods in the generated code
+ speciesTypes = new TreeSet<>(speciesTypes);
+ invokerTypes = new TreeSet<>(invokerTypes);
+ TreeMap<String, Set<String>> newDMHMethods = new TreeMap<>();
+ for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
+ newDMHMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
+ }
+ dmhMethods = newDMHMethods;
+ 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
+ }
+ });
+ }
+
private void addDMHMethodType(String dmh, String methodType) {
validateMethodType(methodType);
Set<String> methodTypes = dmhMethods.get(dmh);
@@ -265,6 +309,25 @@
@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
+ if (ignoreVersion) {
+ System.out.println(
+ PluginsResourceBundle
+ .getMessage(IGNORE_VERSION_WARNING));
+ } else if (!checkVersion(getLinkedVersion(in))) {
+ // The linked images are not version compatible
+ if (mainArgument != null) {
+ // Log a mismatch warning if an argument was specified
+ System.out.println(
+ PluginsResourceBundle
+ .getMessage(VERSION_MISMATCH_WARNING,
+ getLinkedVersion(in),
+ Runtime.version()));
+ }
+ in.transformAndCopy(entry -> entry, out);
+ return out.build();
+ }
+
+ initialize(in);
// Copy all but DMH_ENTRY to out
in.transformAndCopy(entry -> {
// filter out placeholder entries
@@ -277,8 +340,18 @@
return entry;
}
}, out);
+
+ // Generate BMH Species classes
speciesTypes.forEach(types -> generateBMHClass(types, out));
+
+ // Generate LambdaForm Holder classes
generateHolderClasses(out);
+
+ // Let it go
+ speciesTypes = null;
+ invokerTypes = null;
+ dmhMethods = null;
+
return out.build();
}
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties Thu Feb 16 13:55:49 2017 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties Fri Feb 17 18:12:55 2017 +0100
@@ -75,11 +75,23 @@
exclude-jmod-section.description=\
Specify a JMOD section to exclude
-generate-jli-classes.argument=@filename
+generate-jli-classes.argument=@filename[:ignore-version=<true|false>]
generate-jli-classes.description=\
-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.
+Specify a file listing the java.lang.invoke classes to pre-generate. \n\
+By default, this plugin may use a builtin list of classes to pre-generate. \n\
+If this plugin runs on a different runtime version than the image being \n\
+created then code generation will be disabled by default to guarantee \n\
+correctness - add ignore-version=true to override this.
+
+generate-jli-classes.ignore.version.warn=\
+WARNING: --generate-jli-classes set to ignore version mismatch between \n\
+JDK running jlink and target image.
+
+generate-jli-classes.version.mismatch.warn=\
+WARNING: Pre-generation of JLI classes is only supported when linking \n\
+the same version of java.base ({0}) as the JDK running jlink ({1}), \n\
+class generation skipped - specify ignore-version to override.
system-modules.argument=retainModuleTarget