8175026: Capture build-time parameters to --generate-jli-classes
authorredestad
Fri, 17 Feb 2017 18:12:55 +0100
changeset 43837 51a9f82cff03
parent 43836 dd08faade481
child 43838 d696d6394a37
child 43894 c316ff1ea2c7
child 43895 a9970f1b6b70
child 43983 d35bbdbe9d36
8175026: Capture build-time parameters to --generate-jli-classes Reviewed-by: mchung
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
--- 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