8167018: Nashorn and jjs should support --module-path and --add-modules options
authorsundar
Fri, 07 Oct 2016 21:28:20 +0530
changeset 41426 aa60c8d89a92
parent 41425 86226c782b1a
child 41427 d54424bc50b2
8167018: Nashorn and jjs should support --module-path and --add-modules options Reviewed-by: jlaskey, hannesw
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PackagesHelper.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptEnvironment.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/OptionTemplate.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/Options.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Options.properties
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PackagesHelper.java	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PackagesHelper.java	Fri Oct 07 21:28:20 2016 +0530
@@ -75,6 +75,7 @@
     }
 
     private final Context context;
+    private final boolean modulePathSet;
     private final StandardJavaFileManager fm;
     private final Set<JavaFileObject.Kind> fileKinds;
     private final FileSystem jrtfs;
@@ -86,11 +87,17 @@
      */
     PackagesHelper(final Context context) throws IOException {
         this.context = context;
-        final String classPath = context.getEnv()._classpath;
+        final String modulePath = context.getEnv()._module_path;
+        this.modulePathSet = modulePath != null && !modulePath.isEmpty();
         if (isJavacAvailable()) {
+            final String classPath = context.getEnv()._classpath;
             fm = compiler.getStandardFileManager(null, null, null);
             fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS);
 
+            if (this.modulePathSet) {
+                fm.setLocation(StandardLocation.MODULE_PATH, getFiles(modulePath));
+            }
+
             if (classPath != null && !classPath.isEmpty()) {
                 fm.setLocation(StandardLocation.CLASS_PATH, getFiles(classPath));
             } else {
@@ -155,6 +162,13 @@
         final Set<String> props = new HashSet<>();
         if (fm != null) {
             listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props);
+            if (this.modulePathSet) {
+                for (Set<Location> locs : fm.listModuleLocations(StandardLocation.MODULE_PATH)) {
+                    for (Location loc : locs) {
+                        listPackage(loc, pkg, props);
+                    }
+                }
+            }
             listPackage(StandardLocation.CLASS_PATH, pkg, props);
         } else if (jrtfs != null) {
             // look for the /packages/<package_name> directory
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Fri Oct 07 21:28:20 2016 +0530
@@ -56,6 +56,8 @@
 import java.lang.reflect.Module;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.CodeSigner;
@@ -81,6 +83,8 @@
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.logging.Level;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.script.ScriptEngine;
 import jdk.dynalink.DynamicLinker;
 import jdk.internal.org.objectweb.asm.ClassReader;
@@ -614,18 +618,37 @@
         }
         this.errors    = errors;
 
+        // if user passed --module-path, we create a module class loader with
+        // passed appLoader as the parent.
+        final String modulePath = env._module_path;
+        ClassLoader appCl = null;
+        if (!env._compile_only && modulePath != null && !modulePath.isEmpty()) {
+            // make sure that caller can create a class loader.
+            if (sm != null) {
+                sm.checkCreateClassLoader();
+            }
+            appCl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                @Override
+                public ClassLoader run() {
+                    return createModuleLoader(appLoader, modulePath, env._add_modules);
+                }
+            });
+        } else {
+            appCl = appLoader;
+        }
+
         // if user passed -classpath option, make a URLClassLoader with that and
-        // the app loader as the parent.
-        final String classPath = options.getString("classpath");
+        // the app loader or module app loader as the parent.
+        final String classPath = env._classpath;
         if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
             // make sure that caller can create a class loader.
             if (sm != null) {
                 sm.checkCreateClassLoader();
             }
-            this.appLoader = NashornLoader.createClassLoader(classPath, appLoader);
-        } else {
-            this.appLoader = appLoader;
+            appCl = NashornLoader.createClassLoader(classPath, appCl);
         }
+
+        this.appLoader = appCl;
         this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold);
 
         final int cacheSize = env._class_cache_size;
@@ -1750,4 +1773,37 @@
     public SwitchPoint getBuiltinSwitchPoint(final String name) {
         return builtinSwitchPoints.get(name);
     }
+
+    private static ClassLoader createModuleLoader(final ClassLoader cl,
+            final String modulePath, final String addModules) {
+        if (addModules == null) {
+            throw new IllegalArgumentException("--module-path specified with no --add-modules");
+        }
+
+        final Path[] paths = Stream.of(modulePath.split(File.pathSeparator)).
+            map(s -> Paths.get(s)).
+            toArray(sz -> new Path[sz]);
+        final ModuleFinder mf = ModuleFinder.of(paths);
+        final Set<ModuleReference> mrefs = mf.findAll();
+        if (mrefs.isEmpty()) {
+            throw new RuntimeException("No modules in script --module-path: " + modulePath);
+        }
+
+        final Set<String> rootMods;
+        if (addModules.equals("ALL-MODULE-PATH")) {
+            rootMods = mrefs.stream().
+                map(mr->mr.descriptor().name()).
+                collect(Collectors.toSet());
+        } else {
+            rootMods = Stream.of(addModules.split(",")).
+                map(String::trim).
+                collect(Collectors.toSet());
+        }
+
+        final Layer boot = Layer.boot();
+        final Configuration conf = boot.configuration().
+            resolveRequires(mf, ModuleFinder.of(), rootMods);
+        final String firstMod = rootMods.iterator().next();
+        return boot.defineModulesWithOneLoader(conf, cl).findLoader(firstMod);
+    }
 }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Fri Oct 07 21:28:20 2016 +0530
@@ -153,6 +153,12 @@
     /** Create a new class loaded for each compilation */
     public final boolean _loader_per_compile;
 
+    /** --module-path, if any */
+    public final String _module_path;
+
+    /** --add-modules, if any */
+    public final String _add_modules;
+
     /** Do not support Java support extensions. */
     public final boolean _no_java;
 
@@ -285,6 +291,8 @@
             _lazy_compilation = lazy_compilation;
         }
         _loader_per_compile   = options.getBoolean("loader.per.compile");
+        _module_path          = options.getString("module.path");
+        _add_modules          = options.getString("add.modules");
         _no_java              = options.getBoolean("no.java");
         _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
         _no_typed_arrays      = options.getBoolean("no.typed.arrays");
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/OptionTemplate.java	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/OptionTemplate.java	Fri Oct 07 21:28:20 2016 +0530
@@ -77,6 +77,14 @@
     /** is the option value specified as next argument? */
     private boolean valueNextArg;
 
+    /**
+     * Can this option be repeated in command line?
+     *
+     * For a repeatable option, multiple values will be merged as comma
+     * separated values rather than the last value overriding previous ones.
+     */
+    private boolean repeated;
+
     OptionTemplate(final String resource, final String key, final String value, final boolean isHelp, final boolean isXHelp) {
         this.resource = resource;
         this.key = key;
@@ -223,6 +231,14 @@
         return valueNextArg;
     }
 
+    /**
+     * Can this option be repeated?
+     * @return boolean
+     */
+    public boolean isRepeated() {
+        return repeated;
+    }
+
     private static String strip(final String value, final char start, final char end) {
         final int len = value.length();
         if (len > 1 && value.charAt(0) == start && value.charAt(len - 1) == end) {
@@ -281,6 +297,9 @@
                 case "value_next_arg":
                     this.valueNextArg = Boolean.parseBoolean(arg);
                     break;
+                case "repeated":
+                    this.repeated = true;
+                    break;
                 default:
                     throw new IllegalArgumentException(keyToken);
                 }
@@ -302,6 +321,10 @@
         if (name == null && shortName == null) {
             throw new IllegalArgumentException(origValue);
         }
+
+        if (this.repeated && !"string".equals(this.type)) {
+            throw new IllegalArgumentException("repeated option should be of type string: " + this.name);
+        }
     }
 
     boolean nameMatches(final String aName) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/Options.java	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/options/Options.java	Fri Oct 07 21:28:20 2016 +0530
@@ -500,7 +500,16 @@
                 throw new IllegalOptionException(parg.template);
             }
 
-            set(parg.template.getKey(), createOption(parg.template, parg.value));
+            if (parg.template.isRepeated()) {
+                assert parg.template.getType().equals("string");
+
+                final String key = key(parg.template.getKey());
+                final String value = options.containsKey(key)?
+                    (options.get(key).getValue() + "," + parg.value) : Objects.toString(parg.value);
+                options.put(key, new Option<>(value));
+            } else {
+                set(parg.template.getKey(), createOption(parg.template, parg.value));
+            }
 
             // Arg may have a dependency to set other args, e.g.
             // scripting->anon.functions
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Options.properties	Fri Oct 07 10:30:14 2016 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Options.properties	Fri Oct 07 21:28:20 2016 +0530
@@ -237,6 +237,21 @@
     default=true                                   \
 }
 
+nashorn.option.module.path ={                                 \
+    name="--module-path",                                     \
+    desc="--module-path path. Specify where to find user java modules.", \
+    value_next_arg=true,                                      \
+    type=String                                               \
+}
+
+nashorn.option.add.modules ={                                 \
+    name="--add-modules",                                     \
+    desc="--add-modules modules. Specify the root user java modules.", \
+    repeated=true,                                            \
+    value_next_arg=true,                                      \
+    type=String                                               \
+}
+
 nashorn.option.no.java = {                         \
     name="--no-java",                              \
     short_name="-nj",                              \