8174823: Module system implementation refresh (3/2017)
Co-authored-by: Mandy Chung <mandy.chung@oracle.com> Co-authored-by: Sundararajan Athijegannathan <sundararajan.athijegannathan@oracle.com> Co-authored-by: Peter Levart <peter.levart@gmail.com> Reviewed-by: chegar, mchung, alanb
This commit is contained in:
parent
10a42feace
commit
b19eb2766f
@ -128,7 +128,6 @@ SUNWprivate_1.1 {
|
||||
Java_java_lang_Class_isInstance;
|
||||
Java_java_lang_Class_registerNatives;
|
||||
Java_java_lang_ClassLoader_findBootstrapClass;
|
||||
Java_java_lang_ClassLoader_defineClass0;
|
||||
Java_java_lang_ClassLoader_defineClass1;
|
||||
Java_java_lang_ClassLoader_defineClass2;
|
||||
Java_java_lang_ClassLoader_findBuiltinLib;
|
||||
|
@ -64,9 +64,9 @@ import java.util.StringJoiner;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.Resources;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.ConstantPool;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
@ -2563,11 +2563,11 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
Module module = getModule();
|
||||
if (module.isNamed()) {
|
||||
if (!ResourceHelper.isSimpleResource(name)) {
|
||||
if (Resources.canEncapsulate(name)) {
|
||||
Module caller = Reflection.getCallerClass().getModule();
|
||||
if (caller != module) {
|
||||
Set<String> packages = module.getDescriptor().packages();
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
if (packages.contains(pn) && !module.isOpen(pn, caller)) {
|
||||
// resource is in package not open to caller
|
||||
return null;
|
||||
@ -2665,11 +2665,11 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
Module module = getModule();
|
||||
if (module.isNamed()) {
|
||||
if (!ResourceHelper.isSimpleResource(name)) {
|
||||
if (Resources.canEncapsulate(name)) {
|
||||
Module caller = Reflection.getCallerClass().getModule();
|
||||
if (caller != module) {
|
||||
Set<String> packages = module.getDescriptor().packages();
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
if (packages.contains(pn) && !module.isOpen(pn, caller)) {
|
||||
// resource is in package not open to caller
|
||||
return null;
|
||||
|
@ -983,7 +983,7 @@ public abstract class ClassLoader {
|
||||
{
|
||||
protectionDomain = preDefineClass(name, protectionDomain);
|
||||
String source = defineClassSourceLocation(protectionDomain);
|
||||
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
|
||||
Class<?> c = defineClass1(this, name, b, off, len, protectionDomain, source);
|
||||
postDefineClass(c, protectionDomain);
|
||||
return c;
|
||||
}
|
||||
@ -1075,17 +1075,17 @@ public abstract class ClassLoader {
|
||||
|
||||
protectionDomain = preDefineClass(name, protectionDomain);
|
||||
String source = defineClassSourceLocation(protectionDomain);
|
||||
Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
|
||||
Class<?> c = defineClass2(this, name, b, b.position(), len, protectionDomain, source);
|
||||
postDefineClass(c, protectionDomain);
|
||||
return c;
|
||||
}
|
||||
|
||||
private native Class<?> defineClass1(String name, byte[] b, int off, int len,
|
||||
ProtectionDomain pd, String source);
|
||||
static native Class<?> defineClass1(ClassLoader loader, String name, byte[] b, int off, int len,
|
||||
ProtectionDomain pd, String source);
|
||||
|
||||
private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
|
||||
int off, int len, ProtectionDomain pd,
|
||||
String source);
|
||||
static native Class<?> defineClass2(ClassLoader loader, String name, java.nio.ByteBuffer b,
|
||||
int off, int len, ProtectionDomain pd,
|
||||
String source);
|
||||
|
||||
// true if the name is null or has the potential to be a valid binary name
|
||||
private boolean checkName(String name) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,9 +26,7 @@
|
||||
package java.lang;
|
||||
|
||||
import java.security.*;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.StringTokenizer;
|
||||
import java.lang.module.ModuleFinder;
|
||||
|
||||
/**
|
||||
* This class is for runtime permissions. A {@code RuntimePermission}
|
||||
@ -265,6 +263,16 @@ import java.util.StringTokenizer;
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>defineClass</td>
|
||||
* <td>Define a class with
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#defineClass(byte[])
|
||||
* Lookup.defineClass}.</td>
|
||||
* <td>This grants code with a suitably privileged {@code Lookup} object
|
||||
* permission to define classes in the same package as the {@code Lookup}'s
|
||||
* lookup class. </td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>accessDeclaredMembers</td>
|
||||
* <td>Access to the declared members of a class</td>
|
||||
* <td>This grants code permission to query a class for its public,
|
||||
@ -359,6 +367,14 @@ import java.util.StringTokenizer;
|
||||
* <td>See {@link java.lang.System.LoggerFinder java.lang.System.LoggerFinder}
|
||||
* for more information.</td>
|
||||
* </tr>
|
||||
*
|
||||
* <tr>
|
||||
* <td>accessSystemModules</td>
|
||||
* <td>Access system modules in the runtime image.</td>
|
||||
* <td>This grants the permission to access resources in the
|
||||
* {@linkplain ModuleFinder#ofSystem system modules} in the runtime image.</td>
|
||||
* </tr>
|
||||
*
|
||||
* </table>
|
||||
*
|
||||
* @implNote
|
||||
|
@ -982,13 +982,6 @@ final class StackStreamFactory {
|
||||
}
|
||||
|
||||
private static boolean isReflectionFrame(Class<?> c) {
|
||||
if (c.getName().startsWith("jdk.internal.reflect") &&
|
||||
!MethodAccessor.class.isAssignableFrom(c) &&
|
||||
!ConstructorAccessor.class.isAssignableFrom(c)) {
|
||||
throw new InternalError("Not jdk.internal.reflect.MethodAccessor"
|
||||
+ " or jdk.internal.reflect.ConstructorAccessor: "
|
||||
+ c.toString());
|
||||
}
|
||||
// ## should filter all @Hidden frames?
|
||||
return c == Method.class ||
|
||||
c == Constructor.class ||
|
||||
|
@ -43,6 +43,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URL;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Properties;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.Map;
|
||||
@ -1844,6 +1845,39 @@ public final class System {
|
||||
return new PrintStream(new BufferedOutputStream(fos, 128), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an exception/error at initialization time to stdout or stderr.
|
||||
*
|
||||
* @param printToStderr to print to stderr rather than stdout
|
||||
* @param printStackTrace to print the stack trace
|
||||
* @param msg the message to print before the exception, can be {@code null}
|
||||
* @param e the exception or error
|
||||
*/
|
||||
private static void logInitException(boolean printToStderr,
|
||||
boolean printStackTrace,
|
||||
String msg,
|
||||
Throwable e) {
|
||||
if (VM.initLevel() < 1) {
|
||||
throw new InternalError("system classes not initialized");
|
||||
}
|
||||
PrintStream log = (printToStderr) ? err : out;
|
||||
if (msg != null) {
|
||||
log.println(msg);
|
||||
}
|
||||
if (printStackTrace) {
|
||||
e.printStackTrace(log);
|
||||
} else {
|
||||
log.println(e);
|
||||
for (Throwable suppressed : e.getSuppressed()) {
|
||||
log.println("Suppressed: " + suppressed);
|
||||
}
|
||||
Throwable cause = e.getCause();
|
||||
if (cause != null) {
|
||||
log.println("Caused by: " + cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the system class. Called after thread initialization.
|
||||
*/
|
||||
@ -1922,13 +1956,25 @@ public final class System {
|
||||
/*
|
||||
* Invoked by VM. Phase 2 module system initialization.
|
||||
* Only classes in java.base can be loaded in this phase.
|
||||
*
|
||||
* @param printToStderr print exceptions to stderr rather than stdout
|
||||
* @param printStackTrace print stack trace when exception occurs
|
||||
*
|
||||
* @return JNI_OK for success, JNI_ERR for failure
|
||||
*/
|
||||
private static void initPhase2() {
|
||||
// initialize the module system
|
||||
System.bootLayer = ModuleBootstrap.boot();
|
||||
private static int initPhase2(boolean printToStderr, boolean printStackTrace) {
|
||||
try {
|
||||
bootLayer = ModuleBootstrap.boot();
|
||||
} catch (Exception | Error e) {
|
||||
logInitException(printToStderr, printStackTrace,
|
||||
"Error occurred during initialization of boot layer", e);
|
||||
return -1; // JNI_ERR
|
||||
}
|
||||
|
||||
// module system initialized
|
||||
VM.initLevel(2);
|
||||
|
||||
return 0; // JNI_OK
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2034,6 +2080,9 @@ public final class System {
|
||||
public ConcurrentHashMap<?, ?> createOrGetClassLoaderValueMap(ClassLoader cl) {
|
||||
return cl.createOrGetClassLoaderValueMap();
|
||||
}
|
||||
public Class<?> defineClass(ClassLoader loader, String name, byte[] b, ProtectionDomain pd, String source) {
|
||||
return ClassLoader.defineClass1(loader, name, b, 0, b.length, pd, source);
|
||||
}
|
||||
public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
|
||||
return cl.findBootstrapClassOrNull(name);
|
||||
}
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -43,6 +46,9 @@ import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Module;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
@ -191,6 +197,12 @@ public class MethodHandles {
|
||||
}
|
||||
if ((lookup.lookupModes() & Lookup.MODULE) == 0)
|
||||
throw new IllegalAccessException("lookup does not have MODULE lookup mode");
|
||||
if (!callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfOpenedByBackdoor(lookup, targetClass);
|
||||
}
|
||||
}
|
||||
return new Lookup(targetClass);
|
||||
}
|
||||
|
||||
@ -855,6 +867,112 @@ public class MethodHandles {
|
||||
return new Lookup(lookupClass(), newModes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a class to the same class loader and in the same runtime package and
|
||||
* {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
|
||||
* {@linkplain #lookupClass() lookup class}.
|
||||
*
|
||||
* <p> The {@linkplain #lookupModes() lookup modes} for this lookup must include
|
||||
* {@link #PACKAGE PACKAGE} access as default (package) members will be
|
||||
* accessible to the class. The {@code PACKAGE} lookup mode serves to authenticate
|
||||
* that the lookup object was created by a caller in the runtime package (or derived
|
||||
* from a lookup originally created by suitably privileged code to a target class in
|
||||
* the runtime package). The lookup modes cannot include {@link #PRIVATE PRIVATE}
|
||||
* access. A lookup with {@code PRIVATE} access can be downgraded to drop this lookup
|
||||
* mode with the {@linkplain #dropLookupMode(int) dropLookupMode} method. </p>
|
||||
*
|
||||
* <p> The {@code bytes} parameter is the class bytes of a valid class file (as defined
|
||||
* by the <em>The Java Virtual Machine Specification</em>) with a class name in the
|
||||
* same package as the lookup class. </p>
|
||||
*
|
||||
* <p> This method does not run the class initializer. The class initializer may
|
||||
* run at a later time, as detailed in section 12.4 of the <em>The Java Language
|
||||
* Specification</em>. </p>
|
||||
*
|
||||
* <p> If there is a security manager, its {@code checkPermission} method is first called
|
||||
* to check {@code RuntimePermission("defineClass")}. </p>
|
||||
*
|
||||
* @param bytes the class bytes
|
||||
* @return the {@code Class} object for the class
|
||||
* @throws IllegalArgumentException the bytes are for a class in a different package
|
||||
* to the lookup class
|
||||
* @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
|
||||
* @throws UnsupportedOperationException if the lookup class has {@code PRIVATE} access
|
||||
* @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be
|
||||
* verified ({@code VerifyError}), is already defined, or another linkage error occurs
|
||||
* @throws SecurityException if denied by the security manager
|
||||
* @throws NullPointerException if {@code bytes} is {@code null}
|
||||
* @since 9
|
||||
* @spec JPMS
|
||||
* @see Lookup#privateLookupIn
|
||||
* @see Lookup#dropLookupMode
|
||||
* @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
|
||||
*/
|
||||
public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new RuntimePermission("defineClass"));
|
||||
if (hasPrivateAccess())
|
||||
throw new UnsupportedOperationException("PRIVATE access not supported");
|
||||
if ((lookupModes() & PACKAGE) == 0)
|
||||
throw new IllegalAccessException("Lookup does not have PACKAGE access");
|
||||
assert (lookupModes() & (MODULE|PUBLIC)) != 0;
|
||||
|
||||
// parse class bytes to get class name (in internal form)
|
||||
bytes = bytes.clone();
|
||||
String name;
|
||||
try {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
name = reader.getClassName();
|
||||
} catch (RuntimeException e) {
|
||||
// ASM exceptions are poorly specified
|
||||
ClassFormatError cfe = new ClassFormatError();
|
||||
cfe.initCause(e);
|
||||
throw cfe;
|
||||
}
|
||||
|
||||
// get package and class name in binary form
|
||||
String cn, pn;
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
cn = name;
|
||||
pn = "";
|
||||
} else {
|
||||
cn = name.replace('/', '.');
|
||||
pn = cn.substring(0, index);
|
||||
}
|
||||
if (!pn.equals(lookupClass.getPackageName())) {
|
||||
throw new IllegalArgumentException("Class not in same package as lookup class");
|
||||
}
|
||||
|
||||
// invoke the class loader's defineClass method
|
||||
ClassLoader loader = lookupClass.getClassLoader();
|
||||
ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null;
|
||||
String source = "__Lookup_defineClass__";
|
||||
Class<?> clazz = SharedSecrets.getJavaLangAccess().defineClass(loader, cn, bytes, pd, source);
|
||||
assert clazz.getClassLoader() == lookupClass.getClassLoader()
|
||||
&& clazz.getPackageName().equals(lookupClass.getPackageName())
|
||||
&& protectionDomain(clazz) == lookupClassProtectionDomain();
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private ProtectionDomain lookupClassProtectionDomain() {
|
||||
ProtectionDomain pd = cachedProtectionDomain;
|
||||
if (pd == null) {
|
||||
cachedProtectionDomain = pd = protectionDomain(lookupClass);
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
|
||||
private ProtectionDomain protectionDomain(Class<?> clazz) {
|
||||
PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
|
||||
return AccessController.doPrivileged(pa);
|
||||
}
|
||||
|
||||
// cached protection domain
|
||||
private volatile ProtectionDomain cachedProtectionDomain;
|
||||
|
||||
|
||||
// Make sure outer class is initialized first.
|
||||
static { IMPL_NAMES.getClass(); }
|
||||
|
||||
@ -1948,7 +2066,7 @@ return mh1;
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this lookup has {@code PRIVATE} access.
|
||||
* @return {@code true} if this lookup has {@code PRIVATE} acesss.
|
||||
* @return {@code true} if this lookup has {@code PRIVATE} access.
|
||||
* @since 9
|
||||
*/
|
||||
public boolean hasPrivateAccess() {
|
||||
|
@ -112,11 +112,9 @@ public final class Configuration {
|
||||
// module constraints on target
|
||||
private final String osName;
|
||||
private final String osArch;
|
||||
private final String osVersion;
|
||||
|
||||
String osName() { return osName; }
|
||||
String osArch() { return osArch; }
|
||||
String osVersion() { return osVersion; }
|
||||
|
||||
private Configuration() {
|
||||
this.parents = Collections.emptyList();
|
||||
@ -125,7 +123,6 @@ public final class Configuration {
|
||||
this.nameToModule = Collections.emptyMap();
|
||||
this.osName = null;
|
||||
this.osArch = null;
|
||||
this.osVersion = null;
|
||||
}
|
||||
|
||||
private Configuration(List<Configuration> parents,
|
||||
@ -152,7 +149,6 @@ public final class Configuration {
|
||||
|
||||
this.osName = resolver.osName();
|
||||
this.osArch = resolver.osArch();
|
||||
this.osVersion = resolver.osVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,6 +277,7 @@ public final class Configuration {
|
||||
* <em>observability-related</em> reasons: </p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p> A root module, or a direct or transitive dependency, is not
|
||||
* found. </p></li>
|
||||
*
|
||||
@ -289,13 +286,6 @@ public final class Configuration {
|
||||
* descriptor ({@code module-info.class}) or two versions of the same
|
||||
* module are found in the same directory. </p></li>
|
||||
*
|
||||
* <li><p> A module with the required name is found but the module
|
||||
* requires a different {@link ModuleDescriptor#osName() operating
|
||||
* system}, {@link ModuleDescriptor#osArch() architecture}, or {@link
|
||||
* ModuleDescriptor#osVersion() version} to other modules that have
|
||||
* been resolved for the new configuration or modules in the parent
|
||||
* configurations. </p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> Post-resolution consistency checks may fail with {@code
|
||||
@ -306,6 +296,10 @@ public final class Configuration {
|
||||
* <li><p> A cycle is detected, say where module {@code m1} requires
|
||||
* module {@code m2} and {@code m2} requires {@code m1}. </p></li>
|
||||
*
|
||||
* <li><p> A module reads two or more modules with the same name. This
|
||||
* includes the case where a module reads another with the same name as
|
||||
* itself. </p></li>
|
||||
*
|
||||
* <li><p> Two or more modules in the configuration export the same
|
||||
* package to a module that reads both. This includes the case where a
|
||||
* module {@code M} containing package {@code p} reads another module
|
||||
@ -319,8 +313,9 @@ public final class Configuration {
|
||||
* </ul>
|
||||
*
|
||||
* @implNote In the implementation then observability of modules may depend
|
||||
* on referential integrity checks that ensure different builds of tightly
|
||||
* coupled modules are not combined in the same configuration.
|
||||
* on referential integrity or other checks that ensure different builds of
|
||||
* tightly coupled modules or modules for specific operating systems or
|
||||
* architectures are not combined in the same configuration.
|
||||
*
|
||||
* @param before
|
||||
* The <em>before</em> module finder to find modules
|
||||
|
@ -179,8 +179,10 @@ public class ModuleDescriptor
|
||||
private final Set<Modifier> mods;
|
||||
private final String name;
|
||||
private final Version compiledVersion;
|
||||
private final String rawCompiledVersion;
|
||||
|
||||
private Requires(Set<Modifier> ms, String mn, Version v) {
|
||||
private Requires(Set<Modifier> ms, String mn, Version v, String vs) {
|
||||
assert v == null || vs == null;
|
||||
if (ms.isEmpty()) {
|
||||
ms = Collections.emptySet();
|
||||
} else {
|
||||
@ -189,12 +191,14 @@ public class ModuleDescriptor
|
||||
this.mods = ms;
|
||||
this.name = mn;
|
||||
this.compiledVersion = v;
|
||||
this.rawCompiledVersion = vs;
|
||||
}
|
||||
|
||||
private Requires(Set<Modifier> ms, String mn, Version v, boolean unused) {
|
||||
this.mods = ms;
|
||||
this.name = mn;
|
||||
this.compiledVersion = v;
|
||||
this.rawCompiledVersion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,12 +222,33 @@ public class ModuleDescriptor
|
||||
/**
|
||||
* Returns the version of the module if recorded at compile-time.
|
||||
*
|
||||
* @return The version of the module if recorded at compile-time
|
||||
* @return The version of the module if recorded at compile-time,
|
||||
* or an empty {@code Optional} if no version was recorded or
|
||||
* the version string recorded is {@linkplain Version#parse(String)
|
||||
* unparseable}
|
||||
*/
|
||||
public Optional<Version> compiledVersion() {
|
||||
return Optional.ofNullable(compiledVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string with the possibly-unparseable version of the module
|
||||
* if recorded at compile-time.
|
||||
*
|
||||
* @return The string containing the version of the module if recorded
|
||||
* at compile-time, or an empty {@code Optional} if no version
|
||||
* was recorded
|
||||
*
|
||||
* @see #compiledVersion()
|
||||
*/
|
||||
public Optional<String> rawCompiledVersion() {
|
||||
if (compiledVersion != null) {
|
||||
return Optional.of(compiledVersion.toString());
|
||||
} else {
|
||||
return Optional.ofNullable(rawCompiledVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this module dependence to another.
|
||||
*
|
||||
@ -236,7 +261,10 @@ public class ModuleDescriptor
|
||||
* recorded at compile-time are compared. When comparing the versions
|
||||
* recorded at compile-time then a dependence that has a recorded
|
||||
* version is considered to succeed a dependence that does not have a
|
||||
* recorded version. </p>
|
||||
* recorded version. If both recorded versions are {@linkplain
|
||||
* Version#parse(String) unparseable} then the {@linkplain
|
||||
* #rawCompiledVersion() raw version strings} are compared
|
||||
* lexicographically. </p>
|
||||
*
|
||||
* @param that
|
||||
* The module dependence to compare
|
||||
@ -262,6 +290,10 @@ public class ModuleDescriptor
|
||||
c = compare(this.compiledVersion, that.compiledVersion);
|
||||
if (c != 0) return c;
|
||||
|
||||
// rawCompiledVersion
|
||||
c = compare(this.rawCompiledVersion, that.rawCompiledVersion);
|
||||
if (c != 0) return c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -289,7 +321,8 @@ public class ModuleDescriptor
|
||||
return false;
|
||||
Requires that = (Requires)ob;
|
||||
return name.equals(that.name) && mods.equals(that.mods)
|
||||
&& Objects.equals(compiledVersion, that.compiledVersion);
|
||||
&& Objects.equals(compiledVersion, that.compiledVersion)
|
||||
&& Objects.equals(rawCompiledVersion, that.rawCompiledVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,6 +339,8 @@ public class ModuleDescriptor
|
||||
int hash = name.hashCode() * 43 + mods.hashCode();
|
||||
if (compiledVersion != null)
|
||||
hash = hash * 43 + compiledVersion.hashCode();
|
||||
if (rawCompiledVersion != null)
|
||||
hash = hash * 43 + rawCompiledVersion.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@ -774,7 +809,7 @@ public class ModuleDescriptor
|
||||
/**
|
||||
* Returns the fully qualified class name of the service type.
|
||||
*
|
||||
* @return The fully qualified class name of the service type.
|
||||
* @return The fully qualified class name of the service type
|
||||
*/
|
||||
public String service() { return service; }
|
||||
|
||||
@ -1199,6 +1234,7 @@ public class ModuleDescriptor
|
||||
|
||||
private final String name;
|
||||
private final Version version;
|
||||
private final String rawVersionString;
|
||||
private final Set<Modifier> modifiers;
|
||||
private final boolean open; // true if modifiers contains OPEN
|
||||
private final boolean automatic; // true if modifiers contains AUTOMATIC
|
||||
@ -1209,12 +1245,10 @@ public class ModuleDescriptor
|
||||
private final Set<Provides> provides;
|
||||
private final Set<String> packages;
|
||||
private final String mainClass;
|
||||
private final String osName;
|
||||
private final String osArch;
|
||||
private final String osVersion;
|
||||
|
||||
private ModuleDescriptor(String name,
|
||||
Version version,
|
||||
String rawVersionString,
|
||||
Set<Modifier> modifiers,
|
||||
Set<Requires> requires,
|
||||
Set<Exports> exports,
|
||||
@ -1222,13 +1256,12 @@ public class ModuleDescriptor
|
||||
Set<String> uses,
|
||||
Set<Provides> provides,
|
||||
Set<String> packages,
|
||||
String mainClass,
|
||||
String osName,
|
||||
String osArch,
|
||||
String osVersion)
|
||||
String mainClass)
|
||||
{
|
||||
assert version == null || rawVersionString == null;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.rawVersionString = rawVersionString;
|
||||
this.modifiers = emptyOrUnmodifiableSet(modifiers);
|
||||
this.open = modifiers.contains(Modifier.OPEN);
|
||||
this.automatic = modifiers.contains(Modifier.AUTOMATIC);
|
||||
@ -1242,9 +1275,6 @@ public class ModuleDescriptor
|
||||
|
||||
this.packages = emptyOrUnmodifiableSet(packages);
|
||||
this.mainClass = mainClass;
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1261,13 +1291,11 @@ public class ModuleDescriptor
|
||||
Set<Provides> provides,
|
||||
Set<String> packages,
|
||||
String mainClass,
|
||||
String osName,
|
||||
String osArch,
|
||||
String osVersion,
|
||||
int hashCode,
|
||||
boolean unused) {
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.rawVersionString = null;
|
||||
this.modifiers = modifiers;
|
||||
this.open = modifiers.contains(Modifier.OPEN);
|
||||
this.automatic = modifiers.contains(Modifier.AUTOMATIC);
|
||||
@ -1278,9 +1306,6 @@ public class ModuleDescriptor
|
||||
this.provides = provides;
|
||||
this.packages = packages;
|
||||
this.mainClass = mainClass;
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
this.osVersion = osVersion;
|
||||
this.hash = hashCode;
|
||||
}
|
||||
|
||||
@ -1394,18 +1419,37 @@ public class ModuleDescriptor
|
||||
/**
|
||||
* <p> Returns the module version. </p>
|
||||
*
|
||||
* @return This module's version
|
||||
* @return This module's version, or an empty {@code Optional} if the
|
||||
* module does not have a version or the version is
|
||||
* {@linkplain Version#parse(String) unparseable}
|
||||
*/
|
||||
public Optional<Version> version() {
|
||||
return Optional.ofNullable(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns the string with the possibly-unparseable version of the
|
||||
* module </p>
|
||||
*
|
||||
* @return The string containing the version of the module or an empty
|
||||
* {@code Optional} if the module does not have a version
|
||||
*
|
||||
* @see #version()
|
||||
*/
|
||||
public Optional<String> rawVersion() {
|
||||
if (version != null) {
|
||||
return Optional.of(version.toString());
|
||||
} else {
|
||||
return Optional.ofNullable(rawVersionString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Returns a string containing the module name and, if present, its
|
||||
* version. </p>
|
||||
*
|
||||
* @return A string containing the module name and, if present, its
|
||||
* version.
|
||||
* version
|
||||
*/
|
||||
public String toNameAndVersion() {
|
||||
if (version != null) {
|
||||
@ -1424,42 +1468,13 @@ public class ModuleDescriptor
|
||||
return Optional.ofNullable(mainClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operating system name if the module is operating system
|
||||
* specific.
|
||||
*
|
||||
* @return The operating system name or an empty {@code Optional}
|
||||
* if the module is not operating system specific
|
||||
*/
|
||||
public Optional<String> osName() {
|
||||
return Optional.ofNullable(osName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operating system architecture if the module is operating
|
||||
* system architecture specific.
|
||||
*
|
||||
* @return The operating system architecture or an empty {@code Optional}
|
||||
* if the module is not operating system architecture specific
|
||||
*/
|
||||
public Optional<String> osArch() {
|
||||
return Optional.ofNullable(osArch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operating system version if the module is operating
|
||||
* system version specific.
|
||||
*
|
||||
* @return The operating system version or an empty {@code Optional}
|
||||
* if the module is not operating system version specific
|
||||
*/
|
||||
public Optional<String> osVersion() {
|
||||
return Optional.ofNullable(osVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of packages in the module.
|
||||
*
|
||||
* <p> The set of packages includes all exported and open packages, as well
|
||||
* as the packages of any service providers, and the package for the main
|
||||
* class. </p>
|
||||
*
|
||||
* @return A possibly-empty unmodifiable set of the packages in the module
|
||||
*/
|
||||
public Set<String> packages() {
|
||||
@ -1518,9 +1533,7 @@ public class ModuleDescriptor
|
||||
final Set<String> uses = new HashSet<>();
|
||||
final Map<String, Provides> provides = new HashMap<>();
|
||||
Version version;
|
||||
String osName;
|
||||
String osArch;
|
||||
String osVersion;
|
||||
String rawVersionString;
|
||||
String mainClass;
|
||||
|
||||
/**
|
||||
@ -1604,24 +1617,21 @@ public class ModuleDescriptor
|
||||
Objects.requireNonNull(compiledVersion);
|
||||
if (strict)
|
||||
mn = requireModuleName(mn);
|
||||
return requires(new Requires(ms, mn, compiledVersion));
|
||||
return requires(new Requires(ms, mn, compiledVersion, null));
|
||||
}
|
||||
|
||||
/* package */Builder requires(Set<Requires.Modifier> ms,
|
||||
String mn,
|
||||
String compiledVersion) {
|
||||
Version v = null;
|
||||
String rawCompiledVersion) {
|
||||
Requires r;
|
||||
try {
|
||||
v = Version.parse(compiledVersion);
|
||||
Version v = Version.parse(rawCompiledVersion);
|
||||
r = new Requires(ms, mn, v, null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// for now, drop un-parsable version when non-strict
|
||||
if (strict) throw e;
|
||||
r = new Requires(ms, mn, null, rawCompiledVersion);
|
||||
}
|
||||
if (v == null) {
|
||||
return requires(ms, mn);
|
||||
} else {
|
||||
return requires(ms, mn, v);
|
||||
}
|
||||
return requires(r);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1646,7 +1656,7 @@ public class ModuleDescriptor
|
||||
public Builder requires(Set<Requires.Modifier> ms, String mn) {
|
||||
if (strict)
|
||||
mn = requireModuleName(mn);
|
||||
return requires(new Requires(ms, mn, null));
|
||||
return requires(new Requires(ms, mn, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1952,7 +1962,7 @@ public class ModuleDescriptor
|
||||
* a class in a named package
|
||||
* @throws IllegalStateException
|
||||
* If a dependency on the service type has already been declared
|
||||
* or this is a builder for an an automatic module
|
||||
* or this is a builder for an automatic module
|
||||
*/
|
||||
public Builder uses(String service) {
|
||||
if (automatic)
|
||||
@ -2068,6 +2078,7 @@ public class ModuleDescriptor
|
||||
*/
|
||||
public Builder version(Version v) {
|
||||
version = requireNonNull(v);
|
||||
rawVersionString = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -2086,18 +2097,15 @@ public class ModuleDescriptor
|
||||
* @see Version#parse(String)
|
||||
*/
|
||||
public Builder version(String vs) {
|
||||
Version v;
|
||||
if (strict) {
|
||||
v = Version.parse(vs);
|
||||
} else {
|
||||
try {
|
||||
v = Version.parse(vs);
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
// for now, ignore when non-strict
|
||||
return this;
|
||||
}
|
||||
try {
|
||||
version = Version.parse(vs);
|
||||
rawVersionString = null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (strict) throw e;
|
||||
version = null;
|
||||
rawVersionString = vs;
|
||||
}
|
||||
return version(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2131,60 +2139,6 @@ public class ModuleDescriptor
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the operating system name.
|
||||
*
|
||||
* @param name
|
||||
* The operating system name
|
||||
*
|
||||
* @return This builder
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If {@code name} is {@code null} or the empty String
|
||||
*/
|
||||
public Builder osName(String name) {
|
||||
if (name == null || name.isEmpty())
|
||||
throw new IllegalArgumentException("OS name is null or empty");
|
||||
osName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the operating system architecture.
|
||||
*
|
||||
* @param arch
|
||||
* The operating system architecture
|
||||
*
|
||||
* @return This builder
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If {@code name} is {@code null} or the empty String
|
||||
*/
|
||||
public Builder osArch(String arch) {
|
||||
if (arch == null || arch.isEmpty())
|
||||
throw new IllegalArgumentException("OS arch is null or empty");
|
||||
osArch = arch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the operating system version.
|
||||
*
|
||||
* @param version
|
||||
* The operating system version
|
||||
*
|
||||
* @return This builder
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If {@code name} is {@code null} or the empty String
|
||||
*/
|
||||
public Builder osVersion(String version) {
|
||||
if (version == null || version.isEmpty())
|
||||
throw new IllegalArgumentException("OS version is null or empty");
|
||||
osVersion = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a {@code ModuleDescriptor} from its components.
|
||||
*
|
||||
@ -2208,6 +2162,7 @@ public class ModuleDescriptor
|
||||
&& !this.requires.containsKey("java.base")) {
|
||||
requires.add(new Requires(Set.of(Requires.Modifier.MANDATED),
|
||||
"java.base",
|
||||
null,
|
||||
null));
|
||||
}
|
||||
|
||||
@ -2215,6 +2170,7 @@ public class ModuleDescriptor
|
||||
|
||||
return new ModuleDescriptor(name,
|
||||
version,
|
||||
rawVersionString,
|
||||
modifiers,
|
||||
requires,
|
||||
exports,
|
||||
@ -2222,10 +2178,7 @@ public class ModuleDescriptor
|
||||
uses,
|
||||
provides,
|
||||
packages,
|
||||
mainClass,
|
||||
osName,
|
||||
osArch,
|
||||
osVersion);
|
||||
mainClass);
|
||||
}
|
||||
|
||||
}
|
||||
@ -2237,9 +2190,11 @@ public class ModuleDescriptor
|
||||
* module names lexicographically. Where the module names are equal then the
|
||||
* module versions are compared. When comparing the module versions then a
|
||||
* module descriptor with a version is considered to succeed a module
|
||||
* descriptor that does not have a version. Where the module names are equal
|
||||
* and the versions are equal (or not present in both), then the set of
|
||||
* modifiers are compared. Sets of modifiers are compared by comparing
|
||||
* descriptor that does not have a version. If both versions are {@linkplain
|
||||
* Version#parse(String) unparseable} then the {@linkplain #rawVersion()
|
||||
* raw version strings} are compared lexicographically. Where the module names
|
||||
* are equal and the versions are equal (or not present in both), then the
|
||||
* set of modifiers are compared. Sets of modifiers are compared by comparing
|
||||
* a <em>binary value</em> computed for each set. If a modifier is present
|
||||
* in the set then the bit at the position of its ordinal is {@code 1}
|
||||
* in the binary value, otherwise {@code 0}. If the two set of modifiers
|
||||
@ -2263,6 +2218,9 @@ public class ModuleDescriptor
|
||||
c = compare(this.version, that.version);
|
||||
if (c != 0) return c;
|
||||
|
||||
c = compare(this.rawVersionString, that.rawVersionString);
|
||||
if (c != 0) return c;
|
||||
|
||||
long v1 = modsValue(this.modifiers());
|
||||
long v2 = modsValue(that.modifiers());
|
||||
c = Long.compare(v1, v2);
|
||||
@ -2289,15 +2247,6 @@ public class ModuleDescriptor
|
||||
c = compare(this.mainClass, that.mainClass);
|
||||
if (c != 0) return c;
|
||||
|
||||
c = compare(this.osName, that.osName);
|
||||
if (c != 0) return c;
|
||||
|
||||
c = compare(this.osArch, that.osArch);
|
||||
if (c != 0) return c;
|
||||
|
||||
c = compare(this.osVersion, that.osVersion);
|
||||
if (c != 0) return c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2333,10 +2282,8 @@ public class ModuleDescriptor
|
||||
&& uses.equals(that.uses)
|
||||
&& provides.equals(that.provides)
|
||||
&& Objects.equals(version, that.version)
|
||||
&& Objects.equals(mainClass, that.mainClass)
|
||||
&& Objects.equals(osName, that.osName)
|
||||
&& Objects.equals(osArch, that.osArch)
|
||||
&& Objects.equals(osVersion, that.osVersion));
|
||||
&& Objects.equals(rawVersionString, that.rawVersionString)
|
||||
&& Objects.equals(mainClass, that.mainClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2361,10 +2308,8 @@ public class ModuleDescriptor
|
||||
hc = hc * 43 + uses.hashCode();
|
||||
hc = hc * 43 + provides.hashCode();
|
||||
hc = hc * 43 + Objects.hashCode(version);
|
||||
hc = hc * 43 + Objects.hashCode(rawVersionString);
|
||||
hc = hc * 43 + Objects.hashCode(mainClass);
|
||||
hc = hc * 43 + Objects.hashCode(osName);
|
||||
hc = hc * 43 + Objects.hashCode(osArch);
|
||||
hc = hc * 43 + Objects.hashCode(osVersion);
|
||||
if (hc == 0)
|
||||
hc = -1;
|
||||
hash = hc;
|
||||
@ -2713,8 +2658,8 @@ public class ModuleDescriptor
|
||||
public void requires(ModuleDescriptor.Builder builder,
|
||||
Set<Requires.Modifier> ms,
|
||||
String mn,
|
||||
String compiledVersion) {
|
||||
builder.requires(ms, mn, compiledVersion);
|
||||
String rawCompiledVersion) {
|
||||
builder.requires(ms, mn, rawCompiledVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2762,9 +2707,6 @@ public class ModuleDescriptor
|
||||
Set<Provides> provides,
|
||||
Set<String> packages,
|
||||
String mainClass,
|
||||
String osName,
|
||||
String osArch,
|
||||
String osVersion,
|
||||
int hashCode) {
|
||||
return new ModuleDescriptor(name,
|
||||
version,
|
||||
@ -2776,9 +2718,6 @@ public class ModuleDescriptor
|
||||
provides,
|
||||
packages,
|
||||
mainClass,
|
||||
osName,
|
||||
osArch,
|
||||
osVersion,
|
||||
hashCode,
|
||||
false);
|
||||
}
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package java.lang.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -43,9 +41,9 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.module.ModuleBootstrap;
|
||||
import jdk.internal.module.ModulePatcher;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.SystemModuleFinder;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* A finder of modules. A {@code ModuleFinder} is used to find modules during
|
||||
@ -146,9 +144,9 @@ public interface ModuleFinder {
|
||||
*
|
||||
* <p> If there is a security manager set then its {@link
|
||||
* SecurityManager#checkPermission(Permission) checkPermission} method is
|
||||
* invoked to check that the caller has been granted {@link FilePermission}
|
||||
* to recursively read the directory that is the value of the system
|
||||
* property {@code java.home}. </p>
|
||||
* invoked to check that the caller has been granted
|
||||
* {@link RuntimePermission RuntimePermission("accessSystemModules")}
|
||||
* to access the system modules. </p>
|
||||
*
|
||||
* @return A {@code ModuleFinder} that locates the system modules
|
||||
*
|
||||
@ -156,32 +154,55 @@ public interface ModuleFinder {
|
||||
* If denied by the security manager
|
||||
*/
|
||||
static ModuleFinder ofSystem() {
|
||||
String home;
|
||||
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
PrivilegedAction<String> pa = new GetPropertyAction("java.home");
|
||||
home = AccessController.doPrivileged(pa);
|
||||
Permission p = new FilePermission(home + File.separator + "-", "read");
|
||||
sm.checkPermission(p);
|
||||
sm.checkPermission(new RuntimePermission("accessSystemModules"));
|
||||
PrivilegedAction<ModuleFinder> pa = ModuleFinder::privilegedOfSystem;
|
||||
return AccessController.doPrivileged(pa);
|
||||
} else {
|
||||
home = System.getProperty("java.home");
|
||||
return privilegedOfSystem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a module finder that locates the system modules. This method
|
||||
* assumes it has permissions to access the runtime image.
|
||||
*/
|
||||
private static ModuleFinder privilegedOfSystem() {
|
||||
String home = System.getProperty("java.home");
|
||||
Path modules = Paths.get(home, "lib", "modules");
|
||||
if (Files.isRegularFile(modules)) {
|
||||
return SystemModuleFinder.getInstance();
|
||||
} else {
|
||||
Path mlib = Paths.get(home, "modules");
|
||||
if (Files.isDirectory(mlib)) {
|
||||
// exploded build may be patched
|
||||
return ModulePath.of(ModuleBootstrap.patcher(), mlib);
|
||||
Path dir = Paths.get(home, "modules");
|
||||
if (Files.isDirectory(dir)) {
|
||||
return privilegedOf(ModuleBootstrap.patcher(), dir);
|
||||
} else {
|
||||
throw new InternalError("Unable to detect the run-time image");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a module finder that locates the system modules in an exploded
|
||||
* image. The image may be patched.
|
||||
*/
|
||||
private static ModuleFinder privilegedOf(ModulePatcher patcher, Path dir) {
|
||||
ModuleFinder finder = ModulePath.of(patcher, dir);
|
||||
return new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
PrivilegedAction<Optional<ModuleReference>> pa = () -> finder.find(name);
|
||||
return AccessController.doPrivileged(pa);
|
||||
}
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
PrivilegedAction<Set<ModuleReference>> pa = finder::findAll;
|
||||
return AccessController.doPrivileged(pa);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a module finder that locates modules on the file system by
|
||||
* searching a sequence of directories and/or packaged modules.
|
||||
@ -201,7 +222,7 @@ public interface ModuleFinder {
|
||||
*
|
||||
* <p> If an element is a path to a directory of modules then each entry in
|
||||
* the directory is a packaged module or the top-level directory of an
|
||||
* exploded module. It it an error if a directory contains more than one
|
||||
* exploded module. It is an error if a directory contains more than one
|
||||
* module with the same name. If an element is a path to a directory, and
|
||||
* that directory contains a file named {@code module-info.class}, then the
|
||||
* directory is treated as an exploded module rather than a directory of
|
||||
|
@ -48,7 +48,11 @@ import java.util.stream.Stream;
|
||||
* <p> A resource in a module is identified by an abstract name that is a
|
||||
* '{@code /}'-separated path string. For example, module {@code java.base} may
|
||||
* have a resource "{@code java/lang/Object.class}" that, by convention, is the
|
||||
* class file for {@code java.lang.Object}. </p>
|
||||
* class file for {@code java.lang.Object}. A module reader may treat
|
||||
* directories in the module content as resources (whether it does or not is
|
||||
* module reader specific). Where the module content contains a directory
|
||||
* that can be located as a resource then its name ends with a slash ('/'). The
|
||||
* directory can also be located with a name that drops the trailing slash. </p>
|
||||
*
|
||||
* <p> A {@code ModuleReader} is {@linkplain ModuleReference#open open} upon
|
||||
* creation and is closed by invoking the {@link #close close} method. Failure
|
||||
@ -80,6 +84,9 @@ public interface ModuleReader extends Closeable {
|
||||
/**
|
||||
* Finds a resource, returning a URI to the resource in the module.
|
||||
*
|
||||
* <p> If the module reader can determine that the name locates a directory
|
||||
* then the resulting URI will end with a slash ('/'). </p>
|
||||
*
|
||||
* @param name
|
||||
* The name of the resource to open for reading
|
||||
*
|
||||
@ -140,7 +147,7 @@ public interface ModuleReader extends Closeable {
|
||||
*
|
||||
* @apiNote This method is intended for high-performance class loading. It
|
||||
* is not capable (or intended) to read arbitrary large resources that
|
||||
* could potentially be 2GB or larger. The rational for using this method
|
||||
* could potentially be 2GB or larger. The rationale for using this method
|
||||
* in conjunction with the {@code release} method is to allow module reader
|
||||
* implementations manage buffers in an efficient manner.
|
||||
*
|
||||
@ -195,7 +202,9 @@ public interface ModuleReader extends Closeable {
|
||||
|
||||
/**
|
||||
* Lists the contents of the module, returning a stream of elements that
|
||||
* are the names of all resources in the module.
|
||||
* are the names of all resources in the module. Whether the stream of
|
||||
* elements includes names corresponding to directories in the module is
|
||||
* module reader specific.
|
||||
*
|
||||
* <p> In lazy implementations then an {@code IOException} may be thrown
|
||||
* when using the stream to list the module contents. If this occurs then
|
||||
|
@ -47,6 +47,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleReferenceImpl;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
|
||||
/**
|
||||
* The resolver used by {@link Configuration#resolve} and {@link
|
||||
@ -69,11 +70,9 @@ final class Resolver {
|
||||
// module constraints on target platform
|
||||
private String osName;
|
||||
private String osArch;
|
||||
private String osVersion;
|
||||
|
||||
String osName() { return osName; }
|
||||
String osArch() { return osArch; }
|
||||
String osVersion() { return osVersion; }
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if there are more than one parent and
|
||||
@ -110,16 +109,6 @@ final class Resolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
value = parent.osVersion();
|
||||
if (value != null) {
|
||||
if (osVersion == null) {
|
||||
osVersion = value;
|
||||
} else {
|
||||
if (!value.equals(osVersion)) {
|
||||
failParentConflict("OS version", osVersion, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,13 +307,15 @@ final class Resolver {
|
||||
* the target platform with the constraints of other modules.
|
||||
*/
|
||||
private void addFoundModule(ModuleReference mref) {
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
nameToReference.put(descriptor.name(), mref);
|
||||
String mn = mref.descriptor().name();
|
||||
|
||||
if (descriptor.osName().isPresent()
|
||||
|| descriptor.osArch().isPresent()
|
||||
|| descriptor.osVersion().isPresent())
|
||||
checkTargetConstraints(descriptor);
|
||||
if (mref instanceof ModuleReferenceImpl) {
|
||||
ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
|
||||
if (target != null)
|
||||
checkTargetConstraints(mn, target);
|
||||
}
|
||||
|
||||
nameToReference.put(mn, mref);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,58 +323,44 @@ final class Resolver {
|
||||
* conflict with the constraints of other modules resolved so far or
|
||||
* modules in parent configurations.
|
||||
*/
|
||||
private void checkTargetConstraints(ModuleDescriptor descriptor) {
|
||||
String value = descriptor.osName().orElse(null);
|
||||
private void checkTargetConstraints(String mn, ModuleTarget target) {
|
||||
String value = target.osName();
|
||||
if (value != null) {
|
||||
if (osName == null) {
|
||||
osName = value;
|
||||
} else {
|
||||
if (!value.equals(osName)) {
|
||||
failTargetConstraint(descriptor);
|
||||
failTargetConstraint(mn, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
value = descriptor.osArch().orElse(null);
|
||||
value = target.osArch();
|
||||
if (value != null) {
|
||||
if (osArch == null) {
|
||||
osArch = value;
|
||||
} else {
|
||||
if (!value.equals(osArch)) {
|
||||
failTargetConstraint(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
value = descriptor.osVersion().orElse(null);
|
||||
if (value != null) {
|
||||
if (osVersion == null) {
|
||||
osVersion = value;
|
||||
} else {
|
||||
if (!value.equals(osVersion)) {
|
||||
failTargetConstraint(descriptor);
|
||||
failTargetConstraint(mn, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void failTargetConstraint(ModuleDescriptor md) {
|
||||
String s1 = targetAsString(osName, osArch, osVersion);
|
||||
String s2 = targetAsString(md);
|
||||
findFail("Module %s has constraints on target platform that conflict" +
|
||||
" with other modules: %s, %s", md.name(), s1, s2);
|
||||
private void failTargetConstraint(String mn, ModuleTarget target) {
|
||||
String s1 = targetAsString(osName, osArch);
|
||||
String s2 = targetAsString(target.osName(), target.osArch());
|
||||
findFail("Module %s has constraints on target platform (%s) that"
|
||||
+ " conflict with other modules: %s", mn, s1, s2);
|
||||
}
|
||||
|
||||
private String targetAsString(ModuleDescriptor descriptor) {
|
||||
String osName = descriptor.osName().orElse(null);
|
||||
String osArch = descriptor.osArch().orElse(null);
|
||||
String osVersion = descriptor.osVersion().orElse(null);
|
||||
return targetAsString(osName, osArch, osVersion);
|
||||
private String targetAsString(ModuleTarget target) {
|
||||
return targetAsString(target.osName(), target.osArch());
|
||||
}
|
||||
|
||||
private String targetAsString(String osName, String osArch, String osVersion) {
|
||||
private String targetAsString(String osName, String osArch) {
|
||||
return new StringJoiner("-")
|
||||
.add(Objects.toString(osName, "*"))
|
||||
.add(Objects.toString(osArch, "*"))
|
||||
.add(Objects.toString(osVersion, "*"))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@ -712,16 +689,30 @@ final class Resolver {
|
||||
|
||||
|
||||
/**
|
||||
* Checks the readability graph to ensure that no two modules export the
|
||||
* same package to a module. This includes the case where module M has
|
||||
* a local package P and M reads another module that exports P to M.
|
||||
* Also checks the uses/provides of module M to ensure that it reads a
|
||||
* module that exports the package of the service type to M.
|
||||
* Checks the readability graph to ensure that
|
||||
* <ol>
|
||||
* <li><p> A module does not read two or more modules with the same name.
|
||||
* This includes the case where a module reads another another with the
|
||||
* same name as itself. </p></li>
|
||||
* <li><p> Two or more modules in the configuration don't export the same
|
||||
* package to a module that reads both. This includes the case where a
|
||||
* module {@code M} containing package {@code p} reads another module
|
||||
* that exports {@code p} to {@code M}. </p></li>
|
||||
* <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}"
|
||||
* or "{@code provides p.S with ...}" but package {@code p} is neither
|
||||
* in module {@code M} nor exported to {@code M} by any module that
|
||||
* {@code M} reads. </p></li>
|
||||
* </ol>
|
||||
*/
|
||||
private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
|
||||
|
||||
for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
|
||||
ModuleDescriptor descriptor1 = e.getKey().descriptor();
|
||||
String name1 = descriptor1.name();
|
||||
|
||||
// the names of the modules that are read (including self)
|
||||
Set<String> names = new HashSet<>();
|
||||
names.add(name1);
|
||||
|
||||
// the map of packages that are local or exported to descriptor1
|
||||
Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
|
||||
@ -737,9 +728,20 @@ final class Resolver {
|
||||
for (ResolvedModule endpoint : reads) {
|
||||
ModuleDescriptor descriptor2 = endpoint.descriptor();
|
||||
|
||||
String name2 = descriptor2.name();
|
||||
if (descriptor2 != descriptor1 && !names.add(name2)) {
|
||||
if (name2.equals(name1)) {
|
||||
resolveFail("Module %s reads another module named %s",
|
||||
name1, name1);
|
||||
} else{
|
||||
resolveFail("Module %s reads more than one module named %s",
|
||||
name1, name2);
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor2.isAutomatic()) {
|
||||
// automatic modules read self and export all packages
|
||||
if (descriptor2 != descriptor1){
|
||||
if (descriptor2 != descriptor1) {
|
||||
for (String source : descriptor2.packages()) {
|
||||
ModuleDescriptor supplier
|
||||
= packageToExporter.putIfAbsent(source, descriptor2);
|
||||
|
@ -28,9 +28,12 @@ package java.lang.reflect;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.security.AccessController;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.reflect.ReflectionFactory;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* The {@code AccessibleObject} class is the base class for {@code Field},
|
||||
@ -288,27 +291,20 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
if (callerModule == Object.class.getModule()) return true;
|
||||
if (!declaringModule.isNamed()) return true;
|
||||
|
||||
// package is open to caller
|
||||
String pn = packageName(declaringClass);
|
||||
if (declaringModule.isOpen(pn, callerModule)) {
|
||||
dumpStackIfOpenedReflectively(declaringModule, pn, callerModule);
|
||||
return true;
|
||||
}
|
||||
|
||||
// package is exported to caller
|
||||
boolean isExported = declaringModule.isExported(pn, callerModule);
|
||||
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
|
||||
String pn = declaringClass.getPackageName();
|
||||
int modifiers;
|
||||
if (this instanceof Executable) {
|
||||
modifiers = ((Executable) this).getModifiers();
|
||||
} else {
|
||||
modifiers = ((Field) this).getModifiers();
|
||||
}
|
||||
if (isExported && isClassPublic) {
|
||||
|
||||
// class is public and package is exported to caller
|
||||
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
|
||||
if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
|
||||
// member is public
|
||||
if (Modifier.isPublic(modifiers)) {
|
||||
dumpStackIfExportedReflectively(declaringModule, pn, callerModule);
|
||||
logIfExportedByBackdoor(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -316,11 +312,17 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
if (Modifier.isProtected(modifiers)
|
||||
&& Modifier.isStatic(modifiers)
|
||||
&& isSubclassOf(caller, declaringClass)) {
|
||||
dumpStackIfExportedReflectively(declaringModule, pn, callerModule);
|
||||
logIfExportedByBackdoor(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// package is open to caller
|
||||
if (declaringModule.isOpen(pn, callerModule)) {
|
||||
logIfOpenedByBackdoor(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (throwExceptionIfDenied) {
|
||||
// not accessible
|
||||
String msg = "Unable to make ";
|
||||
@ -333,7 +335,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
msg += "opens";
|
||||
msg += " " + pn + "\" to " + callerModule;
|
||||
InaccessibleObjectException e = new InaccessibleObjectException(msg);
|
||||
if (Reflection.printStackTraceWhenAccessFails()) {
|
||||
if (printStackTraceWhenAccessFails()) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
throw e;
|
||||
@ -351,48 +353,35 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void dumpStackIfOpenedReflectively(Module module,
|
||||
String pn,
|
||||
Module other) {
|
||||
dumpStackIfExposedReflectively(module, pn, other, true);
|
||||
private void logIfOpenedByBackdoor(Class<?> caller, Class<?> declaringClass) {
|
||||
Module callerModule = caller.getModule();
|
||||
Module targetModule = declaringClass.getModule();
|
||||
// callerModule is null during early startup
|
||||
if (callerModule != null && !callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfOpenedByBackdoor(caller, declaringClass, this::toShortString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpStackIfExportedReflectively(Module module,
|
||||
String pn,
|
||||
Module other) {
|
||||
dumpStackIfExposedReflectively(module, pn, other, false);
|
||||
}
|
||||
|
||||
private void dumpStackIfExposedReflectively(Module module,
|
||||
String pn,
|
||||
Module other,
|
||||
boolean open)
|
||||
{
|
||||
if (Reflection.printStackTraceWhenAccessSucceeds()
|
||||
&& !module.isStaticallyExportedOrOpen(pn, other, open))
|
||||
{
|
||||
String msg = other + " allowed to invoke setAccessible on ";
|
||||
if (this instanceof Field)
|
||||
msg += "field ";
|
||||
msg += this;
|
||||
new Exception(msg) {
|
||||
private static final long serialVersionUID = 42L;
|
||||
public String toString() {
|
||||
return "WARNING: " + getMessage();
|
||||
}
|
||||
}.printStackTrace(System.err);
|
||||
private void logIfExportedByBackdoor(Class<?> caller, Class<?> declaringClass) {
|
||||
Module callerModule = caller.getModule();
|
||||
Module targetModule = declaringClass.getModule();
|
||||
// callerModule is null during early startup
|
||||
if (callerModule != null && !callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfExportedByBackdoor(caller, declaringClass, this::toShortString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the package name of the given class.
|
||||
* Returns a short descriptive string to describe this object in log messages.
|
||||
*/
|
||||
private static String packageName(Class<?> c) {
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
String pn = c.getPackageName();
|
||||
return (pn != null) ? pn : "";
|
||||
String toShortString() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -409,6 +398,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
* it should use {@link #canAccess(Object)}.
|
||||
*
|
||||
* @revised 9
|
||||
* @spec JPMS
|
||||
*/
|
||||
@Deprecated(since="9")
|
||||
public boolean isAccessible() {
|
||||
@ -483,10 +473,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
} else {
|
||||
targetClass = Modifier.isStatic(modifiers) ? null : obj.getClass();
|
||||
}
|
||||
return Reflection.verifyMemberAccess(caller,
|
||||
declaringClass,
|
||||
targetClass,
|
||||
modifiers);
|
||||
return verifyAccess(caller, declaringClass, targetClass, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -527,7 +514,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
return AnnotatedElement.super.isAnnotationPresent(annotationClass);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @throws NullPointerException {@inheritDoc}
|
||||
* @since 1.8
|
||||
*/
|
||||
@ -597,9 +584,22 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
final void checkAccess(Class<?> caller, Class<?> memberClass,
|
||||
Class<?> targetClass, int modifiers)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
if (!verifyAccess(caller, memberClass, targetClass, modifiers)) {
|
||||
IllegalAccessException e = Reflection.newIllegalAccessException(
|
||||
caller, memberClass, targetClass, modifiers);
|
||||
if (printStackTraceWhenAccessFails()) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean verifyAccess(Class<?> caller, Class<?> memberClass,
|
||||
Class<?> targetClass, int modifiers)
|
||||
{
|
||||
if (caller == memberClass) { // quick check
|
||||
return; // ACCESS IS OK
|
||||
return true; // ACCESS IS OK
|
||||
}
|
||||
Object cache = securityCheckCache; // read volatile
|
||||
if (targetClass != null // instance member or constructor
|
||||
@ -610,26 +610,31 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
Class<?>[] cache2 = (Class<?>[]) cache;
|
||||
if (cache2[1] == targetClass &&
|
||||
cache2[0] == caller) {
|
||||
return; // ACCESS IS OK
|
||||
return true; // ACCESS IS OK
|
||||
}
|
||||
// (Test cache[1] first since range check for [1]
|
||||
// subsumes range check for [0].)
|
||||
}
|
||||
} else if (cache == caller) {
|
||||
// Non-protected case (or targetClass == memberClass or static member).
|
||||
return; // ACCESS IS OK
|
||||
return true; // ACCESS IS OK
|
||||
}
|
||||
|
||||
// If no return, fall through to the slow path.
|
||||
slowCheckMemberAccess(caller, memberClass, targetClass, modifiers);
|
||||
return slowVerifyAccess(caller, memberClass, targetClass, modifiers);
|
||||
}
|
||||
|
||||
// Keep all this slow stuff out of line:
|
||||
void slowCheckMemberAccess(Class<?> caller, Class<?> memberClass,
|
||||
Class<?> targetClass, int modifiers)
|
||||
throws IllegalAccessException
|
||||
private boolean slowVerifyAccess(Class<?> caller, Class<?> memberClass,
|
||||
Class<?> targetClass, int modifiers)
|
||||
{
|
||||
Reflection.ensureMemberAccess(caller, memberClass, targetClass, modifiers);
|
||||
if (!Reflection.verifyMemberAccess(caller, memberClass, targetClass, modifiers)) {
|
||||
// access denied
|
||||
return false;
|
||||
}
|
||||
|
||||
// access okay
|
||||
logIfExportedByBackdoor(caller, memberClass);
|
||||
|
||||
// Success: Update the cache.
|
||||
Object cache = (targetClass != null
|
||||
@ -643,5 +648,27 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
// guarantees that the initializing stores for the cache
|
||||
// elements will occur before the volatile write.
|
||||
securityCheckCache = cache; // write volatile
|
||||
return true;
|
||||
}
|
||||
|
||||
// true to print a stack trace when access fails
|
||||
private static volatile boolean printStackWhenAccessFails;
|
||||
|
||||
// true if printStack* values are initialized
|
||||
private static volatile boolean printStackPropertiesSet;
|
||||
|
||||
/**
|
||||
* Returns true if a stack trace should be printed when access fails.
|
||||
*/
|
||||
private static boolean printStackTraceWhenAccessFails() {
|
||||
if (!printStackPropertiesSet && VM.initLevel() >= 1) {
|
||||
String s = GetPropertyAction.privilegedGetProperty(
|
||||
"sun.reflect.debugModuleAccessChecks");
|
||||
if (s != null) {
|
||||
printStackWhenAccessFails = !s.equalsIgnoreCase("false");
|
||||
}
|
||||
printStackPropertiesSet = true;
|
||||
}
|
||||
return printStackWhenAccessFails;
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import sun.reflect.generics.factory.GenericsFactory;
|
||||
import sun.reflect.generics.scope.ConstructorScope;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.AnnotationFormatError;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* {@code Constructor} provides information about, and access to, a single
|
||||
@ -360,6 +361,20 @@ public final class Constructor<T> extends Executable {
|
||||
sb.append(getDeclaringClass().getTypeName());
|
||||
}
|
||||
|
||||
@Override
|
||||
String toShortString() {
|
||||
StringBuilder sb = new StringBuilder("constructor ");
|
||||
sb.append(getDeclaringClass().getTypeName());
|
||||
sb.append('(');
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
for (Class<?> parameterType : getParameterTypes()) {
|
||||
sj.add(parameterType.getTypeName());
|
||||
}
|
||||
sb.append(sj);
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing this {@code Constructor},
|
||||
* including type parameters. The string is formatted as the
|
||||
|
@ -324,6 +324,11 @@ class Field extends AccessibleObject implements Member {
|
||||
+ getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
String toShortString() {
|
||||
return "field " + getDeclaringClass().getTypeName() + "." + getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing this {@code Field}, including
|
||||
* its generic type. The format is the access modifiers for the
|
||||
|
@ -66,9 +66,7 @@ import sun.security.util.SecurityConstants;
|
||||
* ResolvedModule} in the configuration. For each resolved module that is
|
||||
* {@link ResolvedModule#reads() read}, the {@code Module} {@link
|
||||
* Module#canRead reads} the corresponding run-time {@code Module}, which may
|
||||
* be in the same layer or a {@link #parents() parent} layer. The {@code Module}
|
||||
* {@link Module#isExported(String) exports} and {@link Module#isOpen(String)
|
||||
* opens} the packages described by its {@link ModuleDescriptor}. </p>
|
||||
* be in the same layer or a {@link #parents() parent} layer. </p>
|
||||
*
|
||||
* <p> The {@link #defineModulesWithOneLoader defineModulesWithOneLoader} and
|
||||
* {@link #defineModulesWithManyLoaders defineModulesWithManyLoaders} methods
|
||||
@ -91,6 +89,28 @@ import sun.security.util.SecurityConstants;
|
||||
* built-in</a> into the Java virtual machine. The boot layer will often be
|
||||
* the {@link #parents() parent} when creating additional layers. </p>
|
||||
*
|
||||
* <p> Each {@code Module} in a layer is created so that it {@link
|
||||
* Module#isExported(String) exports} and {@link Module#isOpen(String) opens}
|
||||
* the packages described by its {@link ModuleDescriptor}. Qualified exports
|
||||
* (where a package is exported to a set of target modules rather than all
|
||||
* modules) are reified when creating the layer as follows: </p>
|
||||
* <ul>
|
||||
* <li> If module {@code X} exports a package to {@code Y}, and if the
|
||||
* runtime {@code Module} {@code X} reads {@code Module} {@code Y}, then
|
||||
* the package is exported to {@code Module} {@code Y} (which may be in
|
||||
* the same layer as {@code X} or a parent layer). </li>
|
||||
*
|
||||
* <li> If module {@code X} exports a package to {@code Y}, and if the
|
||||
* runtime {@code Module} {@code X} does not read {@code Y} then target
|
||||
* {@code Y} is located as if by invoking {@link #findModule(String)
|
||||
* findModule} to find the module in the layer or its parent layers. If
|
||||
* {@code Y} is found then the package is exported to the instance of
|
||||
* {@code Y} that was found. If {@code Y} is not found then the qualified
|
||||
* export is ignored. </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Qualified opens are handled in same way as qualified exports. </p>
|
||||
*
|
||||
* <p> As when creating a {@code Configuration},
|
||||
* {@link ModuleDescriptor#isAutomatic() automatic} modules receive special
|
||||
* treatment when creating a layer. An automatic module is created in the
|
||||
@ -193,7 +213,7 @@ public final class Layer {
|
||||
}
|
||||
|
||||
private void ensureInLayer(Module source) {
|
||||
if (!layer.modules().contains(source))
|
||||
if (source.getLayer() != layer)
|
||||
throw new IllegalArgumentException(source + " not in layer");
|
||||
}
|
||||
|
||||
@ -220,9 +240,8 @@ public final class Layer {
|
||||
* @see Module#addReads
|
||||
*/
|
||||
public Controller addReads(Module source, Module target) {
|
||||
Objects.requireNonNull(source);
|
||||
Objects.requireNonNull(target);
|
||||
ensureInLayer(source);
|
||||
Objects.requireNonNull(target);
|
||||
Modules.addReads(source, target);
|
||||
return this;
|
||||
}
|
||||
@ -248,9 +267,9 @@ public final class Layer {
|
||||
* @see Module#addOpens
|
||||
*/
|
||||
public Controller addOpens(Module source, String pn, Module target) {
|
||||
Objects.requireNonNull(source);
|
||||
Objects.requireNonNull(target);
|
||||
ensureInLayer(source);
|
||||
Objects.requireNonNull(pn);
|
||||
Objects.requireNonNull(target);
|
||||
Modules.addOpens(source, pn, target);
|
||||
return this;
|
||||
}
|
||||
@ -408,8 +427,8 @@ public final class Layer {
|
||||
* </ul>
|
||||
*
|
||||
* <p> In addition, a layer cannot be created if the configuration contains
|
||||
* a module named "{@code java.base}" or a module with a package name
|
||||
* starting with "{@code java.}". </p>
|
||||
* a module named "{@code java.base}", or a module contains a package named
|
||||
* "{@code java}" or a package with a name starting with "{@code java.}". </p>
|
||||
*
|
||||
* <p> If there is a security manager then the class loader created by
|
||||
* this method will load classes and resources with privileges that are
|
||||
@ -418,7 +437,7 @@ public final class Layer {
|
||||
* @param cf
|
||||
* The configuration for the layer
|
||||
* @param parentLayers
|
||||
* The list parent layers in search order
|
||||
* The list of parent layers in search order
|
||||
* @param parentLoader
|
||||
* The parent class loader for the class loader created by this
|
||||
* method; may be {@code null} for the bootstrap class loader
|
||||
@ -485,7 +504,7 @@ public final class Layer {
|
||||
* @param cf
|
||||
* The configuration for the layer
|
||||
* @param parentLayers
|
||||
* The list parent layers in search order
|
||||
* The list of parent layers in search order
|
||||
* @param parentLoader
|
||||
* The parent class loader for each of the class loaders created by
|
||||
* this method; may be {@code null} for the bootstrap class loader
|
||||
@ -497,8 +516,10 @@ public final class Layer {
|
||||
* the parent layers, including order
|
||||
* @throws LayerInstantiationException
|
||||
* If the layer cannot be created because the configuration contains
|
||||
* a module named "{@code java.base}" or a module with a package
|
||||
* name starting with "{@code java.}"
|
||||
* a module named "{@code java.base}" or a module contains a package
|
||||
* named "{@code java}" or a package with a name starting with
|
||||
* "{@code java.}"
|
||||
*
|
||||
* @throws SecurityException
|
||||
* If {@code RuntimePermission("createClassLoader")} or
|
||||
* {@code RuntimePermission("getClassLoader")} is denied by
|
||||
@ -558,10 +579,11 @@ public final class Layer {
|
||||
*
|
||||
* <p> In addition, a layer cannot be created if the configuration contains
|
||||
* a module named "{@code java.base}", a configuration contains a module
|
||||
* with a package name starting with "{@code java.}" is mapped to a class
|
||||
* loader other than the {@link ClassLoader#getPlatformClassLoader()
|
||||
* platform class loader}, or the function to map a module name to a class
|
||||
* loader returns {@code null}. </p>
|
||||
* with a package named "{@code java}" or a package name starting with
|
||||
* "{@code java.}" and the module is mapped to a class loader other than
|
||||
* the {@link ClassLoader#getPlatformClassLoader() platform class loader},
|
||||
* or the function to map a module name to a class loader returns
|
||||
* {@code null}. </p>
|
||||
*
|
||||
* <p> If the function to map a module name to class loader throws an error
|
||||
* or runtime exception then it is propagated to the caller of this method.
|
||||
@ -575,7 +597,7 @@ public final class Layer {
|
||||
* @param cf
|
||||
* The configuration for the layer
|
||||
* @param parentLayers
|
||||
* The list parent layers in search order
|
||||
* The list of parent layers in search order
|
||||
* @param clf
|
||||
* The function to map a module name to a class loader
|
||||
*
|
||||
@ -754,10 +776,16 @@ public final class Layer {
|
||||
* @return A possibly-empty unmodifiable set of the modules in this layer
|
||||
*/
|
||||
public Set<Module> modules() {
|
||||
return Collections.unmodifiableSet(
|
||||
nameToModule.values().stream().collect(Collectors.toSet()));
|
||||
Set<Module> modules = this.modules;
|
||||
if (modules == null) {
|
||||
this.modules = modules =
|
||||
Collections.unmodifiableSet(new HashSet<>(nameToModule.values()));
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
private volatile Set<Module> modules;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the module with the given name in this layer, or if not in this
|
||||
@ -776,6 +804,8 @@ public final class Layer {
|
||||
*/
|
||||
public Optional<Module> findModule(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
if (this == EMPTY_LAYER)
|
||||
return Optional.empty();
|
||||
Module m = nameToModule.get(name);
|
||||
if (m != null)
|
||||
return Optional.of(m);
|
||||
|
@ -42,6 +42,7 @@ import sun.reflect.annotation.AnnotationParser;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.AnnotationFormatError;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* A {@code Method} provides information about, and access to, a single method
|
||||
@ -416,6 +417,21 @@ public final class Method extends Executable {
|
||||
sb.append(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
String toShortString() {
|
||||
StringBuilder sb = new StringBuilder("method ");
|
||||
sb.append(getDeclaringClass().getTypeName()).append('.');
|
||||
sb.append(getName());
|
||||
sb.append('(');
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
for (Class<?> parameterType : getParameterTypes()) {
|
||||
sj.add(parameterType.getTypeName());
|
||||
}
|
||||
sb.append(sj);
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing this {@code Method}, including
|
||||
* type parameters. The string is formatted as the method access
|
||||
|
@ -39,8 +39,10 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -51,11 +53,11 @@ import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.JavaLangReflectModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
import jdk.internal.module.Resources;
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
@ -369,28 +371,19 @@ public final class Module implements AnnotatedElement {
|
||||
* If {@code syncVM} is {@code true} then the VM is notified.
|
||||
*/
|
||||
private void implAddReads(Module other, boolean syncVM) {
|
||||
Objects.requireNonNull(other);
|
||||
|
||||
// nothing to do
|
||||
if (other == this || !this.isNamed())
|
||||
return;
|
||||
|
||||
// check if we already read this module
|
||||
Set<Module> reads = this.reads;
|
||||
if (reads != null && reads.contains(other))
|
||||
return;
|
||||
|
||||
// update VM first, just in case it fails
|
||||
if (syncVM) {
|
||||
if (other == ALL_UNNAMED_MODULE) {
|
||||
addReads0(this, null);
|
||||
} else {
|
||||
addReads0(this, other);
|
||||
if (!canRead(other)) {
|
||||
// update VM first, just in case it fails
|
||||
if (syncVM) {
|
||||
if (other == ALL_UNNAMED_MODULE) {
|
||||
addReads0(this, null);
|
||||
} else {
|
||||
addReads0(this, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add reflective read
|
||||
reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE);
|
||||
// add reflective read
|
||||
reflectivelyReads.putIfAbsent(this, other, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -553,7 +546,7 @@ public final class Module implements AnnotatedElement {
|
||||
* Returns {@code true} if this module exports or opens a package to
|
||||
* the given module via its module declaration.
|
||||
*/
|
||||
boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
|
||||
private boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
|
||||
// package is open to everyone or <other>
|
||||
Map<String, Set<Module>> openPackages = this.openPackages;
|
||||
if (openPackages != null) {
|
||||
@ -909,9 +902,7 @@ public final class Module implements AnnotatedElement {
|
||||
* Returns an array of the package names of the packages in this module.
|
||||
*
|
||||
* <p> For named modules, the returned array contains an element for each
|
||||
* package in the module. It may contain elements corresponding to packages
|
||||
* added to the module, <a href="Proxy.html#dynamicmodule">dynamic modules</a>
|
||||
* for example, after it was loaded.
|
||||
* package in the module. </p>
|
||||
*
|
||||
* <p> For unnamed modules, this method is the equivalent to invoking the
|
||||
* {@link ClassLoader#getDefinedPackages() getDefinedPackages} method of
|
||||
@ -949,15 +940,6 @@ public final class Module implements AnnotatedElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to this module.
|
||||
*
|
||||
* @apiNote This method is for Proxy use.
|
||||
*/
|
||||
void addPackage(String pn) {
|
||||
implAddPackage(pn, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to this module without notifying the VM.
|
||||
*
|
||||
@ -1080,20 +1062,28 @@ public final class Module implements AnnotatedElement {
|
||||
|
||||
// reads
|
||||
Set<Module> reads = new HashSet<>();
|
||||
|
||||
// name -> source Module when in parent layer
|
||||
Map<String, Module> nameToSource = Collections.emptyMap();
|
||||
|
||||
for (ResolvedModule other : resolvedModule.reads()) {
|
||||
Module m2 = null;
|
||||
if (other.configuration() == cf) {
|
||||
String dn = other.reference().descriptor().name();
|
||||
m2 = nameToModule.get(dn);
|
||||
// this configuration
|
||||
m2 = nameToModule.get(other.name());
|
||||
assert m2 != null;
|
||||
} else {
|
||||
// parent layer
|
||||
for (Layer parent: layer.parents()) {
|
||||
m2 = findModule(parent, other);
|
||||
if (m2 != null)
|
||||
break;
|
||||
}
|
||||
assert m2 != null;
|
||||
if (nameToSource.isEmpty())
|
||||
nameToSource = new HashMap<>();
|
||||
nameToSource.put(other.name(), m2);
|
||||
}
|
||||
assert m2 != null;
|
||||
|
||||
reads.add(m2);
|
||||
|
||||
// update VM view
|
||||
@ -1107,7 +1097,7 @@ public final class Module implements AnnotatedElement {
|
||||
}
|
||||
|
||||
// exports and opens
|
||||
initExportsAndOpens(descriptor, nameToModule, m);
|
||||
initExportsAndOpens(m, nameToSource, nameToModule, layer.parents());
|
||||
}
|
||||
|
||||
// register the modules in the boot layer
|
||||
@ -1159,15 +1149,17 @@ public final class Module implements AnnotatedElement {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the maps of exported and open packages for module m.
|
||||
*/
|
||||
private static void initExportsAndOpens(ModuleDescriptor descriptor,
|
||||
private static void initExportsAndOpens(Module m,
|
||||
Map<String, Module> nameToSource,
|
||||
Map<String, Module> nameToModule,
|
||||
Module m)
|
||||
{
|
||||
List<Layer> parents) {
|
||||
// The VM doesn't special case open or automatic modules so need to
|
||||
// export all packages
|
||||
ModuleDescriptor descriptor = m.getDescriptor();
|
||||
if (descriptor.isOpen() || descriptor.isAutomatic()) {
|
||||
assert descriptor.opens().isEmpty();
|
||||
for (String source : descriptor.packages()) {
|
||||
@ -1187,8 +1179,7 @@ public final class Module implements AnnotatedElement {
|
||||
// qualified opens
|
||||
Set<Module> targets = new HashSet<>();
|
||||
for (String target : opens.targets()) {
|
||||
// only open to modules that are in this configuration
|
||||
Module m2 = nameToModule.get(target);
|
||||
Module m2 = findModule(target, nameToSource, nameToModule, parents);
|
||||
if (m2 != null) {
|
||||
addExports0(m, source, m2);
|
||||
targets.add(m2);
|
||||
@ -1217,8 +1208,7 @@ public final class Module implements AnnotatedElement {
|
||||
// qualified exports
|
||||
Set<Module> targets = new HashSet<>();
|
||||
for (String target : exports.targets()) {
|
||||
// only export to modules that are in this configuration
|
||||
Module m2 = nameToModule.get(target);
|
||||
Module m2 = findModule(target, nameToSource, nameToModule, parents);
|
||||
if (m2 != null) {
|
||||
// skip qualified export if already open to m2
|
||||
if (openToTargets == null || !openToTargets.contains(m2)) {
|
||||
@ -1244,6 +1234,32 @@ public final class Module implements AnnotatedElement {
|
||||
m.exportedPackages = exportedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the runtime Module with the given name. The module name is the
|
||||
* name of a target module in a qualified exports or opens directive.
|
||||
*
|
||||
* @param target The target module to find
|
||||
* @param nameToSource The modules in parent layers that are read
|
||||
* @param nameToModule The modules in the layer under construction
|
||||
* @param parents The parent layers
|
||||
*/
|
||||
private static Module findModule(String target,
|
||||
Map<String, Module> nameToSource,
|
||||
Map<String, Module> nameToModule,
|
||||
List<Layer> parents) {
|
||||
Module m = nameToSource.get(target);
|
||||
if (m == null) {
|
||||
m = nameToModule.get(target);
|
||||
if (m == null) {
|
||||
for (Layer parent : parents) {
|
||||
m = parent.findModule(target).orElse(null);
|
||||
if (m != null) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
// -- annotations --
|
||||
|
||||
@ -1428,12 +1444,12 @@ public final class Module implements AnnotatedElement {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
if (isNamed() && !ResourceHelper.isSimpleResource(name)) {
|
||||
if (isNamed() && Resources.canEncapsulate(name)) {
|
||||
Module caller = Reflection.getCallerClass().getModule();
|
||||
if (caller != this && caller != Object.class.getModule()) {
|
||||
// ignore packages added for proxies via addPackage
|
||||
Set<String> packages = getDescriptor().packages();
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
if (packages.contains(pn) && !isOpen(pn, caller)) {
|
||||
// resource is in package not open to caller
|
||||
return null;
|
||||
@ -1531,24 +1547,24 @@ public final class Module implements AnnotatedElement {
|
||||
m.implAddReads(Module.ALL_UNNAMED_MODULE);
|
||||
}
|
||||
@Override
|
||||
public void addExports(Module m, String pn) {
|
||||
m.implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, false, true);
|
||||
}
|
||||
@Override
|
||||
public void addExports(Module m, String pn, Module other) {
|
||||
m.implAddExportsOrOpens(pn, other, false, true);
|
||||
}
|
||||
@Override
|
||||
public void addOpens(Module m, String pn, Module other) {
|
||||
m.implAddExportsOrOpens(pn, other, true, true);
|
||||
public void addExportsToAllUnnamed(Module m, String pn) {
|
||||
m.implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, false, true);
|
||||
}
|
||||
@Override
|
||||
public void addExportsToAll(Module m, String pn) {
|
||||
m.implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, false, true);
|
||||
}
|
||||
@Override
|
||||
public void addOpensToAll(Module m, String pn) {
|
||||
public void addOpens(Module m, String pn) {
|
||||
m.implAddExportsOrOpens(pn, Module.EVERYONE_MODULE, true, true);
|
||||
}
|
||||
@Override
|
||||
public void addExportsToAllUnnamed(Module m, String pn) {
|
||||
m.implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, false, true);
|
||||
public void addOpens(Module m, String pn, Module other) {
|
||||
m.implAddExportsOrOpens(pn, other, true, true);
|
||||
}
|
||||
@Override
|
||||
public void addOpensToAllUnnamed(Module m, String pn) {
|
||||
@ -1559,10 +1575,6 @@ public final class Module implements AnnotatedElement {
|
||||
m.implAddUses(service);
|
||||
}
|
||||
@Override
|
||||
public void addPackage(Module m, String pn) {
|
||||
m.implAddPackage(pn, true);
|
||||
}
|
||||
@Override
|
||||
public ServicesCatalog getServicesCatalog(Layer layer) {
|
||||
return layer.getServicesCatalog();
|
||||
}
|
||||
@ -1574,10 +1586,6 @@ public final class Module implements AnnotatedElement {
|
||||
public Stream<Layer> layers(ClassLoader loader) {
|
||||
return Layer.layers(loader);
|
||||
}
|
||||
@Override
|
||||
public boolean isStaticallyExported(Module module, String pn, Module other) {
|
||||
return module.isStaticallyExportedOrOpen(pn, other, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -359,10 +359,11 @@ public class Proxy implements java.io.Serializable {
|
||||
* @throws NullPointerException if the {@code interfaces} array
|
||||
* argument or any of its elements are {@code null}
|
||||
*
|
||||
* @deprecated Proxy classes generated in a named module are encapsulated and not
|
||||
* accessible to code outside its module.
|
||||
* {@link Constructor#newInstance(Object...) Constructor.newInstance} will throw
|
||||
* {@code IllegalAccessException} when it is called on an inaccessible proxy class.
|
||||
* @deprecated Proxy classes generated in a named module are encapsulated
|
||||
* and not accessible to code outside its module.
|
||||
* {@link Constructor#newInstance(Object...) Constructor.newInstance}
|
||||
* will throw {@code IllegalAccessException} when it is called on
|
||||
* an inaccessible proxy class.
|
||||
* Use {@link #newProxyInstance(ClassLoader, Class[], InvocationHandler)}
|
||||
* to create a proxy instance instead.
|
||||
*
|
||||
@ -511,17 +512,19 @@ public class Proxy implements java.io.Serializable {
|
||||
"Unnamed package cannot be added to " + m);
|
||||
}
|
||||
|
||||
// add the package to the runtime module if not exists
|
||||
if (m.isNamed()) {
|
||||
m.addPackage(proxyPkg);
|
||||
if (!m.getDescriptor().packages().contains(proxyPkg)) {
|
||||
throw new InternalError(proxyPkg + " not exist in " + m.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose a name for the proxy class to generate.
|
||||
*/
|
||||
long num = nextUniqueNumber.getAndIncrement();
|
||||
String proxyName = proxyPkg.isEmpty() ? proxyClassNamePrefix + num
|
||||
: proxyPkg + "." + proxyClassNamePrefix + num;
|
||||
String proxyName = proxyPkg.isEmpty()
|
||||
? proxyClassNamePrefix + num
|
||||
: proxyPkg + "." + proxyClassNamePrefix + num;
|
||||
|
||||
ClassLoader loader = getLoader(m);
|
||||
trace(proxyName, m, loader, interfaces);
|
||||
@ -581,9 +584,13 @@ public class Proxy implements java.io.Serializable {
|
||||
c.getModule().getName(), c.getName(), access, ld);
|
||||
}
|
||||
|
||||
static void trace(String cn, Module module, ClassLoader loader, List<Class<?>> interfaces) {
|
||||
static void trace(String cn,
|
||||
Module module,
|
||||
ClassLoader loader,
|
||||
List<Class<?>> interfaces) {
|
||||
if (isDebug()) {
|
||||
System.out.format("PROXY: %s/%s defined by %s%n", module.getName(), cn, loader);
|
||||
System.err.format("PROXY: %s/%s defined by %s%n",
|
||||
module.getName(), cn, loader);
|
||||
}
|
||||
if (isDebug("debug")) {
|
||||
interfaces.stream()
|
||||
@ -592,7 +599,7 @@ public class Proxy implements java.io.Serializable {
|
||||
}
|
||||
|
||||
private static final String DEBUG =
|
||||
GetPropertyAction.privilegedGetProperty("jdk.proxy.debug", "");
|
||||
GetPropertyAction.privilegedGetProperty("jdk.proxy.debug", "");
|
||||
|
||||
private static boolean isDebug() {
|
||||
return !DEBUG.isEmpty();
|
||||
@ -603,15 +610,16 @@ public class Proxy implements java.io.Serializable {
|
||||
|
||||
// ProxyBuilder instance members start here....
|
||||
|
||||
private final ClassLoader loader;
|
||||
private final List<Class<?>> interfaces;
|
||||
private final Module module;
|
||||
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
|
||||
if (!VM.isModuleSystemInited()) {
|
||||
throw new InternalError("Proxy is not supported until module system is fully initialized");
|
||||
throw new InternalError("Proxy is not supported until "
|
||||
+ "module system is fully initialized");
|
||||
}
|
||||
if (interfaces.size() > 65535) {
|
||||
throw new IllegalArgumentException("interface limit exceeded: " + interfaces.size());
|
||||
throw new IllegalArgumentException("interface limit exceeded: "
|
||||
+ interfaces.size());
|
||||
}
|
||||
|
||||
Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
|
||||
@ -619,7 +627,6 @@ public class Proxy implements java.io.Serializable {
|
||||
// IAE if violates any restrictions specified in newProxyInstance
|
||||
validateProxyInterfaces(loader, interfaces, refTypes);
|
||||
|
||||
this.loader = loader;
|
||||
this.interfaces = interfaces;
|
||||
this.module = mapToModule(loader, interfaces, refTypes);
|
||||
assert getLoader(module) == loader;
|
||||
@ -659,8 +666,8 @@ public class Proxy implements java.io.Serializable {
|
||||
* Validate the given proxy interfaces and the given referenced types
|
||||
* are visible to the defining loader.
|
||||
*
|
||||
* @throws IllegalArgumentException if it violates the restrictions specified
|
||||
* in {@link Proxy#newProxyInstance}
|
||||
* @throws IllegalArgumentException if it violates the restrictions
|
||||
* specified in {@link Proxy#newProxyInstance}
|
||||
*/
|
||||
private static void validateProxyInterfaces(ClassLoader loader,
|
||||
List<Class<?>> interfaces,
|
||||
@ -731,9 +738,9 @@ public class Proxy implements java.io.Serializable {
|
||||
* is in the same module of the package-private interface.
|
||||
*
|
||||
* If all proxy interfaces are public and at least one in a non-exported
|
||||
* package, then the proxy class is in a dynamic module in a non-exported
|
||||
* package. Reads edge and qualified exports are added for
|
||||
* dynamic module to access.
|
||||
* package, then the proxy class is in a dynamic module in a
|
||||
* non-exported package. Reads edge and qualified exports are added
|
||||
* for dynamic module to access.
|
||||
*/
|
||||
private static Module mapToModule(ClassLoader loader,
|
||||
List<Class<?>> interfaces,
|
||||
@ -752,11 +759,12 @@ public class Proxy implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
// all proxy interfaces are public and exported, the proxy class is in unnamed module
|
||||
// Such proxy class is accessible to any unnamed module and named module that
|
||||
// can read unnamed module
|
||||
// all proxy interfaces are public and exported, the proxy class
|
||||
// is in unnamed module. Such proxy class is accessible to
|
||||
// any unnamed module and named module that can read unnamed module
|
||||
if (packagePrivateTypes.isEmpty() && modulePrivateTypes.isEmpty()) {
|
||||
return loader != null ? loader.getUnnamedModule() : BootLoader.getUnnamedModule();
|
||||
return loader != null ? loader.getUnnamedModule()
|
||||
: BootLoader.getUnnamedModule();
|
||||
}
|
||||
|
||||
if (packagePrivateTypes.size() > 0) {
|
||||
@ -778,7 +786,8 @@ public class Proxy implements java.io.Serializable {
|
||||
Module target = null;
|
||||
for (Module m : packagePrivateTypes.values()) {
|
||||
if (getLoader(m) != loader) {
|
||||
// the specified loader is not the same class loader of the non-public interface
|
||||
// the specified loader is not the same class loader
|
||||
// of the non-public interface
|
||||
throw new IllegalArgumentException(
|
||||
"non-public interface is not defined by the given loader");
|
||||
}
|
||||
@ -799,8 +808,9 @@ public class Proxy implements java.io.Serializable {
|
||||
return target;
|
||||
}
|
||||
|
||||
// all proxy interfaces are public and at least one in a non-exported package
|
||||
// map to dynamic proxy module and add reads edge and qualified exports, if necessary
|
||||
// All proxy interfaces are public and at least one in a non-exported
|
||||
// package. So maps to a dynamic proxy module and add reads edge
|
||||
// and qualified exports, if necessary
|
||||
Module target = getDynamicModule(loader);
|
||||
|
||||
// set up proxy class access to proxy interfaces and types
|
||||
@ -856,8 +866,8 @@ public class Proxy implements java.io.Serializable {
|
||||
private static final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
/*
|
||||
* Define a dynamic module for the generated proxy classes in a non-exported package
|
||||
* named com.sun.proxy.$MODULE.
|
||||
* Define a dynamic module for the generated proxy classes in
|
||||
* a non-exported package named com.sun.proxy.$MODULE.
|
||||
*
|
||||
* Each class loader will have one dynamic module.
|
||||
*/
|
||||
|
@ -1007,6 +1007,7 @@ public final class ServiceLoader<S>
|
||||
{
|
||||
static final String PREFIX = "META-INF/services/";
|
||||
|
||||
Set<String> providerNames = new HashSet<>(); // to avoid duplicates
|
||||
Enumeration<URL> configs;
|
||||
Iterator<String> pending;
|
||||
Class<?> nextClass;
|
||||
@ -1016,7 +1017,7 @@ public final class ServiceLoader<S>
|
||||
|
||||
/**
|
||||
* Parse a single line from the given configuration file, adding the
|
||||
* name on the line to the names list.
|
||||
* name on the line to set of names if not already seen.
|
||||
*/
|
||||
private int parseLine(URL u, BufferedReader r, int lc, Set<String> names)
|
||||
throws IOException
|
||||
@ -1041,7 +1042,9 @@ public final class ServiceLoader<S>
|
||||
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
|
||||
fail(service, u, lc, "Illegal provider-class name: " + ln);
|
||||
}
|
||||
names.add(ln);
|
||||
if (providerNames.add(ln)) {
|
||||
names.add(ln);
|
||||
}
|
||||
}
|
||||
return lc + 1;
|
||||
}
|
||||
@ -1072,7 +1075,7 @@ public final class ServiceLoader<S>
|
||||
return true;
|
||||
}
|
||||
|
||||
Class<?> clazz = null;
|
||||
Class<?> clazz;
|
||||
do {
|
||||
if (configs == null) {
|
||||
try {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -59,7 +59,7 @@ public class JmodFile implements AutoCloseable {
|
||||
bis.read(magic);
|
||||
if (magic[0] != JMOD_MAGIC_NUMBER[0] ||
|
||||
magic[1] != JMOD_MAGIC_NUMBER[1]) {
|
||||
throw new IOException("Invalid jmod file: " + file.toString());
|
||||
throw new IOException("Invalid JMOD file: " + file.toString());
|
||||
}
|
||||
if (magic[2] > JMOD_MAJOR_VERSION ||
|
||||
(magic[2] == JMOD_MAJOR_VERSION && magic[3] > JMOD_MINOR_VERSION)) {
|
||||
@ -130,6 +130,13 @@ public class JmodFile implements AutoCloseable {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entry is a directory in the JMOD file.
|
||||
*/
|
||||
public boolean isDirectory() {
|
||||
return zipEntry.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this entry.
|
||||
*/
|
||||
@ -186,12 +193,12 @@ public class JmodFile implements AutoCloseable {
|
||||
public Entry getEntry(Section section, String name) {
|
||||
String entry = section.jmodDir() + "/" + name;
|
||||
ZipEntry ze = zipfile.getEntry(entry);
|
||||
return (ze == null || ze.isDirectory()) ? null : new Entry(ze);
|
||||
return (ze != null) ? new Entry(ze) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an {@code InputStream} for reading the named entry of the given
|
||||
* section in this jmod file.
|
||||
* section in this JMOD file.
|
||||
*
|
||||
* @throws IOException if the named entry is not found, or I/O error
|
||||
* occurs when reading it
|
||||
@ -201,7 +208,7 @@ public class JmodFile implements AutoCloseable {
|
||||
{
|
||||
String entry = section.jmodDir() + "/" + name;
|
||||
ZipEntry e = zipfile.getEntry(entry);
|
||||
if (e == null || e.isDirectory()) {
|
||||
if (e == null) {
|
||||
throw new IOException(name + " not found: " + file);
|
||||
}
|
||||
return zipfile.getInputStream(e);
|
||||
@ -217,11 +224,10 @@ public class JmodFile implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of non-directory entries in this jmod file.
|
||||
* Returns a stream of entries in this JMOD file.
|
||||
*/
|
||||
public Stream<Entry> stream() {
|
||||
return zipfile.stream()
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(Entry::new);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -65,14 +65,12 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Need FilePermission ${java.home}/-", "read" to create or get jrt:/
|
||||
* Need RuntimePermission "accessSystemModules" to create or get jrt:/
|
||||
*/
|
||||
private void checkPermission() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
String home = SystemImage.RUNTIME_HOME;
|
||||
FilePermission perm
|
||||
= new FilePermission(home + File.separator + "-", "read");
|
||||
RuntimePermission perm = new RuntimePermission("accessSystemModules");
|
||||
sm.checkPermission(perm);
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,14 @@ public class BootLoader {
|
||||
return CLASS_LOADER_VALUE_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there is a class path associated with the
|
||||
* BootLoader.
|
||||
*/
|
||||
public static boolean hasClassPath() {
|
||||
return ClassLoaders.bootLoader().hasClassPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a module with this class loader so that its classes (and
|
||||
* resources) become visible via this class loader.
|
||||
@ -187,14 +195,6 @@ public class BootLoader {
|
||||
.map(name -> getDefinedPackage(name.replace('/', '.')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there is a class path associated with the
|
||||
* BootLoader.
|
||||
*/
|
||||
public static boolean hasClassPath() {
|
||||
return ClassLoaders.bootLoader().hasClassPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to define {@code Package} objects for packages in modules
|
||||
* defined to the boot loader.
|
||||
|
@ -60,6 +60,7 @@ import java.util.stream.Stream;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.ModulePatcher.PatchedModuleReader;
|
||||
import jdk.internal.module.SystemModules;
|
||||
import jdk.internal.module.Resources;
|
||||
|
||||
|
||||
/**
|
||||
@ -162,6 +163,14 @@ public class BuiltinClassLoader
|
||||
this.moduleToReader = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there is a class path associated with this
|
||||
* class loader.
|
||||
*/
|
||||
boolean hasClassPath() {
|
||||
return ucp != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a module this this class loader. This has the effect of making
|
||||
* the types in the module visible.
|
||||
@ -248,18 +257,24 @@ public class BuiltinClassLoader
|
||||
*/
|
||||
@Override
|
||||
public URL findResource(String name) {
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
LoadedModule module = packageToModule.get(pn);
|
||||
if (module != null) {
|
||||
|
||||
// resource is in a package of a module defined to this loader
|
||||
if (module.loader() == this
|
||||
&& (name.endsWith(".class") || isOpen(module.mref(), pn))) {
|
||||
if (module.loader() == this) {
|
||||
URL url;
|
||||
try {
|
||||
return findResource(module.name(), name); // checks URL
|
||||
url = findResource(module.name(), name); // checks URL
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
if (url != null
|
||||
&& (name.endsWith(".class")
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -293,15 +308,17 @@ public class BuiltinClassLoader
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
List<URL> checked = new ArrayList<>(); // list of checked URLs
|
||||
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
LoadedModule module = packageToModule.get(pn);
|
||||
if (module != null) {
|
||||
|
||||
// resource is in a package of a module defined to this loader
|
||||
if (module.loader() == this
|
||||
&& (name.endsWith(".class") || isOpen(module.mref(), pn))) {
|
||||
URL url = findResource(module.name(), name); // checks URL
|
||||
if (url != null) {
|
||||
if (module.loader() == this) {
|
||||
URL url = findResource(module.name(), name); // checks URL
|
||||
if (url != null
|
||||
&& (name.endsWith(".class")
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
checked.add(url);
|
||||
}
|
||||
}
|
||||
@ -351,11 +368,13 @@ public class BuiltinClassLoader
|
||||
new PrivilegedExceptionAction<>() {
|
||||
@Override
|
||||
public List<URL> run() throws IOException {
|
||||
List<URL> result = new ArrayList<>();
|
||||
List<URL> result = null;
|
||||
for (ModuleReference mref : nameToModule.values()) {
|
||||
URI u = moduleReaderFor(mref).find(name).orElse(null);
|
||||
if (u != null) {
|
||||
try {
|
||||
if (result == null)
|
||||
result = new ArrayList<>();
|
||||
result.add(u.toURL());
|
||||
} catch (MalformedURLException |
|
||||
IllegalArgumentException e) {
|
||||
@ -375,7 +394,7 @@ public class BuiltinClassLoader
|
||||
map = new ConcurrentHashMap<>();
|
||||
this.resourceCache = new SoftReference<>(map);
|
||||
}
|
||||
if (urls.isEmpty())
|
||||
if (urls == null)
|
||||
urls = Collections.emptyList();
|
||||
map.putIfAbsent(name, urls);
|
||||
}
|
||||
@ -869,14 +888,6 @@ public class BuiltinClassLoader
|
||||
sealBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if there is a class path associated with this
|
||||
* class loader.
|
||||
*/
|
||||
boolean hasClassPath() {
|
||||
return ucp != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the specified package name is sealed according to
|
||||
* the given manifest.
|
||||
@ -975,7 +986,7 @@ public class BuiltinClassLoader
|
||||
*/
|
||||
private boolean isOpen(ModuleReference mref, String pn) {
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
if (descriptor.isOpen())
|
||||
if (descriptor.isOpen() || descriptor.isAutomatic())
|
||||
return true;
|
||||
for (ModuleDescriptor.Opens opens : descriptor.opens()) {
|
||||
String source = opens.source();
|
||||
|
@ -60,6 +60,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.Resources;
|
||||
|
||||
|
||||
/**
|
||||
@ -356,45 +357,52 @@ public final class Loader extends SecureClassLoader {
|
||||
|
||||
@Override
|
||||
public URL findResource(String name) {
|
||||
URL url = null;
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
LoadedModule module = localPackageToModule.get(pn);
|
||||
|
||||
if (module != null) {
|
||||
if (name.endsWith(".class") || isOpen(module.mref(), pn)) {
|
||||
try {
|
||||
url = findResource(module.name(), name);
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
try {
|
||||
URL url = findResource(module.name(), name);
|
||||
if (url != null
|
||||
&& (name.endsWith(".class")
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
return url;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
} else {
|
||||
for (ModuleReference mref : nameToModule.values()) {
|
||||
try {
|
||||
url = findResource(mref.descriptor().name(), name);
|
||||
if (url != null)
|
||||
break;
|
||||
URL url = findResource(mref.descriptor().name(), name);
|
||||
if (url != null) return url;
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return url;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
String pn = ResourceHelper.getPackageName(name);
|
||||
String pn = Resources.toPackageName(name);
|
||||
LoadedModule module = localPackageToModule.get(pn);
|
||||
if (module != null) {
|
||||
if (name.endsWith(".class") || isOpen(module.mref(), pn)) {
|
||||
try {
|
||||
URL url = findResource(module.name(), name);
|
||||
if (url != null)
|
||||
urls.add(url);
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
try {
|
||||
URL url = findResource(module.name(), name);
|
||||
if (url != null
|
||||
&& (name.endsWith(".class")
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
urls.add(url);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
for (ModuleReference mref : nameToModule.values()) {
|
||||
@ -643,7 +651,7 @@ public final class Loader extends SecureClassLoader {
|
||||
*/
|
||||
private boolean isOpen(ModuleReference mref, String pn) {
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
if (descriptor.isOpen())
|
||||
if (descriptor.isOpen() || descriptor.isAutomatic())
|
||||
return true;
|
||||
for (ModuleDescriptor.Opens opens : descriptor.opens()) {
|
||||
String source = opens.source();
|
||||
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.internal.module.Checks;
|
||||
|
||||
/**
|
||||
* Helper class for Class#getResource, Module#getResourceAsStream, and other
|
||||
* methods that locate a resource in a module.
|
||||
*/
|
||||
public final class ResourceHelper {
|
||||
private ResourceHelper() { }
|
||||
|
||||
/**
|
||||
* Returns the <em>package name</em> for a resource or the empty package if
|
||||
* the resource name does not contain a slash.
|
||||
*/
|
||||
public static String getPackageName(String name) {
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index != -1) {
|
||||
return name.substring(0, index).replace("/", ".");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the resource is a <em>simple resource</em>. Simple
|
||||
* resources can never be encapsulated. Resources ending in "{@code .class}"
|
||||
* or where the package name is not a legal package name can not be
|
||||
* encapsulated.
|
||||
*/
|
||||
public static boolean isSimpleResource(String name) {
|
||||
int len = name.length();
|
||||
if (len > 6 && name.endsWith(".class")) {
|
||||
return true;
|
||||
}
|
||||
if (!Checks.isPackageName(getPackageName(name))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a resource name to a file path. Returns {@code null} if the
|
||||
* resource name cannot be converted into a file path. Resource names
|
||||
* with empty elements, or elements that are "." or ".." are rejected,
|
||||
* as is a resource name that translates to a file path with a root
|
||||
* component.
|
||||
*/
|
||||
public static Path toFilePath(String name) {
|
||||
// scan the resource name to eagerly reject obviously invalid names
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('/', off)) != -1) {
|
||||
int len = next - off;
|
||||
if (!mayTranslate(name, off, len)) {
|
||||
return null;
|
||||
}
|
||||
off = next + 1;
|
||||
}
|
||||
int rem = name.length() - off;
|
||||
if (!mayTranslate(name, off, rem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// convert to file path
|
||||
Path path;
|
||||
if (File.separatorChar == '/') {
|
||||
path = Paths.get(name);
|
||||
} else {
|
||||
// not allowed to embed file separators
|
||||
if (name.contains(File.separator))
|
||||
return null;
|
||||
path = Paths.get(name.replace('/', File.separatorChar));
|
||||
}
|
||||
|
||||
// file path not allowed to have root component
|
||||
return (path.getRoot() == null) ? path : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the element in a resource name is a candidate
|
||||
* to translate to the element of a file path.
|
||||
*/
|
||||
private static boolean mayTranslate(String name, int off, int len) {
|
||||
if (len <= 2) {
|
||||
if (len == 0)
|
||||
return false;
|
||||
boolean starsWithDot = (name.charAt(off) == '.');
|
||||
if (len == 1 && starsWithDot)
|
||||
return false;
|
||||
if (len == 2 && starsWithDot && (name.charAt(off+1) == '.'))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -33,6 +33,7 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URL;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
@ -149,6 +150,11 @@ public interface JavaLangAccess {
|
||||
*/
|
||||
ConcurrentHashMap<?, ?> createOrGetClassLoaderValueMap(ClassLoader cl);
|
||||
|
||||
/**
|
||||
* Defines a class with the given name to a class loader.
|
||||
*/
|
||||
Class<?> defineClass(ClassLoader cl, String name, byte[] b, ProtectionDomain pd, String source);
|
||||
|
||||
/**
|
||||
* Returns a class loaded by the bootstrap class loader.
|
||||
*/
|
||||
|
@ -73,7 +73,7 @@ public interface JavaLangModuleAccess {
|
||||
void requires(ModuleDescriptor.Builder builder,
|
||||
Set<Requires.Modifier> ms,
|
||||
String mn,
|
||||
String compiledVersion);
|
||||
String rawCompiledVersion);
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleDescriptor.Requires} of the given modifiers
|
||||
@ -127,9 +127,6 @@ public interface JavaLangModuleAccess {
|
||||
Set<Provides> provides,
|
||||
Set<String> packages,
|
||||
String mainClass,
|
||||
String osName,
|
||||
String osArch,
|
||||
String osVersion,
|
||||
int hashCode);
|
||||
|
||||
/**
|
||||
|
@ -65,6 +65,11 @@ public interface JavaLangReflectModuleAccess {
|
||||
*/
|
||||
void addReadsAllUnnamed(Module m);
|
||||
|
||||
/**
|
||||
* Update module m to export a package to all modules.
|
||||
*/
|
||||
void addExports(Module m, String pn);
|
||||
|
||||
/**
|
||||
* Updates module m1 to export a package to module m2. The export does
|
||||
* not result in a strong reference to m2 (m2 can be GC'ed).
|
||||
@ -72,25 +77,20 @@ public interface JavaLangReflectModuleAccess {
|
||||
void addExports(Module m1, String pkg, Module m2);
|
||||
|
||||
/**
|
||||
* Updates module m1 to open a package to module m2. Opening the
|
||||
* package does not result in a strong reference to m2 (m2 can be GC'ed).
|
||||
* Updates a module m to export a package to all unnamed modules.
|
||||
*/
|
||||
void addOpens(Module m1, String pkg, Module m2);
|
||||
|
||||
/**
|
||||
* Updates a module m to export a package to all modules.
|
||||
*/
|
||||
void addExportsToAll(Module m, String pkg);
|
||||
void addExportsToAllUnnamed(Module m, String pkg);
|
||||
|
||||
/**
|
||||
* Updates a module m to open a package to all modules.
|
||||
*/
|
||||
void addOpensToAll(Module m, String pkg);
|
||||
void addOpens(Module m, String pkg);
|
||||
|
||||
/**
|
||||
* Updates a module m to export a package to all unnamed modules.
|
||||
* Updates module m1 to open a package to module m2. Opening the
|
||||
* package does not result in a strong reference to m2 (m2 can be GC'ed).
|
||||
*/
|
||||
void addExportsToAllUnnamed(Module m, String pkg);
|
||||
void addOpens(Module m1, String pkg, Module m2);
|
||||
|
||||
/**
|
||||
* Updates a module m to open a package to all unnamed modules.
|
||||
@ -102,11 +102,6 @@ public interface JavaLangReflectModuleAccess {
|
||||
*/
|
||||
void addUses(Module m, Class<?> service);
|
||||
|
||||
/**
|
||||
* Add a package to the given module.
|
||||
*/
|
||||
void addPackage(Module m, String pkg);
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for the given Layer.
|
||||
*/
|
||||
@ -123,12 +118,4 @@ public interface JavaLangReflectModuleAccess {
|
||||
* given class loader.
|
||||
*/
|
||||
Stream<Layer> layers(ClassLoader loader);
|
||||
|
||||
/**
|
||||
* Tests if a module exports a package at least {@code other} via its
|
||||
* module declaration.
|
||||
*
|
||||
* @apiNote This is a temporary method for debugging features.
|
||||
*/
|
||||
boolean isStaticallyExported(Module module, String pn, Module other);
|
||||
}
|
@ -145,9 +145,6 @@ final class Builder {
|
||||
Set<Provides> provides;
|
||||
Version version;
|
||||
String mainClass;
|
||||
String osName;
|
||||
String osArch;
|
||||
String osVersion;
|
||||
|
||||
Builder(String name) {
|
||||
this.name = name;
|
||||
@ -247,30 +244,6 @@ final class Builder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OS name.
|
||||
*/
|
||||
public Builder osName(String name) {
|
||||
this.osName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OS arch.
|
||||
*/
|
||||
public Builder osArch(String arch) {
|
||||
this.osArch = arch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the OS version.
|
||||
*/
|
||||
public Builder osVersion(String version) {
|
||||
this.osVersion = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of the module modifiers derived from the flags.
|
||||
*/
|
||||
@ -305,9 +278,6 @@ final class Builder {
|
||||
provides,
|
||||
packages,
|
||||
mainClass,
|
||||
osName,
|
||||
osArch,
|
||||
osVersion,
|
||||
hashCode);
|
||||
}
|
||||
}
|
||||
|
@ -180,41 +180,38 @@ public final class Checks {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the last character of the given name is legal
|
||||
* as the last character of a module name.
|
||||
*
|
||||
* @throws IllegalArgumentException if name is empty
|
||||
* Returns {@code true} if a given legal module name contains an identifier
|
||||
* that doesn't end with a Java letter.
|
||||
*/
|
||||
public static boolean hasLegalModuleNameLastCharacter(String name) {
|
||||
if (name.isEmpty())
|
||||
throw new IllegalArgumentException("name is empty");
|
||||
int len = name.length();
|
||||
if (isASCIIString(name)) {
|
||||
char c = name.charAt(len-1);
|
||||
return Character.isJavaIdentifierStart(c);
|
||||
} else {
|
||||
int i = 0;
|
||||
int cp = -1;
|
||||
while (i < len) {
|
||||
cp = name.codePointAt(i);
|
||||
i += Character.charCount(cp);
|
||||
}
|
||||
return Character.isJavaIdentifierStart(cp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given string only contains ASCII characters.
|
||||
*/
|
||||
private static boolean isASCIIString(String s) {
|
||||
public static boolean hasJavaIdentifierWithTrailingDigit(String name) {
|
||||
// quick scan to allow names that are just ASCII without digits
|
||||
boolean needToParse = false;
|
||||
int i = 0;
|
||||
while (i < s.length()) {
|
||||
int c = s.charAt(i);
|
||||
if (c > 0x7F)
|
||||
return false;
|
||||
while (i < name.length()) {
|
||||
int c = name.charAt(i);
|
||||
if (c > 0x7F || (c >= '0' && c <= '9')) {
|
||||
needToParse = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
if (!needToParse)
|
||||
return false;
|
||||
|
||||
// slow path
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('.', off)) != -1) {
|
||||
int last = isJavaIdentifier(name, off, (next - off));
|
||||
if (!Character.isJavaIdentifierStart(last))
|
||||
return true;
|
||||
off = next+1;
|
||||
}
|
||||
int last = isJavaIdentifier(name, off, name.length() - off);
|
||||
if (!Character.isJavaIdentifierStart(last))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,11 +292,11 @@ public final class ClassFileAttributes {
|
||||
attr.putShort(module_flags);
|
||||
|
||||
// module_version
|
||||
Version v = descriptor.version().orElse(null);
|
||||
if (v == null) {
|
||||
String vs = descriptor.rawVersion().orElse(null);
|
||||
if (vs == null) {
|
||||
attr.putShort(0);
|
||||
} else {
|
||||
int module_version_index = cw.newUTF8(v.toString());
|
||||
int module_version_index = cw.newUTF8(vs);
|
||||
attr.putShort(module_version_index);
|
||||
}
|
||||
|
||||
@ -320,11 +320,11 @@ public final class ClassFileAttributes {
|
||||
attr.putShort(requires_flags);
|
||||
|
||||
int requires_version_index;
|
||||
v = r.compiledVersion().orElse(null);
|
||||
if (v == null) {
|
||||
vs = r.rawCompiledVersion().orElse(null);
|
||||
if (vs == null) {
|
||||
requires_version_index = 0;
|
||||
} else {
|
||||
requires_version_index = cw.newUTF8(v.toString());
|
||||
requires_version_index = cw.newUTF8(vs);
|
||||
}
|
||||
attr.putShort(requires_version_index);
|
||||
}
|
||||
@ -553,8 +553,6 @@ public final class ClassFileAttributes {
|
||||
* u2 os_name_index;
|
||||
* // index to CONSTANT_utf8_info structure with the OS arch
|
||||
* u2 os_arch_index
|
||||
* // index to CONSTANT_utf8_info structure with the OS version
|
||||
* u2 os_version_index;
|
||||
* }
|
||||
*
|
||||
* } </pre>
|
||||
@ -562,17 +560,23 @@ public final class ClassFileAttributes {
|
||||
public static class ModuleTargetAttribute extends Attribute {
|
||||
private final String osName;
|
||||
private final String osArch;
|
||||
private final String osVersion;
|
||||
|
||||
public ModuleTargetAttribute(String osName, String osArch, String osVersion) {
|
||||
public ModuleTargetAttribute(String osName, String osArch) {
|
||||
super(MODULE_TARGET);
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
public ModuleTargetAttribute() {
|
||||
this(null, null, null);
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public String osName() {
|
||||
return osName;
|
||||
}
|
||||
|
||||
public String osArch() {
|
||||
return osArch;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -586,7 +590,6 @@ public final class ClassFileAttributes {
|
||||
|
||||
String osName = null;
|
||||
String osArch = null;
|
||||
String osVersion = null;
|
||||
|
||||
int name_index = cr.readUnsignedShort(off);
|
||||
if (name_index != 0)
|
||||
@ -598,12 +601,7 @@ public final class ClassFileAttributes {
|
||||
osArch = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
|
||||
int version_index = cr.readUnsignedShort(off);
|
||||
if (version_index != 0)
|
||||
osVersion = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
|
||||
return new ModuleTargetAttribute(osName, osArch, osVersion);
|
||||
return new ModuleTargetAttribute(osName, osArch);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -625,11 +623,6 @@ public final class ClassFileAttributes {
|
||||
arch_index = cw.newUTF8(osArch);
|
||||
attr.putShort(arch_index);
|
||||
|
||||
int version_index = 0;
|
||||
if (osVersion != null && osVersion.length() > 0)
|
||||
version_index = cw.newUTF8(osVersion);
|
||||
attr.putShort(version_index);
|
||||
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Supports logging of access to members of API packages that are exported or
|
||||
* opened via backdoor mechanisms to code in unnamed modules.
|
||||
*/
|
||||
|
||||
public final class IllegalAccessLogger {
|
||||
|
||||
// true to print stack trace
|
||||
private static final boolean PRINT_STACK_TRACE;
|
||||
static {
|
||||
String s = System.getProperty("sun.reflect.debugModuleAccessChecks");
|
||||
PRINT_STACK_TRACE = "access".equals(s);
|
||||
}
|
||||
|
||||
private static final StackWalker STACK_WALKER
|
||||
= StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
|
||||
// the maximum number of frames to capture
|
||||
private static final int MAX_STACK_FRAMES = 32;
|
||||
|
||||
// lock to avoid interference when printing stack traces
|
||||
private static final Object OUTPUT_LOCK = new Object();
|
||||
|
||||
// caller -> usages
|
||||
private final Map<Class<?>, Set<Usage>> callerToUsages = new WeakHashMap<>();
|
||||
|
||||
// module -> (package name -> CLI option)
|
||||
private final Map<Module, Map<String, String>> exported;
|
||||
private final Map<Module, Map<String, String>> opened;
|
||||
|
||||
private IllegalAccessLogger(Map<Module, Map<String, String>> exported,
|
||||
Map<Module, Map<String, String>> opened) {
|
||||
this.exported = deepCopy(exported);
|
||||
this.opened = deepCopy(opened);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns that a Builder that is seeded with the packages known to this logger.
|
||||
*/
|
||||
public Builder toBuilder() {
|
||||
return new Builder(exported, opened);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access to the member of a target class by a caller class if the class
|
||||
* is in a package that is exported via a backdoor mechanism.
|
||||
*
|
||||
* The {@code whatSupplier} supplies the message that describes the member.
|
||||
*/
|
||||
public void logIfExportedByBackdoor(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Map<String, String> packages = exported.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller, whatSupplier.get(), how);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access to the member of a target class by a caller class if the class
|
||||
* is in a package that is opened via a backdoor mechanism.
|
||||
*
|
||||
* The {@code what} parameter supplies the message that describes the member.
|
||||
*/
|
||||
public void logIfOpenedByBackdoor(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Map<String, String> packages = opened.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller, whatSupplier.get(), how);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access by a caller class. The {@code what} parameter describes
|
||||
* the member is accessed, the {@code how} parameter is the means by which
|
||||
* access is allocated (CLI option for example).
|
||||
*/
|
||||
private void log(Class<?> caller, String what, String how) {
|
||||
log(caller, what, () -> {
|
||||
PrivilegedAction<ProtectionDomain> pa = caller::getProtectionDomain;
|
||||
CodeSource cs = AccessController.doPrivileged(pa).getCodeSource();
|
||||
URL url = (cs != null) ? cs.getLocation() : null;
|
||||
String source = caller.getName();
|
||||
if (url != null)
|
||||
source += " (" + url + ")";
|
||||
return "WARNING: Illegal access by " + source + " to " + what
|
||||
+ " (permitted by " + how + ")";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs access to caller class if the class is in a package that is opened via
|
||||
* a backdoor mechanism.
|
||||
*/
|
||||
public void logIfOpenedByBackdoor(MethodHandles.Lookup caller, Class<?> target) {
|
||||
Map<String, String> packages = opened.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller.lookupClass(), target.getName(), () ->
|
||||
"WARNING: Illegal access using Lookup on " + caller.lookupClass()
|
||||
+ " to " + target + " (permitted by " + how + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log access by a caller. The {@code what} parameter describes the class or
|
||||
* member that is being accessed. The {@code msgSupplier} supplies the log
|
||||
* message.
|
||||
*
|
||||
* To reduce output, this method only logs the access if it hasn't been seen
|
||||
* previously. "Seen previously" is implemented as a map of caller class -> Usage,
|
||||
* where a Usage is the "what" and a hash of the stack trace. The map has weak
|
||||
* keys so it can be expunged when the caller is GC'ed/unloaded.
|
||||
*/
|
||||
private void log(Class<?> caller, String what, Supplier<String> msgSupplier) {
|
||||
// stack trace without the top-most frames in java.base
|
||||
List<StackWalker.StackFrame> stack = STACK_WALKER.walk(s ->
|
||||
s.dropWhile(this::isJavaBase)
|
||||
.limit(MAX_STACK_FRAMES)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
// check if the access has already been recorded
|
||||
Usage u = new Usage(what, hash(stack));
|
||||
boolean firstUsage;
|
||||
synchronized (this) {
|
||||
firstUsage = callerToUsages.computeIfAbsent(caller, k -> new HashSet<>()).add(u);
|
||||
}
|
||||
|
||||
// log message if first usage
|
||||
if (firstUsage) {
|
||||
String msg = msgSupplier.get();
|
||||
if (PRINT_STACK_TRACE) {
|
||||
synchronized (OUTPUT_LOCK) {
|
||||
System.err.println(msg);
|
||||
stack.forEach(f -> System.err.println("\tat " + f));
|
||||
}
|
||||
} else {
|
||||
System.err.println(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Usage {
|
||||
private final String what;
|
||||
private final int stack;
|
||||
Usage(String what, int stack) {
|
||||
this.what = what;
|
||||
this.stack = stack;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return what.hashCode() ^ stack;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (ob instanceof Usage) {
|
||||
Usage that = (Usage)ob;
|
||||
return what.equals(that.what) && stack == (that.stack);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the stack frame is for a class in java.base.
|
||||
*/
|
||||
private boolean isJavaBase(StackWalker.StackFrame frame) {
|
||||
Module caller = frame.getDeclaringClass().getModule();
|
||||
return "java.base".equals(caller.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a hash code for the give stack frames. The hash code is based
|
||||
* on the class, method name, and BCI.
|
||||
*/
|
||||
private int hash(List<StackWalker.StackFrame> stack) {
|
||||
int hash = 0;
|
||||
for (StackWalker.StackFrame frame : stack) {
|
||||
hash = (31 * hash) + Objects.hash(frame.getDeclaringClass(),
|
||||
frame.getMethodName(),
|
||||
frame.getByteCodeIndex());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// system-wide IllegalAccessLogger
|
||||
private static volatile IllegalAccessLogger logger;
|
||||
|
||||
/**
|
||||
* Sets the system-wide IllegalAccessLogger
|
||||
*/
|
||||
public static void setIllegalAccessLogger(IllegalAccessLogger l) {
|
||||
if (l.exported.isEmpty() && l.opened.isEmpty()) {
|
||||
logger = null;
|
||||
} else {
|
||||
logger = l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system-wide IllegalAccessLogger or {@code null} if there is
|
||||
* no logger.
|
||||
*/
|
||||
public static IllegalAccessLogger illegalAccessLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for IllegalAccessLogger objects.
|
||||
*/
|
||||
public static class Builder {
|
||||
private Map<Module, Map<String, String>> exported;
|
||||
private Map<Module, Map<String, String>> opened;
|
||||
|
||||
public Builder() { }
|
||||
|
||||
public Builder(Map<Module, Map<String, String>> exported,
|
||||
Map<Module, Map<String, String>> opened) {
|
||||
this.exported = deepCopy(exported);
|
||||
this.opened = deepCopy(opened);
|
||||
}
|
||||
|
||||
public void logAccessToExportedPackage(Module m, String pn, String how) {
|
||||
if (!m.isExported(pn)) {
|
||||
if (exported == null)
|
||||
exported = new HashMap<>();
|
||||
exported.computeIfAbsent(m, k -> new HashMap<>()).putIfAbsent(pn, how);
|
||||
}
|
||||
}
|
||||
|
||||
public void logAccessToOpenPackage(Module m, String pn, String how) {
|
||||
// opens implies exported at run-time.
|
||||
logAccessToExportedPackage(m, pn, how);
|
||||
|
||||
if (!m.isOpen(pn)) {
|
||||
if (opened == null)
|
||||
opened = new HashMap<>();
|
||||
opened.computeIfAbsent(m, k -> new HashMap<>()).putIfAbsent(pn, how);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the logger.
|
||||
*/
|
||||
public IllegalAccessLogger build() {
|
||||
return new IllegalAccessLogger(exported, opened);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Map<Module, Map<String, String>> deepCopy(Map<Module, Map<String, String>> map) {
|
||||
if (map == null || map.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
} else {
|
||||
Map<Module, Map<String, String>> newMap = new HashMap<>();
|
||||
for (Map.Entry<Module, Map<String, String>> e : map.entrySet()) {
|
||||
newMap.put(e.getKey(), new HashMap<>(e.getValue()));
|
||||
}
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.reflect.LayerInstantiationException;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
@ -46,7 +47,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
@ -327,8 +327,9 @@ public final class ModuleBootstrap {
|
||||
for (String p : descriptor.packages()) {
|
||||
String other = packageToModule.putIfAbsent(p, name);
|
||||
if (other != null) {
|
||||
fail("Package " + p + " in both module "
|
||||
+ name + " and module " + other);
|
||||
String msg = "Package " + p + " in both module "
|
||||
+ name + " and module " + other;
|
||||
throw new LayerInstantiationException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -359,7 +360,7 @@ public final class ModuleBootstrap {
|
||||
PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
|
||||
|
||||
|
||||
// --add-reads, -add-exports/-add-opens
|
||||
// --add-reads, --add-exports/--add-opens
|
||||
addExtraReads(bootLayer);
|
||||
addExtraExportsAndOpens(bootLayer);
|
||||
|
||||
@ -514,26 +515,44 @@ public final class ModuleBootstrap {
|
||||
* additional packages specified on the command-line.
|
||||
*/
|
||||
private static void addExtraExportsAndOpens(Layer bootLayer) {
|
||||
IllegalAccessLogger.Builder builder = new IllegalAccessLogger.Builder();
|
||||
|
||||
// --add-exports
|
||||
String prefix = "jdk.module.addexports.";
|
||||
Map<String, List<String>> extraExports = decode(prefix);
|
||||
if (!extraExports.isEmpty()) {
|
||||
addExtraExportsOrOpens(bootLayer, extraExports, false);
|
||||
addExtraExportsOrOpens(bootLayer, extraExports, false, builder);
|
||||
}
|
||||
|
||||
// --add-opens
|
||||
prefix = "jdk.module.addopens.";
|
||||
Map<String, List<String>> extraOpens = decode(prefix);
|
||||
if (!extraOpens.isEmpty()) {
|
||||
addExtraExportsOrOpens(bootLayer, extraOpens, true);
|
||||
addExtraExportsOrOpens(bootLayer, extraOpens, true, builder);
|
||||
}
|
||||
|
||||
// --permit-illegal-access
|
||||
if (getAndRemoveProperty("jdk.module.permitIllegalAccess") != null) {
|
||||
warn("--permit-illegal-access will be removed in the next major release");
|
||||
bootLayer.modules().stream().forEach(m -> {
|
||||
m.getDescriptor()
|
||||
.packages()
|
||||
.stream()
|
||||
.filter(pn -> !m.isOpen(pn))
|
||||
.forEach(pn -> {
|
||||
builder.logAccessToOpenPackage(m, pn, "--permit-illegal-access");
|
||||
Modules.addOpensToAllUnnamed(m, pn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
IllegalAccessLogger.setIllegalAccessLogger(builder.build());
|
||||
}
|
||||
|
||||
private static void addExtraExportsOrOpens(Layer bootLayer,
|
||||
Map<String, List<String>> map,
|
||||
boolean opens)
|
||||
boolean opens,
|
||||
IllegalAccessLogger.Builder builder)
|
||||
{
|
||||
String option = opens ? ADD_OPENS : ADD_EXPORTS;
|
||||
for (Map.Entry<String, List<String>> e : map.entrySet()) {
|
||||
@ -542,12 +561,12 @@ public final class ModuleBootstrap {
|
||||
String key = e.getKey();
|
||||
String[] s = key.split("/");
|
||||
if (s.length != 2)
|
||||
fail(unableToParse(option, "<module>/<package>", key));
|
||||
fail(unableToParse(option, "<module>/<package>", key));
|
||||
|
||||
String mn = s[0];
|
||||
String pn = s[1];
|
||||
if (mn.isEmpty() || pn.isEmpty())
|
||||
fail(unableToParse(option, "<module>/<package>", key));
|
||||
fail(unableToParse(option, "<module>/<package>", key));
|
||||
|
||||
// The exporting module is in the boot layer
|
||||
Module m;
|
||||
@ -581,8 +600,10 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
if (allUnnamed) {
|
||||
if (opens) {
|
||||
builder.logAccessToOpenPackage(m, pn, option);
|
||||
Modules.addOpensToAllUnnamed(m, pn);
|
||||
} else {
|
||||
builder.logAccessToExportedPackage(m, pn, option);
|
||||
Modules.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
} else {
|
||||
@ -632,7 +653,7 @@ public final class ModuleBootstrap {
|
||||
|
||||
// value is <module>(,<module>)* or <file>(<pathsep><file>)*
|
||||
if (!allowDuplicates && map.containsKey(key))
|
||||
fail(key + " specified more than once in " + option(prefix));
|
||||
fail(key + " specified more than once to " + option(prefix));
|
||||
List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
|
||||
int ntargets = 0;
|
||||
for (String s : rhs.split(regex)) {
|
||||
@ -676,10 +697,6 @@ public final class ModuleBootstrap {
|
||||
ModuleReference mref = rm.reference();
|
||||
String mn = mref.descriptor().name();
|
||||
|
||||
// emit warning if module name ends with a non-Java letter
|
||||
if (!Checks.hasLegalModuleNameLastCharacter(mn))
|
||||
warn("Module name \"" + mn + "\" may soon be illegal");
|
||||
|
||||
// emit warning if the WARN_INCUBATING module resolution bit set
|
||||
if (ModuleResolution.hasIncubatingWarning(mref)) {
|
||||
if (incubating == null) {
|
||||
@ -705,7 +722,7 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
|
||||
static void warnUnknownModule(String option, String mn) {
|
||||
warn("Unknown module: " + mn + " specified in " + option);
|
||||
warn("Unknown module: " + mn + " specified to " + option);
|
||||
}
|
||||
|
||||
static String unableToParse(String option, String text, String value) {
|
||||
|
@ -89,18 +89,24 @@ public final class ModuleInfo {
|
||||
*/
|
||||
public static final class Attributes {
|
||||
private final ModuleDescriptor descriptor;
|
||||
private final ModuleTarget target;
|
||||
private final ModuleHashes recordedHashes;
|
||||
private final ModuleResolution moduleResolution;
|
||||
Attributes(ModuleDescriptor descriptor,
|
||||
ModuleTarget target,
|
||||
ModuleHashes recordedHashes,
|
||||
ModuleResolution moduleResolution) {
|
||||
this.descriptor = descriptor;
|
||||
this.target = target;
|
||||
this.recordedHashes = recordedHashes;
|
||||
this.moduleResolution = moduleResolution;
|
||||
}
|
||||
public ModuleDescriptor descriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
public ModuleTarget target() {
|
||||
return target;
|
||||
}
|
||||
public ModuleHashes recordedHashes() {
|
||||
return recordedHashes;
|
||||
}
|
||||
@ -221,8 +227,8 @@ public final class ModuleInfo {
|
||||
Builder builder = null;
|
||||
Set<String> allPackages = null;
|
||||
String mainClass = null;
|
||||
String[] osValues = null;
|
||||
ModuleHashes hashes = null;
|
||||
ModuleTarget moduleTarget = null;
|
||||
ModuleHashes moduelHashes = null;
|
||||
ModuleResolution moduleResolution = null;
|
||||
|
||||
for (int i = 0; i < attributes_count ; i++) {
|
||||
@ -251,12 +257,12 @@ public final class ModuleInfo {
|
||||
break;
|
||||
|
||||
case MODULE_TARGET :
|
||||
osValues = readModuleTargetAttribute(in, cpool);
|
||||
moduleTarget = readModuleTargetAttribute(in, cpool);
|
||||
break;
|
||||
|
||||
case MODULE_HASHES :
|
||||
if (parseHashes) {
|
||||
hashes = readModuleHashesAttribute(in, cpool);
|
||||
moduelHashes = readModuleHashesAttribute(in, cpool);
|
||||
} else {
|
||||
in.skipBytes(length);
|
||||
}
|
||||
@ -282,15 +288,10 @@ public final class ModuleInfo {
|
||||
throw invalidModuleDescriptor(MODULE + " attribute not found");
|
||||
}
|
||||
|
||||
// ModuleMainClass and ModuleTarget attributes
|
||||
// ModuleMainClass attribute
|
||||
if (mainClass != null) {
|
||||
builder.mainClass(mainClass);
|
||||
}
|
||||
if (osValues != null) {
|
||||
if (osValues[0] != null) builder.osName(osValues[0]);
|
||||
if (osValues[1] != null) builder.osArch(osValues[1]);
|
||||
if (osValues[2] != null) builder.osVersion(osValues[2]);
|
||||
}
|
||||
|
||||
// If the ModulePackages attribute is not present then the packageFinder
|
||||
// is used to find the set of packages
|
||||
@ -323,7 +324,10 @@ public final class ModuleInfo {
|
||||
}
|
||||
|
||||
ModuleDescriptor descriptor = builder.build();
|
||||
return new Attributes(descriptor, hashes, moduleResolution);
|
||||
return new Attributes(descriptor,
|
||||
moduleTarget,
|
||||
moduelHashes,
|
||||
moduleResolution);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -422,7 +426,11 @@ public final class ModuleInfo {
|
||||
Set<String> targets = new HashSet<>(exports_to_count);
|
||||
for (int j=0; j<exports_to_count; j++) {
|
||||
int exports_to_index = in.readUnsignedShort();
|
||||
targets.add(cpool.getModuleName(exports_to_index));
|
||||
String target = cpool.getModuleName(exports_to_index);
|
||||
if (!targets.add(target)) {
|
||||
throw invalidModuleDescriptor(pkg + " exported to "
|
||||
+ target + " more than once");
|
||||
}
|
||||
}
|
||||
builder.exports(mods, pkg, targets);
|
||||
} else {
|
||||
@ -458,7 +466,11 @@ public final class ModuleInfo {
|
||||
Set<String> targets = new HashSet<>(open_to_count);
|
||||
for (int j=0; j<open_to_count; j++) {
|
||||
int opens_to_index = in.readUnsignedShort();
|
||||
targets.add(cpool.getModuleName(opens_to_index));
|
||||
String target = cpool.getModuleName(opens_to_index);
|
||||
if (!targets.add(target)) {
|
||||
throw invalidModuleDescriptor(pkg + " opened to "
|
||||
+ target + " more than once");
|
||||
}
|
||||
}
|
||||
builder.opens(mods, pkg, targets);
|
||||
} else {
|
||||
@ -486,7 +498,10 @@ public final class ModuleInfo {
|
||||
for (int j=0; j<with_count; j++) {
|
||||
index = in.readUnsignedShort();
|
||||
String pn = cpool.getClassName(index);
|
||||
providers.add(pn);
|
||||
if (!providers.add(pn)) {
|
||||
throw invalidModuleDescriptor(sn + " provides " + pn
|
||||
+ " more than once");
|
||||
}
|
||||
}
|
||||
builder.provides(sn, providers);
|
||||
}
|
||||
@ -528,24 +543,21 @@ public final class ModuleInfo {
|
||||
/**
|
||||
* Reads the ModuleTarget attribute
|
||||
*/
|
||||
private String[] readModuleTargetAttribute(DataInput in, ConstantPool cpool)
|
||||
private ModuleTarget readModuleTargetAttribute(DataInput in, ConstantPool cpool)
|
||||
throws IOException
|
||||
{
|
||||
String[] values = new String[3];
|
||||
String osName = null;
|
||||
String osArch = null;
|
||||
|
||||
int name_index = in.readUnsignedShort();
|
||||
if (name_index != 0)
|
||||
values[0] = cpool.getUtf8(name_index);
|
||||
osName = cpool.getUtf8(name_index);
|
||||
|
||||
int arch_index = in.readUnsignedShort();
|
||||
if (arch_index != 0)
|
||||
values[1] = cpool.getUtf8(arch_index);
|
||||
osArch = cpool.getUtf8(arch_index);
|
||||
|
||||
int version_index = in.readUnsignedShort();
|
||||
if (version_index != 0)
|
||||
values[2] = cpool.getUtf8(version_index);
|
||||
|
||||
return values;
|
||||
return new ModuleTarget(osName, osArch);
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,7 +65,6 @@ public final class ModuleInfoExtender {
|
||||
// the values for the ModuleTarget attribute
|
||||
private String osName;
|
||||
private String osArch;
|
||||
private String osVersion;
|
||||
|
||||
// the hashes for the ModuleHashes attribute
|
||||
private ModuleHashes hashes;
|
||||
@ -111,12 +110,9 @@ public final class ModuleInfoExtender {
|
||||
/**
|
||||
* Sets the values for the ModuleTarget attribute.
|
||||
*/
|
||||
public ModuleInfoExtender targetPlatform(String osName,
|
||||
String osArch,
|
||||
String osVersion) {
|
||||
public ModuleInfoExtender targetPlatform(String osName, String osArch) {
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
this.osVersion = osVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -203,8 +199,8 @@ public final class ModuleInfoExtender {
|
||||
cv.addAttribute(new ModulePackagesAttribute(packages));
|
||||
if (mainClass != null)
|
||||
cv.addAttribute(new ModuleMainClassAttribute(mainClass));
|
||||
if (osName != null || osArch != null || osVersion != null)
|
||||
cv.addAttribute(new ModuleTargetAttribute(osName, osArch, osVersion));
|
||||
if (osName != null || osArch != null)
|
||||
cv.addAttribute(new ModuleTargetAttribute(osName, osArch));
|
||||
if (hashes != null)
|
||||
cv.addAttribute(new ModuleHashesAttribute(hashes));
|
||||
if (moduleResolution != null)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -48,7 +48,7 @@ public final class ModuleInfoWriter {
|
||||
* Writes the given module descriptor to a module-info.class file,
|
||||
* returning it in a byte array.
|
||||
*/
|
||||
private static byte[] toModuleInfo(ModuleDescriptor md) {
|
||||
private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(Opcodes.V1_9, ACC_MODULE, "module-info", null, null, null);
|
||||
cw.visitAttribute(new ModuleAttribute(md));
|
||||
@ -66,18 +66,29 @@ public final class ModuleInfoWriter {
|
||||
// write ModuleMainClass if the module has a main class
|
||||
md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
|
||||
|
||||
// write ModuleTarget attribute if have any of OS name/arch/version
|
||||
String osName = md.osName().orElse(null);
|
||||
String osArch = md.osArch().orElse(null);
|
||||
String osVersion = md.osVersion().orElse(null);
|
||||
if (osName != null || osArch != null || osVersion != null) {
|
||||
cw.visitAttribute(new ModuleTargetAttribute(osName, osArch, osVersion));
|
||||
// write ModuleTarget if there is a platform OS/arch
|
||||
if (target != null) {
|
||||
cw.visitAttribute(new ModuleTargetAttribute(target.osName(),
|
||||
target.osArch()));
|
||||
}
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a module descriptor to the given output stream as a
|
||||
* module-info.class.
|
||||
*/
|
||||
public static void write(ModuleDescriptor descriptor,
|
||||
ModuleTarget target,
|
||||
OutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
byte[] bytes = toModuleInfo(descriptor, target);
|
||||
out.write(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a module descriptor to the given output stream as a
|
||||
* module-info.class.
|
||||
@ -85,8 +96,7 @@ public final class ModuleInfoWriter {
|
||||
public static void write(ModuleDescriptor descriptor, OutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
byte[] bytes = toModuleInfo(descriptor);
|
||||
out.write(bytes);
|
||||
write(descriptor, null, out);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +104,7 @@ public final class ModuleInfoWriter {
|
||||
* in module-info.class format.
|
||||
*/
|
||||
public static ByteBuffer toByteBuffer(ModuleDescriptor descriptor) {
|
||||
byte[] bytes = toModuleInfo(descriptor);
|
||||
byte[] bytes = toModuleInfo(descriptor, null);
|
||||
return ByteBuffer.wrap(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.Resource;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import sun.net.www.ParseUtil;
|
||||
@ -165,9 +164,6 @@ public final class ModulePatcher {
|
||||
|
||||
descriptor.version().ifPresent(builder::version);
|
||||
descriptor.mainClass().ifPresent(builder::mainClass);
|
||||
descriptor.osName().ifPresent(builder::osName);
|
||||
descriptor.osArch().ifPresent(builder::osArch);
|
||||
descriptor.osVersion().ifPresent(builder::osVersion);
|
||||
|
||||
// original + new packages
|
||||
builder.packages(descriptor.packages());
|
||||
@ -179,10 +175,12 @@ public final class ModulePatcher {
|
||||
// return a module reference to the patched module
|
||||
URI location = mref.location().orElse(null);
|
||||
|
||||
ModuleTarget target = null;
|
||||
ModuleHashes recordedHashes = null;
|
||||
ModuleResolution mres = null;
|
||||
if (mref instanceof ModuleReferenceImpl) {
|
||||
ModuleReferenceImpl impl = (ModuleReferenceImpl)mref;
|
||||
target = impl.moduleTarget();
|
||||
recordedHashes = impl.recordedHashes();
|
||||
mres = impl.moduleResolution();
|
||||
}
|
||||
@ -191,6 +189,7 @@ public final class ModulePatcher {
|
||||
location,
|
||||
() -> new PatchedModuleReader(paths, mref),
|
||||
this,
|
||||
target,
|
||||
recordedHashes,
|
||||
null,
|
||||
mres);
|
||||
@ -226,7 +225,7 @@ public final class ModulePatcher {
|
||||
private volatile ModuleReader delegate;
|
||||
|
||||
/**
|
||||
* Creates the ModuleReader to reads resources a patched module.
|
||||
* Creates the ModuleReader to reads resources in a patched module.
|
||||
*/
|
||||
PatchedModuleReader(List<Path> patches, ModuleReference mref) {
|
||||
List<ResourceFinder> finders = new ArrayList<>();
|
||||
@ -291,13 +290,16 @@ public final class ModulePatcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a resources in the patch locations. Returns null if not found.
|
||||
* Finds a resources in the patch locations. Returns null if not found
|
||||
* or the name is "module-info.class" as that cannot be overridden.
|
||||
*/
|
||||
private Resource findResourceInPatch(String name) throws IOException {
|
||||
for (ResourceFinder finder : finders) {
|
||||
Resource r = finder.find(name);
|
||||
if (r != null)
|
||||
return r;
|
||||
if (!name.equals("module-info.class")) {
|
||||
for (ResourceFinder finder : finders) {
|
||||
Resource r = finder.find(name);
|
||||
if (r != null)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -478,9 +480,7 @@ public final class ModulePatcher {
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
return jf.stream()
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(JarEntry::getName);
|
||||
return jf.stream().map(JarEntry::getName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,14 +500,12 @@ public final class ModulePatcher {
|
||||
|
||||
@Override
|
||||
public Resource find(String name) throws IOException {
|
||||
Path path = ResourceHelper.toFilePath(name);
|
||||
if (path != null) {
|
||||
Path file = dir.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
return newResource(name, dir, file);
|
||||
}
|
||||
Path file = Resources.toFilePath(dir, name);
|
||||
if (file != null) {
|
||||
return newResource(name, dir, file);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Resource newResource(String name, Path top, Path file) {
|
||||
@ -550,11 +548,9 @@ public final class ModulePatcher {
|
||||
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
return Files.find(dir, Integer.MAX_VALUE,
|
||||
(path, attrs) -> attrs.isRegularFile())
|
||||
.map(f -> dir.relativize(f)
|
||||
.toString()
|
||||
.replace(File.separatorChar, '/'));
|
||||
return Files.walk(dir, Integer.MAX_VALUE)
|
||||
.map(f -> Resources.toResourceName(dir, f))
|
||||
.filter(s -> s.length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,7 @@ public class ModulePath implements ModuleFinder {
|
||||
String pn = packageName(cn);
|
||||
if (!packages.contains(pn)) {
|
||||
String msg = "Provider class " + cn + " not in module";
|
||||
throw new IOException(msg);
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
providerClasses.add(cn);
|
||||
}
|
||||
@ -533,7 +533,7 @@ public class ModulePath implements ModuleFinder {
|
||||
String pn = packageName(mainClass);
|
||||
if (!packages.contains(pn)) {
|
||||
String msg = "Main-Class " + mainClass + " not in module";
|
||||
throw new IOException(msg);
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
builder.mainClass(mainClass);
|
||||
}
|
||||
@ -609,11 +609,10 @@ public class ModulePath implements ModuleFinder {
|
||||
// no module-info.class so treat it as automatic module
|
||||
try {
|
||||
ModuleDescriptor md = deriveModuleDescriptor(jf);
|
||||
attrs = new ModuleInfo.Attributes(md, null, null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FindException(
|
||||
"Unable to derive module descriptor for: "
|
||||
+ jf.getName(), e);
|
||||
attrs = new ModuleInfo.Attributes(md, null, null, null);
|
||||
} catch (RuntimeException e) {
|
||||
throw new FindException("Unable to derive module descriptor for "
|
||||
+ jf.getName(), e);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -672,18 +671,18 @@ public class ModulePath implements ModuleFinder {
|
||||
/**
|
||||
* Maps the name of an entry in a JAR or ZIP file to a package name.
|
||||
*
|
||||
* @throws IllegalArgumentException if the name is a class file in
|
||||
* the top-level directory of the JAR/ZIP file (and it's
|
||||
* not module-info.class)
|
||||
* @throws InvalidModuleDescriptorException if the name is a class file in
|
||||
* the top-level directory of the JAR/ZIP file (and it's not
|
||||
* module-info.class)
|
||||
*/
|
||||
private Optional<String> toPackageName(String name) {
|
||||
assert !name.endsWith("/");
|
||||
int index = name.lastIndexOf("/");
|
||||
if (index == -1) {
|
||||
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
|
||||
throw new IllegalArgumentException(name
|
||||
+ " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)");
|
||||
String msg = name + " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)";
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -701,8 +700,8 @@ public class ModulePath implements ModuleFinder {
|
||||
* Maps the relative path of an entry in an exploded module to a package
|
||||
* name.
|
||||
*
|
||||
* @throws IllegalArgumentException if the name is a class file in
|
||||
* the top-level directory (and it's not module-info.class)
|
||||
* @throws InvalidModuleDescriptorException if the name is a class file in
|
||||
* the top-level directory (and it's not module-info.class)
|
||||
*/
|
||||
private Optional<String> toPackageName(Path file) {
|
||||
assert file.getRoot() == null;
|
||||
@ -711,9 +710,9 @@ public class ModulePath implements ModuleFinder {
|
||||
if (parent == null) {
|
||||
String name = file.toString();
|
||||
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
|
||||
throw new IllegalArgumentException(name
|
||||
+ " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)");
|
||||
String msg = name + " found in top-level directory"
|
||||
+ " (unnamed package not allowed in module)";
|
||||
throw new InvalidModuleDescriptorException(msg);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
// non-null if the module is patched
|
||||
private final ModulePatcher patcher;
|
||||
|
||||
// ModuleTarget if the module is OS/architecture specific
|
||||
private final ModuleTarget target;
|
||||
|
||||
// the hashes of other modules recorded in this module
|
||||
private final ModuleHashes recordedHashes;
|
||||
|
||||
@ -65,6 +68,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier,
|
||||
ModulePatcher patcher,
|
||||
ModuleTarget target,
|
||||
ModuleHashes recordedHashes,
|
||||
ModuleHashes.HashSupplier hasher,
|
||||
ModuleResolution moduleResolution)
|
||||
@ -72,6 +76,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
super(descriptor, Objects.requireNonNull(location));
|
||||
this.readerSupplier = readerSupplier;
|
||||
this.patcher = patcher;
|
||||
this.target = target;
|
||||
this.recordedHashes = recordedHashes;
|
||||
this.hasher = hasher;
|
||||
this.moduleResolution = moduleResolution;
|
||||
@ -93,6 +98,13 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
return (patcher != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ModuleTarget or {@code null} if the no target platform.
|
||||
*/
|
||||
public ModuleTarget moduleTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashes recorded in this module or {@code null} if there
|
||||
* are no hashes recorded.
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -50,7 +49,6 @@ import java.util.stream.Stream;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ModuleHashes.HashSupplier;
|
||||
import jdk.internal.util.jar.VersionedStream;
|
||||
@ -78,6 +76,7 @@ class ModuleReferences {
|
||||
uri,
|
||||
supplier,
|
||||
null,
|
||||
attrs.target(),
|
||||
attrs.recordedHashes(),
|
||||
hasher,
|
||||
attrs.moduleResolution());
|
||||
@ -242,8 +241,7 @@ class ModuleReferences {
|
||||
}
|
||||
|
||||
private JarEntry getEntry(String name) {
|
||||
JarEntry entry = jf.getJarEntry(Objects.requireNonNull(name));
|
||||
return (entry == null || entry.isDirectory()) ? null : entry;
|
||||
return jf.getJarEntry(Objects.requireNonNull(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -252,6 +250,8 @@ class ModuleReferences {
|
||||
if (je != null) {
|
||||
if (jf.isMultiRelease())
|
||||
name = SharedSecrets.javaUtilJarAccess().getRealName(jf, je);
|
||||
if (je.isDirectory() && !name.endsWith("/"))
|
||||
name += "/";
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
String uris = "jar:" + uri + "!/" + encodedPath;
|
||||
return Optional.of(URI.create(uris));
|
||||
@ -274,7 +274,6 @@ class ModuleReferences {
|
||||
Stream<String> implList() throws IOException {
|
||||
// take snapshot to avoid async close
|
||||
List<String> names = VersionedStream.stream(jf)
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(JarEntry::getName)
|
||||
.collect(Collectors.toList());
|
||||
return names.stream();
|
||||
@ -316,6 +315,8 @@ class ModuleReferences {
|
||||
Optional<URI> implFind(String name) {
|
||||
JmodFile.Entry je = getEntry(name);
|
||||
if (je != null) {
|
||||
if (je.isDirectory() && !name.endsWith("/"))
|
||||
name += "/";
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
String uris = "jmod:" + uri + "!/" + encodedPath;
|
||||
return Optional.of(URI.create(uris));
|
||||
@ -376,26 +377,10 @@ class ModuleReferences {
|
||||
if (closed) throw new IOException("ModuleReader is closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Path to access the given resource. Returns null if the
|
||||
* resource name does not convert to a file path that locates a regular
|
||||
* file in the module.
|
||||
*/
|
||||
private Path toFilePath(String name) {
|
||||
Path path = ResourceHelper.toFilePath(name);
|
||||
if (path != null) {
|
||||
Path file = dir.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = toFilePath(name);
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
try {
|
||||
return Optional.of(path.toUri());
|
||||
@ -410,7 +395,7 @@ class ModuleReferences {
|
||||
@Override
|
||||
public Optional<InputStream> open(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = toFilePath(name);
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
return Optional.of(Files.newInputStream(path));
|
||||
} else {
|
||||
@ -421,7 +406,7 @@ class ModuleReferences {
|
||||
@Override
|
||||
public Optional<ByteBuffer> read(String name) throws IOException {
|
||||
ensureOpen();
|
||||
Path path = toFilePath(name);
|
||||
Path path = Resources.toFilePath(dir, name);
|
||||
if (path != null) {
|
||||
return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path)));
|
||||
} else {
|
||||
@ -432,12 +417,9 @@ class ModuleReferences {
|
||||
@Override
|
||||
public Stream<String> list() throws IOException {
|
||||
ensureOpen();
|
||||
// sym links not followed
|
||||
return Files.find(dir, Integer.MAX_VALUE,
|
||||
(path, attrs) -> attrs.isRegularFile())
|
||||
.map(f -> dir.relativize(f)
|
||||
.toString()
|
||||
.replace(File.separatorChar, '/'));
|
||||
return Files.walk(dir, Integer.MAX_VALUE)
|
||||
.map(f -> Resources.toResourceName(dir, f))
|
||||
.filter(s -> s.length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
public final class ModuleTarget {
|
||||
|
||||
private final String osName;
|
||||
private final String osArch;
|
||||
|
||||
public ModuleTarget(String osName, String osArch) {
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
}
|
||||
|
||||
public String osName() {
|
||||
return osName;
|
||||
}
|
||||
|
||||
public String osArch() {
|
||||
return osArch;
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,6 @@ import java.lang.reflect.Module;
|
||||
import java.net.URI;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.ClassLoaders;
|
||||
@ -39,10 +38,10 @@ import jdk.internal.misc.JavaLangReflectModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
|
||||
/**
|
||||
* A helper class to allow JDK classes create dynamic modules and to update
|
||||
* modules, exports and the readability graph. It is also invoked by the VM
|
||||
* to add read edges when agents are instrumenting code that need to link
|
||||
* to supporting classes.
|
||||
* A helper class for creating and updating modules. This class is intended to
|
||||
* support command-line options, tests, and the instrumentation API. It is also
|
||||
* used by the VM to add read edges when agents are instrumenting code that
|
||||
* need to link to supporting classes.
|
||||
*
|
||||
* The parameters that are package names in this API are the fully-qualified
|
||||
* names of the packages as defined in section 6.5.3 of <cite>The Java™
|
||||
@ -72,25 +71,7 @@ public class Modules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a new module to the VM. The module has the given set of
|
||||
* packages and is defined to the given class loader.
|
||||
*
|
||||
* The resulting Module is in a larval state in that it does not not read
|
||||
* any other module and does not have any exports.
|
||||
*/
|
||||
public static Module defineModule(ClassLoader loader,
|
||||
String name,
|
||||
Set<String> packages)
|
||||
{
|
||||
ModuleDescriptor descriptor = ModuleDescriptor.newModule(name)
|
||||
.packages(packages)
|
||||
.build();
|
||||
|
||||
return JLRMA.defineModule(loader, descriptor, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a read-edge so that module {@code m1} reads module {@code m1}.
|
||||
* Updates m1 to read m2.
|
||||
* Same as m1.addReads(m2) but without a caller check.
|
||||
*/
|
||||
public static void addReads(Module m1, Module m2) {
|
||||
@ -98,20 +79,45 @@ public class Modules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module {@code m} to read all unnamed modules.
|
||||
* Update module m to read all unnamed modules.
|
||||
*/
|
||||
public static void addReadsAllUnnamed(Module m) {
|
||||
JLRMA.addReadsAllUnnamed(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module m to export a package to all modules.
|
||||
*
|
||||
* This method is for intended for use by tests only.
|
||||
*/
|
||||
public static void addExports(Module m, String pn) {
|
||||
JLRMA.addExports(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m1 to export a package to module m2.
|
||||
* Same as m1.addExports(pn, m2) but without a caller check.
|
||||
* Same as m1.addExports(pn, m2) but without a caller check
|
||||
*/
|
||||
public static void addExports(Module m1, String pn, Module m2) {
|
||||
JLRMA.addExports(m1, pn, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to export a package to all unnamed modules.
|
||||
*/
|
||||
public static void addExportsToAllUnnamed(Module m, String pn) {
|
||||
JLRMA.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module m to open a package to all modules.
|
||||
*
|
||||
* This method is for intended for use by tests only.
|
||||
*/
|
||||
public static void addOpens(Module m, String pn) {
|
||||
JLRMA.addOpens(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m1 to open a package to module m2.
|
||||
* Same as m1.addOpens(pn, m2) but without a caller check.
|
||||
@ -120,27 +126,6 @@ public class Modules {
|
||||
JLRMA.addOpens(m1, pn, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a module m to export a package to all modules.
|
||||
*/
|
||||
public static void addExportsToAll(Module m, String pn) {
|
||||
JLRMA.addExportsToAll(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a module m to open a package to all modules.
|
||||
*/
|
||||
public static void addOpensToAll(Module m, String pn) {
|
||||
JLRMA.addOpensToAll(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to export a package to all unnamed modules.
|
||||
*/
|
||||
public static void addExportsToAllUnnamed(Module m, String pn) {
|
||||
JLRMA.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to open a package to all unnamed modules.
|
||||
*/
|
||||
@ -149,7 +134,8 @@ public class Modules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m to use a service
|
||||
* Updates module m to use a service.
|
||||
* Same as m2.addUses(service) but without a caller check.
|
||||
*/
|
||||
public static void addUses(Module m, Class<?> service) {
|
||||
JLRMA.addUses(m, service);
|
||||
@ -182,16 +168,6 @@ public class Modules {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package to a module's content.
|
||||
*
|
||||
* This method is a no-op if the module already contains the package or the
|
||||
* module is an unnamed module.
|
||||
*/
|
||||
public static void addPackage(Module m, String pn) {
|
||||
JLRMA.addPackage(m, pn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the VM when code in the given Module has been transformed by
|
||||
* an agent and so may have been instrumented to call into supporting
|
||||
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* A helper class to support working with resources in modules. Also provides
|
||||
* support for translating resource names to file paths.
|
||||
*/
|
||||
public final class Resources {
|
||||
private Resources() { }
|
||||
|
||||
/**
|
||||
* Return true if a resource can be encapsulated. Resource with names
|
||||
* ending in ".class" or "/" cannot be encapsulated. Resource names
|
||||
* that map to a legal package name can be encapsulated.
|
||||
*/
|
||||
public static boolean canEncapsulate(String name) {
|
||||
int len = name.length();
|
||||
if (len > 6 && name.endsWith(".class")) {
|
||||
return false;
|
||||
} else {
|
||||
return Checks.isPackageName(toPackageName(name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a <em>package name</em> for a resource. The package name
|
||||
* returned by this method may not be a legal package name. This method
|
||||
* returns null if the the resource name ends with a "/" (a directory)
|
||||
* or the resource name does not contain a "/".
|
||||
*/
|
||||
public static String toPackageName(String name) {
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index == -1 || index == name.length()-1) {
|
||||
return "";
|
||||
} else {
|
||||
return name.substring(0, index).replace("/", ".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource name corresponding to the relative file path
|
||||
* between {@code dir} and {@code file}. If the file is a directory
|
||||
* then the name will end with a "/", except the top-level directory
|
||||
* where the empty string is returned.
|
||||
*/
|
||||
public static String toResourceName(Path dir, Path file) {
|
||||
String s = dir.relativize(file)
|
||||
.toString()
|
||||
.replace(File.separatorChar, '/');
|
||||
if (s.length() > 0 && Files.isDirectory(file))
|
||||
s += "/";
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file path to a resource in a file tree. If the resource
|
||||
* name has a trailing "/" then the file path will locate a directory.
|
||||
* Returns {@code null} if the resource does not map to a file in the
|
||||
* tree file.
|
||||
*/
|
||||
public static Path toFilePath(Path dir, String name) throws IOException {
|
||||
boolean expectDirectory = name.endsWith("/");
|
||||
if (expectDirectory) {
|
||||
name = name.substring(0, name.length() - 1); // drop trailing "/"
|
||||
}
|
||||
Path path = toSafeFilePath(name);
|
||||
if (path != null) {
|
||||
Path file = dir.resolve(path);
|
||||
try {
|
||||
BasicFileAttributes attrs;
|
||||
attrs = Files.readAttributes(file, BasicFileAttributes.class);
|
||||
if (attrs.isDirectory()
|
||||
|| (!attrs.isDirectory() && !expectDirectory))
|
||||
return file;
|
||||
} catch (NoSuchFileException ignore) { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a resource name to a "safe" file path. Returns {@code null} if
|
||||
* the resource name cannot be converted into a "safe" file path.
|
||||
*
|
||||
* Resource names with empty elements, or elements that are "." or ".."
|
||||
* are rejected, as are resource names that translates to a file path
|
||||
* with a root component.
|
||||
*/
|
||||
private static Path toSafeFilePath(String name) {
|
||||
// scan elements of resource name
|
||||
int next;
|
||||
int off = 0;
|
||||
while ((next = name.indexOf('/', off)) != -1) {
|
||||
int len = next - off;
|
||||
if (!mayTranslate(name, off, len)) {
|
||||
return null;
|
||||
}
|
||||
off = next + 1;
|
||||
}
|
||||
int rem = name.length() - off;
|
||||
if (!mayTranslate(name, off, rem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// convert to file path
|
||||
Path path;
|
||||
if (File.separatorChar == '/') {
|
||||
path = Paths.get(name);
|
||||
} else {
|
||||
// not allowed to embed file separators
|
||||
if (name.contains(File.separator))
|
||||
return null;
|
||||
path = Paths.get(name.replace('/', File.separatorChar));
|
||||
}
|
||||
|
||||
// file path not allowed to have root component
|
||||
return (path.getRoot() == null) ? path : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the element in a resource name is a candidate
|
||||
* to translate to the element of a file path.
|
||||
*/
|
||||
private static boolean mayTranslate(String name, int off, int len) {
|
||||
if (len <= 2) {
|
||||
if (len == 0)
|
||||
return false;
|
||||
boolean starsWithDot = (name.charAt(off) == '.');
|
||||
if (len == 1 && starsWithDot)
|
||||
return false;
|
||||
if (len == 2 && starsWithDot && (name.charAt(off+1) == '.'))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -150,18 +150,21 @@ public class SystemModuleFinder implements ModuleFinder {
|
||||
System.getProperty("jdk.system.module.finder.disabledFastPath") != null;
|
||||
|
||||
ModuleDescriptor[] descriptors;
|
||||
ModuleTarget[] targets;
|
||||
ModuleHashes[] recordedHashes;
|
||||
ModuleResolution[] moduleResolutions;
|
||||
|
||||
// fast loading of ModuleDescriptor of system modules
|
||||
if (isFastPathSupported() && !disabled) {
|
||||
descriptors = SystemModules.descriptors();
|
||||
targets = SystemModules.targets();
|
||||
recordedHashes = SystemModules.hashes();
|
||||
moduleResolutions = SystemModules.moduleResolutions();
|
||||
} else {
|
||||
// if fast loading of ModuleDescriptors is disabled
|
||||
// fallback to read module-info.class
|
||||
descriptors = new ModuleDescriptor[n];
|
||||
targets = new ModuleTarget[n];
|
||||
recordedHashes = new ModuleHashes[n];
|
||||
moduleResolutions = new ModuleResolution[n];
|
||||
ImageReader imageReader = SystemImage.reader();
|
||||
@ -171,6 +174,7 @@ public class SystemModuleFinder implements ModuleFinder {
|
||||
ModuleInfo.Attributes attrs =
|
||||
ModuleInfo.read(imageReader.getResourceBuffer(loc), null);
|
||||
descriptors[i] = attrs.descriptor();
|
||||
targets[i] = attrs.target();
|
||||
recordedHashes[i] = attrs.recordedHashes();
|
||||
moduleResolutions[i] = attrs.moduleResolution();
|
||||
}
|
||||
@ -206,6 +210,7 @@ public class SystemModuleFinder implements ModuleFinder {
|
||||
|
||||
// create the ModuleReference
|
||||
ModuleReference mref = toModuleReference(md,
|
||||
targets[i],
|
||||
recordedHashes[i],
|
||||
hashSupplier(names[i]),
|
||||
moduleResolutions[i]);
|
||||
@ -233,6 +238,7 @@ public class SystemModuleFinder implements ModuleFinder {
|
||||
}
|
||||
|
||||
private ModuleReference toModuleReference(ModuleDescriptor md,
|
||||
ModuleTarget target,
|
||||
ModuleHashes recordedHashes,
|
||||
HashSupplier hasher,
|
||||
ModuleResolution mres) {
|
||||
@ -246,9 +252,14 @@ public class SystemModuleFinder implements ModuleFinder {
|
||||
}
|
||||
};
|
||||
|
||||
ModuleReference mref =
|
||||
new ModuleReferenceImpl(md, uri, readerSupplier, null,
|
||||
recordedHashes, hasher, mres);
|
||||
ModuleReference mref = new ModuleReferenceImpl(md,
|
||||
uri,
|
||||
readerSupplier,
|
||||
null,
|
||||
target,
|
||||
recordedHashes,
|
||||
hasher,
|
||||
mres);
|
||||
|
||||
// may need a reference to a patched module if --patch-module specified
|
||||
mref = ModuleBootstrap.patcher().patchIfNeeded(mref);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,7 @@ package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
|
||||
/*
|
||||
/**
|
||||
* SystemModules class will be generated at link time to create
|
||||
* ModuleDescriptor for the system modules directly to improve
|
||||
* the module descriptor reconstitution time.
|
||||
@ -65,7 +65,7 @@ public final class SystemModules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty array of ModuleDescriptors in the run-time image.
|
||||
* Returns a non-empty array of ModuleDescriptor objects in the run-time image.
|
||||
*
|
||||
* When running an exploded image it returns an empty array.
|
||||
*/
|
||||
@ -73,6 +73,15 @@ public final class SystemModules {
|
||||
throw new InternalError("expected to be overridden at link time");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty array of ModuleTarget objects in the run-time image.
|
||||
*
|
||||
* When running an exploded image it returns an empty array.
|
||||
*/
|
||||
public static ModuleTarget[] targets() {
|
||||
throw new InternalError("expected to be overridden at link time");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty array of ModuleHashes recorded in each module
|
||||
* in the run-time image.
|
||||
|
@ -2508,7 +2508,7 @@ public class ClassReader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a CONSTANT_Pakcage_info item in {@code b}. This method is
|
||||
* Reads a CONSTANT_Package_info item in {@code b}. This method is
|
||||
* intended for {@link Attribute} sub slasses, and is normally not needed
|
||||
* by class generators or adapters.</i>
|
||||
*
|
||||
|
@ -31,9 +31,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/** Common utility routines used by both java.lang and
|
||||
java.lang.reflect */
|
||||
@ -104,39 +102,40 @@ public class Reflection {
|
||||
int modifiers)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
if (currentClass == null || memberClass == null) {
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
|
||||
throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
|
||||
throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify access to a member, returning {@code false} if no access
|
||||
* Verify access to a member and return {@code true} if it is granted.
|
||||
*
|
||||
* @param currentClass the class performing the access
|
||||
* @param memberClass the declaring class of the member being accessed
|
||||
* @param targetClass the class of target object if accessing instance
|
||||
* field or method;
|
||||
* or the declaring class if accessing constructor;
|
||||
* or null if accessing static field or method
|
||||
* @param modifiers the member's access modifiers
|
||||
* @return {@code true} if access to member is granted
|
||||
*/
|
||||
public static boolean verifyMemberAccess(Class<?> currentClass,
|
||||
Class<?> memberClass,
|
||||
Class<?> targetClass,
|
||||
int modifiers)
|
||||
{
|
||||
// Verify that currentClass can access a field, method, or
|
||||
// constructor of memberClass, where that member's access bits are
|
||||
// "modifiers".
|
||||
|
||||
boolean gotIsSameClassPackage = false;
|
||||
boolean isSameClassPackage = false;
|
||||
|
||||
if (currentClass == memberClass) {
|
||||
// Always succeeds
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!verifyModuleAccess(currentClass, memberClass)) {
|
||||
if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean gotIsSameClassPackage = false;
|
||||
boolean isSameClassPackage = false;
|
||||
|
||||
if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
|
||||
isSameClassPackage = isSameClassPackage(currentClass, memberClass);
|
||||
gotIsSameClassPackage = true;
|
||||
@ -196,31 +195,20 @@ public class Reflection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if memberClass's's module exports memberClass's
|
||||
* package to currentClass's module.
|
||||
* Returns {@code true} if memberClass's module exports memberClass's
|
||||
* package to currentModule.
|
||||
*/
|
||||
public static boolean verifyModuleAccess(Class<?> currentClass,
|
||||
Class<?> memberClass) {
|
||||
return verifyModuleAccess(currentClass.getModule(), memberClass);
|
||||
}
|
||||
|
||||
public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
|
||||
Module memberModule = memberClass.getModule();
|
||||
|
||||
// module may be null during startup (initLevel 0)
|
||||
if (currentModule == memberModule)
|
||||
return true; // same module (named or unnamed)
|
||||
|
||||
String pkg = memberClass.getPackageName();
|
||||
boolean allowed = memberModule.isExported(pkg, currentModule);
|
||||
if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) {
|
||||
if (!SharedSecrets.getJavaLangReflectModuleAccess()
|
||||
.isStaticallyExported(memberModule, pkg, currentModule)) {
|
||||
String msg = currentModule + " allowed access to member of " + memberClass;
|
||||
new Exception(msg).printStackTrace(System.err);
|
||||
}
|
||||
if (currentModule == memberModule) {
|
||||
// same module (named or unnamed) or both null if called
|
||||
// before module system is initialized, which means we are
|
||||
// dealing with java.base only.
|
||||
return true;
|
||||
} else {
|
||||
String pkg = memberClass.getPackageName();
|
||||
return memberModule.isExported(pkg, currentModule);
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,46 +332,14 @@ public class Reflection {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// true to print a stack trace when access fails
|
||||
private static volatile boolean printStackWhenAccessFails;
|
||||
|
||||
// true to print a stack trace when access succeeds
|
||||
private static volatile boolean printStackWhenAccessSucceeds;
|
||||
|
||||
// true if printStack* values are initialized
|
||||
private static volatile boolean printStackPropertiesSet;
|
||||
|
||||
private static void ensurePrintStackPropertiesSet() {
|
||||
if (!printStackPropertiesSet && VM.initLevel() >= 1) {
|
||||
String s = GetPropertyAction.privilegedGetProperty(
|
||||
"sun.reflect.debugModuleAccessChecks");
|
||||
if (s != null) {
|
||||
printStackWhenAccessFails = !s.equalsIgnoreCase("false");
|
||||
printStackWhenAccessSucceeds = s.equalsIgnoreCase("access");
|
||||
}
|
||||
printStackPropertiesSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean printStackTraceWhenAccessFails() {
|
||||
ensurePrintStackPropertiesSet();
|
||||
return printStackWhenAccessFails;
|
||||
}
|
||||
|
||||
public static boolean printStackTraceWhenAccessSucceeds() {
|
||||
ensurePrintStackPropertiesSet();
|
||||
return printStackWhenAccessSucceeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IllegalAccessException with the an exception message based on
|
||||
* Returns an IllegalAccessException with an exception message based on
|
||||
* the access that is denied.
|
||||
*/
|
||||
private static void throwIllegalAccessException(Class<?> currentClass,
|
||||
Class<?> memberClass,
|
||||
Object target,
|
||||
int modifiers)
|
||||
public static IllegalAccessException newIllegalAccessException(Class<?> currentClass,
|
||||
Class<?> memberClass,
|
||||
Class<?> targetClass,
|
||||
int modifiers)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
String currentSuffix = "";
|
||||
@ -411,20 +367,6 @@ public class Reflection {
|
||||
if (m2.isNamed()) msg += " to " + m1;
|
||||
}
|
||||
|
||||
throwIllegalAccessException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws IllegalAccessException with the given exception message.
|
||||
*/
|
||||
public static void throwIllegalAccessException(String msg)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
IllegalAccessException e = new IllegalAccessException(msg);
|
||||
ensurePrintStackPropertiesSet();
|
||||
if (printStackWhenAccessFails) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
throw e;
|
||||
return new IllegalAccessException(msg);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -44,6 +44,7 @@ import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
@ -585,17 +586,10 @@ public class ReflectionFactory {
|
||||
private static void checkInitted() {
|
||||
if (initted) return;
|
||||
|
||||
// Tests to ensure the system properties table is fully
|
||||
// initialized. This is needed because reflection code is
|
||||
// called very early in the initialization process (before
|
||||
// command-line arguments have been parsed and therefore
|
||||
// these user-settable properties installed.) We assume that
|
||||
// if System.out is non-null then the System class has been
|
||||
// fully initialized and that the bulk of the startup code
|
||||
// has been run.
|
||||
|
||||
if (System.out == null) {
|
||||
// java.lang.System not yet fully initialized
|
||||
// Defer initialization until module system is initialized so as
|
||||
// to avoid inflation and spinning bytecode in unnamed modules
|
||||
// during early startup.
|
||||
if (!VM.isModuleSystemInited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,10 +125,9 @@ module java.base {
|
||||
jdk.jlink;
|
||||
exports jdk.internal.loader to
|
||||
java.instrument,
|
||||
java.logging,
|
||||
jdk.jlink;
|
||||
java.logging;
|
||||
exports jdk.internal.jmod to
|
||||
jdk.compiler,
|
||||
jdk.compiler, // reflective dependency
|
||||
jdk.jlink;
|
||||
exports jdk.internal.logger to
|
||||
java.logging;
|
||||
@ -140,10 +139,7 @@ module java.base {
|
||||
exports jdk.internal.org.objectweb.asm.tree to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.org.objectweb.asm.util to
|
||||
jdk.jlink,
|
||||
jdk.scripting.nashorn;
|
||||
exports jdk.internal.org.objectweb.asm.tree.analysis to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.org.objectweb.asm.commons to
|
||||
jdk.scripting.nashorn;
|
||||
exports jdk.internal.org.objectweb.asm.signature to
|
||||
@ -157,7 +153,6 @@ module java.base {
|
||||
jdk.jlink;
|
||||
exports jdk.internal.misc to
|
||||
java.desktop,
|
||||
jdk.incubator.httpclient,
|
||||
java.logging,
|
||||
java.management,
|
||||
java.naming,
|
||||
@ -166,8 +161,8 @@ module java.base {
|
||||
java.sql,
|
||||
java.xml,
|
||||
jdk.charsets,
|
||||
jdk.compiler,
|
||||
jdk.jartool,
|
||||
jdk.compiler, // reflective dependency
|
||||
jdk.incubator.httpclient,
|
||||
jdk.jdeps,
|
||||
jdk.jlink,
|
||||
jdk.jshell,
|
||||
@ -210,11 +205,10 @@ module java.base {
|
||||
jdk.naming.dns;
|
||||
exports sun.net.util to
|
||||
java.desktop,
|
||||
jdk.jconsole,
|
||||
jdk.naming.dns;
|
||||
jdk.jconsole;
|
||||
exports sun.net.www to
|
||||
jdk.incubator.httpclient,
|
||||
java.desktop,
|
||||
jdk.incubator.httpclient,
|
||||
jdk.jartool;
|
||||
exports sun.net.www.protocol.http to
|
||||
java.security.jgss;
|
||||
|
@ -85,6 +85,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.module.Modules;
|
||||
|
||||
|
||||
@ -428,14 +429,20 @@ public final class LauncherHelper {
|
||||
abort(null, "java.launcher.jar.error3", jarname);
|
||||
}
|
||||
|
||||
// Add-Exports and Add-Opens to break encapsulation
|
||||
// Add-Exports and Add-Opens to allow illegal access
|
||||
String exports = mainAttrs.getValue(ADD_EXPORTS);
|
||||
if (exports != null) {
|
||||
addExportsOrOpens(exports, false);
|
||||
String warn = getLocalizedMessage("java.launcher.permitaccess.warning",
|
||||
jarname, ADD_EXPORTS);
|
||||
System.err.println(warn);
|
||||
addExportsOrOpens(exports, false, ADD_EXPORTS);
|
||||
}
|
||||
String opens = mainAttrs.getValue(ADD_OPENS);
|
||||
if (opens != null) {
|
||||
addExportsOrOpens(opens, true);
|
||||
String warn = getLocalizedMessage("java.launcher.permitaccess.warning",
|
||||
jarname, ADD_OPENS);
|
||||
System.err.println(warn);
|
||||
addExportsOrOpens(opens, true, ADD_OPENS);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -460,23 +467,36 @@ public final class LauncherHelper {
|
||||
* Process the Add-Exports or Add-Opens value. The value is
|
||||
* {@code <module>/<package> ( <module>/<package>)*}.
|
||||
*/
|
||||
static void addExportsOrOpens(String value, boolean open) {
|
||||
static void addExportsOrOpens(String value, boolean open, String how) {
|
||||
IllegalAccessLogger.Builder builder;
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger == null) {
|
||||
builder = new IllegalAccessLogger.Builder();
|
||||
} else {
|
||||
builder = logger.toBuilder();
|
||||
}
|
||||
|
||||
for (String moduleAndPackage : value.split(" ")) {
|
||||
String[] s = moduleAndPackage.trim().split("/");
|
||||
if (s.length == 2) {
|
||||
String mn = s[0];
|
||||
String pn = s[1];
|
||||
|
||||
Layer.boot().findModule(mn).ifPresent(m -> {
|
||||
if (m.getDescriptor().packages().contains(pn)) {
|
||||
if (open) {
|
||||
builder.logAccessToOpenPackage(m, pn, how);
|
||||
Modules.addOpensToAllUnnamed(m, pn);
|
||||
} else {
|
||||
builder.logAccessToExportedPackage(m, pn, how);
|
||||
Modules.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
IllegalAccessLogger.setIllegalAccessLogger(builder.build());
|
||||
}
|
||||
|
||||
// From src/share/bin/java.c:
|
||||
|
@ -24,12 +24,15 @@
|
||||
#
|
||||
|
||||
# Translators please note do not translate the options themselves
|
||||
java.launcher.opt.header = Usage: {0} [options] class [args...]\n\
|
||||
\ (to execute a class)\n or {0} [options] -jar jarfile [args...]\n\
|
||||
java.launcher.opt.header = Usage: {0} [options] <mainclass> [args...]\n\
|
||||
\ (to execute a class)\n or {0} [options] -jar <jarfile> [args...]\n\
|
||||
\ (to execute a jar file)\n\
|
||||
\ or {0} [options] -p <modulepath> -m <modulename>[/<mainclass>] [args...]\n\
|
||||
\ or {0} [options] -m <module>[/<mainclass>] [args...]\n\
|
||||
\ {0} [options] --module <module>[/<mainclass>] [args...]\n\
|
||||
\ (to execute the main class in a module)\n\n\
|
||||
where options include:\n\n
|
||||
\ Arguments following the main class, -jar <jarfile>, -m or --module\n\
|
||||
\ <module>/<mainclass> are passed as the arguments to main class.\n\n\
|
||||
\ where options include:\n\n
|
||||
|
||||
java.launcher.opt.datamodel =\ -d{0}\t Deprecated, will be removed in a future release\n
|
||||
java.launcher.opt.vmselect =\ {0}\t to select the "{1}" VM\n
|
||||
@ -49,10 +52,6 @@ java.launcher.opt.footer =\ -cp <class search path of directories and zip
|
||||
\ A {0} separated list of directories, each directory\n\
|
||||
\ is a directory of modules that replace upgradeable\n\
|
||||
\ modules in the runtime image\n\
|
||||
\ -m <module>[/<mainclass>]\n\
|
||||
\ --module <modulename>[/<mainclass>]\n\
|
||||
\ the initial module to resolve, and the name of the main class\n\
|
||||
\ to execute if not specified by the module\n\
|
||||
\ --add-modules <modulename>[,<modulename>...]\n\
|
||||
\ root modules to resolve in addition to the initial module.\n\
|
||||
\ <modulename> can also be ALL-DEFAULT, ALL-SYSTEM,\n\
|
||||
@ -157,6 +156,10 @@ java.launcher.X.usage=\n\
|
||||
\ --add-opens <module>/<package>=<target-module>(,<target-module>)*\n\
|
||||
\ updates <module> to open <package> to\n\
|
||||
\ <target-module>, regardless of module declaration.\n\
|
||||
\ --permit-illegal-access\n\
|
||||
\ permit illegal access to members of types in named modules\n\
|
||||
\ by code in unnamed modules. This compatibility option will\n\
|
||||
\ be removed in the next release.\n\
|
||||
\ --disable-@files disable further argument file expansion\n\
|
||||
\ --patch-module <module>=<file>({0}<file>)*\n\
|
||||
\ Override or augment a module with classes and resources\n\
|
||||
@ -208,4 +211,6 @@ java.launcher.module.error2=\
|
||||
java.launcher.module.error3=\
|
||||
Error: Unable to load main class {0} from module {1}\n\
|
||||
\t{2}
|
||||
java.launcher.permitaccess.warning=\
|
||||
WARNING: Main manifest of {0} contains {1} attribute to permit illegal access
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,8 +26,6 @@
|
||||
package sun.net.www.protocol.jrt;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
@ -44,7 +42,6 @@ import jdk.internal.loader.URLClassPath;
|
||||
import jdk.internal.loader.Resource;
|
||||
import sun.net.www.ParseUtil;
|
||||
import sun.net.www.URLConnection;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* URLConnection implementation that can be used to connect to resources
|
||||
@ -66,9 +63,6 @@ public class JavaRuntimeURLConnection extends URLConnection {
|
||||
// the Resource when connected
|
||||
private volatile Resource resource;
|
||||
|
||||
// the permission to access resources in the runtime image, created lazily
|
||||
private static volatile Permission permission;
|
||||
|
||||
JavaRuntimeURLConnection(URL url) throws IOException {
|
||||
super(url);
|
||||
String path = url.getPath();
|
||||
@ -164,14 +158,8 @@ public class JavaRuntimeURLConnection extends URLConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission getPermission() throws IOException {
|
||||
Permission p = permission;
|
||||
if (p == null) {
|
||||
String home = GetPropertyAction.privilegedGetProperty("java.home");
|
||||
p = new FilePermission(home + File.separator + "-", "read");
|
||||
permission = p;
|
||||
}
|
||||
return p;
|
||||
public Permission getPermission() {
|
||||
return new RuntimePermission("accessSystemModules");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,23 +72,9 @@ getUTF(JNIEnv *env, jstring str, char* localBuf, int bufSize)
|
||||
return utfStr;
|
||||
}
|
||||
|
||||
// The existence or signature of this method is not guaranteed since it
|
||||
// supports a private method. This method will be changed in 1.7.
|
||||
JNIEXPORT jclass JNICALL
|
||||
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
|
||||
jobject loader,
|
||||
jstring name,
|
||||
jbyteArray data,
|
||||
jint offset,
|
||||
jint length,
|
||||
jobject pd)
|
||||
{
|
||||
return Java_java_lang_ClassLoader_defineClass1(env, loader, name, data, offset,
|
||||
length, pd, NULL);
|
||||
}
|
||||
|
||||
JNIEXPORT jclass JNICALL
|
||||
Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
|
||||
jclass cls,
|
||||
jobject loader,
|
||||
jstring name,
|
||||
jbyteArray data,
|
||||
@ -163,6 +149,7 @@ Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
|
||||
|
||||
JNIEXPORT jclass JNICALL
|
||||
Java_java_lang_ClassLoader_defineClass2(JNIEnv *env,
|
||||
jclass cls,
|
||||
jobject loader,
|
||||
jstring name,
|
||||
jobject data,
|
||||
|
@ -73,30 +73,32 @@ Java_java_lang_reflect_Module_defineModule0(JNIEnv *env, jclass cls, jobject mod
|
||||
jstring location, jobjectArray packages)
|
||||
{
|
||||
char** pkgs = NULL;
|
||||
jsize idx;
|
||||
jsize num_packages = (*env)->GetArrayLength(env, packages);
|
||||
|
||||
if (num_packages != 0 && (pkgs = calloc(num_packages, sizeof(char*))) == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
return;
|
||||
} else {
|
||||
int valid = 1;
|
||||
} else if ((*env)->EnsureLocalCapacity(env, (jint)num_packages) == 0) {
|
||||
jboolean failed = JNI_FALSE;
|
||||
int idx;
|
||||
for (idx = 0; idx < num_packages; idx++) {
|
||||
jstring pkg = (*env)->GetObjectArrayElement(env, packages, idx);
|
||||
pkgs[idx] = GetInternalPackageName(env, pkg, NULL, 0);
|
||||
if (pkgs[idx] == NULL) {
|
||||
valid = 0;
|
||||
char* name = GetInternalPackageName(env, pkg, NULL, 0);
|
||||
if (name != NULL) {
|
||||
pkgs[idx] = name;
|
||||
} else {
|
||||
failed = JNI_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid != 0) {
|
||||
if (!failed) {
|
||||
JVM_DefineModule(env, module, is_open, version, location,
|
||||
(const char* const*)pkgs, num_packages);
|
||||
(const char* const*)pkgs, num_packages);
|
||||
}
|
||||
}
|
||||
|
||||
if (num_packages > 0) {
|
||||
int idx;
|
||||
for (idx = 0; idx < num_packages; idx++) {
|
||||
if (pkgs[idx] != NULL) {
|
||||
free(pkgs[idx]);
|
||||
|
@ -191,6 +191,9 @@ public interface ClassFileTransformer {
|
||||
* if the input does not represent a well-formed class file
|
||||
* @return a well-formed class file buffer (the result of the transform),
|
||||
* or {@code null} if no transform is performed
|
||||
*
|
||||
* @revised 9
|
||||
* @spec JPMS
|
||||
*/
|
||||
default byte[]
|
||||
transform( ClassLoader loader,
|
||||
|
@ -63,6 +63,7 @@ import jdk.internal.module.ModuleHashesBuilder;
|
||||
import jdk.internal.module.ModuleInfo;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.internal.util.jar.JarIndex;
|
||||
|
||||
import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
|
||||
@ -1780,6 +1781,7 @@ public class Main {
|
||||
{
|
||||
ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null);
|
||||
ModuleDescriptor md = attrs.descriptor();
|
||||
ModuleTarget target = attrs.target();
|
||||
ModuleHashes hashes = attrs.recordedHashes();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -1824,11 +1826,14 @@ public class Main {
|
||||
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
if (target != null) {
|
||||
String osName = target.osName();
|
||||
if (osName != null)
|
||||
sb.append("\n operating-system-name " + osName);
|
||||
String osArch = target.osArch();
|
||||
if (osArch != null)
|
||||
sb.append("\n operating-system-architecture " + osArch);
|
||||
}
|
||||
|
||||
if (hashes != null) {
|
||||
hashes.names().stream().sorted().forEach(
|
||||
|
@ -151,16 +151,8 @@ public final class DefaultImageBuilder implements ImageBuilder {
|
||||
@Override
|
||||
public void storeFiles(ResourcePool files) {
|
||||
try {
|
||||
// populate targetOsName field up-front because it's used elsewhere.
|
||||
Optional<ResourcePoolModule> javaBase = files.moduleView().findModule("java.base");
|
||||
javaBase.ifPresent(mod -> {
|
||||
// fill release information available from transformed "java.base" module!
|
||||
ModuleDescriptor desc = mod.descriptor();
|
||||
desc.osName().ifPresent(s -> {
|
||||
this.targetOsName = s;
|
||||
});
|
||||
});
|
||||
|
||||
this.targetOsName = files.moduleView().
|
||||
findModule("java.base").get().osName();
|
||||
if (this.targetOsName == null) {
|
||||
throw new PluginException("ModuleTarget attribute is missing for java.base module");
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.jimage.decompressor.Decompressor;
|
||||
import jdk.internal.module.ModuleInfo.Attributes;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.tools.jlink.plugin.Plugin;
|
||||
import jdk.tools.jlink.builder.ImageBuilder;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
@ -298,6 +300,7 @@ public final class ImagePluginStack {
|
||||
final ResourcePoolModule module;
|
||||
// lazily initialized
|
||||
ModuleDescriptor descriptor;
|
||||
ModuleTarget target;
|
||||
|
||||
LastModule(ResourcePoolModule module) {
|
||||
this.module = module;
|
||||
@ -316,12 +319,30 @@ public final class ImagePluginStack {
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor descriptor() {
|
||||
if (descriptor == null) {
|
||||
descriptor = ResourcePoolManager.readModuleDescriptor(this);
|
||||
}
|
||||
initModuleAttributes();
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String osName() {
|
||||
initModuleAttributes();
|
||||
return target != null? target.osName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String osArch() {
|
||||
initModuleAttributes();
|
||||
return target != null? target.osArch() : null;
|
||||
}
|
||||
|
||||
private void initModuleAttributes() {
|
||||
if (this.descriptor == null) {
|
||||
Attributes attr = ResourcePoolManager.readModuleAttributes(this);
|
||||
this.descriptor = attr.descriptor();
|
||||
this.target = attr.target();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> packages() {
|
||||
return module.packages();
|
||||
|
@ -54,7 +54,6 @@ import jdk.tools.jlink.internal.ImagePluginStack.ImageProvider;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
import jdk.tools.jlink.builder.DefaultImageBuilder;
|
||||
import jdk.tools.jlink.plugin.Plugin;
|
||||
import jdk.internal.module.Checks;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
|
||||
@ -423,13 +422,6 @@ public class JlinkTask {
|
||||
ModuleFinder.of(),
|
||||
roots);
|
||||
|
||||
// emit warning for modules that end with a digit
|
||||
cf.modules().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.filter(mn -> !Checks.hasLegalModuleNameLastCharacter(mn))
|
||||
.forEach(mn -> System.err.println("WARNING: Module name \""
|
||||
+ mn + "\" may soon be illegal"));
|
||||
|
||||
// emit a warning for any incubating modules in the configuration
|
||||
if (log != null) {
|
||||
String im = cf.modules()
|
||||
|
@ -66,9 +66,6 @@ final class ResourcePoolConfiguration {
|
||||
|
||||
md.version().ifPresent(builder::version);
|
||||
md.mainClass().ifPresent(builder::mainClass);
|
||||
md.osName().ifPresent(builder::osName);
|
||||
md.osArch().ifPresent(builder::osArch);
|
||||
md.osVersion().ifPresent(builder::osVersion);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
@ -35,7 +35,10 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.jimage.decompressor.CompressedResourceHeader;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.module.Resources;
|
||||
import jdk.internal.module.ModuleInfo;
|
||||
import jdk.internal.module.ModuleInfo.Attributes;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
@ -47,8 +50,8 @@ import jdk.tools.jlink.plugin.PluginException;
|
||||
* A manager for pool of resources.
|
||||
*/
|
||||
public class ResourcePoolManager {
|
||||
// utility to read ModuleDescriptor of the given ResourcePoolModule
|
||||
static ModuleDescriptor readModuleDescriptor(ResourcePoolModule mod) {
|
||||
// utility to read Module Attributes of the given ResourcePoolModule
|
||||
static Attributes readModuleAttributes(ResourcePoolModule mod) {
|
||||
String p = "/" + mod.name() + "/module-info.class";
|
||||
Optional<ResourcePoolEntry> content = mod.findEntry(p);
|
||||
if (!content.isPresent()) {
|
||||
@ -57,9 +60,9 @@ public class ResourcePoolManager {
|
||||
}
|
||||
ByteBuffer bb = ByteBuffer.wrap(content.get().contentBytes());
|
||||
try {
|
||||
return ModuleDescriptor.read(bb);
|
||||
return ModuleInfo.read(bb, null);
|
||||
} catch (RuntimeException re) {
|
||||
throw new RuntimeException("module descriptor cannot be read for " + mod.name(), re);
|
||||
throw new RuntimeException("module info cannot be read for " + mod.name(), re);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +71,7 @@ public class ResourcePoolManager {
|
||||
*/
|
||||
public static boolean isNamedPackageResource(String path) {
|
||||
return (path.endsWith(".class") && !path.endsWith("module-info.class")) ||
|
||||
!ResourceHelper.isSimpleResource(path);
|
||||
Resources.canEncapsulate(path);
|
||||
}
|
||||
|
||||
class ResourcePoolModuleImpl implements ResourcePoolModule {
|
||||
@ -76,6 +79,8 @@ public class ResourcePoolManager {
|
||||
final Map<String, ResourcePoolEntry> moduleContent = new LinkedHashMap<>();
|
||||
// lazily initialized
|
||||
private ModuleDescriptor descriptor;
|
||||
private ModuleTarget target;
|
||||
|
||||
final String name;
|
||||
|
||||
private ResourcePoolModuleImpl(String name) {
|
||||
@ -100,12 +105,30 @@ public class ResourcePoolManager {
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor descriptor() {
|
||||
if (descriptor == null) {
|
||||
descriptor = readModuleDescriptor(this);
|
||||
}
|
||||
initModuleAttributes();
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String osName() {
|
||||
initModuleAttributes();
|
||||
return target != null? target.osName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String osArch() {
|
||||
initModuleAttributes();
|
||||
return target != null? target.osArch() : null;
|
||||
}
|
||||
|
||||
private void initModuleAttributes() {
|
||||
if (this.descriptor == null) {
|
||||
Attributes attr = readModuleAttributes(this);
|
||||
this.descriptor = attr.descriptor();
|
||||
this.target = attr.target();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> packages() {
|
||||
Set<String> pkgs = new HashSet<>();
|
||||
|
@ -115,7 +115,7 @@ public final class ExcludeVMPlugin implements Plugin {
|
||||
@Override
|
||||
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
|
||||
ResourcePoolModule javaBase = in.moduleView().findModule("java.base").get();
|
||||
String[] jvmlibs = jvmlibs(javaBase.descriptor().osName().get());
|
||||
String[] jvmlibs = jvmlibs(javaBase.osName());
|
||||
TreeSet<Jvm> existing = new TreeSet<>(new JvmComparator());
|
||||
TreeSet<Jvm> removed = new TreeSet<>(new JvmComparator());
|
||||
if (!keepAll) {
|
||||
|
@ -137,15 +137,13 @@ public final class ReleaseInfoPlugin implements Plugin {
|
||||
javaBase.ifPresent(mod -> {
|
||||
// fill release information available from transformed "java.base" module!
|
||||
ModuleDescriptor desc = mod.descriptor();
|
||||
desc.osName().ifPresent(s -> {
|
||||
release.put("OS_NAME", quote(s));
|
||||
});
|
||||
desc.osVersion().ifPresent(s -> release.put("OS_VERSION", quote(s)));
|
||||
desc.osArch().ifPresent(s -> release.put("OS_ARCH", quote(s)));
|
||||
desc.version().ifPresent(s -> release.put("JAVA_VERSION",
|
||||
quote(parseVersion(s.toString()))));
|
||||
desc.version().ifPresent(s -> release.put("JAVA_FULL_VERSION",
|
||||
quote(s.toString())));
|
||||
|
||||
release.put("OS_NAME", quote(mod.osName()));
|
||||
release.put("OS_ARCH", quote(mod.osArch()));
|
||||
});
|
||||
|
||||
// put topological sorted module names separated by space
|
||||
|
@ -52,6 +52,7 @@ import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleInfo.Attributes;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.internal.module.SystemModules;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
@ -206,12 +207,12 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
// add ModulePackages attribute if this module contains some packages
|
||||
// and ModulePackages is not present
|
||||
this.addModulePackages = packages.size() > 0 && !hasModulePackages();
|
||||
|
||||
// drop target attribute only if any OS property is present
|
||||
if (dropModuleTarget) {
|
||||
this.dropModuleTarget =
|
||||
descriptor.osName().isPresent() ||
|
||||
descriptor.osArch().isPresent() ||
|
||||
descriptor.osVersion().isPresent();
|
||||
ModuleTarget target = attrs.target();
|
||||
if (dropModuleTarget && target != null) {
|
||||
this.dropModuleTarget = (target.osName() != null)
|
||||
|| (target.osArch() != null);
|
||||
} else {
|
||||
this.dropModuleTarget = false;
|
||||
}
|
||||
@ -230,6 +231,10 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
return packages;
|
||||
}
|
||||
|
||||
ModuleTarget target() {
|
||||
return attrs.target();
|
||||
}
|
||||
|
||||
ModuleHashes recordedHashes() {
|
||||
return attrs.recordedHashes();
|
||||
}
|
||||
@ -372,7 +377,7 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
}
|
||||
|
||||
void dropModuleTarget() {
|
||||
extender.targetPlatform("", "", "");
|
||||
extender.targetPlatform("", "");
|
||||
}
|
||||
|
||||
byte[] getBytes() throws IOException {
|
||||
@ -399,6 +404,10 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
"java/lang/module/ModuleDescriptor$Exports$Modifier";
|
||||
private static final String OPENS_MODIFIER_CLASSNAME =
|
||||
"java/lang/module/ModuleDescriptor$Opens$Modifier";
|
||||
private static final String MODULE_TARGET_CLASSNAME =
|
||||
"jdk/internal/module/ModuleTarget";
|
||||
private static final String MODULE_TARGET_ARRAY_SIGNATURE =
|
||||
"[Ljdk/internal/module/ModuleTarget;";
|
||||
private static final String MODULE_HASHES_ARRAY_SIGNATURE =
|
||||
"[Ljdk/internal/module/ModuleHashes;";
|
||||
private static final String MODULE_RESOLUTION_CLASSNAME =
|
||||
@ -414,6 +423,7 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
|
||||
private final int BUILDER_VAR = 0;
|
||||
private final int MD_VAR = 1; // variable for ModuleDescriptor
|
||||
private final int MT_VAR = 1; // variable for ModuleTarget
|
||||
private final int MH_VAR = 1; // variable for ModuleHashes
|
||||
private int nextLocalVar = 2; // index to next local variable
|
||||
|
||||
@ -515,11 +525,10 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
if (entry.moduleName().equals("java.base")) {
|
||||
moduleInfo = new ModuleInfo(entry.contentBytes(), packages, false);
|
||||
ModuleDescriptor md = moduleInfo.descriptor;
|
||||
// drop Moduletarget attribute only if java.base has all OS properties
|
||||
// otherwise, retain it
|
||||
if (dropModuleTarget &&
|
||||
md.osName().isPresent() && md.osArch().isPresent() &&
|
||||
md.osVersion().isPresent()) {
|
||||
// drop ModuleTarget attribute if java.base has all OS properties
|
||||
ModuleTarget target = moduleInfo.target();
|
||||
if (dropModuleTarget
|
||||
&& (target.osName() != null) && (target.osArch() != null)) {
|
||||
dropModuleTarget = true;
|
||||
} else {
|
||||
dropModuleTarget = false;
|
||||
@ -584,15 +593,20 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
|
||||
// generate SystemModules::descriptors
|
||||
genDescriptorsMethod();
|
||||
|
||||
// generate SystemModules::targets
|
||||
genTargetsMethod();
|
||||
|
||||
// generate SystemModules::hashes
|
||||
genHashesMethod();
|
||||
|
||||
// generate SystemModules::moduleResolutions
|
||||
genModuleResolutionsMethod();
|
||||
|
||||
return cw;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Generate bytecode for SystemModules::descriptors method
|
||||
*/
|
||||
private void genDescriptorsMethod() {
|
||||
@ -616,10 +630,47 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Generate bytecode for SystemModules::targets method
|
||||
*/
|
||||
private void genTargetsMethod() {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
|
||||
"targets",
|
||||
"()" + MODULE_TARGET_ARRAY_SIGNATURE,
|
||||
"()" + MODULE_TARGET_ARRAY_SIGNATURE,
|
||||
null);
|
||||
mv.visitCode();
|
||||
pushInt(mv, moduleInfos.size());
|
||||
mv.visitTypeInsn(ANEWARRAY, MODULE_TARGET_CLASSNAME);
|
||||
mv.visitVarInsn(ASTORE, MT_VAR);
|
||||
|
||||
for (int index=0; index < moduleInfos.size(); index++) {
|
||||
ModuleInfo minfo = moduleInfos.get(index);
|
||||
if (minfo.target() != null && !minfo.dropModuleTarget) {
|
||||
mv.visitVarInsn(ALOAD, MT_VAR);
|
||||
pushInt(mv, index);
|
||||
|
||||
// new ModuleTarget(String, String)
|
||||
mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn(minfo.target().osName());
|
||||
mv.visitLdcInsn(minfo.target().osArch());
|
||||
mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
|
||||
"<init>", "(Ljava/lang/String;Ljava/lang/String;)V", false);
|
||||
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitVarInsn(ALOAD, MT_VAR);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate bytecode for SystemModules::hashes method
|
||||
*/
|
||||
private void genHashesMethod() {
|
||||
@ -647,10 +698,9 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
hmv.visitInsn(ARETURN);
|
||||
hmv.visitMaxs(0, 0);
|
||||
hmv.visitEnd();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Generate bytecode for SystemModules::methodResoultions method
|
||||
*/
|
||||
private void genModuleResolutionsMethod() {
|
||||
@ -749,6 +799,7 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
final ModuleDescriptor md;
|
||||
final Set<String> packages;
|
||||
final int index;
|
||||
|
||||
ModuleDescriptorBuilder(ModuleDescriptor md, Set<String> packages, int index) {
|
||||
if (md.isAutomatic()) {
|
||||
throw new InternalError("linking automatic module is not supported");
|
||||
@ -786,11 +837,6 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
// main class
|
||||
md.mainClass().ifPresent(this::mainClass);
|
||||
|
||||
// os name, arch, version
|
||||
targetPlatform(md.osName().orElse(null),
|
||||
md.osArch().orElse(null),
|
||||
md.osVersion().orElse(null));
|
||||
|
||||
putModuleDescriptor();
|
||||
}
|
||||
|
||||
@ -1088,25 +1134,6 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke Builder.osName(String name)
|
||||
* Builder.osArch(String arch)
|
||||
* Builder.osVersion(String version)
|
||||
*/
|
||||
void targetPlatform(String osName, String osArch, String osVersion) {
|
||||
if (osName != null) {
|
||||
invokeBuilderMethod("osName", osName);
|
||||
}
|
||||
|
||||
if (osArch != null) {
|
||||
invokeBuilderMethod("osArch", osArch);
|
||||
}
|
||||
|
||||
if (osVersion != null) {
|
||||
invokeBuilderMethod("osVersion", osVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void invokeBuilderMethod(String methodName, String value) {
|
||||
mv.visitVarInsn(ALOAD, BUILDER_VAR);
|
||||
mv.visitLdcInsn(value);
|
||||
|
@ -28,6 +28,7 @@ import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
|
||||
/**
|
||||
* Link-time representation of a module.
|
||||
@ -56,6 +57,20 @@ public interface ResourcePoolModule {
|
||||
*/
|
||||
public ModuleDescriptor descriptor();
|
||||
|
||||
/**
|
||||
* The module target OS name for this module.
|
||||
*
|
||||
* @return The module target OS name
|
||||
*/
|
||||
public String osName();
|
||||
|
||||
/**
|
||||
* The module target OS arch for this module.
|
||||
*
|
||||
* @return The module target OS arch
|
||||
*/
|
||||
public String osArch();
|
||||
|
||||
/**
|
||||
* Retrieves all the packages located in this module.
|
||||
*
|
||||
|
@ -96,13 +96,14 @@ import jdk.internal.joptsimple.OptionParser;
|
||||
import jdk.internal.joptsimple.OptionSet;
|
||||
import jdk.internal.joptsimple.OptionSpec;
|
||||
import jdk.internal.joptsimple.ValueConverter;
|
||||
import jdk.internal.loader.ResourceHelper;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleHashesBuilder;
|
||||
import jdk.internal.module.ModuleInfo;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.ModulePath;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.internal.module.Resources;
|
||||
import jdk.tools.jlink.internal.Utils;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
@ -178,7 +179,6 @@ public class JmodTask {
|
||||
String mainClass;
|
||||
String osName;
|
||||
String osArch;
|
||||
String osVersion;
|
||||
Pattern modulesToHash;
|
||||
ModuleResolution moduleResolution;
|
||||
boolean dryrun;
|
||||
@ -311,7 +311,9 @@ public class JmodTask {
|
||||
try (JmodFile jf = new JmodFile(options.jmodFile)) {
|
||||
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
|
||||
ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
|
||||
printModuleDescriptor(attrs.descriptor(), attrs.recordedHashes());
|
||||
printModuleDescriptor(attrs.descriptor(),
|
||||
attrs.target(),
|
||||
attrs.recordedHashes());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("err.module.descriptor.not.found");
|
||||
@ -325,7 +327,9 @@ public class JmodTask {
|
||||
.collect(joining(" "));
|
||||
}
|
||||
|
||||
private void printModuleDescriptor(ModuleDescriptor md, ModuleHashes hashes)
|
||||
private void printModuleDescriptor(ModuleDescriptor md,
|
||||
ModuleTarget target,
|
||||
ModuleHashes hashes)
|
||||
throws IOException
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -365,11 +369,14 @@ public class JmodTask {
|
||||
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
if (target != null) {
|
||||
String osName = target.osName();
|
||||
if (osName != null)
|
||||
sb.append("\n operating-system-name " + osName);
|
||||
String osArch = target.osArch();
|
||||
if (osArch != null)
|
||||
sb.append("\n operating-system-architecture " + osArch);
|
||||
}
|
||||
|
||||
if (hashes != null) {
|
||||
hashes.names().stream().sorted().forEach(
|
||||
@ -432,7 +439,6 @@ public class JmodTask {
|
||||
final String mainClass = options.mainClass;
|
||||
final String osName = options.osName;
|
||||
final String osArch = options.osArch;
|
||||
final String osVersion = options.osVersion;
|
||||
final List<PathMatcher> excludes = options.excludes;
|
||||
final ModuleResolution moduleResolution = options.moduleResolution;
|
||||
|
||||
@ -528,9 +534,9 @@ public class JmodTask {
|
||||
if (mainClass != null)
|
||||
extender.mainClass(mainClass);
|
||||
|
||||
// --os-name, --os-arch, --os-version
|
||||
if (osName != null || osArch != null || osVersion != null)
|
||||
extender.targetPlatform(osName, osArch, osVersion);
|
||||
// --os-name, --os-arch
|
||||
if (osName != null || osArch != null)
|
||||
extender.targetPlatform(osName, osArch);
|
||||
|
||||
// --module-version
|
||||
if (moduleVersion != null)
|
||||
@ -675,7 +681,7 @@ public class JmodTask {
|
||||
*/
|
||||
boolean isResource(String name) {
|
||||
name = name.replace(File.separatorChar, '/');
|
||||
return name.endsWith(".class") || !ResourceHelper.isSimpleResource(name);
|
||||
return name.endsWith(".class") || Resources.canEncapsulate(name);
|
||||
}
|
||||
|
||||
|
||||
@ -1331,11 +1337,6 @@ public class JmodTask {
|
||||
.withRequiredArg()
|
||||
.describedAs(getMessage("main.opt.os-arch.arg"));
|
||||
|
||||
OptionSpec<String> osVersion
|
||||
= parser.accepts("os-version", getMessage("main.opt.os-version"))
|
||||
.withRequiredArg()
|
||||
.describedAs(getMessage("main.opt.os-version.arg"));
|
||||
|
||||
OptionSpec<Void> doNotResolveByDefault
|
||||
= parser.accepts("do-not-resolve-by-default",
|
||||
getMessage("main.opt.do-not-resolve-by-default"));
|
||||
@ -1403,8 +1404,6 @@ public class JmodTask {
|
||||
options.osName = getLastElement(opts.valuesOf(osName));
|
||||
if (opts.has(osArch))
|
||||
options.osArch = getLastElement(opts.valuesOf(osArch));
|
||||
if (opts.has(osVersion))
|
||||
options.osVersion = getLastElement(opts.valuesOf(osVersion));
|
||||
if (opts.has(warnIfResolved))
|
||||
options.moduleResolution = getLastElement(opts.valuesOf(warnIfResolved));
|
||||
if (opts.has(doNotResolveByDefault)) {
|
||||
|
@ -68,8 +68,6 @@ main.opt.os-name=Operating system name
|
||||
main.opt.os-name.arg=os-name
|
||||
main.opt.os-arch=Operating system architecture
|
||||
main.opt.os-arch.arg=os-arch
|
||||
main.opt.os-version=Operating system version
|
||||
main.opt.os-version.arg=os-version
|
||||
main.opt.module-path=Module path
|
||||
main.opt.hash-modules=Compute and record hashes to tie a packaged module\
|
||||
\ with modules matching the given <regex-pattern> and depending upon it directly\
|
||||
|
@ -813,6 +813,7 @@ public final class Unsafe {
|
||||
/**
|
||||
* Tells the VM to define a class, without security checks. By default, the
|
||||
* class loader and protection domain come from the caller's class.
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineClass(byte[])
|
||||
*/
|
||||
@ForceInline
|
||||
public Class<?> defineClass(String name, byte[] b, int off, int len,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -380,6 +380,7 @@ public class FieldSetAccessibleTest {
|
||||
permissions.add(new RuntimePermission("closeClassLoader"));
|
||||
permissions.add(new RuntimePermission("getClassLoader"));
|
||||
permissions.add(new RuntimePermission("accessDeclaredMembers"));
|
||||
permissions.add(new RuntimePermission("accessSystemModules"));
|
||||
permissions.add(new ReflectPermission("suppressAccessChecks"));
|
||||
permissions.add(new PropertyPermission("*", "read"));
|
||||
permissions.add(new FilePermission("<<ALL FILES>>", "read"));
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @build Driver Main JarUtils jdk.testlibrary.ProcessTools
|
||||
* @run main Driver
|
||||
* @summary Test ClassLoader.getResourceXXX to locate resources in an automatic
|
||||
* module
|
||||
*/
|
||||
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
|
||||
/**
|
||||
* The driver creates a JAR file containing p/Foo.class, p/foo.properties,
|
||||
* and p/resources/bar.properties. This ensures there are is a resource in
|
||||
* a module package and a resource that is not in the module package. The
|
||||
* test is then launched to locate every resource in the JAR file.
|
||||
*/
|
||||
|
||||
public class Driver {
|
||||
|
||||
private static final String TEST_CLASSES = System.getProperty("test.classes");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// create content for JAR file
|
||||
Path dir = Files.createTempDirectory("classes");
|
||||
Path p = Files.createDirectory(dir.resolve("p"));
|
||||
Files.createFile(p.resolve("Foo.class"));
|
||||
Files.createFile(p.resolve("foo.properties"));
|
||||
Path resources = Files.createDirectory(p.resolve("resources"));
|
||||
Files.createFile(resources.resolve("bar.properties"));
|
||||
|
||||
// create the JAR file, including a manifest
|
||||
Path jarFile = Paths.get("library-1.0.jar");
|
||||
Manifest man = new Manifest();
|
||||
Attributes attrs = man.getMainAttributes();
|
||||
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
|
||||
JarUtils.createJarFile(jarFile, man, dir, p);
|
||||
|
||||
// get the module name
|
||||
ModuleFinder finder = ModuleFinder.of(jarFile);
|
||||
ModuleReference mref = finder.findAll().stream().findAny().orElse(null);
|
||||
if (mref == null)
|
||||
throw new RuntimeException("Module not found!!!");
|
||||
String name = mref.descriptor().name();
|
||||
|
||||
// launch the test with the JAR file on the module path
|
||||
if (ProcessTools.executeTestJava("-p", jarFile.toString(),
|
||||
"--add-modules", name,
|
||||
"-cp", TEST_CLASSES,
|
||||
"Main", name)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
.getExitValue() != 0)
|
||||
throw new RuntimeException("Test failed - see output");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Usage: Main $MODULE
|
||||
*
|
||||
* Finds $MODULE in the boot layer and then tests that it can locate every
|
||||
* resource in the module content (JAR file).
|
||||
*/
|
||||
|
||||
public class Main {
|
||||
|
||||
static void testFind(String name) throws Exception {
|
||||
// getResource
|
||||
URL url = Main.class.getClassLoader().getResource(name);
|
||||
if (url == null)
|
||||
throw new RuntimeException("Unable to locate: " + name);
|
||||
System.out.println(name + " => " + url);
|
||||
|
||||
// getResources
|
||||
Enumeration<URL> urls = Main.class.getClassLoader().getResources(name);
|
||||
if (!urls.hasMoreElements())
|
||||
throw new RuntimeException("Unable to locate: " + name);
|
||||
URL first = urls.nextElement();
|
||||
if (!first.toURI().equals(url.toURI()))
|
||||
throw new RuntimeException("found " + first + " ???");
|
||||
|
||||
// getResourceAsStream
|
||||
if (!url.toString().endsWith("/")) {
|
||||
InputStream in = Main.class.getClassLoader().getResourceAsStream(name);
|
||||
if (in == null)
|
||||
throw new RuntimeException("Unable to locate: " + name);
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void testFindUnchecked(String name) {
|
||||
try {
|
||||
testFind(name);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String mn = args[0];
|
||||
|
||||
ModuleReference mref = Layer.boot()
|
||||
.configuration()
|
||||
.findModule(mn)
|
||||
.map(ResolvedModule::reference)
|
||||
.orElseThrow(() -> new RuntimeException(mn + " not resolved!!"));
|
||||
|
||||
try (ModuleReader reader = mref.open()) {
|
||||
reader.list().forEach(name -> {
|
||||
testFindUnchecked(name);
|
||||
|
||||
// if the resource is a directory then find without trailing slash
|
||||
if (name.endsWith("/")) {
|
||||
testFindUnchecked(name.substring(0, name.length() - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
352
jdk/test/java/lang/invoke/DefineClassTest.java
Normal file
352
jdk/test/java/lang/invoke/DefineClassTest.java
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @modules java.base/java.lang:open
|
||||
* java.base/jdk.internal.org.objectweb.asm
|
||||
* @run testng/othervm test.DefineClassTest
|
||||
* @summary Basic test for java.lang.invoke.MethodHandles.Lookup.defineClass
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class DefineClassTest {
|
||||
private static final String THIS_PACKAGE = DefineClassTest.class.getPackageName();
|
||||
|
||||
/**
|
||||
* Test that a class has the same class loader, and is in the same package and
|
||||
* protection domain, as a lookup class.
|
||||
*/
|
||||
void testSameAbode(Class<?> clazz, Class<?> lc) {
|
||||
assertTrue(clazz.getClassLoader() == lc.getClassLoader());
|
||||
assertEquals(clazz.getPackageName(), lc.getPackageName());
|
||||
assertTrue(clazz.getProtectionDomain() == lc.getProtectionDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a class is discoverable by name using Class.forName and
|
||||
* lookup.findClass
|
||||
*/
|
||||
void testDiscoverable(Class<?> clazz, Lookup lookup) throws Exception {
|
||||
String cn = clazz.getName();
|
||||
ClassLoader loader = clazz.getClassLoader();
|
||||
assertTrue(Class.forName(cn, false, loader) == clazz);
|
||||
assertTrue(lookup.findClass(cn) == clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of defineClass to define a class in the same package as test.
|
||||
*/
|
||||
@Test
|
||||
public void testDefineClass() throws Exception {
|
||||
final String CLASS_NAME = THIS_PACKAGE + ".Foo";
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
Class<?> clazz = lookup.defineClass(generateClass(CLASS_NAME));
|
||||
|
||||
// test name
|
||||
assertEquals(clazz.getName(), CLASS_NAME);
|
||||
|
||||
// test loader/package/protection-domain
|
||||
testSameAbode(clazz, lookup.lookupClass());
|
||||
|
||||
// test discoverable
|
||||
testDiscoverable(clazz, lookup);
|
||||
|
||||
// attempt defineClass again
|
||||
try {
|
||||
lookup.defineClass(generateClass(CLASS_NAME));
|
||||
assertTrue(false);
|
||||
} catch (LinkageError expected) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Test public/package/protected/private access from class defined with defineClass.
|
||||
*/
|
||||
@Test
|
||||
public void testAccess() throws Exception {
|
||||
final String THIS_CLASS = this.getClass().getName();
|
||||
final String CLASS_NAME = THIS_PACKAGE + ".Runner";
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
|
||||
// public
|
||||
byte[] classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method1");
|
||||
testInvoke(lookup.defineClass(classBytes));
|
||||
|
||||
// package
|
||||
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method2");
|
||||
testInvoke(lookup.defineClass(classBytes));
|
||||
|
||||
// protected (same package)
|
||||
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method3");
|
||||
testInvoke(lookup.defineClass(classBytes));
|
||||
|
||||
// private
|
||||
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method4");
|
||||
Class<?> clazz = lookup.defineClass(classBytes);
|
||||
Runnable r = (Runnable) clazz.newInstance();
|
||||
try {
|
||||
r.run();
|
||||
assertTrue(false);
|
||||
} catch (IllegalAccessError expected) { }
|
||||
}
|
||||
|
||||
public static void method1() { }
|
||||
static void method2() { }
|
||||
protected static void method3() { }
|
||||
private static void method4() { }
|
||||
|
||||
void testInvoke(Class<?> clazz) throws Exception {
|
||||
Object obj = clazz.newInstance();
|
||||
((Runnable) obj).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that defineClass does not run the class initializer
|
||||
*/
|
||||
@Test
|
||||
public void testInitializerNotRun() throws Exception {
|
||||
final String THIS_CLASS = this.getClass().getName();
|
||||
final String CLASS_NAME = THIS_PACKAGE + ".ClassWithClinit";
|
||||
|
||||
byte[] classBytes = generateClassWithInitializer(CLASS_NAME, THIS_CLASS, "fail");
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
|
||||
Class<?> clazz = lookup.defineClass(classBytes);
|
||||
// trigger initializer to run
|
||||
try {
|
||||
clazz.newInstance();
|
||||
assertTrue(false);
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
assertTrue(e.getCause() instanceof IllegalCallerException);
|
||||
}
|
||||
}
|
||||
|
||||
static void fail() { throw new IllegalCallerException(); }
|
||||
|
||||
|
||||
/**
|
||||
* Test defineClass to define classes in a package containing classes with
|
||||
* different protection domains.
|
||||
*/
|
||||
@Test
|
||||
public void testTwoProtectionDomains() throws Exception {
|
||||
// p.C1 in one exploded directory
|
||||
Path dir1 = Files.createTempDirectory("classes");
|
||||
Path p = Files.createDirectory(dir1.resolve("p"));
|
||||
Files.write(p.resolve("C1.class"), generateClass("p.C1"));
|
||||
URL url1 = dir1.toUri().toURL();
|
||||
|
||||
// p.C2 in another exploded directory
|
||||
Path dir2 = Files.createTempDirectory("classes");
|
||||
p = Files.createDirectory(dir2.resolve("p"));
|
||||
Files.write(p.resolve("C2.class"), generateClass("p.C2"));
|
||||
URL url2 = dir2.toUri().toURL();
|
||||
|
||||
// load p.C1 and p.C2
|
||||
ClassLoader loader = new URLClassLoader(new URL[] { url1, url2 });
|
||||
Class<?> target1 = Class.forName("p.C1", false, loader);
|
||||
Class<?> target2 = Class.forName("p.C2", false, loader);
|
||||
assertTrue(target1.getClassLoader() == loader);
|
||||
assertTrue(target1.getClassLoader() == loader);
|
||||
assertNotEquals(target1.getProtectionDomain(), target2.getProtectionDomain());
|
||||
|
||||
// protection domain 1
|
||||
Lookup lookup1 = privateLookupIn(target1, lookup()).dropLookupMode(PRIVATE);
|
||||
|
||||
Class<?> clazz = lookup1.defineClass(generateClass("p.Foo"));
|
||||
testSameAbode(clazz, lookup1.lookupClass());
|
||||
testDiscoverable(clazz, lookup1);
|
||||
|
||||
// protection domain 2
|
||||
Lookup lookup2 = privateLookupIn(target2, lookup()).dropLookupMode(PRIVATE);
|
||||
|
||||
clazz = lookup2.defineClass(generateClass("p.Bar"));
|
||||
testSameAbode(clazz, lookup2.lookupClass());
|
||||
testDiscoverable(clazz, lookup2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test defineClass defining a class to the boot loader
|
||||
*/
|
||||
@Test
|
||||
public void testBootLoader() throws Exception {
|
||||
Lookup lookup = privateLookupIn(Thread.class, lookup()).dropLookupMode(PRIVATE);
|
||||
assertTrue(lookup.getClass().getClassLoader() == null);
|
||||
|
||||
Class<?> clazz = lookup.defineClass(generateClass("java.lang.Foo"));
|
||||
assertEquals(clazz.getName(), "java.lang.Foo");
|
||||
testSameAbode(clazz, Thread.class);
|
||||
testDiscoverable(clazz, lookup);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { IllegalArgumentException.class })
|
||||
public void testWrongPackage() throws Exception {
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
lookup.defineClass(generateClass("other.C"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { IllegalAccessException.class })
|
||||
public void testNoPackageAccess() throws Exception {
|
||||
Lookup lookup = lookup().dropLookupMode(PACKAGE);
|
||||
lookup.defineClass(generateClass(THIS_PACKAGE + ".C"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { UnsupportedOperationException.class })
|
||||
public void testHasPrivateAccess() throws Exception {
|
||||
Lookup lookup = lookup();
|
||||
assertTrue(lookup.hasPrivateAccess());
|
||||
lookup.defineClass(generateClass(THIS_PACKAGE + ".C"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { ClassFormatError.class })
|
||||
public void testTruncatedClassFile() throws Exception {
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
lookup.defineClass(new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { NullPointerException.class })
|
||||
public void testNull() throws Exception {
|
||||
Lookup lookup = lookup().dropLookupMode(PRIVATE);
|
||||
lookup.defineClass(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class file with the given class name
|
||||
*/
|
||||
byte[] generateClass(String className) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V1_9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a class file with the given class name. The class implements Runnable
|
||||
* with a run method to invokestatic the given targetClass/targetMethod.
|
||||
*/
|
||||
byte[] generateRunner(String className,
|
||||
String targetClass,
|
||||
String targetMethod) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V1_9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
new String[] { "java/lang/Runnable" });
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// run()
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
|
||||
mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a class file with the given class name. The class will initializer
|
||||
* to invokestatic the given targetClass/targetMethod.
|
||||
*/
|
||||
byte[] generateClassWithInitializer(String className,
|
||||
String targetClass,
|
||||
String targetMethod) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V1_9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// <clinit>
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private int nextNumber() {
|
||||
return ++nextNumber;
|
||||
}
|
||||
|
||||
private int nextNumber;
|
||||
}
|
@ -773,9 +773,6 @@ public class AutomaticModulesTest {
|
||||
// test miscellaneous methods
|
||||
assertTrue(m.isAutomatic());
|
||||
assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC));
|
||||
assertFalse(m.osName().isPresent());
|
||||
assertFalse(m.osArch().isPresent());
|
||||
assertFalse(m.osVersion().isPresent());
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,25 +25,31 @@
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.module
|
||||
* @build ConfigurationTest ModuleUtils
|
||||
* @run testng ConfigurationTest
|
||||
* @summary Basic tests for java.lang.module.Configuration
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.FindException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Builder;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ResolutionException;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ModuleInfoWriter;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
@ -437,70 +443,6 @@ public class ConfigurationTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of "requires transitive" with configurations.
|
||||
*
|
||||
* The test consists of three configurations:
|
||||
* - Configuration cf1: m1, m2 requires transitive m1
|
||||
* - Configuration cf2: m1, m3 requires transitive m1
|
||||
* - Configuration cf3(cf1,cf2): m4 requires m2, m3
|
||||
*/
|
||||
public void testRequiresTransitive6() {
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor3 = newBuilder("m3")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor4 = newBuilder("m4")
|
||||
.requires("m2")
|
||||
.requires("m3")
|
||||
.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
Configuration cf1 = resolve(finder1, "m2");
|
||||
assertTrue(cf1.modules().size() == 2);
|
||||
assertTrue(cf1.findModule("m1").isPresent());
|
||||
assertTrue(cf1.findModule("m2").isPresent());
|
||||
assertTrue(cf1.parents().size() == 1);
|
||||
assertTrue(cf1.parents().get(0) == Configuration.empty());
|
||||
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3);
|
||||
Configuration cf2 = resolve(finder2, "m3");
|
||||
assertTrue(cf2.modules().size() == 2);
|
||||
assertTrue(cf2.findModule("m3").isPresent());
|
||||
assertTrue(cf2.findModule("m1").isPresent());
|
||||
assertTrue(cf2.parents().size() == 1);
|
||||
assertTrue(cf2.parents().get(0) == Configuration.empty());
|
||||
|
||||
ModuleFinder finder3 = ModuleUtils.finderOf(descriptor4);
|
||||
Configuration cf3 = Configuration.resolve(finder3,
|
||||
List.of(cf1, cf2),
|
||||
ModuleFinder.of(),
|
||||
Set.of("m4"));
|
||||
assertTrue(cf3.modules().size() == 1);
|
||||
assertTrue(cf3.findModule("m4").isPresent());
|
||||
|
||||
ResolvedModule m1_l = cf1.findModule("m1").get();
|
||||
ResolvedModule m1_r = cf2.findModule("m1").get();
|
||||
ResolvedModule m2 = cf1.findModule("m2").get();
|
||||
ResolvedModule m3 = cf2.findModule("m3").get();
|
||||
ResolvedModule m4 = cf3.findModule("m4").get();
|
||||
assertTrue(m4.configuration() == cf3);
|
||||
|
||||
assertTrue(m4.reads().size() == 4);
|
||||
assertTrue(m4.reads().contains(m1_l));
|
||||
assertTrue(m4.reads().contains(m1_r));
|
||||
assertTrue(m4.reads().contains(m2));
|
||||
assertTrue(m4.reads().contains(m3));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of "requires static":
|
||||
* m1 requires static m2
|
||||
@ -1602,6 +1544,76 @@ public class ConfigurationTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test to detect reading a module with the same name as itself
|
||||
*
|
||||
* The test consists of three configurations:
|
||||
* - Configuration cf1: m1, m2 requires transitive m1
|
||||
* - Configuration cf2: m1 requires m2
|
||||
*/
|
||||
@Test(expectedExceptions = { ResolutionException.class })
|
||||
public void testReadModuleWithSameNameAsSelf() {
|
||||
ModuleDescriptor descriptor1_v1 = newBuilder("m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor1_v2 = newBuilder("m1")
|
||||
.requires("m2")
|
||||
.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1_v1, descriptor2);
|
||||
Configuration cf1 = resolve(finder1, "m2");
|
||||
assertTrue(cf1.modules().size() == 2);
|
||||
|
||||
// resolve should throw ResolutionException
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1_v2);
|
||||
resolve(cf1, finder2, "m1");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test to detect reading two modules with the same name
|
||||
*
|
||||
* The test consists of three configurations:
|
||||
* - Configuration cf1: m1, m2 requires transitive m1
|
||||
* - Configuration cf2: m1, m3 requires transitive m1
|
||||
* - Configuration cf3(cf1,cf2): m4 requires m2, m3
|
||||
*/
|
||||
@Test(expectedExceptions = { ResolutionException.class })
|
||||
public void testReadTwoModuleWithSameName() {
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor3 = newBuilder("m3")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor4 = newBuilder("m4")
|
||||
.requires("m2")
|
||||
.requires("m3")
|
||||
.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
Configuration cf1 = resolve(finder1, "m2");
|
||||
assertTrue(cf1.modules().size() == 2);
|
||||
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3);
|
||||
Configuration cf2 = resolve(finder2, "m3");
|
||||
assertTrue(cf2.modules().size() == 2);
|
||||
|
||||
// should throw ResolutionException as m4 will read modules named "m1".
|
||||
ModuleFinder finder3 = ModuleUtils.finderOf(descriptor4);
|
||||
Configuration.resolve(finder3, List.of(cf1, cf2), ModuleFinder.of(), Set.of("m4"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test two modules exporting package p to a module that reads both.
|
||||
*/
|
||||
@ -1832,26 +1844,17 @@ public class ConfigurationTest {
|
||||
public Object[][] createPlatformMatches() {
|
||||
return new Object[][]{
|
||||
|
||||
{ "linux-*-*", "*-*-*" },
|
||||
{ "*-arm-*", "*-*-*" },
|
||||
{ "*-*-2.6", "*-*-*" },
|
||||
{ "linux-arm", "*-*" },
|
||||
{ "linux-*", "*-*" },
|
||||
{ "*-arm", "*-*" },
|
||||
|
||||
{ "linux-arm-*", "*-*-*" },
|
||||
{ "linux-*-2.6", "*-*-*" },
|
||||
{ "*-arm-2.6", "*-*-*" },
|
||||
{ "linux-*", "linux-*" },
|
||||
{ "linux-arm", "linux-*" },
|
||||
|
||||
{ "linux-arm-2.6", "*-*-*" },
|
||||
{ "*-arm", "*-arm" },
|
||||
{ "linux-arm", "*-arm" },
|
||||
|
||||
{ "linux-*-*", "linux-*-*" },
|
||||
{ "*-arm-*", "*-arm-*" },
|
||||
{ "*-*-2.6", "*-*-2.6" },
|
||||
|
||||
{ "linux-arm-*", "linux-arm-*" },
|
||||
{ "linux-arm-*", "linux-*-*" },
|
||||
{ "linux-*-2.6", "linux-*-2.6" },
|
||||
{ "linux-*-2.6", "linux-arm-*" },
|
||||
|
||||
{ "linux-arm-2.6", "linux-arm-2.6" },
|
||||
{ "linux-arm", "linux-arm" },
|
||||
|
||||
};
|
||||
|
||||
@ -1861,9 +1864,10 @@ public class ConfigurationTest {
|
||||
public Object[][] createBad() {
|
||||
return new Object[][] {
|
||||
|
||||
{ "linux-*-*", "solaris-*-*" },
|
||||
{ "linux-x86-*", "linux-arm-*" },
|
||||
{ "linux-*-2.4", "linux-x86-2.6" },
|
||||
{ "linux-*", "solaris-*" },
|
||||
{ "*-arm", "*-sparc" },
|
||||
{ "linux-x86", "solaris-sparc" },
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -1871,21 +1875,25 @@ public class ConfigurationTest {
|
||||
* Test creating a configuration containing platform specific modules.
|
||||
*/
|
||||
@Test(dataProvider = "platformmatch")
|
||||
public void testPlatformMatch(String s1, String s2) {
|
||||
public void testPlatformMatch(String s1, String s2) throws IOException {
|
||||
|
||||
Builder builder = newBuilder("m1").requires("m2");
|
||||
addPlatformConstraints(builder, s1);
|
||||
ModuleDescriptor descriptor1 = builder.build();
|
||||
ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build();
|
||||
Path system = writeModule(base, "*-*");
|
||||
|
||||
builder = newBuilder("m2");
|
||||
addPlatformConstraints(builder, s2);
|
||||
ModuleDescriptor descriptor2 = builder.build();
|
||||
ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1")
|
||||
.requires("m2")
|
||||
.build();
|
||||
Path dir1 = writeModule(descriptor1, s1);
|
||||
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build();
|
||||
Path dir2 = writeModule(descriptor2, s2);
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(system, dir1, dir2);
|
||||
|
||||
Configuration cf = resolve(finder, "m1");
|
||||
|
||||
assertTrue(cf.modules().size() == 2);
|
||||
assertTrue(cf.modules().size() == 3);
|
||||
assertTrue(cf.findModule("java.base").isPresent());
|
||||
assertTrue(cf.findModule("m1").isPresent());
|
||||
assertTrue(cf.findModule("m2").isPresent());
|
||||
}
|
||||
@ -1896,11 +1904,10 @@ public class ConfigurationTest {
|
||||
*/
|
||||
@Test(dataProvider = "platformmismatch",
|
||||
expectedExceptions = FindException.class )
|
||||
public void testPlatformMisMatch(String s1, String s2) {
|
||||
public void testPlatformMisMatch(String s1, String s2) throws IOException {
|
||||
testPlatformMatch(s1, s2);
|
||||
}
|
||||
|
||||
|
||||
// no parents
|
||||
|
||||
@Test(expectedExceptions = { IllegalArgumentException.class })
|
||||
@ -1917,21 +1924,23 @@ public class ConfigurationTest {
|
||||
|
||||
|
||||
// parents with modules for specific platforms
|
||||
|
||||
@Test(dataProvider = "platformmatch")
|
||||
public void testResolveRequiresWithCompatibleParents(String s1, String s2) {
|
||||
Builder builder = newBuilder("m1");
|
||||
addPlatformConstraints(builder, s1);
|
||||
ModuleDescriptor descriptor1 = builder.build();
|
||||
public void testResolveRequiresWithCompatibleParents(String s1, String s2)
|
||||
throws IOException
|
||||
{
|
||||
ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build();
|
||||
Path system = writeModule(base, "*-*");
|
||||
|
||||
builder = newBuilder("m2");
|
||||
addPlatformConstraints(builder, s2);
|
||||
ModuleDescriptor descriptor2 = builder.build();
|
||||
ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").build();
|
||||
Path dir1 = writeModule(descriptor1, s1);
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build();
|
||||
Path dir2 = writeModule(descriptor2, s2);
|
||||
|
||||
ModuleFinder finder1 = ModuleFinder.of(system, dir1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
|
||||
ModuleFinder finder2 = ModuleFinder.of(system, dir2);
|
||||
Configuration cf2 = resolve(finder2, "m2");
|
||||
|
||||
Configuration cf3 = Configuration.resolve(ModuleFinder.of(),
|
||||
@ -1941,32 +1950,16 @@ public class ConfigurationTest {
|
||||
assertTrue(cf3.parents().size() == 2);
|
||||
}
|
||||
|
||||
|
||||
@Test(dataProvider = "platformmismatch",
|
||||
expectedExceptions = IllegalArgumentException.class )
|
||||
public void testResolveRequiresWithConflictingParents(String s1, String s2) {
|
||||
Builder builder = newBuilder("m1");
|
||||
addPlatformConstraints(builder, s1);
|
||||
ModuleDescriptor descriptor1 = builder.build();
|
||||
|
||||
builder = newBuilder("m2");
|
||||
addPlatformConstraints(builder, s2);
|
||||
ModuleDescriptor descriptor2 = builder.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
|
||||
Configuration cf2 = resolve(finder2, "m2");
|
||||
|
||||
// should throw IAE
|
||||
Configuration.resolve(ModuleFinder.of(),
|
||||
List.of(cf1, cf2),
|
||||
ModuleFinder.of(),
|
||||
Set.of());
|
||||
public void testResolveRequiresWithConflictingParents(String s1, String s2)
|
||||
throws IOException
|
||||
{
|
||||
testResolveRequiresWithCompatibleParents(s1, s2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// null handling
|
||||
|
||||
// finder1, finder2, roots
|
||||
@ -2120,31 +2113,24 @@ public class ConfigurationTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the configuration contains module mn1
|
||||
* that reads module mn2.
|
||||
*/
|
||||
static boolean reads(Configuration cf, String mn1, String mn2) {
|
||||
Optional<ResolvedModule> om1 = cf.findModule(mn1);
|
||||
if (!om1.isPresent())
|
||||
return false;
|
||||
|
||||
return om1.get().reads().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.anyMatch(mn2::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the platform string and calls the builder osName/osArch/osVersion
|
||||
* methods to set the platform constraints.
|
||||
*/
|
||||
static void addPlatformConstraints(Builder builder, String platformString) {
|
||||
static Path writeModule(ModuleDescriptor descriptor, String platformString)
|
||||
throws IOException
|
||||
{
|
||||
String[] s = platformString.split("-");
|
||||
if (!s[0].equals("*"))
|
||||
builder.osName(s[0]);
|
||||
if (!s[1].equals("*"))
|
||||
builder.osArch(s[1]);
|
||||
if (!s[2].equals("*"))
|
||||
builder.osVersion(s[2]);
|
||||
String osName = !s[0].equals("*") ? s[0] : null;
|
||||
String osArch = !s[1].equals("*") ? s[1] : null;
|
||||
ModuleTarget target = new ModuleTarget(osName, osArch);
|
||||
|
||||
String name = descriptor.name();
|
||||
Path dir = Files.createTempDirectory(name);
|
||||
Path mi = dir.resolve("module-info.class");
|
||||
try (OutputStream out = Files.newOutputStream(mi)) {
|
||||
ModuleInfoWriter.write(descriptor, target, out);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
@ -50,11 +50,13 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
|
||||
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ModuleInfoWriter;
|
||||
import org.testng.annotations.DataProvider;
|
||||
@ -988,6 +990,107 @@ public class ModuleDescriptorTest {
|
||||
}
|
||||
|
||||
|
||||
@DataProvider(name = "unparseableVersions")
|
||||
public Object[][] unparseableVersions() {
|
||||
return new Object[][]{
|
||||
|
||||
{ null, "A1" }, // no version < unparseable
|
||||
{ "A1", "A2" }, // unparseable < unparseable
|
||||
{ "A1", "1.0" }, // unparseable < parseable
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test for unparseable module versions
|
||||
*/
|
||||
@Test(dataProvider = "unparseableVersions")
|
||||
public void testUnparseableModuleVersion(String vs1, String vs2) {
|
||||
ModuleDescriptor descriptor1 = newModule("m", vs1);
|
||||
ModuleDescriptor descriptor2 = newModule("m", vs2);
|
||||
|
||||
if (vs1 != null && !isParsableVersion(vs1)) {
|
||||
assertFalse(descriptor1.version().isPresent());
|
||||
assertTrue(descriptor1.rawVersion().isPresent());
|
||||
assertEquals(descriptor1.rawVersion().get(), vs1);
|
||||
}
|
||||
|
||||
if (vs2 != null && !isParsableVersion(vs2)) {
|
||||
assertFalse(descriptor2.version().isPresent());
|
||||
assertTrue(descriptor2.rawVersion().isPresent());
|
||||
assertEquals(descriptor2.rawVersion().get(), vs2);
|
||||
}
|
||||
|
||||
assertFalse(descriptor1.equals(descriptor2));
|
||||
assertFalse(descriptor2.equals(descriptor1));
|
||||
assertTrue(descriptor1.compareTo(descriptor2) == -1);
|
||||
assertTrue(descriptor2.compareTo(descriptor1) == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test for requiring a module with an unparseable version recorded
|
||||
* at compile version.
|
||||
*/
|
||||
@Test(dataProvider = "unparseableVersions")
|
||||
public void testUnparseableCompiledVersion(String vs1, String vs2) {
|
||||
Requires r1 = newRequires("m", vs1);
|
||||
Requires r2 = newRequires("m", vs2);
|
||||
|
||||
if (vs1 != null && !isParsableVersion(vs1)) {
|
||||
assertFalse(r1.compiledVersion().isPresent());
|
||||
assertTrue(r1.rawCompiledVersion().isPresent());
|
||||
assertEquals(r1.rawCompiledVersion().get(), vs1);
|
||||
}
|
||||
|
||||
if (vs2 != null && !isParsableVersion(vs2)) {
|
||||
assertFalse(r2.compiledVersion().isPresent());
|
||||
assertTrue(r2.rawCompiledVersion().isPresent());
|
||||
assertEquals(r2.rawCompiledVersion().get(), vs2);
|
||||
}
|
||||
|
||||
assertFalse(r1.equals(r2));
|
||||
assertFalse(r2.equals(r1));
|
||||
assertTrue(r1.compareTo(r2) == -1);
|
||||
assertTrue(r2.compareTo(r1) == 1);
|
||||
}
|
||||
|
||||
private ModuleDescriptor newModule(String name, String vs) {
|
||||
JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
Builder builder = JLMA.newModuleBuilder(name, false, Set.of());
|
||||
if (vs != null)
|
||||
builder.version(vs);
|
||||
builder.requires("java.base");
|
||||
ByteBuffer bb = ModuleInfoWriter.toByteBuffer(builder.build());
|
||||
return ModuleDescriptor.read(bb);
|
||||
}
|
||||
|
||||
private Requires newRequires(String name, String vs) {
|
||||
JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
Builder builder = JLMA.newModuleBuilder("foo", false, Set.of());
|
||||
if (vs == null) {
|
||||
builder.requires(name);
|
||||
} else {
|
||||
JLMA.requires(builder, Set.of(), name, vs);
|
||||
}
|
||||
Set<ModuleDescriptor.Requires> requires = builder.build().requires();
|
||||
Iterator<ModuleDescriptor.Requires> iterator = requires.iterator();
|
||||
ModuleDescriptor.Requires r = iterator.next();
|
||||
if (r.name().equals("java.base")) {
|
||||
r = iterator.next();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private boolean isParsableVersion(String vs) {
|
||||
try {
|
||||
Version.parse(vs);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// toNameAndVersion
|
||||
|
||||
public void testToNameAndVersion() {
|
||||
@ -1170,59 +1273,6 @@ public class ModuleDescriptorTest {
|
||||
}
|
||||
|
||||
|
||||
// osName
|
||||
|
||||
public void testOsName() {
|
||||
String osName = ModuleDescriptor.newModule("foo").osName("Linux").build().osName().get();
|
||||
assertEquals(osName, "Linux");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNullOsName() {
|
||||
ModuleDescriptor.newModule("foo").osName(null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testEmptyOsName() {
|
||||
ModuleDescriptor.newModule("foo").osName("");
|
||||
}
|
||||
|
||||
|
||||
// osArch
|
||||
|
||||
public void testOsArch() {
|
||||
String osArch = ModuleDescriptor.newModule("foo").osName("arm").build().osName().get();
|
||||
assertEquals(osArch, "arm");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNullOsArch() {
|
||||
ModuleDescriptor.newModule("foo").osArch(null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testEmptyOsArch() {
|
||||
ModuleDescriptor.newModule("foo").osArch("");
|
||||
}
|
||||
|
||||
|
||||
// osVersion
|
||||
|
||||
public void testOsVersion() {
|
||||
String osVersion = ModuleDescriptor.newModule("foo").osName("11.2").build().osName().get();
|
||||
assertEquals(osVersion, "11.2");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNullOsVersion() {
|
||||
ModuleDescriptor.newModule("foo").osVersion(null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testEmptyOsVersion() {
|
||||
ModuleDescriptor.newModule("foo").osVersion("");
|
||||
}
|
||||
|
||||
// reads
|
||||
|
||||
private static InputStream EMPTY_INPUT_STREAM = new InputStream() {
|
||||
@ -1239,7 +1289,9 @@ public class ModuleDescriptorTest {
|
||||
}
|
||||
};
|
||||
|
||||
// basic test reading module-info.class
|
||||
/**
|
||||
* Basic test reading module-info.class
|
||||
*/
|
||||
public void testRead() throws Exception {
|
||||
Module base = Object.class.getModule();
|
||||
|
||||
@ -1256,6 +1308,7 @@ public class ModuleDescriptorTest {
|
||||
assertEquals(descriptor.name(), "java.base");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ModuleDescriptor with a packager finder
|
||||
*/
|
||||
|
@ -373,7 +373,7 @@ public class ModuleFinderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Test ModuleModule with a JAR file containing a .class file in the top
|
||||
* Test ModuleFinder with a JAR file containing a .class file in the top
|
||||
* level directory.
|
||||
*/
|
||||
public void testOfOneJarFileWithTopLevelClass() throws Exception {
|
||||
@ -386,6 +386,7 @@ public class ModuleFinderTest {
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
|
||||
assertTrue(e.getCause().getMessage().contains("Mojo.class"));
|
||||
}
|
||||
|
||||
finder = ModuleFinder.of(jar);
|
||||
@ -394,11 +395,12 @@ public class ModuleFinderTest {
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
|
||||
assertTrue(e.getCause().getMessage().contains("Mojo.class"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ModuleModule with a JAR file containing a .class file in the top
|
||||
* Test ModuleFinder with a JAR file containing a .class file in the top
|
||||
* level directory.
|
||||
*/
|
||||
public void testOfOneExplodedModuleWithTopLevelClass() throws Exception {
|
||||
@ -411,6 +413,7 @@ public class ModuleFinderTest {
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
|
||||
assertTrue(e.getCause().getMessage().contains("Mojo.class"));
|
||||
}
|
||||
|
||||
finder = ModuleFinder.of(m_dir);
|
||||
@ -419,6 +422,7 @@ public class ModuleFinderTest {
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
|
||||
assertTrue(e.getCause().getMessage().contains("Mojo.class"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @run main/othervm ModuleFinderWithSecurityManager allow
|
||||
* @run main/othervm ModuleFinderWithSecurityManager deny
|
||||
* @summary Basic test for ModuleFinder.ofSystem() with security manager
|
||||
*/
|
||||
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModuleFinderWithSecurityManager {
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean allow = args[0].equals("allow");
|
||||
|
||||
// set security policy to allow access
|
||||
if (allow) {
|
||||
String testSrc = System.getProperty("test.src");
|
||||
if (testSrc == null)
|
||||
testSrc = ".";
|
||||
Path policyFile = Paths.get(testSrc, "java.policy");
|
||||
System.setProperty("java.security.policy", policyFile.toString());
|
||||
}
|
||||
|
||||
System.setSecurityManager(new SecurityManager());
|
||||
|
||||
ModuleFinder finder = null;
|
||||
try {
|
||||
finder = ModuleFinder.ofSystem();
|
||||
if (!allow) throw new RuntimeException("SecurityException expected");
|
||||
} catch (SecurityException e) {
|
||||
if (allow) throw new RuntimeException("SecurityException not expected");
|
||||
}
|
||||
|
||||
// no additional permissions should be required to locate modules
|
||||
if (finder != null) {
|
||||
ModuleReference base = finder.find("java.base").orElse(null);
|
||||
if (base == null)
|
||||
throw new RuntimeException("java.base not found");
|
||||
Set<ModuleReference> allModules = finder.findAll();
|
||||
if (!allModules.contains(base))
|
||||
throw new RuntimeException("java.base not in all modules");
|
||||
}
|
||||
}
|
||||
}
|
@ -79,17 +79,21 @@ public class ModuleReaderTest {
|
||||
"java/lang/Object.class"
|
||||
};
|
||||
|
||||
// resource names that should not be found in the base module
|
||||
private static final String[] BAD_BASE_RESOURCES = {
|
||||
"NotFound",
|
||||
// (directory) resources that may be in the base module
|
||||
private static final String[] MAYBE_BASE_RESOURCES = {
|
||||
"java",
|
||||
"/java",
|
||||
"//java",
|
||||
"java/",
|
||||
"java/lang",
|
||||
"java/lang/",
|
||||
};
|
||||
|
||||
// resource names that should not be found in the base module
|
||||
private static final String[] NOT_BASE_RESOURCES = {
|
||||
"NotFound",
|
||||
"/java",
|
||||
"//java",
|
||||
"/java/lang",
|
||||
"//java/lang",
|
||||
"java/lang/",
|
||||
"java//lang",
|
||||
"/java/lang/Object.class",
|
||||
"//java/lang/Object.class",
|
||||
@ -109,13 +113,17 @@ public class ModuleReaderTest {
|
||||
"p/Main.class"
|
||||
};
|
||||
|
||||
// resource names that should not be found in the test module
|
||||
private static final String[] BAD_TEST_RESOURCES = {
|
||||
"NotFound",
|
||||
// (directory) resources that may be in the test module
|
||||
private static final String[] MAYBE_TEST_RESOURCES = {
|
||||
"p",
|
||||
"p/"
|
||||
};
|
||||
|
||||
// resource names that should not be found in the test module
|
||||
private static final String[] NOT_TEST_RESOURCES = {
|
||||
"NotFound",
|
||||
"/p",
|
||||
"//p",
|
||||
"p/",
|
||||
"/p/Main.class",
|
||||
"//p/Main.class",
|
||||
"p/Main.class/",
|
||||
@ -160,11 +168,19 @@ public class ModuleReaderTest {
|
||||
testOpen(reader, name, expectedBytes);
|
||||
testRead(reader, name, expectedBytes);
|
||||
testList(reader, name);
|
||||
|
||||
}
|
||||
|
||||
// test "not found"
|
||||
for (String name : BAD_BASE_RESOURCES) {
|
||||
// test resources that may be in the base module
|
||||
for (String name : MAYBE_BASE_RESOURCES) {
|
||||
Optional<URI> ouri = reader.find(name);
|
||||
ouri.ifPresent(uri -> {
|
||||
if (name.endsWith("/"))
|
||||
assertTrue(uri.toString().endsWith("/"));
|
||||
});
|
||||
}
|
||||
|
||||
// test "not found" in java.base module
|
||||
for (String name : NOT_BASE_RESOURCES) {
|
||||
assertFalse(reader.find(name).isPresent());
|
||||
assertFalse(reader.open(name).isPresent());
|
||||
assertFalse(reader.read(name).isPresent());
|
||||
@ -261,7 +277,7 @@ public class ModuleReaderTest {
|
||||
|
||||
try (reader) {
|
||||
|
||||
// test each of the known resources in the module
|
||||
// test resources in test module
|
||||
for (String name : TEST_RESOURCES) {
|
||||
byte[] expectedBytes
|
||||
= Files.readAllBytes(MODS_DIR
|
||||
@ -274,8 +290,18 @@ public class ModuleReaderTest {
|
||||
testList(reader, name);
|
||||
}
|
||||
|
||||
// test "not found"
|
||||
for (String name : BAD_TEST_RESOURCES) {
|
||||
// test resources that may be in the test module
|
||||
for (String name : MAYBE_TEST_RESOURCES) {
|
||||
System.out.println(name);
|
||||
Optional<URI> ouri = reader.find(name);
|
||||
ouri.ifPresent(uri -> {
|
||||
if (name.endsWith("/"))
|
||||
assertTrue(uri.toString().endsWith("/"));
|
||||
});
|
||||
}
|
||||
|
||||
// test "not found" in test module
|
||||
for (String name : NOT_TEST_RESOURCES) {
|
||||
assertFalse(reader.find(name).isPresent());
|
||||
assertFalse(reader.open(name).isPresent());
|
||||
assertFalse(reader.read(name).isPresent());
|
||||
@ -394,9 +420,6 @@ public class ModuleReaderTest {
|
||||
for (String e : names) {
|
||||
assertTrue(reader.find(e).isPresent());
|
||||
}
|
||||
|
||||
// should not contain directories
|
||||
names.forEach(e -> assertFalse(e.endsWith("/")));
|
||||
}
|
||||
|
||||
}
|
||||
|
4
jdk/test/java/lang/module/java.policy
Normal file
4
jdk/test/java/lang/module/java.policy
Normal file
@ -0,0 +1,4 @@
|
||||
grant {
|
||||
// ModuleFinder.ofSystem() needs this
|
||||
permission java.lang.RuntimePermission "accessSystemModules";
|
||||
};
|
@ -45,6 +45,8 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@ -669,6 +671,258 @@ public class BasicLayerTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* does not read the target module.
|
||||
*
|
||||
* m1 { exports p to m2 }
|
||||
* m2 { }
|
||||
*/
|
||||
public void testQualifiedExports1() {
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").
|
||||
exports("p", Set.of("m2"))
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
|
||||
Configuration cf = resolve(finder1, "m1", "m2");
|
||||
|
||||
ClassLoader cl = new ClassLoader() { };
|
||||
Layer layer = Layer.empty().defineModules(cf, mn -> cl);
|
||||
assertTrue(layer.modules().size() == 2);
|
||||
|
||||
Module m1 = layer.findModule("m1").get();
|
||||
Module m2 = layer.findModule("m2").get();
|
||||
|
||||
// check m1 exports p to m2
|
||||
assertFalse(m1.isExported("p"));
|
||||
assertTrue(m1.isExported("p", m2));
|
||||
assertFalse(m1.isOpen("p", m2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* reads the target module.
|
||||
*
|
||||
* m1 { exports p to m2; }
|
||||
* m2 { requires m1; }
|
||||
*/
|
||||
public void testQualifiedExports2() {
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1")
|
||||
.exports("p", Set.of("m2"))
|
||||
.build();
|
||||
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires("m1")
|
||||
.build();
|
||||
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
|
||||
Configuration cf = resolve(finder1, "m2");
|
||||
ClassLoader cl = new ClassLoader() { };
|
||||
Layer layer = Layer.empty().defineModules(cf, mn -> cl);
|
||||
assertTrue(layer.modules().size() == 2);
|
||||
|
||||
Module m1 = layer.findModule("m1").get();
|
||||
Module m2 = layer.findModule("m2").get();
|
||||
|
||||
// check m1 exports p to m2
|
||||
assertFalse(m1.isExported("p"));
|
||||
assertTrue(m1.isExported("p", m2));
|
||||
assertFalse(m1.isOpen("p", m2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* does not read the target module in the parent layer.
|
||||
*
|
||||
* - Configuration/layer1: m1 { }
|
||||
* - Configuration/layer2: m2 { exports p to m1; }
|
||||
*/
|
||||
public void testQualifiedExports3() {
|
||||
// create layer1 with m1
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").build();
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
ClassLoader cl1 = new ClassLoader() { };
|
||||
Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1);
|
||||
assertTrue(layer1.modules().size() == 1);
|
||||
|
||||
// create layer2 with m2
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.exports("p", Set.of("m1"))
|
||||
.build();
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
|
||||
Configuration cf2 = resolve(cf1, finder2, "m2");
|
||||
ClassLoader cl2 = new ClassLoader() { };
|
||||
Layer layer2 = layer1.defineModules(cf2, mn -> cl2);
|
||||
assertTrue(layer2.modules().size() == 1);
|
||||
|
||||
Module m1 = layer1.findModule("m1").get();
|
||||
Module m2 = layer2.findModule("m2").get();
|
||||
|
||||
// check m2 exports p to layer1/m1
|
||||
assertFalse(m2.isExported("p"));
|
||||
assertTrue(m2.isExported("p", m1));
|
||||
assertFalse(m2.isOpen("p", m1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* reads the target module in the parent layer.
|
||||
*
|
||||
* - Configuration/layer1: m1 { }
|
||||
* - Configuration/layer2: m2 { requires m1; exports p to m1; }
|
||||
*/
|
||||
public void testQualifiedExports4() {
|
||||
// create layer1 with m1
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").build();
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
ClassLoader cl1 = new ClassLoader() { };
|
||||
Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1);
|
||||
assertTrue(layer1.modules().size() == 1);
|
||||
|
||||
// create layer2 with m2
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires("m1")
|
||||
.exports("p", Set.of("m1"))
|
||||
.build();
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
|
||||
Configuration cf2 = resolve(cf1, finder2, "m2");
|
||||
ClassLoader cl2 = new ClassLoader() { };
|
||||
Layer layer2 = layer1.defineModules(cf2, mn -> cl2);
|
||||
assertTrue(layer2.modules().size() == 1);
|
||||
|
||||
Module m1 = layer1.findModule("m1").get();
|
||||
Module m2 = layer2.findModule("m2").get();
|
||||
|
||||
// check m2 exports p to layer1/m1
|
||||
assertFalse(m2.isExported("p"));
|
||||
assertTrue(m2.isExported("p", m1));
|
||||
assertFalse(m2.isOpen("p", m1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* does not read the target module.
|
||||
*
|
||||
* - Configuration/layer1: m1
|
||||
* - Configuration/layer2: m1, m2 { exports p to m1; }
|
||||
*/
|
||||
public void testQualifiedExports5() {
|
||||
// create layer1 with m1
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").build();
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
ClassLoader cl1 = new ClassLoader() { };
|
||||
Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1);
|
||||
assertTrue(layer1.modules().size() == 1);
|
||||
|
||||
// create layer2 with m1 and m2
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2").exports("p", Set.of("m1")).build();
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
Configuration cf2 = resolve(cf1, finder2, "m1", "m2");
|
||||
ClassLoader cl2 = new ClassLoader() { };
|
||||
Layer layer2 = layer1.defineModules(cf2, mn -> cl2);
|
||||
assertTrue(layer2.modules().size() == 2);
|
||||
|
||||
Module m1_v1 = layer1.findModule("m1").get();
|
||||
Module m1_v2 = layer2.findModule("m1").get();
|
||||
Module m2 = layer2.findModule("m2").get();
|
||||
|
||||
// check m2 exports p to layer2/m2
|
||||
assertFalse(m2.isExported("p"));
|
||||
assertTrue(m2.isExported("p", m1_v2));
|
||||
assertFalse(m2.isExported("p", m1_v1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The module exporting the package
|
||||
* reads the target module in the parent layer (due to requires transitive).
|
||||
*
|
||||
* - Configuration/layer1: m1, m2 { requires transitive m1; }
|
||||
* - Configuration/layer2: m1, m3 { requires m2; exports p to m1; }
|
||||
*/
|
||||
public void testQualifiedExports6() {
|
||||
// create layer1 with m1 and m2
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").build();
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
|
||||
.build();
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
Configuration cf1 = resolve(finder1, "m2");
|
||||
ClassLoader loader1 = new ClassLoader() { };
|
||||
Layer layer1 = Layer.empty().defineModules(cf1, mn -> loader1);
|
||||
assertTrue(layer1.modules().size() == 2);
|
||||
|
||||
// create layer2 with m1 and m3
|
||||
ModuleDescriptor descriptor3 = newBuilder("m3")
|
||||
.requires("m2")
|
||||
.exports("p", Set.of("m1"))
|
||||
.build();
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3);
|
||||
Configuration cf2 = resolve(cf1, finder2, "m1", "m3");
|
||||
ClassLoader loader2 = new ClassLoader() { };
|
||||
Layer layer2 = layer1.defineModules(cf2, mn -> loader2);
|
||||
assertTrue(layer2.modules().size() == 2);
|
||||
|
||||
Module m1_v1 = layer1.findModule("m1").get();
|
||||
Module m2 = layer1.findModule("m2").get();
|
||||
|
||||
Module m1_v2 = layer2.findModule("m1").get();
|
||||
Module m3 = layer2.findModule("m3").get();
|
||||
|
||||
assertTrue(m3.canRead(m1_v1));
|
||||
assertFalse(m3.canRead(m1_v2));
|
||||
|
||||
assertFalse(m3.isExported("p"));
|
||||
assertTrue(m3.isExported("p", m1_v1));
|
||||
assertFalse(m3.isExported("p", m1_v2));
|
||||
assertFalse(m3.isExported("p", m2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test layers with a qualified export. The target module is not in any layer.
|
||||
*
|
||||
* - Configuration/layer1: m1 { }
|
||||
* - Configuration/layer2: m2 { exports p to m3; }
|
||||
*/
|
||||
public void testQualifiedExports7() {
|
||||
// create layer1 with m1
|
||||
ModuleDescriptor descriptor1 = newBuilder("m1").build();
|
||||
ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
|
||||
Configuration cf1 = resolve(finder1, "m1");
|
||||
ClassLoader cl1 = new ClassLoader() { };
|
||||
Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1);
|
||||
assertTrue(layer1.modules().size() == 1);
|
||||
|
||||
// create layer2 with m2
|
||||
ModuleDescriptor descriptor2 = newBuilder("m2")
|
||||
.exports("p", Set.of("m3"))
|
||||
.build();
|
||||
ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
|
||||
Configuration cf2 = resolve(cf1, finder2, "m2");
|
||||
ClassLoader cl2 = new ClassLoader() { };
|
||||
Layer layer2 = layer1.defineModules(cf2, mn -> cl2);
|
||||
assertTrue(layer2.modules().size() == 1);
|
||||
|
||||
Module m1 = layer1.findModule("m1").get();
|
||||
Module m2 = layer2.findModule("m2").get();
|
||||
|
||||
// check m2 does not export p to anyone
|
||||
assertFalse(m2.isExported("p"));
|
||||
assertFalse(m2.isExported("p", m1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to use Layer defineModules to create a layer with a module
|
||||
* defined to a class loader that already has a module of the same name
|
||||
@ -796,29 +1050,31 @@ public class BasicLayerTest {
|
||||
}
|
||||
|
||||
|
||||
@DataProvider(name = "javaPackages")
|
||||
public Object[][] javaPackages() {
|
||||
return new Object[][] { { "m1", "java" }, { "m2", "java.x" } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a Layer with a module containing a "java." package.
|
||||
* Attempt to create a Layer with a module containing a "java" package.
|
||||
* This should only be allowed when the module is defined to the platform
|
||||
* class loader.
|
||||
*/
|
||||
@Test(enabled = false)
|
||||
public void testLayerWithJavaPackage() {
|
||||
ModuleDescriptor descriptor = newBuilder("foo")
|
||||
.packages(Set.of("java.foo"))
|
||||
.build();
|
||||
|
||||
@Test(dataProvider = "javaPackages")
|
||||
public void testLayerWithJavaPackage(String mn, String pn) {
|
||||
ModuleDescriptor descriptor = newBuilder(mn).packages(Set.of(pn)).build();
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor);
|
||||
|
||||
Configuration cf = Layer.boot()
|
||||
.configuration()
|
||||
.resolve(finder, ModuleFinder.of(), Set.of("foo"));
|
||||
.resolve(finder, ModuleFinder.of(), Set.of(mn));
|
||||
assertTrue(cf.modules().size() == 1);
|
||||
|
||||
ClassLoader pcl = ClassLoader.getPlatformClassLoader();
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
try {
|
||||
Layer.boot().defineModules(cf, mn -> new ClassLoader() { });
|
||||
Layer.boot().defineModules(cf, _mn -> new ClassLoader() { });
|
||||
assertTrue(false);
|
||||
} catch (LayerInstantiationException e) { }
|
||||
|
||||
@ -833,13 +1089,13 @@ public class BasicLayerTest {
|
||||
} catch (LayerInstantiationException e) { }
|
||||
|
||||
// create layer with module defined to platform class loader
|
||||
Layer layer = Layer.boot().defineModules(cf, mn -> pcl);
|
||||
Optional<Module> om = layer.findModule("foo");
|
||||
Layer layer = Layer.boot().defineModules(cf, _mn -> pcl);
|
||||
Optional<Module> om = layer.findModule(mn);
|
||||
assertTrue(om.isPresent());
|
||||
Module foo = om.get();
|
||||
assertTrue(foo.getClassLoader() == pcl);
|
||||
assertTrue(foo.getPackages().length == 1);
|
||||
assertTrue(foo.getPackages()[0].equals("java.foo"));
|
||||
assertTrue(foo.getPackages()[0].equals(pn));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,5 +23,6 @@
|
||||
|
||||
grant {
|
||||
permission java.lang.RuntimePermission "getClassLoader";
|
||||
permission java.io.FilePermission "${java.home}/-", "read";
|
||||
permission java.lang.RuntimePermission "accessSystemModules";
|
||||
permission java.io.FilePermission "${java.home}/modules/-", "read"; // exploded build
|
||||
};
|
||||
|
@ -76,6 +76,7 @@ if [ \! -d $EXTD ]; then
|
||||
(cd $JARD; "$JAR" ${TESTTOOLVMOPTS} -cf ../p$n.jar *)
|
||||
done
|
||||
|
||||
cp p2.jar p2dup.jar
|
||||
mv p3.jar $EXTD
|
||||
|
||||
cp $TESTCLASSES/Load.class $TESTD
|
||||
@ -117,6 +118,8 @@ go "$TESTD" "" FooProvider1
|
||||
|
||||
go ".${SEP}p2.jar" "" FooProvider2
|
||||
|
||||
go ".${SEP}p2.jar${SEP}p2dup.jar" "" FooProvider2
|
||||
|
||||
go "${P3JAR}${SEP}p2.jar" "" FooProvider3 FooProvider2
|
||||
|
||||
go "$TESTD${SEP}p2.jar" "" FooProvider1 FooProvider2
|
||||
|
@ -1,3 +1,3 @@
|
||||
grant {
|
||||
permission java.io.FilePermission "${java.home}/-", "read";
|
||||
permission java.lang.RuntimePermission "accessSystemModules";
|
||||
};
|
||||
|
101
jdk/test/lib/testlibrary/ModuleTargetHelper.java
Normal file
101
jdk/test/lib/testlibrary/ModuleTargetHelper.java
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import jdk.internal.module.ClassFileConstants;
|
||||
import jdk.internal.module.ClassFileAttributes;
|
||||
import jdk.internal.module.ClassFileAttributes.ModuleTargetAttribute;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
public class ModuleTargetHelper {
|
||||
private ModuleTargetHelper() {}
|
||||
|
||||
public static final class ModuleTarget {
|
||||
private String osName, osArch;
|
||||
|
||||
public ModuleTarget(String osName, String osArch) {
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
}
|
||||
|
||||
public String osName() {
|
||||
return osName;
|
||||
}
|
||||
|
||||
public String osArch() {
|
||||
return osArch;
|
||||
}
|
||||
}
|
||||
|
||||
public static ModuleTarget getJavaBaseTarget() throws IOException {
|
||||
Path p = Paths.get(URI.create("jrt:/modules/java.base/module-info.class"));
|
||||
try (InputStream in = Files.newInputStream(p)) {
|
||||
return read(in);
|
||||
}
|
||||
}
|
||||
|
||||
public static ModuleTarget read(InputStream in) throws IOException {
|
||||
ModuleTargetAttribute[] modTargets = new ModuleTargetAttribute[1];
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
if (attr instanceof ModuleTargetAttribute) {
|
||||
modTargets[0] = (ModuleTargetAttribute)attr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// prototype of attributes that should be parsed
|
||||
Attribute[] attrs = new Attribute[] {
|
||||
new ModuleTargetAttribute()
|
||||
};
|
||||
|
||||
// parse module-info.class
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(cv, attrs, 0);
|
||||
if (modTargets[0] != null) {
|
||||
return new ModuleTarget(modTargets[0].osName(), modTargets[0].osArch());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ModuleTarget read(ModuleReference modRef) throws IOException {
|
||||
ModuleReader reader = modRef.open();
|
||||
try (InputStream in = reader.open("module-info.class").get()) {
|
||||
return read(in);
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
grant {
|
||||
permission java.io.FilePermission "${java.home}/-", "read";
|
||||
permission java.lang.RuntimePermission "accessSystemModules";
|
||||
};
|
||||
|
@ -28,6 +28,7 @@
|
||||
* java.security.jgss/sun.security.krb5.internal:+open
|
||||
* java.security.jgss/sun.security.jgss
|
||||
* java.security.jgss/sun.security.krb5:+open
|
||||
* java.security.jgss/sun.security.krb5.internal.ccache
|
||||
* java.security.jgss/sun.security.krb5.internal.crypto
|
||||
* java.security.jgss/sun.security.krb5.internal.ktab
|
||||
* jdk.security.auth
|
||||
|
@ -214,7 +214,8 @@ public class IntegrationTest {
|
||||
checkReleaseProperty(props, "JAVA_FULL_VERSION");
|
||||
checkReleaseProperty(props, "OS_NAME");
|
||||
checkReleaseProperty(props, "OS_ARCH");
|
||||
checkReleaseProperty(props, "OS_VERSION");
|
||||
// OS_VERSION is added from makefile. We're testing API-way to create image here!
|
||||
// checkReleaseProperty(props, "OS_VERSION");
|
||||
|
||||
if (!Files.exists(output.resolve("toto.txt"))) {
|
||||
throw new AssertionError("Post processing not called");
|
||||
|
@ -193,7 +193,7 @@ public class JLinkNegativeTest {
|
||||
.output(imageFile)
|
||||
.addMods("not_zip")
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.call().assertFailure("Error: java.io.IOException: Invalid jmod file");
|
||||
.call().assertFailure("Error: java.io.IOException: Invalid JMOD file");
|
||||
} finally {
|
||||
deleteDirectory(jmod);
|
||||
}
|
||||
|
@ -81,9 +81,13 @@ public class CompiledVersionTest {
|
||||
Path msrc = SRC_DIR.resolve(mn);
|
||||
if (version.equals("0")) {
|
||||
assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
|
||||
"--add-exports", "java.base/jdk.internal.module=m1",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1",
|
||||
"--module-source-path", SRC_DIR.toString()));
|
||||
} else {
|
||||
assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
|
||||
"--add-exports", "java.base/jdk.internal.module=m1",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1",
|
||||
"--module-source-path", SRC_DIR.toString(),
|
||||
"--module-version", version));
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.*;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -40,7 +42,11 @@ import static org.testng.Assert.*;
|
||||
/**
|
||||
* @test
|
||||
* @bug 8142968 8173381
|
||||
* @library /lib/testlibrary
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @modules java.base/jdk.internal.module
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @build ModuleTargetHelper
|
||||
* @run testng SystemModulesTest
|
||||
* @summary Verify the properties of ModuleDescriptor created
|
||||
* by SystemModules
|
||||
@ -62,7 +68,6 @@ public class SystemModulesTest {
|
||||
return;
|
||||
|
||||
ModuleFinder.ofSystem().findAll().stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.forEach(this::checkAttributes);
|
||||
}
|
||||
|
||||
@ -72,9 +77,7 @@ public class SystemModulesTest {
|
||||
if (name.equals(OS_NAME))
|
||||
return true;
|
||||
|
||||
if (OS_NAME.equals("Mac OS X")) {
|
||||
return name.equals("Darwin");
|
||||
} else if (OS_NAME.startsWith("Windows")) {
|
||||
if (OS_NAME.startsWith("Windows")) {
|
||||
return name.startsWith("Windows");
|
||||
} else {
|
||||
System.err.println("ERROR: " + name + " but expected: " + OS_NAME);
|
||||
@ -89,28 +92,28 @@ public class SystemModulesTest {
|
||||
switch (OS_ARCH) {
|
||||
case "i386":
|
||||
case "x86":
|
||||
return name.equals("i586");
|
||||
return name.equals("x86");
|
||||
case "amd64":
|
||||
return name.equals("x86_64");
|
||||
default:
|
||||
System.err.println("ERROR: " + name + " but expected: " + OS_ARCH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAttributes(ModuleDescriptor md) {
|
||||
System.out.format("%s %s %s %s%n", md.name(),
|
||||
md.osName(), md.osArch(), md.osVersion());
|
||||
|
||||
if (md.name().equals("java.base")) {
|
||||
assertTrue(checkOSName(md.osName().get()));
|
||||
assertTrue(checkOSArch(md.osArch().get()));
|
||||
assertTrue(md.osVersion().isPresent());
|
||||
} else {
|
||||
// target platform attribute is dropped by jlink plugin
|
||||
assertFalse(md.osName().isPresent());
|
||||
assertFalse(md.osArch().isPresent());
|
||||
assertFalse(md.osVersion().isPresent());
|
||||
assertTrue(md.packages().size() > 0
|
||||
|| EMPTY_MODULES.contains(md.name()), md.name());
|
||||
private void checkAttributes(ModuleReference modRef) {
|
||||
try {
|
||||
if (modRef.descriptor().name().equals("java.base")) {
|
||||
ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
|
||||
assertTrue(checkOSName(mt.osName()));
|
||||
assertTrue(checkOSArch(mt.osArch()));
|
||||
} else {
|
||||
// target platform attribute is dropped by jlink plugin for other modules
|
||||
ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
|
||||
assertTrue(mt == null || (mt.osName() == null && mt.osArch() == null));
|
||||
}
|
||||
} catch (IOException exp) {
|
||||
throw new UncheckedIOException(exp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ import jdk.testlibrary.FileUtils;
|
||||
|
||||
import static jdk.testlibrary.ProcessTools.*;
|
||||
|
||||
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
@ -48,7 +47,9 @@ import static org.testng.Assert.*;
|
||||
* @bug 8142968 8173381 8174740
|
||||
* @library /lib/testlibrary
|
||||
* @modules jdk.compiler jdk.jlink
|
||||
* @build UserModuleTest CompilerUtils jdk.testlibrary.FileUtils jdk.testlibrary.ProcessTools
|
||||
* @modules java.base/jdk.internal.module
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @build ModuleTargetHelper UserModuleTest CompilerUtils jdk.testlibrary.FileUtils jdk.testlibrary.ProcessTools
|
||||
* @run testng UserModuleTest
|
||||
*/
|
||||
|
||||
@ -85,7 +86,9 @@ public class UserModuleTest {
|
||||
for (String mn : modules) {
|
||||
Path msrc = SRC_DIR.resolve(mn);
|
||||
assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
|
||||
"--module-source-path", SRC_DIR.toString()));
|
||||
"--module-source-path", SRC_DIR.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=" + mn,
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=" + mn));
|
||||
}
|
||||
|
||||
if (Files.exists(IMAGE)) {
|
||||
@ -106,7 +109,10 @@ public class UserModuleTest {
|
||||
if (!hasJmods()) return;
|
||||
|
||||
Path java = IMAGE.resolve("bin").resolve("java");
|
||||
assertTrue(executeProcess(java.toString(), "-m", MAIN_MID)
|
||||
assertTrue(executeProcess(java.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=m1,m4",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
|
||||
"-m", MAIN_MID)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
.getExitValue() == 0);
|
||||
@ -136,6 +142,8 @@ public class UserModuleTest {
|
||||
|
||||
Path java = IMAGE.resolve("bin").resolve("java");
|
||||
assertTrue(executeProcess(java.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=m1,m4",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
|
||||
"-Djdk.system.module.finder.disabledFastPath",
|
||||
"-m", MAIN_MID)
|
||||
.outputTo(System.out)
|
||||
@ -154,7 +162,10 @@ public class UserModuleTest {
|
||||
Path dir = Paths.get("dedupSetTest");
|
||||
createImage(dir, "m1", "m2", "m3", "m4");
|
||||
Path java = dir.resolve("bin").resolve("java");
|
||||
assertTrue(executeProcess(java.toString(), "-m", MAIN_MID)
|
||||
assertTrue(executeProcess(java.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=m1,m4",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
|
||||
"-m", MAIN_MID)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
.getExitValue() == 0);
|
||||
@ -205,11 +216,13 @@ public class UserModuleTest {
|
||||
}
|
||||
|
||||
private void createJmods(String... modules) throws IOException {
|
||||
// use the same target platform as in java.base
|
||||
ModuleDescriptor md = Layer.boot().findModule("java.base").get()
|
||||
.getDescriptor();
|
||||
String osName = md.osName().get();
|
||||
String osArch = md.osArch().get();
|
||||
ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.getJavaBaseTarget();
|
||||
if (mt == null) {
|
||||
throw new RuntimeException("ModuleTarget is missing for java.base");
|
||||
}
|
||||
|
||||
String osName = mt.osName();
|
||||
String osArch = mt.osArch();
|
||||
|
||||
// create JMOD files
|
||||
Files.createDirectories(JMODS_DIR);
|
||||
@ -246,6 +259,8 @@ public class UserModuleTest {
|
||||
// verify ModuleDescriptor
|
||||
Path java = dir.resolve("bin").resolve("java");
|
||||
assertTrue(executeProcess(java.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=m1,m4",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
|
||||
"--add-modules=m1", "-m", "m4")
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
@ -275,6 +290,8 @@ public class UserModuleTest {
|
||||
// verify ModuleDescriptor
|
||||
Path java = dir.resolve("bin").resolve("java");
|
||||
assertTrue(executeProcess(java.toString(),
|
||||
"--add-exports", "java.base/jdk.internal.module=m1,m4",
|
||||
"--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
|
||||
"--add-modules=m1", "-m", "m4", "retainModuleTarget")
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
package p1;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Layer;
|
||||
@ -35,7 +36,38 @@ import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.module.ClassFileAttributes;
|
||||
import jdk.internal.module.ClassFileAttributes.ModuleTargetAttribute;
|
||||
import jdk.internal.module.ClassFileConstants;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
public class Main {
|
||||
private static boolean hasModuleTarget(InputStream in) throws IOException {
|
||||
ModuleTargetAttribute[] modTargets = new ModuleTargetAttribute[1];
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
if (attr instanceof ModuleTargetAttribute) {
|
||||
modTargets[0] = (ModuleTargetAttribute)attr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// prototype of attributes that should be parsed
|
||||
Attribute[] attrs = new Attribute[] {
|
||||
new ModuleTargetAttribute()
|
||||
};
|
||||
|
||||
// parse module-info.class
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(cv, attrs, 0);
|
||||
return modTargets[0] != null &&
|
||||
(modTargets[0].osName() != null || modTargets[0].osArch() != null);
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
// load another package
|
||||
p2.T.test();
|
||||
@ -44,12 +76,13 @@ public class Main {
|
||||
validate(Main.class.getModule());
|
||||
|
||||
// validate the Moduletarget attribute for java.base
|
||||
ModuleDescriptor md = Layer.boot().findModule("java.base").get()
|
||||
.getDescriptor();
|
||||
if (!md.osName().isPresent() || !md.osArch().isPresent() ||
|
||||
!md.osVersion().isPresent()) {
|
||||
throw new RuntimeException("java.base: " + md.osName() + " " +
|
||||
md.osArch() + " " + md.osVersion());
|
||||
FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
|
||||
Collections.emptyMap());
|
||||
Path path = fs.getPath("/", "modules", "java.base", "module-info.class");
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
if (! hasModuleTarget(in)) {
|
||||
throw new RuntimeException("Missing ModuleTarget for java.base");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,9 +100,9 @@ public class Main {
|
||||
checkPackages(md.packages(), "p1", "p2");
|
||||
checkPackages(md1.packages(), "p1", "p2");
|
||||
|
||||
// check ModuleTarget attribute
|
||||
checkModuleTargetAttribute(md);
|
||||
checkModuleTargetAttribute(md1);
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
checkModuleTargetAttribute(in, "p1");
|
||||
}
|
||||
}
|
||||
|
||||
static void checkPackages(Set<String> pkgs, String... expected) {
|
||||
@ -78,10 +111,9 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
static void checkModuleTargetAttribute(ModuleDescriptor md) {
|
||||
if (md.osName().isPresent() || md.osArch().isPresent() ||
|
||||
md.osVersion().isPresent()) {
|
||||
throw new RuntimeException(md.osName() + " " + md.osArch() + " " + md.osVersion());
|
||||
static void checkModuleTargetAttribute(InputStream in, String modName) throws IOException {
|
||||
if (hasModuleTarget(in)) {
|
||||
throw new RuntimeException("ModuleTarget present for " + modName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,47 @@ import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.module.ClassFileAttributes;
|
||||
import jdk.internal.module.ClassFileAttributes.ModuleTargetAttribute;
|
||||
import jdk.internal.module.ClassFileConstants;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
public class Main {
|
||||
private static boolean hasModuleTarget(InputStream in) throws IOException {
|
||||
ModuleTargetAttribute[] modTargets = new ModuleTargetAttribute[1];
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
if (attr instanceof ModuleTargetAttribute) {
|
||||
modTargets[0] = (ModuleTargetAttribute)attr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// prototype of attributes that should be parsed
|
||||
Attribute[] attrs = new Attribute[] {
|
||||
new ModuleTargetAttribute()
|
||||
};
|
||||
|
||||
// parse module-info.class
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(cv, attrs, 0);
|
||||
return modTargets[0] != null &&
|
||||
(modTargets[0].osName() != null || modTargets[0].osArch() != null);
|
||||
}
|
||||
|
||||
private static boolean hasModuleTarget(String modName) throws IOException {
|
||||
FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
|
||||
Collections.emptyMap());
|
||||
Path path = fs.getPath("/", "modules", modName, "module-info.class");
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
return hasModuleTarget(in);
|
||||
}
|
||||
}
|
||||
|
||||
// the system module plugin by default drops ModuleTarget attribute
|
||||
private static boolean expectModuleTarget = false;
|
||||
public static void main(String... args) throws IOException {
|
||||
@ -49,13 +89,8 @@ public class Main {
|
||||
}
|
||||
|
||||
// java.base is packaged with osName/osArch/osVersion
|
||||
ModuleDescriptor md = Layer.boot().findModule("java.base").get()
|
||||
.getDescriptor();
|
||||
if (!md.osName().isPresent() ||
|
||||
!md.osArch().isPresent() ||
|
||||
!md.osVersion().isPresent()) {
|
||||
throw new RuntimeException("osName/osArch/osVersion is missing: " +
|
||||
md.osName() + " " + md.osArch() + " " + md.osVersion());
|
||||
if (! hasModuleTarget("java.base")) {
|
||||
throw new RuntimeException("ModuleTarget absent for java.base");
|
||||
}
|
||||
|
||||
// verify module-info.class for m1 and m4
|
||||
@ -82,7 +117,7 @@ public class Main {
|
||||
checkModuleDescriptor(ModuleDescriptor.read(Files.newInputStream(path)), packages);
|
||||
}
|
||||
|
||||
static void checkModuleDescriptor(ModuleDescriptor md, String... packages) {
|
||||
static void checkModuleDescriptor(ModuleDescriptor md, String... packages) throws IOException {
|
||||
String mainClass = md.name().replace('m', 'p') + ".Main";
|
||||
if (!md.mainClass().get().equals(mainClass)) {
|
||||
throw new RuntimeException(md.mainClass().toString());
|
||||
@ -90,22 +125,16 @@ public class Main {
|
||||
|
||||
if (expectModuleTarget) {
|
||||
// ModuleTarget attribute is retained
|
||||
if (!md.osName().isPresent() || !md.osArch().isPresent()) {
|
||||
throw new RuntimeException("osName or osArch is missing: " +
|
||||
md.osName() + " " + md.osArch());
|
||||
if (! hasModuleTarget(md.name())) {
|
||||
throw new RuntimeException("ModuleTarget missing for " + md.name());
|
||||
}
|
||||
} else {
|
||||
// by default ModuleTarget attribute is dropped
|
||||
if (md.osName().isPresent() || md.osArch().isPresent()) {
|
||||
throw new RuntimeException("osName and osArch should not be set: " +
|
||||
md.osName() + " " + md.osArch());
|
||||
if (hasModuleTarget(md.name())) {
|
||||
throw new RuntimeException("ModuleTarget present for " + md.name());
|
||||
}
|
||||
}
|
||||
|
||||
if (md.osVersion().isPresent()) {
|
||||
throw new RuntimeException("Expected no osVersion set: " + md.osVersion());
|
||||
}
|
||||
|
||||
Set<String> pkgs = md.packages();
|
||||
if (!pkgs.equals(Set.of(packages))) {
|
||||
throw new RuntimeException(pkgs + " expected: " + Set.of(packages));
|
||||
|
@ -134,16 +134,16 @@ public class AddExportsTestWarningError {
|
||||
return new Object[][]{
|
||||
|
||||
// source not found
|
||||
{"DoesNotExist/p=m1", "WARNING: Unknown module: DoesNotExist specified in --add-exports"},
|
||||
{"DoesNotExist/p=m1", "WARNING: Unknown module: DoesNotExist specified to --add-exports"},
|
||||
{"m1/DoesNotExist=m2", "WARNING: package DoesNotExist not in m1"},
|
||||
|
||||
// target not found
|
||||
{"m1/p1=DoesNotExist", "WARNING: Unknown module: DoesNotExist specified in --add-exports"},
|
||||
{"m1/p1=DoesNotExist", "WARNING: Unknown module: DoesNotExist specified to --add-exports"},
|
||||
|
||||
// bad names
|
||||
{"m*/p1=m2", "WARNING: Unknown module: m* specified in --add-exports"},
|
||||
{"m*/p1=m2", "WARNING: Unknown module: m* specified to --add-exports"},
|
||||
{"m1/p!=m2", "WARNING: package p! not in m1"},
|
||||
{"m1/p1=m!", "WARNING: Unknown module: m! specified in --add-exports"},
|
||||
{"m1/p1=m!", "WARNING: Unknown module: m! specified to --add-exports"},
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -168,14 +168,14 @@ public class AddReadsTestWarningError {
|
||||
return new Object[][]{
|
||||
|
||||
// source not found
|
||||
{"DoesNotExist=m2", "WARNING: Unknown module: DoesNotExist specified in --add-reads"},
|
||||
{"DoesNotExist=m2", "WARNING: Unknown module: DoesNotExist specified to --add-reads"},
|
||||
|
||||
// target not found
|
||||
{"m2=DoesNotExist", "WARNING: Unknown module: DoesNotExist specified in --add-reads"},
|
||||
{"m2=DoesNotExist", "WARNING: Unknown module: DoesNotExist specified to --add-reads"},
|
||||
|
||||
// bad names
|
||||
{"m*=m2", "WARNING: Unknown module: m* specified in --add-reads"},
|
||||
{"m2=m!", "WARNING: Unknown module: m! specified in --add-reads"},
|
||||
{"m*=m2", "WARNING: Unknown module: m* specified to --add-reads"},
|
||||
{"m2=m!", "WARNING: Unknown module: m! specified to --add-reads"},
|
||||
|
||||
};
|
||||
}
|
||||
|
116
jdk/test/tools/launcher/modules/basic/InitErrors.java
Normal file
116
jdk/test/tools/launcher/modules/basic/InitErrors.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @build InitErrors jdk.testlibrary.*
|
||||
* @run testng InitErrors
|
||||
* @summary Basic test to ensure that module system initialization errors
|
||||
* go the right stream and with the right level of verbosity
|
||||
*/
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
import jdk.testlibrary.OutputAnalyzer;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class InitErrors {
|
||||
|
||||
// the option to cause module initialization to fail
|
||||
private static final String ADD_UNKNOWN_MODULE = "--add-modules=XXX";
|
||||
|
||||
// the expected error message
|
||||
private static final String UNKNOWN_MODULE_NOT_FOUND= "Module XXX not found";
|
||||
|
||||
// output expected in the stack trace when using -Xlog:init=debug
|
||||
private static final String STACK_FRAME = "java.base/java.lang.System.initPhase2";
|
||||
|
||||
|
||||
/**
|
||||
* Default behavior, send error message to stdout
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultOutput() throws Exception {
|
||||
expectFail(showVersion(ADD_UNKNOWN_MODULE)
|
||||
.stdoutShouldContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stdoutShouldNotContain(STACK_FRAME)
|
||||
.stderrShouldNotContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stderrShouldNotContain(STACK_FRAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* -XX:+DisplayVMOutputToStderr should send error message to stderr
|
||||
*/
|
||||
@Test
|
||||
public void testOutputToStderr() throws Exception {
|
||||
expectFail(showVersion(ADD_UNKNOWN_MODULE, "-XX:+DisplayVMOutputToStderr")
|
||||
.stdoutShouldNotContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stdoutShouldNotContain(STACK_FRAME)
|
||||
.stderrShouldContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stderrShouldNotContain(STACK_FRAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* -Xlog:init=debug should print stack trace to stdout
|
||||
*/
|
||||
@Test
|
||||
public void testStackTrace() throws Exception {
|
||||
expectFail(showVersion(ADD_UNKNOWN_MODULE, "-Xlog:init=debug")
|
||||
.stdoutShouldContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stdoutShouldContain(STACK_FRAME)
|
||||
.stderrShouldNotContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stderrShouldNotContain(STACK_FRAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* -Xlog:init=debug -XX:+DisplayVMOutputToStderr should print stack trace
|
||||
* to stderr
|
||||
*/
|
||||
@Test
|
||||
public void testStackTraceToStderr() throws Exception {
|
||||
expectFail(showVersion(ADD_UNKNOWN_MODULE,
|
||||
"-Xlog:init=debug",
|
||||
"-XX:+DisplayVMOutputToStderr")
|
||||
.stdoutShouldNotContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stdoutShouldNotContain(STACK_FRAME)
|
||||
.stderrShouldContain(UNKNOWN_MODULE_NOT_FOUND)
|
||||
.stderrShouldContain(STACK_FRAME));
|
||||
}
|
||||
|
||||
private OutputAnalyzer showVersion(String... args) throws Exception {
|
||||
int len = args.length;
|
||||
args = Arrays.copyOf(args, len+1);
|
||||
args[len] = "-version";
|
||||
return ProcessTools.executeTestJava(args)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out);
|
||||
}
|
||||
|
||||
private void expectFail(OutputAnalyzer output) {
|
||||
assertFalse(output.getExitValue() == 0);
|
||||
}
|
||||
|
||||
}
|
@ -179,7 +179,7 @@ public class PatchTestWarningError {
|
||||
"-m", "test/jdk.test.Main", arg)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
.shouldContain("WARNING: Unknown module: DoesNotExist specified in --patch-module")
|
||||
.shouldContain("WARNING: Unknown module: DoesNotExist specified to --patch-module")
|
||||
.getExitValue();
|
||||
|
||||
assertTrue(exitValue == 0);
|
||||
|
68
jdk/test/tools/launcher/modules/permit/AttemptAccess.java
Normal file
68
jdk/test/tools/launcher/modules/permit/AttemptAccess.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Launched by PermitIllegalAccess to attempt illegal access.
|
||||
*/
|
||||
|
||||
public class AttemptAccess {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String action = args[0];
|
||||
int count = Integer.parseInt(args[1]);
|
||||
|
||||
for (int i=0; i<count; i++) {
|
||||
switch (action) {
|
||||
case "access":
|
||||
tryAccess();
|
||||
break;
|
||||
case "setAccessible":
|
||||
trySetAccessible();
|
||||
break;
|
||||
case "trySetAccessible":
|
||||
tryTrySetAccessible();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tryAccess() throws Exception {
|
||||
Class<?> clazz = Class.forName("sun.security.x509.X500Name");
|
||||
Constructor<?> ctor = clazz.getConstructor(String.class);
|
||||
Object name = ctor.newInstance("CN=user");
|
||||
}
|
||||
|
||||
static void trySetAccessible() throws Exception {
|
||||
Method find = ClassLoader.class.getDeclaredMethod("findClass", String.class);
|
||||
find.setAccessible(true);
|
||||
}
|
||||
|
||||
static void tryTrySetAccessible() throws Exception {
|
||||
Method find = ClassLoader.class.getDeclaredMethod("findClass", String.class);
|
||||
find.trySetAccessible();
|
||||
}
|
||||
|
||||
}
|
208
jdk/test/tools/launcher/modules/permit/PermitIllegalAccess.java
Normal file
208
jdk/test/tools/launcher/modules/permit/PermitIllegalAccess.java
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @build PermitIllegalAccess AttemptAccess jdk.testlibrary.*
|
||||
* @run testng PermitIllegalAccess
|
||||
* @summary Basic test for java --permit-illegal-access
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
import jdk.testlibrary.OutputAnalyzer;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* Basic test of --permit-illegal-access to ensure that it permits access
|
||||
* via core reflection and setAccessible/trySetAccessible.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public class PermitIllegalAccess {
|
||||
|
||||
static final String TEST_CLASSES = System.getProperty("test.classes");
|
||||
static final String TEST_MAIN = "AttemptAccess";
|
||||
|
||||
static final String WARNING = "WARNING";
|
||||
static final String STARTUP_WARNING =
|
||||
"WARNING: --permit-illegal-access will be removed in the next major release";
|
||||
static final String ILLEGAL_ACCESS_WARNING =
|
||||
"WARNING: Illegal access by " + TEST_MAIN;
|
||||
|
||||
/**
|
||||
* Launches AttemptAccess to execute an action, returning the OutputAnalyzer
|
||||
* to analyze the output/exitCode.
|
||||
*/
|
||||
private OutputAnalyzer tryAction(String action, int count) throws Exception {
|
||||
String arg = "" + count;
|
||||
return ProcessTools
|
||||
.executeTestJava("-cp", TEST_CLASSES, TEST_MAIN, action, arg)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches AttemptAccess with --permit-illegal-access to execute an action,
|
||||
* returning the OutputAnalyzer to analyze the output/exitCode.
|
||||
*/
|
||||
private OutputAnalyzer tryActionPermittingIllegalAccess(String action,
|
||||
int count)
|
||||
throws Exception
|
||||
{
|
||||
String arg = "" + count;
|
||||
return ProcessTools
|
||||
.executeTestJava("-cp", TEST_CLASSES, "--permit-illegal-access",
|
||||
TEST_MAIN, action, arg)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanity check to ensure that IllegalAccessException is thrown.
|
||||
*/
|
||||
public void testAccessFail() throws Exception {
|
||||
int exitValue = tryAction("access", 1)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("IllegalAccessException")
|
||||
.stderrShouldNotContain(WARNING)
|
||||
.stderrShouldContain("IllegalAccessException")
|
||||
.getExitValue();
|
||||
assertTrue(exitValue != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanity check to ensure that InaccessibleObjectException is thrown.
|
||||
*/
|
||||
public void testSetAccessibleFail() throws Exception {
|
||||
int exitValue = tryAction("setAccessible", 1)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldNotContain(WARNING)
|
||||
.stderrShouldContain("InaccessibleObjectException")
|
||||
.getExitValue();
|
||||
assertTrue(exitValue != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit illegal access to succeed
|
||||
*/
|
||||
public void testAccessPermitted() throws Exception {
|
||||
tryActionPermittingIllegalAccess("access", 1)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("IllegalAccessException")
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldNotContain("IllegalAccessException")
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit repeated illegal access to succeed
|
||||
*/
|
||||
public void testRepeatedAccessPermitted() throws Exception {
|
||||
OutputAnalyzer outputAnalyzer = tryActionPermittingIllegalAccess("access", 10)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("IllegalAccessException")
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldNotContain("IllegalAccessException")
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);;
|
||||
|
||||
// should only have one illegal access warning
|
||||
assertTrue(containsCount(outputAnalyzer.asLines(), ILLEGAL_ACCESS_WARNING) == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit setAccessible to succeed
|
||||
*/
|
||||
public void testSetAccessiblePermitted() throws Exception {
|
||||
tryActionPermittingIllegalAccess("setAccessible", 1)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit repeated calls to setAccessible to succeed
|
||||
*/
|
||||
public void testRepeatedSetAccessiblePermitted() throws Exception {
|
||||
OutputAnalyzer outputAnalyzer = tryActionPermittingIllegalAccess("setAccessible", 10)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// should only have one illegal access warning
|
||||
assertTrue(containsCount(outputAnalyzer.asLines(), ILLEGAL_ACCESS_WARNING) == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit trySetAccessible to succeed
|
||||
*/
|
||||
public void testTrySetAccessiblePermitted() throws Exception {
|
||||
tryActionPermittingIllegalAccess("trySetAccessible", 1)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permit repeated calls to trySetAccessible to succeed
|
||||
*/
|
||||
public void testRepeatedTrySetAccessiblePermitted() throws Exception {
|
||||
OutputAnalyzer outputAnalyzer = tryActionPermittingIllegalAccess("trySetAccessible", 10)
|
||||
.stdoutShouldNotContain(WARNING)
|
||||
.stdoutShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(STARTUP_WARNING)
|
||||
.stderrShouldNotContain("InaccessibleObjectException")
|
||||
.stderrShouldContain(ILLEGAL_ACCESS_WARNING)
|
||||
.shouldHaveExitValue(0);
|
||||
|
||||
// should only have one illegal access warning
|
||||
assertTrue(containsCount(outputAnalyzer.asLines(), ILLEGAL_ACCESS_WARNING) == 1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of lines in the given input that contain the
|
||||
* given char sequence.
|
||||
*/
|
||||
private int containsCount(List<String> lines, CharSequence cs) {
|
||||
int count = 0;
|
||||
for (String line : lines) {
|
||||
if (line.contains(cs)) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user