8161067: jlink: Enable plugins to use the module pool for class lookup
authorjlaskey
Tue, 12 Jul 2016 10:58:58 -0300
changeset 39635 07c4b195280d
parent 39634 812020bcb9f1
child 39636 7ddf0ba87d81
8161067: jlink: Enable plugins to use the module pool for class lookup Reviewed-by: sundar, psandoz
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleEntryFactory.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModulePoolImpl.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ModulePool.java
jdk/test/tools/jlink/JLinkPluginsTest.java
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Tue Jul 12 10:58:58 2016 -0300
@@ -400,6 +400,14 @@
         }
 
         @Override
+        public Optional<ModuleEntry> findEntryInContext(String path, ModuleEntry context) {
+            Objects.requireNonNull(path);
+            Objects.requireNonNull(context);
+            Optional<ModuleEntry> res = pool.findEntryInContext(path, context);
+            return res.map(this::getUncompressed);
+        }
+
+        @Override
         public boolean contains(ModuleEntry res) {
             return pool.contains(res);
         }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleEntryFactory.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleEntryFactory.java	Tue Jul 12 10:58:58 2016 -0300
@@ -51,16 +51,32 @@
                 original.getPath(), original.getType(), file);
     }
 
-    private static String moduleFrom(String path) {
+    static String moduleFrom(String path) {
         Objects.requireNonNull(path);
         if (path.isEmpty() || path.charAt(0) != '/') {
             throw new IllegalArgumentException(path + " must start with /");
         }
-        String noRoot = path.substring(1);
-        int idx = noRoot.indexOf('/');
+        int idx = path.indexOf('/', 1);
         if (idx == -1) {
             throw new IllegalArgumentException("/ missing after module: " + path);
         }
-        return noRoot.substring(0, idx);
+        return path.substring(1, idx);
+    }
+
+    static String packageFrom(String path) {
+        Objects.requireNonNull(path);
+        int idx = path.lastIndexOf('/');
+        if (idx == -1) {
+            throw new IllegalArgumentException("/ missing from path: " + path);
+        }
+        if (path.startsWith("/")) {
+            int jdx = path.indexOf('/', 1);
+            if (jdx == -1) {
+                throw new IllegalArgumentException("/ missing after module: " + path);
+            }
+            return path.substring(jdx + 1, idx);
+        } else {
+            return path.substring(0, idx);
+        }
     }
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModulePoolImpl.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModulePoolImpl.java	Tue Jul 12 10:58:58 2016 -0300
@@ -265,6 +265,25 @@
     }
 
     /**
+     * Get the ModuleEntry for the passed path restricted to supplied context.
+     *
+     * @param path A data path
+     * @param context A context of the search
+     * @return A ModuleEntry instance or null if the data is not found
+     */
+    @Override
+    public Optional<ModuleEntry> findEntryInContext(String path, ModuleEntry context) {
+        Objects.requireNonNull(path);
+        Objects.requireNonNull(context);
+        LinkModule module = modules.get(context.getModule());
+        Objects.requireNonNull(module);
+        Optional<ModuleEntry> entry = module.findEntry(path);
+        // Navigating other modules via requires and exports is problematic
+        // since we cannot construct the runtime model of loaders and layers.
+        return entry;
+     }
+
+    /**
      * Check if the ModulePool contains the given ModuleEntry.
      *
      * @param data The module data to check existence for.
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java	Tue Jul 12 10:58:58 2016 -0300
@@ -28,7 +28,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.stream.Collectors;
+import java.util.Optional;
 import jdk.tools.jlink.plugin.ModulePool;
 import jdk.tools.jlink.plugin.Plugin.Category;
 import jdk.internal.org.objectweb.asm.ClassReader;
@@ -67,7 +67,7 @@
         return index == -1 ? "" : binaryName.substring(0, index);
     }
 
-    private ModuleEntry transform(ModuleEntry resource, Map<String, ModuleEntry> classes) {
+    private ModuleEntry transform(ModuleEntry resource, ModulePool pool) {
         byte[] inBytes = resource.getBytes();
         ClassReader cr = new ClassReader(inBytes);
         ClassNode cn = new ClassNode();
@@ -96,10 +96,11 @@
                         min.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) {
                         String ldcClassName = ldc.cst.toString();
                         String thatClassName = ldcClassName.replaceAll("\\.", "/");
-                        ModuleEntry thatClass = classes.get(thatClassName);
+                        Optional<ModuleEntry> thatClass =
+                            pool.findEntryInContext(thatClassName + ".class", resource);
 
-                        if (thatClass != null) {
-                            int thatAccess = getAccess(thatClass);
+                        if (thatClass.isPresent()) {
+                            int thatAccess = getAccess(thatClass.get());
                             String thatPackage = getPackage(thatClassName);
 
                             if ((thatAccess & Opcodes.ACC_PRIVATE) != Opcodes.ACC_PRIVATE &&
@@ -142,19 +143,13 @@
     public void visit(ModulePool in, ModulePool out) {
         Objects.requireNonNull(in);
         Objects.requireNonNull(out);
-        Map<String, ModuleEntry> classes = in.entries()
-            .filter(resource -> resource != null &&
-                    resource.getPath().endsWith(".class") &&
-                    !resource.getPath().endsWith("/module-info.class"))
-            .collect(Collectors.toMap(resource -> binaryClassName(resource.getPath()),
-                                      resource -> resource));
+
         in.entries()
-            .filter(resource -> resource != null)
             .forEach(resource -> {
                 String path = resource.getPath();
 
                 if (path.endsWith(".class") && !path.endsWith("/module-info.class")) {
-                    out.add(transform(resource, classes));
+                    out.add(transform(resource, in));
                 } else {
                     out.add(resource);
                 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ModulePool.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ModulePool.java	Tue Jul 12 10:58:58 2016 -0300
@@ -89,7 +89,16 @@
      * @param path A data path
      * @return A ModuleEntry instance or null if the data is not found
      */
-   public Optional<ModuleEntry> findEntry(String path);
+    public Optional<ModuleEntry> findEntry(String path);
+
+    /**
+     * Get the ModuleEntry for the passed path restricted to supplied context.
+     *
+     * @param path A data path
+     * @param context A context of the search
+     * @return A ModuleEntry instance or null if the data is not found
+     */
+    public Optional<ModuleEntry> findEntryInContext(String path, ModuleEntry context);
 
     /**
      * Check if the ModulePool contains the given ModuleEntry.
--- a/jdk/test/tools/jlink/JLinkPluginsTest.java	Tue Jul 12 11:29:01 2016 +0100
+++ b/jdk/test/tools/jlink/JLinkPluginsTest.java	Tue Jul 12 10:58:58 2016 -0300
@@ -75,5 +75,13 @@
             Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
             helper.checkImage(imageDir, moduleName, res, null);
         }
+        {
+            // Optimize Class.forName
+            String[] userOptions = {"--class-for-name"};
+            String moduleName = "classforname";
+            helper.generateDefaultJModule(moduleName, "composite2");
+            Path imageDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
+            helper.checkImage(imageDir, moduleName, null, null);
+        }
     }
 }