8167018: Nashorn and jjs should support --module-path and --add-modules options
Reviewed-by: jlaskey, hannesw
--- 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", \