8167018: Nashorn and jjs should support --module-path and --add-modules options

Reviewed-by: jlaskey, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2016-10-07 21:28:20 +05:30
parent e29b338cd7
commit 5cb4f9c787
6 changed files with 132 additions and 7 deletions

View File

@ -75,6 +75,7 @@ final class PackagesHelper {
}
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 @@ final class PackagesHelper {
*/
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 class PackagesHelper {
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

View File

@ -56,6 +56,8 @@ import java.lang.reflect.Modifier;
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.concurrent.atomic.LongAdder;
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 @@ public final class Context {
}
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 final class Context {
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);
}
}

View File

@ -153,6 +153,12 @@ public final class ScriptEnvironment {
/** 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 @@ public final class ScriptEnvironment {
_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");

View File

@ -77,6 +77,14 @@ public final class OptionTemplate implements Comparable<OptionTemplate> {
/** 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 @@ public final class OptionTemplate implements Comparable<OptionTemplate> {
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 @@ public final class OptionTemplate implements Comparable<OptionTemplate> {
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 @@ public final class OptionTemplate implements Comparable<OptionTemplate> {
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) {

View File

@ -500,7 +500,16 @@ public final class Options {
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

View File

@ -237,6 +237,21 @@ nashorn.option.loader.per.compile = { \
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", \