--- old/make/src/classes/build/tools/jigsaw/GenGraphs.java 2017-02-07 13:13:20.387348824 +0000 +++ new/make/src/classes/build/tools/jigsaw/GenGraphs.java 2017-02-07 13:13:20.217337149 +0000 @@ -153,9 +153,9 @@ */ void genDotFile(String name, Set roots) throws IOException { Configuration cf = - Configuration.empty().resolveRequires(ModuleFinder.ofSystem(), - ModuleFinder.of(), - roots); + Configuration.empty().resolve(ModuleFinder.ofSystem(), + ModuleFinder.of(), + roots); Set mds = cf.modules().stream() .map(ResolvedModule::reference) --- old/make/src/classes/build/tools/jigsaw/ModuleSummary.java 2017-02-07 13:13:20.867381790 +0000 +++ new/make/src/classes/build/tools/jigsaw/ModuleSummary.java 2017-02-07 13:13:20.696370046 +0000 @@ -291,9 +291,9 @@ static Configuration resolve(Set roots) { return Configuration.empty() - .resolveRequires(ModuleFinder.ofSystem(), - ModuleFinder.of(), - roots); + .resolve(ModuleFinder.ofSystem(), + ModuleFinder.of(), + roots); } static class HtmlDocument { --- old/src/java.base/share/classes/java/lang/Class.java 2017-02-07 13:13:21.352415098 +0000 +++ new/src/java.base/share/classes/java/lang/Class.java 2017-02-07 13:13:21.181403355 +0000 @@ -425,6 +425,7 @@ * * * @since 9 + * @spec JPMS */ @CallerSensitive public static Class forName(Module module, String name) { @@ -819,6 +820,7 @@ * @return the module that this class or interface is a member of * * @since 9 + * @spec JPMS */ public Module getModule() { return module; @@ -924,6 +926,8 @@ * this method returns {@code null}. * * @return the package of this class. + * @revised 9 + * @spec JPMS */ public Package getPackage() { if (isPrimitive() || isArray()) { @@ -951,20 +955,30 @@ * declaring class} of the {@link #getEnclosingMethod enclosing method} or * {@link #getEnclosingConstructor enclosing constructor}. * - *

This method returns {@code null} if this class represents an array type, - * a primitive type or void. + *

If this class represents an array type then this method returns the + * package name of the element type. If this class represents a primitive + * type or void then the package name "{@code java.lang}" is returned. * * @return the fully qualified package name * * @since 9 + * @spec JPMS * @jls 6.7 Fully Qualified Names */ public String getPackageName() { String pn = this.packageName; - if (pn == null && !isArray() && !isPrimitive()) { - String cn = getName(); - int dot = cn.lastIndexOf('.'); - pn = (dot != -1) ? cn.substring(0, dot).intern() : ""; + if (pn == null) { + Class c = this; + while (c.isArray()) { + c = c.getComponentType(); + } + if (c.isPrimitive()) { + pn = "java.lang"; + } else { + String cn = c.getName(); + int dot = cn.lastIndexOf('.'); + pn = (dot != -1) ? cn.substring(0, dot).intern() : ""; + } this.packageName = pn; } return pn; @@ -2491,10 +2505,16 @@ * Finds a resource with a given name. * *

If this class is in a named {@link Module Module} then this method - * will attempt to find the resource in the module by means of the absolute - * resource name, subject to the rules for encapsulation specified in the - * {@code Module} {@link Module#getResourceAsStream getResourceAsStream} - * method. + * will attempt to find the resource in the module. This is done by + * delegating to the module's class loader {@link + * ClassLoader#findResource(String,String) findResource(String,String)} + * method, invoking it with the module name and the absolute name of the + * resource. Resources in named modules are subject to the rules for + * encapsulation specified in the {@code Module} {@link + * Module#getResourceAsStream getResourceAsStream} method and so this + * method returns {@code null} when the resource is a + * non-"{@code .class}" resource in a package that is not open to the + * caller's module. * *

Otherwise, if this class is not in a named module then the rules for * searching resources associated with a given class are implemented by the @@ -2503,9 +2523,8 @@ * the bootstrap class loader, the method delegates to {@link * ClassLoader#getSystemResourceAsStream}. * - *

Before finding a resource in the caller's module or delegation to a - * class loader, an absolute resource name is constructed from the given - * resource name using this algorithm: + *

Before delegation, an absolute resource name is constructed from the + * given resource name using this algorithm: * *

    * @@ -2532,7 +2551,11 @@ * least the caller module, or access to the resource is denied * by the security manager. * @throws NullPointerException If {@code name} is {@code null} + * + * @see Module#getResourceAsStream(String) * @since 1.1 + * @revised 9 + * @spec JPMS */ @CallerSensitive public InputStream getResourceAsStream(String name) { @@ -2585,10 +2608,16 @@ * Finds a resource with a given name. * *

    If this class is in a named {@link Module Module} then this method - * will attempt to find the resource in the module by means of the absolute - * resource name, subject to the rules for encapsulation specified in the - * {@code Module} {@link Module#getResourceAsStream getResourceAsStream} - * method. + * will attempt to find the resource in the module. This is done by + * delegating to the module's class loader {@link + * ClassLoader#findResource(String,String) findResource(String,String)} + * method, invoking it with the module name and the absolute name of the + * resource. Resources in named modules are subject to the rules for + * encapsulation specified in the {@code Module} {@link + * Module#getResourceAsStream getResourceAsStream} method and so this + * method returns {@code null} when the resource is a + * non-"{@code .class}" resource in a package that is not open to the + * caller's module. * *

    Otherwise, if this class is not in a named module then the rules for * searching resources associated with a given class are implemented by the @@ -2627,6 +2656,8 @@ * manager. * @throws NullPointerException If {@code name} is {@code null} * @since 1.1 + * @revised 9 + * @spec JPMS */ @CallerSensitive public URL getResource(String name) { --- old/src/java.base/share/classes/java/lang/ClassLoader.java 2017-02-07 13:13:21.897452528 +0000 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2017-02-07 13:13:21.727440853 +0000 @@ -207,6 +207,8 @@ * @jls 13.1 The Form of a Binary * @see #resolveClass(Class) * @since 1.0 + * @revised 9 + * @spec JPMS */ public abstract class ClassLoader { @@ -380,12 +382,12 @@ * method doesn't allow creation of a new class loader. * * @since 9 + * @spec JPMS */ protected ClassLoader(String name, ClassLoader parent) { this(checkCreateClassLoader(name), name, parent); } - /** * Creates a new class loader using the specified parent class loader for * delegation. @@ -440,6 +442,7 @@ * this class loader is not named. * * @since 9 + * @spec JPMS */ public String getName() { return name; @@ -710,6 +713,7 @@ * if the class could not be found. * * @since 9 + * @spec JPMS */ protected Class findClass(String moduleName, String name) { if (moduleName == null) { @@ -834,6 +838,8 @@ * @see java.security.SecureClassLoader * * @since 1.1 + * @revised 9 + * @spec JPMS */ protected final Class defineClass(String name, byte[] b, int off, int len) throws ClassFormatError @@ -967,6 +973,9 @@ * certificates than this class, or if {@code name} begins with * "{@code java.}" and this class loader is not the platform * class loader or its ancestor. + * + * @revised 9 + * @spec JPMS */ protected final Class defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) @@ -1041,6 +1050,8 @@ * @see #defineClass(String, byte[], int, int, ProtectionDomain) * * @since 1.5 + * @revised 9 + * @spec JPMS */ protected final Class defineClass(String name, java.nio.ByteBuffer b, ProtectionDomain protectionDomain) @@ -1264,11 +1275,11 @@ * Class loader implementations that support the loading from modules * should override this method. * - * @apiNote This method is the basis for the {@code Class} {@link - * Class#getResource getResource} and {@link Class#getResourceAsStream - * getResourceAsStream} methods. It is not subject to the rules for - * encapsulation specified by {@code Module} {@link - * Module#getResourceAsStream getResourceAsStream}. + * @apiNote This method is the basis for the {@link + * Class#getResource Class.getResource}, {@link Class#getResourceAsStream + * Class.getResourceAsStream}, and {@link Module#getResourceAsStream + * Module.getResourceAsStream} methods. It is not subject to the rules for + * encapsulation specified by {@code Module.getResourceAsStream}. * * @implSpec The default implementation attempts to find the resource by * invoking {@link #findResource(String)} when the {@code moduleName} is @@ -1292,6 +1303,7 @@ * * @see java.lang.module.ModuleReader#find(String) * @since 9 + * @spec JPMS */ protected URL findResource(String moduleName, String name) throws IOException { if (moduleName == null) { @@ -1342,6 +1354,8 @@ * @throws NullPointerException If {@code name} is {@code null} * * @since 1.1 + * @revised 9 + * @spec JPMS */ public URL getResource(String name) { Objects.requireNonNull(name); @@ -1403,6 +1417,8 @@ * @see #findResources(String) * * @since 1.2 + * @revised 9 + * @spec JPMS */ public Enumeration getResources(String name) throws IOException { Objects.requireNonNull(name); @@ -1499,6 +1515,8 @@ * denied by the security manager. * * @since 1.2 + * @revised 9 + * @spec JPMS */ protected URL findResource(String name) { return null; @@ -1531,6 +1549,8 @@ * If I/O errors occur * * @since 1.2 + * @revised 9 + * @spec JPMS */ protected Enumeration findResources(String name) throws IOException { return Collections.emptyEnumeration(); @@ -1601,6 +1621,8 @@ * denied by the security manager. * * @since 1.1 + * @revised 9 + * @spec JPMS */ public static URL getSystemResource(String name) { return getSystemClassLoader().getResource(name); @@ -1636,6 +1658,8 @@ * If I/O errors occur * * @since 1.2 + * @revised 9 + * @spec JPMS */ public static Enumeration getSystemResources(String name) throws IOException @@ -1667,6 +1691,8 @@ * @throws NullPointerException If {@code name} is {@code null} * * @since 1.1 + * @revised 9 + * @spec JPMS */ public InputStream getResourceAsStream(String name) { Objects.requireNonNull(name); @@ -1699,6 +1725,8 @@ * denied by the security manager. * * @since 1.1 + * @revised 9 + * @spec JPMS */ public static InputStream getSystemResourceAsStream(String name) { URL url = getSystemResource(name); @@ -1749,6 +1777,7 @@ * * @see Module#isNamed() * @since 9 + * @spec JPMS */ public final Module getUnnamedModule() { return unnamedModule; @@ -1772,6 +1801,7 @@ * {@link RuntimePermission}{@code ("getClassLoader")} * * @since 9 + * @spec JPMS */ @CallerSensitive public static ClassLoader getPlatformClassLoader() { @@ -1847,6 +1877,8 @@ * {@link Throwable#getCause()} method. * * @revised 1.4 + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ClassLoader getSystemClassLoader() { @@ -2101,6 +2133,8 @@ * defined by this class loader * * @since 1.2 + * @revised 9 + * @spec JPMS * * @see * The JAR File Specification: Package Versioning @@ -2138,6 +2172,7 @@ * if {@code name} is {@code null}. * * @since 9 + * @spec JPMS */ public final Package getDefinedPackage(String name) { Objects.requireNonNull(name, "name cannot be null"); @@ -2160,6 +2195,7 @@ * or an zero length array if no package has been defined by this class loader. * * @since 9 + * @spec JPMS */ public final Package[] getDefinedPackages() { return packages().toArray(Package[]::new); @@ -2196,6 +2232,8 @@ * a {@code Package} for the specified class loader. * * @since 1.2 + * @revised 9 + * @spec JPMS */ @Deprecated(since="9") protected Package getPackage(String name) { @@ -2220,6 +2258,8 @@ * class loader and its ancestors * * @since 1.2 + * @revised 9 + * @spec JPMS */ protected Package[] getPackages() { Stream pkgs = packages(); --- old/src/java.base/share/classes/java/lang/Package.java 2017-02-07 13:13:22.439489751 +0000 +++ new/src/java.base/share/classes/java/lang/Package.java 2017-02-07 13:13:22.268478007 +0000 @@ -111,6 +111,8 @@ * @see ClassLoader#definePackage(String, String, String, String, String, String, String, URL) * * @since 1.2 + * @revised 9 + * @spec JPMS */ public class Package extends NamedPackage implements java.lang.reflect.AnnotatedElement { /** @@ -207,6 +209,9 @@ * is returned if it is not known. * @return the vendor that implemented this package, {@code null} * is returned if it is not known. + * + * @revised 9 + * @spec JPMS */ public String getImplementationVendor() { return versionInfo.implVendor; @@ -334,6 +339,9 @@ * a {@code Package} for the specified class loader. * * @see ClassLoader#getDefinedPackage + * + * @revised 9 + * @spec JPMS */ @CallerSensitive @Deprecated(since="9") @@ -356,6 +364,9 @@ * class loader and its ancestors * * @see ClassLoader#getDefinedPackages + * + * @revised 9 + * @spec JPMS */ @CallerSensitive public static Package[] getPackages() { --- old/src/java.base/share/classes/java/lang/SecurityManager.java 2017-02-07 13:13:22.918522648 +0000 +++ new/src/java.base/share/classes/java/lang/SecurityManager.java 2017-02-07 13:13:22.752511247 +0000 @@ -1458,6 +1458,18 @@ } /** + * Called by java.security.Security + */ + static void invalidatePackageAccessCache() { + synchronized (packageAccessLock) { + packageAccessValid = false; + } + synchronized (packageDefinitionLock) { + packageDefinitionValid = false; + } + } + + /** * Returns true if the module's loader is the boot or platform loader. */ private static boolean isBootOrPlatformModule(Module m) { --- old/src/java.base/share/classes/java/lang/StackStreamFactory.java 2017-02-07 13:13:23.418556987 +0000 +++ new/src/java.base/share/classes/java/lang/StackStreamFactory.java 2017-02-07 13:13:23.249545380 +0000 @@ -684,7 +684,7 @@ frames[n++] = caller; } if (frames[1] == null) { - throw new IllegalStateException("no caller frame"); + throw new IllegalCallerException("no caller frame"); } return n; } --- old/src/java.base/share/classes/java/lang/StackTraceElement.java 2017-02-07 13:13:23.908590638 +0000 +++ new/src/java.base/share/classes/java/lang/StackTraceElement.java 2017-02-07 13:13:23.738578963 +0000 @@ -92,6 +92,8 @@ * @throws NullPointerException if {@code declaringClass} or * {@code methodName} is null * @since 1.5 + * @revised 9 + * @spec JPMS */ public StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber) { @@ -128,6 +130,7 @@ * or {@code methodName} is {@code null} * * @since 9 + * @spec JPMS */ public StackTraceElement(String classLoaderName, String moduleName, String moduleVersion, @@ -187,6 +190,7 @@ * point represented by this stack trace element; {@code null} * if the module name is not available. * @since 9 + * @spec JPMS * @see java.lang.reflect.Module#getName() */ public String getModuleName() { @@ -201,6 +205,7 @@ * point represented by this stack trace element; {@code null} * if the module version is not available. * @since 9 + * @spec JPMS * @see java.lang.module.ModuleDescriptor.Version */ public String getModuleVersion() { @@ -216,6 +221,7 @@ * if the class loader is not named. * * @since 9 + * @spec JPMS * @see java.lang.ClassLoader#getName() */ public String getClassLoaderName() { @@ -329,6 +335,8 @@ * {@link java.lang.StackWalker.StackFrame}, where an implementation may * choose to omit some element in the returned string. * + * @revised 9 + * @spec JPMS * @see Throwable#printStackTrace() */ public String toString() { @@ -376,6 +384,9 @@ * @return true if the specified object is another * {@code StackTraceElement} instance representing the same * execution point as this instance. + * + * @revised 9 + * @spec JPMS */ public boolean equals(Object obj) { if (obj==this) --- old/src/java.base/share/classes/java/lang/StackWalker.java 2017-02-07 13:13:24.367622162 +0000 +++ new/src/java.base/share/classes/java/lang/StackWalker.java 2017-02-07 13:13:24.202610830 +0000 @@ -465,8 +465,8 @@ } /** - * Gets the {@code Class} object of the caller invoking the method - * that calls this {@code getCallerClass} method. + * Gets the {@code Class} object of the caller who invoked the method + * that invoked {@code getCallerClass}. * *

    Reflection frames, {@link java.lang.invoke.MethodHandle}, and * hidden frames are filtered regardless of the @@ -474,19 +474,21 @@ * and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options * this {@code StackWalker} has been configured with. * + *

    This method should be called when a caller frame is present. If + * it is called from the bottom most frame on the stack, + * {@code IllegalCallerException} will be thrown. + * *

    This method throws {@code UnsupportedOperationException} * if this {@code StackWalker} is not configured with the * {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option. - * This method should be called when a caller frame is present. If - * it is called from the last frame on the stack, - * {@code IllegalStateException} will be thrown. * * @apiNote * For example, {@code Util::getResourceBundle} loads a resource bundle - * on behalf of the caller. It calls this {@code getCallerClass} method - * to find the method calling {@code Util::getResourceBundle} and uses the caller's - * class loader to load the resource bundle. The caller class in this example - * is the {@code MyTool} class. + * on behalf of the caller. It invokes {@code getCallerClass} to identify + * the class whose method called {@code Util::getResourceBundle}. + * Then, it obtains the class loader of that class, and uses + * the class loader to load the resource bundle. The caller class + * in this example is {@code MyTool}. * *

    {@code
          * class Util {
    @@ -517,17 +519,17 @@
          * }
    * * When the {@code getCallerClass} method is called from a method that - * is the last frame on the stack, + * is the bottom most frame on the stack, * for example, {@code static public void main} method launched by the * {@code java} launcher, or a method invoked from a JNI attached thread, - * {@code IllegalStateException} is thrown. + * {@code IllegalCallerException} is thrown. * * @return {@code Class} object of the caller's caller invoking this method. * * @throws UnsupportedOperationException if this {@code StackWalker} * is not configured with {@link Option#RETAIN_CLASS_REFERENCE * Option.RETAIN_CLASS_REFERENCE}. - * @throws IllegalStateException if there is no caller frame, i.e. + * @throws IllegalCallerException if there is no caller frame, i.e. * when this {@code getCallerClass} method is called from a method * which is the last frame on the stack. */ --- old/src/java.base/share/classes/java/lang/System.java 2017-02-07 13:13:24.850655333 +0000 +++ new/src/java.base/share/classes/java/lang/System.java 2017-02-07 13:13:24.677643452 +0000 @@ -1942,10 +1942,6 @@ * the application classpath or modulepath. */ private static void initPhase3() { - // Initialize publicLookup early, to avoid bootstrapping circularities - // with security manager using java.lang.invoke infrastructure. - java.lang.invoke.MethodHandles.publicLookup(); - // set security manager String cn = System.getProperty("java.security.manager"); if (cn != null) { @@ -2053,6 +2049,9 @@ public String fastUUID(long lsb, long msb) { return Long.fastUUID(lsb, msb); } + public void invalidatePackageAccessCache() { + SecurityManager.invalidatePackageAccessCache(); + } }); } } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2017-02-07 13:13:25.356690084 +0000 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2017-02-07 13:13:25.183678202 +0000 @@ -25,8 +25,6 @@ package java.lang.invoke; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; @@ -111,13 +109,17 @@ /** * Returns a {@link Lookup lookup object} which is trusted minimally. - * It can only be used to create method handles to public members in + * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes. + * It can only be used to create method handles to public members of * public classes in packages that are exported unconditionally. *

    - * For now, the {@linkplain Lookup#lookupClass lookup class} of this lookup - * object is in an unnamed module. - * Consequently, the lookup context of this lookup object will be the bootstrap - * class loader, which means it cannot find user classes. + * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class} + * of this lookup object will be {@link java.lang.Object}. + * + * @apiNote The use of Object is conventional, and because the lookup modes are + * limited, there is no special access provided to the internals of Object, its package + * or its module. Consequently, the lookup context of this lookup object will be the + * bootstrap class loader, which means it cannot find user classes. * *

    * Discussion: @@ -129,17 +131,12 @@ * Also, it cannot access * caller sensitive methods. * @return a lookup object which is trusted minimally + * + * @revised 9 + * @spec JPMS */ public static Lookup publicLookup() { - // During VM startup then only classes in the java.base module can be - // loaded and linked. This is because java.base exports aren't setup until - // the module system is initialized, hence types in the unnamed module - // (or any named module) can't link to java/lang/Object. - if (!jdk.internal.misc.VM.isModuleSystemInited()) { - return new Lookup(Object.class, Lookup.PUBLIC); - } else { - return LookupHelper.PUBLIC_LOOKUP; - } + return Lookup.PUBLIC_LOOKUP; } /** @@ -172,6 +169,7 @@ * @throws IllegalAccessException if the access check specified above fails * @throws SecurityException if denied by the security manager * @since 9 + * @spec JPMS * @see Lookup#dropLookupMode */ public static Lookup privateLookupIn(Class targetClass, Lookup lookup) throws IllegalAccessException { @@ -183,11 +181,11 @@ throw new IllegalArgumentException(targetClass + " is an array class"); Module targetModule = targetClass.getModule(); Module callerModule = lookup.lookupClass().getModule(); - if (callerModule != targetModule && targetModule.isNamed()) { - if (!callerModule.canRead(targetModule)) - throw new IllegalAccessException(callerModule + " does not read " + targetModule); + if (!callerModule.canRead(targetModule)) + throw new IllegalAccessException(callerModule + " does not read " + targetModule); + if (targetModule.isNamed()) { String pn = targetClass.getPackageName(); - assert pn != null && pn.length() > 0 : "unnamed package cannot be in named module"; + assert pn.length() > 0 : "unnamed package cannot be in named module"; if (!targetModule.isOpen(pn, callerModule)) throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule); } @@ -601,6 +599,8 @@ * so that there can be a secure foundation for lookups. * Nearly all other methods in the JSR 292 API rely on lookup * objects to check access requests. + * + * @revised 9 */ public static final class Lookup { @@ -647,15 +647,32 @@ * lookup class and public types in packages exported by other modules * to the module of the lookup class. * @since 9 + * @spec JPMS */ public static final int MODULE = PACKAGE << 1; - private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE); + /** A single-bit mask representing {@code unconditional} access + * which may contribute to the result of {@link #lookupModes lookupModes}. + * The value is {@code 0x20}, which does not correspond meaningfully to + * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. + * A {@code Lookup} with this lookup mode assumes {@linkplain + * java.lang.reflect.Module#canRead(java.lang.reflect.Module) readability}. + * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} + * with this lookup mode can access all public members of public types + * of all modules where the type is in a package that is {@link + * java.lang.reflect.Module#isExported(String) exported unconditionally}. + * @see #publicLookup() + * @since 9 + */ + public static final int UNCONDITIONAL = PACKAGE << 2; + + private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL); + private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL); private static final int TRUSTED = -1; private static int fixmods(int mods) { - mods &= (ALL_MODES - PACKAGE - MODULE); - return (mods != 0) ? mods : (PACKAGE | MODULE); + mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL); + return (mods != 0) ? mods : (PACKAGE | MODULE | UNCONDITIONAL); } /** Tells which class is performing the lookup. It is this class against @@ -682,13 +699,14 @@ * {@linkplain #PRIVATE PRIVATE (0x02)}, * {@linkplain #PROTECTED PROTECTED (0x04)}, * {@linkplain #PACKAGE PACKAGE (0x08)}, - * and {@linkplain #MODULE MODULE (0x10)}. + * {@linkplain #MODULE MODULE (0x10)}, + * and {@linkplain #UNCONDITIONAL UNCONDITIONAL (0x20)}. *

    * A freshly-created lookup object - * on the {@linkplain java.lang.invoke.MethodHandles#lookup() caller's class} - * has all possible bits set, since the caller class can access all its own members, - * all public types in the caller's module, and all public types in packages exported - * by other modules to the caller's module. + * on the {@linkplain java.lang.invoke.MethodHandles#lookup() caller's class} has + * all possible bits set, except {@code UNCONDITIONAL}. The lookup can be used to + * access all members of the caller's class, all public types in the caller's module, + * and all public types in packages exported by other modules to the caller's module. * A lookup object on a new lookup class * {@linkplain java.lang.invoke.MethodHandles.Lookup#in created from a previous lookup object} * may have some mode bits set to zero. @@ -701,6 +719,9 @@ * @return the lookup modes, which limit the kinds of access performed by this lookup object * @see #in * @see #dropLookupMode + * + * @revised 9 + * @spec JPMS */ public int lookupModes() { return allowedModes & ALL_MODES; @@ -712,9 +733,9 @@ * which in turn is called by a method not in this package. */ Lookup(Class lookupClass) { - this(lookupClass, ALL_MODES); + this(lookupClass, FULL_POWER_MODES); // make sure we haven't accidentally picked up a privileged class: - checkUnprivilegedlookupClass(lookupClass, ALL_MODES); + checkUnprivilegedlookupClass(lookupClass, FULL_POWER_MODES); } private Lookup(Class lookupClass, int allowedModes) { @@ -730,19 +751,20 @@ * However, the resulting {@code Lookup} object is guaranteed * to have no more access capabilities than the original. * In particular, access capabilities can be lost as follows:

      - *
    • If the lookup class for this {@code Lookup} is not in a named module, - * and the new lookup class is in a named module {@code M}, then no members in - * {@code M}'s non-exported packages will be accessible. - *
    • If the lookup for this {@code Lookup} is in a named module, and the - * new lookup class is in a different module {@code M}, then no members, not even - * public members in {@code M}'s exported packages, will be accessible. - *
    • If the new lookup class differs from the old one, - * protected members will not be accessible by virtue of inheritance. - * (Protected members may continue to be accessible because of package sharing.) + *
    • If the old lookup class is in a {@link Module#isNamed() named} module, and + * the new lookup class is in a different module {@code M}, then no members, not + * even public members in {@code M}'s exported packages, will be accessible. + * The exception to this is when this lookup is {@link #publicLookup() + * publicLookup}, in which case {@code PUBLIC} access is not lost. + *
    • If the old lookup class is in an unnamed module, and the new lookup class + * is a different module then {@link #MODULE MODULE} access is lost. + *
    • If the new lookup class differs from the old one then {@code UNCONDITIONAL} is lost. *
    • If the new lookup class is in a different package * than the old one, protected and default (package) members will not be accessible. *
    • If the new lookup class is not within the same package member - * as the old one, private members will not be accessible. + * as the old one, private members will not be accessible, and protected members + * will not be accessible by virtue of inheritance. + * (Protected members may continue to be accessible because of package sharing.) *
    • If the new lookup class is not accessible to the old lookup class, * then no members, not even public members, will be accessible. * (In all other cases, public members will continue to be accessible.) @@ -757,32 +779,34 @@ * @return a lookup object which reports the desired lookup class, or the same object * if there is no change * @throws NullPointerException if the argument is null + * + * @revised 9 + * @spec JPMS */ public Lookup in(Class requestedLookupClass) { Objects.requireNonNull(requestedLookupClass); if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all - return new Lookup(requestedLookupClass, ALL_MODES); + return new Lookup(requestedLookupClass, FULL_POWER_MODES); if (requestedLookupClass == this.lookupClass) return this; // keep same capabilities - - int newModes = (allowedModes & (ALL_MODES & ~PROTECTED)); + int newModes = (allowedModes & FULL_POWER_MODES); if (!VerifyAccess.isSameModule(this.lookupClass, requestedLookupClass)) { - // Allowed to teleport from an unnamed to a named module but resulting - // Lookup has no access to module private members - if (this.lookupClass.getModule().isNamed()) { + // Need to drop all access when teleporting from a named module to another + // module. The exception is publicLookup where PUBLIC is not lost. + if (this.lookupClass.getModule().isNamed() + && (this.allowedModes & UNCONDITIONAL) == 0) newModes = 0; - } else { - newModes &= ~MODULE; - } + else + newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED); } if ((newModes & PACKAGE) != 0 && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) { - newModes &= ~(PACKAGE|PRIVATE); + newModes &= ~(PACKAGE|PRIVATE|PROTECTED); } // Allow nestmate lookups to be created without special privilege: if ((newModes & PRIVATE) != 0 && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) { - newModes &= ~PRIVATE; + newModes &= ~(PRIVATE|PROTECTED); } if ((newModes & PUBLIC) != 0 && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) { @@ -801,28 +825,29 @@ * finds members, but with a lookup mode that has lost the given lookup mode. * The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE * MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}. - * {@link #PROTECTED PROTECTED} is always dropped and so the resulting lookup - * mode will never have this access capability. When dropping {@code PACKAGE} - * then the resulting lookup will not have {@code PACKAGE} or {@code PRIVATE} - * access. When dropping {@code MODULE} then the resulting lookup will not - * have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code - * PUBLIC} is dropped then the resulting lookup has no access. + * {@link #PROTECTED PROTECTED} and {@link #UNCONDITIONAL UNCONDITIONAL} are always + * dropped and so the resulting lookup mode will never have these access capabilities. + * When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE} + * or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will + * not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC} + * is dropped then the resulting lookup has no access. * @param modeToDrop the lookup mode to drop * @return a lookup object which lacks the indicated mode, or the same object if there is no change * @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC}, - * {@code MODULE}, {@code PACKAGE}, {@code PROTECTED} or {@code PRIVATE} - * @since 9 + * {@code MODULE}, {@code PACKAGE}, {@code PROTECTED}, {@code PRIVATE} or {@code UNCONDITIONAL} * @see MethodHandles#privateLookupIn + * @since 9 */ public Lookup dropLookupMode(int modeToDrop) { int oldModes = lookupModes(); - int newModes = oldModes & ~(modeToDrop | PROTECTED); + int newModes = oldModes & ~(modeToDrop | PROTECTED | UNCONDITIONAL); switch (modeToDrop) { case PUBLIC: newModes &= ~(ALL_MODES); break; case MODULE: newModes &= ~(PACKAGE | PRIVATE); break; case PACKAGE: newModes &= ~(PRIVATE); break; case PROTECTED: - case PRIVATE: break; + case PRIVATE: + case UNCONDITIONAL: break; default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop"); } if (newModes == oldModes) return this; // return self if no change @@ -835,6 +860,12 @@ /** Package-private version of lookup which is trusted. */ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, TRUSTED); + /** Version of lookup which is trusted minimally. + * It can only be used to create method handles to publicly accessible + * members in packages that are exported unconditionally. + */ + static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, (PUBLIC|UNCONDITIONAL)); + private static void checkUnprivilegedlookupClass(Class lookupClass, int allowedModes) { String name = lookupClass.getName(); if (name.startsWith("java.lang.invoke.")) @@ -845,7 +876,7 @@ // TODO replace with a more formal and less fragile mechanism // that does not bluntly restrict classes under packages within // java.base from looking up MethodHandles or VarHandles. - if (allowedModes == ALL_MODES && lookupClass.getClassLoader() == null) { + if (allowedModes == FULL_POWER_MODES && lookupClass.getClassLoader() == null) { if ((name.startsWith("java.") && !name.equals("java.lang.Thread") && !name.startsWith("java.util.concurrent.")) || @@ -866,6 +897,7 @@ *
        *
      • If no access is allowed, the suffix is "/noaccess". *
      • If only public access to types in exported packages is allowed, the suffix is "/public". + *
      • If only public access and unconditional access are allowed, the suffix is "/publicLookup". *
      • If only public and module access are allowed, the suffix is "/module". *
      • If only public, module and package access are allowed, the suffix is "/package". *
      • If only public, module, package, and private access are allowed, the suffix is "/private". @@ -884,6 +916,9 @@ * because it requires a direct subclass relationship between * caller and callee.) * @see #in + * + * @revised 9 + * @spec JPMS */ @Override public String toString() { @@ -893,13 +928,15 @@ return cname + "/noaccess"; case PUBLIC: return cname + "/public"; + case PUBLIC|UNCONDITIONAL: + return cname + "/publicLookup"; case PUBLIC|MODULE: return cname + "/module"; case PUBLIC|MODULE|PACKAGE: return cname + "/package"; - case ALL_MODES & ~PROTECTED: + case FULL_POWER_MODES & ~PROTECTED: return cname + "/private"; - case ALL_MODES: + case FULL_POWER_MODES: return cname; case TRUSTED: return "/trusted"; // internal only; not exported @@ -1580,6 +1617,7 @@ if (refKind == REF_invokeSpecial) refKind = REF_invokeVirtual; assert(method.isMethod()); + @SuppressWarnings("deprecation") Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this; return lookup.getDirectMethodNoSecurityManager(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method)); } @@ -1662,6 +1700,7 @@ public MethodHandle unreflectConstructor(Constructor c) throws IllegalAccessException { MemberName ctor = new MemberName(c); assert(ctor.isConstructor()); + @SuppressWarnings("deprecation") Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this; return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor); } @@ -1692,6 +1731,7 @@ assert(isSetter ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind()) : MethodHandleNatives.refKindIsGetter(field.getReferenceKind())); + @SuppressWarnings("deprecation") Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this; return lookup.getDirectFieldNoSecurityManager(field.getReferenceKind(), f.getDeclaringClass(), field); } @@ -2033,9 +2073,9 @@ (defc == refc || Modifier.isPublic(refc.getModifiers()))); if (!classOK && (allowedModes & PACKAGE) != 0) { - classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), ALL_MODES) && + classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), FULL_POWER_MODES) && (defc == refc || - VerifyAccess.isClassAccessible(refc, lookupClass(), ALL_MODES))); + VerifyAccess.isClassAccessible(refc, lookupClass(), FULL_POWER_MODES))); } if (!classOK) return "class is not public"; @@ -2348,53 +2388,6 @@ } /** - * Helper class used to lazily create PUBLIC_LOOKUP with a lookup class - * in an unnamed module. - * - * @see Lookup#publicLookup - */ - private static class LookupHelper { - private static final String UNNAMED = "Unnamed"; - private static final String OBJECT = "java/lang/Object"; - - private static Class createClass() { - try { - ClassWriter cw = new ClassWriter(0); - cw.visit(Opcodes.V1_8, - Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, - UNNAMED, - null, - OBJECT, - null); - cw.visitSource(UNNAMED, null); - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - ClassLoader loader = new ClassLoader(null) { - @Override - protected Class findClass(String cn) throws ClassNotFoundException { - if (cn.equals(UNNAMED)) - return super.defineClass(UNNAMED, bytes, 0, bytes.length); - throw new ClassNotFoundException(cn); - } - }; - return loader.loadClass(UNNAMED); - } catch (Exception e) { - throw new InternalError(e); - } - } - - private static final Class PUBLIC_LOOKUP_CLASS = createClass(); - - /** - * Lookup that is trusted minimally. It can only be used to create - * method handles to publicly accessible members in exported packages. - * - * @see MethodHandles#publicLookup - */ - static final Lookup PUBLIC_LOOKUP = new Lookup(PUBLIC_LOOKUP_CLASS, Lookup.PUBLIC); - } - - /** * Produces a method handle constructing arrays of a desired type. * The return type of the method handle will be the array type. * The type of its sole argument will be {@code int}, which specifies the size of the array. --- old/src/java.base/share/classes/java/lang/module/Configuration.java 2017-02-07 13:13:25.967732046 +0000 +++ new/src/java.base/share/classes/java/lang/module/Configuration.java 2017-02-07 13:13:25.799720508 +0000 @@ -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 @@ -42,126 +42,48 @@ import java.util.stream.Stream; /** - * The configuration that is the result of resolution or resolution with - * service binding. - * - *

        Resolution

        - * - *

        Resolution is the process of computing the transitive closure of a set - * of root modules over a set of observable modules by resolving the - * dependences expressed by {@code requires} clauses. - * - * The dependence graph is augmented with edges that take account of - * implicitly declared dependences ({@code requires transitive}) to create a - * readability graph. A {@code Configuration} encapsulates the - * resulting graph of {@link ResolvedModule resolved modules}. - * - *

        Suppose we have the following observable modules:

        - *
         {@code
        - *     module m1 { requires m2; }
        - *     module m2 { requires transitive m3; }
        - *     module m3 { }
        - *     module m4 { }
        - * } 
        - * - *

        If the module {@code m1} is resolved then the resulting configuration - * contains three modules ({@code m1}, {@code m2}, {@code m3}). The edges in - * its readability graph are:

        - *
         {@code
        - *     m1 --> m2  (meaning m1 reads m2)
        - *     m1 --> m3
        - *     m2 --> m3
        - * } 
        - * - *

        Resolution is an additive process. When computing the transitive closure - * then the dependence relation may include dependences on modules in parent - * configurations. The result is a relative configuration that is - * relative to one or more parent configurations and where the readability graph - * may have edges from modules in the configuration to modules in parent - * configurations. - * - *

        - * - *

        Suppose we have the following observable modules:

        - *
         {@code
        - *     module m1 { requires m2; requires java.xml; }
        - *     module m2 { }
        - * } 
        - * - *

        If module {@code m1} is resolved with the configuration for the {@link - * java.lang.reflect.Layer#boot() boot} layer as the parent then the resulting - * configuration contains two modules ({@code m1}, {@code m2}). The edges in - * its readability graph are: - *

         {@code
        - *     m1 --> m2
        - *     m1 --> java.xml
        - * } 
        - * where module {@code java.xml} is in the parent configuration. For - * simplicity, this example omits the implicitly declared dependence on the - * {@code java.base} module. - * - * - *

        {@link ModuleDescriptor#isAutomatic() Automatic} modules receive special - * treatment during resolution. Each automatic module is resolved so that it - * reads all other modules in the configuration and all parent configurations. - * Each automatic module is also resolved as if it {@code requires transitive} - * all other automatic modules in the configuration (and all automatic modules - * in parent configurations).

        - - *

        Service binding

        - * - *

        Service binding is the process of augmenting a graph of resolved modules - * from the set of observable modules induced by the service-use dependence - * ({@code uses} and {@code provides} clauses). Any module that was not - * previously in the graph requires resolution to compute its transitive - * closure. Service binding is an iterative process in that adding a module - * that satisfies some service-use dependence may introduce new service-use - * dependences.

        - * - *

        Suppose we have the following observable modules:

        - *
         {@code
        - *     module m1 { exports p; uses p.S; }
        - *     module m2 { requires m1; provides p.S with p2.S2; }
        - *     module m3 { requires m1; requires m4; provides p.S with p3.S3; }
        - *     module m4 { }
        - * } 
        - * - *

        If the module {@code m1} is resolved then the resulting graph of modules - * has one module ({@code m1}). If the graph is augmented with modules induced - * by the service-use dependence relation then the configuration will contain - * four modules ({@code m1}, {@code m2}, {@code m3}, {@code m4}). The edges in - * its readability graph are:

        - *
         {@code
        - *     m2 --> m1
        - *     m3 --> m1
        - *     m3 --> m4
        - * } 
        - *

        The edges in the conceptual service-use graph are:

        - *
         {@code
        - *     m1 --> m2  (meaning m1 uses a service that is provided by m2)
        - *     m1 --> m3
        - * } 
        - * - *

        If this configuration is instantiated as a {@code Layer}, and if code in - * module {@code m1} uses {@link java.util.ServiceLoader ServiceLoader} to - * iterate over implementations of {@code p.S.class}, then it will iterate over - * an instance of {@code p2.S2} and {@code p3.S3}.

        + * A configuration that is the result of + * resolution or resolution with + * service binding. + * + *

        A configuration encapsulates the readability graph that is the + * output of resolution. A readability graph is a directed graph where the nodes + * are of type {@link ResolvedModule} and the edges represent the readability + * amongst the modules. {@code Configuration} defines the {@link #modules() + * modules()} method to get the set of resolved modules in the graph. {@code + * ResolvedModule} defines the {@link ResolvedModule#reads() reads()} method to + * get the set of modules that a resolved module reads. The modules that are + * read may be in the same configuration or may be in {@link #parents() parent} + * configurations.

        + * + *

        Configuration defines the {@link #resolve(ModuleFinder,List,ModuleFinder,Collection) + * resolve} method to resolve a collection of root modules, and the {@link + * #resolveAndBind(ModuleFinder,List,ModuleFinder,Collection) resolveAndBind} + * method to do resolution with service binding. There are instance and + * static variants of both methods. The instance methods create a configuration + * with the receiver as the parent configuration. The static methods are for + * more advanced cases where there can be more than one parent configuration.

        + * + *

        Each {@link java.lang.reflect.Layer layer} of modules in the Java virtual + * machine is created from a configuration. The configuration for the {@link + * java.lang.reflect.Layer#boot() boot} layer is obtained by invoking {@code + * Layer.boot().configuration()}. The configuration for the boot layer will + * often be the parent when creating new configurations.

        * *

        Example

        * - *

        The following example uses the {@code resolveRequires} method to resolve - * a module named myapp with the configuration for the boot layer as - * the parent configuration. It prints the name of each resolved module and - * the names of the modules that each module reads.

        + *

        The following example uses the {@link + * #resolve(ModuleFinder,ModuleFinder,Collection) resolve} method to resolve a + * module named myapp with the configuration for the boot layer as the + * parent configuration. It prints the name of each resolved module and the + * names of the modules that each module reads.

        * *
        {@code
          *    ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3);
          *
          *    Configuration parent = Layer.boot().configuration();
          *
        - *    Configuration cf = parent.resolveRequires(finder,
        - *                                              ModuleFinder.of(),
        - *                                              Set.of("myapp"));
        + *    Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("myapp"));
          *    cf.modules().forEach(m -> {
          *        System.out.format("%s -> %s%n",
          *            m.name(),
        @@ -172,6 +94,7 @@
          * }
        * * @since 9 + * @spec JPMS * @see java.lang.reflect.Layer */ public final class Configuration { @@ -186,11 +109,23 @@ private final Set modules; private final Map nameToModule; + // 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(); this.graph = Collections.emptyMap(); this.modules = Collections.emptySet(); this.nameToModule = Collections.emptyMap(); + this.osName = null; + this.osArch = null; + this.osVersion = null; } private Configuration(List parents, @@ -214,27 +149,30 @@ this.graph = g; this.modules = Set.of(moduleArray); this.nameToModule = Map.ofEntries(nameEntries); - } + this.osName = resolver.osName(); + this.osArch = resolver.osArch(); + this.osVersion = resolver.osVersion(); + } /** * Resolves a collection of root modules, with this configuration as its * parent, to create a new configuration. This method works exactly as * specified by the static {@link - * #resolveRequires(ModuleFinder,List,ModuleFinder,Collection) resolveRequires} + * #resolve(ModuleFinder,List,ModuleFinder,Collection) resolve} * method when invoked with this configuration as the parent. In other words, * if this configuration is {@code cf} then this method is equivalent to * invoking: *
         {@code
        -     *     Configuration.resolveRequires(before, List.of(cf), after, roots);
        +     *     Configuration.resolve(before, List.of(cf), after, roots);
              * }
        * * @param before * The before module finder to find modules * @param after - * The after module finder to locate modules when a - * module cannot be located by the {@code before} module finder - * and the module is not in this configuration + * The after module finder to locate modules when not + * located by the {@code before} module finder or in parent + * configurations * @param roots * The possibly-empty collection of module names of the modules * to resolve @@ -242,16 +180,20 @@ * @return The configuration that is the result of resolving the given * root modules * + * @throws FindException + * If resolution fails for any of the observability-related reasons + * specified by the static {@code resolve} method * @throws ResolutionException - * If resolution or the post-resolution checks fail + * If any of the post-resolution consistency checks specified by + * the static {@code resolve} method fail * @throws SecurityException * If locating a module is denied by the security manager */ - public Configuration resolveRequires(ModuleFinder before, - ModuleFinder after, - Collection roots) + public Configuration resolve(ModuleFinder before, + ModuleFinder after, + Collection roots) { - return resolveRequires(before, List.of(this), after, roots); + return resolve(before, List.of(this), after, roots); } @@ -259,12 +201,12 @@ * Resolves a collection of root modules, with service binding, and with * this configuration as its parent, to create a new configuration. * This method works exactly as specified by the static {@link - * #resolveRequiresAndUses(ModuleFinder,List,ModuleFinder,Collection) - * resolveRequiresAndUses} method when invoked with this configuration + * #resolveAndBind(ModuleFinder,List,ModuleFinder,Collection) + * resolveAndBind} method when invoked with this configuration * as the parent. In other words, if this configuration is {@code cf} then * this method is equivalent to invoking: *
         {@code
        -     *     Configuration.resolveRequiresAndUses(before, List.of(cf), after, roots);
        +     *     Configuration.resolveAndBind(before, List.of(cf), after, roots);
              * }
        * * @@ -272,25 +214,29 @@ * The before module finder to find modules * @param after * The after module finder to locate modules when not - * located by the {@code before} module finder and this - * configuration + * located by the {@code before} module finder or in parent + * configurations * @param roots * The possibly-empty collection of module names of the modules * to resolve * - * @return The configuration that is the result of resolving the given - * root modules + * @return The configuration that is the result of resolving, with service + * binding, the given root modules * + * @throws FindException + * If resolution fails for any of the observability-related reasons + * specified by the static {@code resolve} method * @throws ResolutionException - * If resolution or the post-resolution checks fail + * If any of the post-resolution consistency checks specified by + * the static {@code resolve} method fail * @throws SecurityException * If locating a module is denied by the security manager */ - public Configuration resolveRequiresAndUses(ModuleFinder before, - ModuleFinder after, - Collection roots) + public Configuration resolveAndBind(ModuleFinder before, + ModuleFinder after, + Collection roots) { - return resolveRequiresAndUses(before, List.of(this), after, roots); + return resolveAndBind(before, List.of(this), after, roots); } @@ -301,14 +247,14 @@ * * This method is used to create the configuration for the boot layer. */ - static Configuration resolveRequiresAndUses(ModuleFinder finder, - Collection roots, - boolean check, - PrintStream traceOutput) + static Configuration resolveAndBind(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) { List parents = List.of(empty()); Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput); - resolver.resolveRequires(roots).resolveUses(); + resolver.resolve(roots).bind(); return new Configuration(parents, resolver, check); } @@ -328,11 +274,11 @@ * *

        When all modules have been resolved then the resulting dependency * graph is checked to ensure that it does not contain cycles. A - * readability graph is constructed and in conjunction with the module + * readability graph is constructed, and in conjunction with the module * exports and service use, checked for consistency.

        * - *

        Resolution and the (post-resolution) consistency checks may fail for - * following reasons:

        + *

        Resolution may fail with {@code FindException} for the following + * observability-related reasons:

        * *
          *
        • A root module, or a direct or transitive dependency, is not @@ -343,6 +289,20 @@ * descriptor ({@code module-info.class}) or two versions of the same * module are found in the same directory.

        • * + *
        • 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.

        • + * + *
        + * + *

        Post-resolution consistency checks may fail with {@code + * ResolutionException} for the following reasons:

        + * + *
          + * *
        • A cycle is detected, say where module {@code m1} requires * module {@code m2} and {@code m2} requires {@code m1}.

        • * @@ -356,21 +316,12 @@ * module {@code M} nor exported to {@code M} by any module that * {@code M} reads.

          * - *
        • A module {@code M} declares that it - * "{@code provides ... with q.T}" but package {@code q} is not in - * module {@code M}.

        • - * - *
        • Two or more modules in the configuration are specific to - * different {@link ModuleDescriptor#osName() operating systems}, - * {@link ModuleDescriptor#osArch() architectures}, or {@link - * ModuleDescriptor#osVersion() versions}.

        • - * - *
        • Other implementation specific checks, for example referential - * integrity checks to ensure that different versions of tighly coupled - * modules cannot be combined in the same configuration.

        • - * *
        * + * @implNote In the implementation then observability of modules may depend + * on referential integrity checks that ensure that different builds of + * tightly coupled modules cannot be combined in the same configuration. + * * @param before * The before module finder to find modules * @param parents @@ -386,17 +337,22 @@ * @return The configuration that is the result of resolving the given * root modules * + * @throws FindException + * If resolution fails for an observability-related reason * @throws ResolutionException - * If resolution or the post-resolution checks fail + * If a post-resolution consistency checks fails * @throws IllegalArgumentException - * If the list of parents is empty + * If the list of parents is empty, or the list has two or more + * parents with modules for different target operating systems, + * architectures, or versions + * * @throws SecurityException * If locating a module is denied by the security manager */ - public static Configuration resolveRequires(ModuleFinder before, - List parents, - ModuleFinder after, - Collection roots) + public static Configuration resolve(ModuleFinder before, + List parents, + ModuleFinder after, + Collection roots) { Objects.requireNonNull(before); Objects.requireNonNull(after); @@ -407,7 +363,7 @@ throw new IllegalArgumentException("'parents' is empty"); Resolver resolver = new Resolver(before, parentList, after, null); - resolver.resolveRequires(roots); + resolver.resolve(roots); return new Configuration(parentList, resolver, true); } @@ -417,24 +373,24 @@ * configuration. * *

        This method works exactly as specified by {@link - * #resolveRequires(ModuleFinder,List,ModuleFinder,Collection) - * resolveRequires} except that the graph of resolved modules is augmented + * #resolve(ModuleFinder,List,ModuleFinder,Collection) + * resolve} except that the graph of resolved modules is augmented * with modules induced by the service-use dependence relation.

        * *

        More specifically, the root modules are resolved as if by calling - * {@code resolveRequires}. The resolved modules, and all modules in the + * {@code resolve}. The resolved modules, and all modules in the * parent configurations, with {@link ModuleDescriptor#uses() service * dependences} are then examined. All modules found by the given module * finders that {@link ModuleDescriptor#provides() provide} an * implementation of one or more of the service types are added to the * module graph and then resolved as if by calling the {@code - * resolveRequires} method. Adding modules to the module graph may - * introduce new service-use dependences and so the process works - * iteratively until no more modules are added.

        - * - *

        As service binding involves resolution then it may fail with {@link - * ResolutionException} for exactly the same reasons specified in - * {@code resolveRequires}.

        + * resolve} method. Adding modules to the module graph may introduce new + * service-use dependences and so the process works iteratively until no + * more modules are added.

        + * + *

        As service binding involves resolution then it may fail with {@code + * FindException} or {@code ResolutionException} for exactly the same + * reasons specified in {@code resolve}.

        * * @param before * The before module finder to find modules @@ -448,20 +404,26 @@ * The possibly-empty collection of module names of the modules * to resolve * - * @return The configuration that is the result of resolving the given - * root modules + * @return The configuration that is the result of resolving, with service + * binding, the given root modules * + * @throws FindException + * If resolution fails for any of the observability-related reasons + * specified by the static {@code resolve} method * @throws ResolutionException - * If resolution or the post-resolution checks fail + * If any of the post-resolution consistency checks specified by + * the static {@code resolve} method fail * @throws IllegalArgumentException - * If the list of parents is empty + * If the list of parents is empty, or the list has two or more + * parents with modules for different target operating systems, + * architectures, or versions * @throws SecurityException * If locating a module is denied by the security manager */ - public static Configuration resolveRequiresAndUses(ModuleFinder before, - List parents, - ModuleFinder after, - Collection roots) + public static Configuration resolveAndBind(ModuleFinder before, + List parents, + ModuleFinder after, + Collection roots) { Objects.requireNonNull(before); Objects.requireNonNull(after); @@ -472,7 +434,7 @@ throw new IllegalArgumentException("'parents' is empty"); Resolver resolver = new Resolver(before, parentList, after, null); - resolver.resolveRequires(roots).resolveUses(); + resolver.resolve(roots).bind(); return new Configuration(parentList, resolver, true); } --- old/src/java.base/share/classes/java/lang/module/FindException.java 2017-02-07 13:13:26.441764599 +0000 +++ new/src/java.base/share/classes/java/lang/module/FindException.java 2017-02-07 13:13:26.274753130 +0000 @@ -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 @@ -26,10 +26,14 @@ package java.lang.module; /** - * Thrown by module finders when finding a module fails. + * Thrown by a {@link ModuleFinder ModuleFinder} when an error occurs finding + * a module. Also thrown by {@link + * Configuration#resolve(ModuleFinder,java.util.List,ModuleFinder,java.util.Collection) + * Configuration.resolve} when resolution fails for observability-related + * reasons. * - * @see ModuleFinder * @since 9 + * @spec JPMS */ public class FindException extends RuntimeException { --- old/src/java.base/share/classes/java/lang/module/InvalidModuleDescriptorException.java 2017-02-07 13:13:26.889795367 +0000 +++ new/src/java.base/share/classes/java/lang/module/InvalidModuleDescriptorException.java 2017-02-07 13:13:26.726784172 +0000 @@ -31,6 +31,7 @@ * * @see ModuleDescriptor#read * @since 9 + * @spec JPMS */ public class InvalidModuleDescriptorException extends RuntimeException { private static final long serialVersionUID = 4863390386809347380L; --- old/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 2017-02-07 13:13:27.346826752 +0000 +++ new/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 2017-02-07 13:13:27.176815077 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -37,11 +37,13 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,18 +58,37 @@ /** * A module descriptor. * - *

        A {@code ModuleDescriptor} is typically created from the binary form - * of a module declaration. Alternatively, the {@link ModuleDescriptor.Builder} - * class can be used to create a {@code ModuleDescriptor} from its components. - * The {@link #module module}, {@link #openModule openModule}, and {@link - * #automaticModule automaticModule} methods create builders for building - * different kinds of modules.

        + *

        A module descriptor describes a named module and defines methods to + * obtain each of its components. The module descriptor for a named module + * in the Java virtual machine is obtained by invoking the {@link + * java.lang.reflect.Module Module}'s {@link java.lang.reflect.Module#getDescriptor + * getDescriptor} method. Module descriptors can also be created using the + * {@link ModuleDescriptor.Builder} class or by reading the binary form of a + * module declaration ({@code module-info.class}) using the {@link + * #read(InputStream,Supplier) read} methods defined here.

        + * + *

        A module descriptor describes a normal, open, or automatic + * module. Normal modules and open modules describe their {@link + * #requires() dependences}, {@link #exports() exported-packages}, the services + * that they {@link #uses() use} or {@link #provides() provide}, and other + * components. Normal modules may {@link #opens() open} specific + * packages. The module descriptor for an open modules does not declare any + * open packages (its {@code opens} method returns an empty set) but when + * instantiated in the Java virtual machine then it is treated as if all + * packages are open. The module descriptor for an automatic module does not + * declare any dependences (except for the mandatory dependency on {@code + * java.base}), and does not declare any exported or open packages. Automatic + * module receive special treatment during resolution so that they read all + * other modules in the configuration. When an automatic module is instantiated + * in the Java virtual machine then it reads every unnamed module and is + * treated as if all packages are exported and open.

        * *

        {@code ModuleDescriptor} objects are immutable and safe for use by * multiple concurrent threads.

        * - * @since 9 * @see java.lang.reflect.Module + * @since 9 + * @spec JPMS */ public class ModuleDescriptor @@ -75,10 +96,45 @@ { /** + * A modifier on a module. + * + * @see ModuleDescriptor#modifiers() + * @since 9 + */ + public static enum Modifier { + /** + * An open module. An open module does not declare any open packages + * but the resulting module is treated as if all packages are open. + */ + OPEN, + + /** + * An automatic module. An automatic module is treated as if it exports + * and opens all packages. + * + * @apiNote This modifier does not correspond to a module flag in the + * binary form of a module declaration ({@code module-info.class}). + */ + AUTOMATIC, + + /** + * The module was not explicitly or implicitly declared. + */ + SYNTHETIC, + + /** + * The module was implicitly declared. + */ + MANDATED; + } + + + /** *

        A dependence upon a module

        * * @see ModuleDescriptor#requires() * @since 9 + * @spec JPMS */ public final static class Requires @@ -88,7 +144,9 @@ /** * A modifier on a module dependence. * + * @see Requires#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { @@ -171,14 +229,18 @@ * Compares this module dependence to another. * *

        Two {@code Requires} objects are compared by comparing their - * module name lexicographically. Where the module names are equal then - * the sets of modifiers are compared based on a value computed from the - * ordinal of each modifier. Where the module names are equal and the - * set of modifiers are equal then the version of the modules 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.

        + * module names lexicographically. Where the module names are equal + * then the sets of modifiers are compared in the same way that + * module modifiers are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the module names are equal and + * the set of modifiers are equal then the version of the modules + * 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.

        + * + * @param that + * The module dependence to compare * * @return A negative integer, zero, or a positive integer if this module * dependence is less than, equal to, or greater than the given @@ -186,39 +248,22 @@ */ @Override public int compareTo(Requires that) { + if (this == that) return 0; + int c = this.name().compareTo(that.name()); - if (c != 0) - return c; + if (c != 0) return c; // modifiers - c = Long.compare(this.modsValue(), that.modsValue()); - if (c != 0) - return c; + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) return c; // compiledVersion - if (this.compiledVersion != null) { - if (that.compiledVersion != null) - c = this.compiledVersion.compareTo(that.compiledVersion); - else - c = 1; - } else { - if (that.compiledVersion != null) - c = -1; - } - - return c; - } + c = compare(this.compiledVersion, that.compiledVersion); + if (c != 0) return c; - /** - * Return a value for the modifiers to allow sets of modifiers to be - * compared. - */ - private long modsValue() { - long value = 0; - for (Modifier m : mods) { - value += 1 << m.ordinal(); - } - return value; + return 0; } /** @@ -266,9 +311,9 @@ } /** - * Returns a string describing module dependence. + * Returns a string describing this module dependence. * - * @return A string describing module dependence + * @return A string describing this module dependence */ @Override public String toString() { @@ -285,18 +330,23 @@ /** - *

        A module export, may be qualified or unqualified.

        + *

        A package exported by a module, may be qualified or unqualified.

        * * @see ModuleDescriptor#exports() * @since 9 + * @spec JPMS */ - public final static class Exports { + public final static class Exports + implements Comparable + { /** - * A modifier on a module export. + * A modifier on an exported package. * + * @see Exports#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { @@ -381,6 +431,51 @@ } /** + * Compares this module export to another. + * + *

        Two {@code Exports} objects are compared by comparing the package + * names lexicographically. Where the packages names are equal then the + * sets of modifiers are compared in the same way that module modifiers + * are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the package names are equal and + * the set of modifiers are equal then the set of target modules are + * compared. This is done by sorting the sets, iterating over both sets + * in ascending order, and comparing the corresponding elements + * lexicographically. Where the sets differ in size, and the larger set + * contains all elements of the smaller set, then the larger set is + * considered to succeed the smaller set.

        + * + * @param that + * The module export to compare + * + * @return A negative integer, zero, or a positive integer if this module + * export is less than, equal to, or greater than the given + * export dependence + */ + @Override + public int compareTo(Exports that) { + if (this == that) return 0; + + int c = source.compareTo(that.source); + if (c != 0) + return c; + + // modifiers + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) + return c; + + // targets + c = compare(targets, that.targets); + if (c != 0) + return c; + + return 0; + } + + /** * Computes a hash code for this module export. * *

        The hash code is based upon the modifiers, the package name, @@ -425,9 +520,9 @@ } /** - * Returns a string describing module export. + * Returns a string describing the exported package. * - * @return A string describing module export + * @return A string describing the exported package */ @Override public String toString() { @@ -441,8 +536,7 @@ /** - *

        Represents a module opens directive, may be qualified or - * unqualified.

        + *

        A package opened by a module, may be qualified or unqualified.

        * *

        The opens directive in a module declaration declares a * package to be open to allow all types in the package, and all their @@ -452,26 +546,30 @@ * * @see ModuleDescriptor#opens() * @since 9 + * @spec JPMS */ - public final static class Opens { - + public final static class Opens + implements Comparable + { /** - * A modifier on a module opens directive. + * A modifier on an open package. * + * @see Opens#modifiers() * @since 9 + * @spec JPMS */ public static enum Modifier { /** - * The opens was not explicitly or implicitly declared in the - * source of the module declaration. + * The open package was not explicitly or implicitly declared in + * the source of the module declaration. */ SYNTHETIC, /** - * The opens was implicitly declared in the source of the module - * declaration. + * The open package was implicitly declared in the source of the + * module declaration. */ MANDATED; @@ -544,6 +642,51 @@ } /** + * Compares this module opens to another. + * + *

        Two {@code Opens} objects are compared by comparing the package + * names lexicographically. Where the packages names are equal then the + * sets of modifiers are compared in the same way that module modifiers + * are compared (see {@link ModuleDescriptor#compareTo + * ModuleDescriptor.compareTo}). Where the package names are equal and + * the set of modifiers are equal then the set of target modules are + * compared. This is done by sorting the sets, iterating over both sets + * in ascending order, and comparing the corresponding elements + * lexicographically. Where the sets differ in size, and the larger set + * contains all elements of the smaller set, then the larger set is + * considered to succeed the smaller set.

        + * + * @param that + * The module opens to compare + * + * @return A negative integer, zero, or a positive integer if this module + * opens is less than, equal to, or greater than the given + * module opens + */ + @Override + public int compareTo(Opens that) { + if (this == that) return 0; + + int c = source.compareTo(that.source); + if (c != 0) + return c; + + // modifiers + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) + return c; + + // targets + c = compare(targets, that.targets); + if (c != 0) + return c; + + return 0; + } + + /** * Computes a hash code for this module opens. * *

        The hash code is based upon the modifiers, the package name, @@ -588,9 +731,9 @@ } /** - * Returns a string describing module opens. + * Returns a string describing the open package. * - * @return A string describing module opens + * @return A string describing the open package */ @Override public String toString() { @@ -608,10 +751,12 @@ * * @see ModuleDescriptor#provides() * @since 9 + * @spec JPMS */ - public final static class Provides { - + public final static class Provides + implements Comparable + { private final String service; private final List providers; @@ -642,6 +787,46 @@ public List providers() { return providers; } /** + * Compares this provides to another. + * + *

        Two {@code Provides} objects are compared by comparing the fully + * qualified class name of the service type lexicographically. Where the + * class names are equal then the list of the provider class names are + * compared by comparing the corresponding elements of both lists + * lexicographically and in sequence. Where the lists differ in size, + * {@code N} is the size of the shorter list, and the first {@code N} + * corresponding elements are equal, then the longer list is considered + * to succeed the shorter list.

        + * + * @param that + * The {@code Provides} to compare + * + * @return A negative integer, zero, or a positive integer if this provides + * is less than, equal to, or greater than the given provides + */ + public int compareTo(Provides that) { + if (this == that) return 0; + + int c = service.compareTo(that.service); + if (c != 0) return c; + + // compare provider class names in sequence + int size1 = this.providers.size(); + int size2 = that.providers.size(); + for (int index=0; index size2) ? 1 : -1; + } + } + + /** * Computes a hash code for this provides. * *

        The hash code is based upon the service type and the set of @@ -699,7 +884,7 @@ * *

        A version string has three components: The version number itself, an * optional pre-release version, and an optional build version. Each - * component is sequence of tokens; each token is either a non-negative + * component is a sequence of tokens; each token is either a non-negative * integer or a string. Tokens are separated by the punctuation characters * {@code '.'}, {@code '-'}, or {@code '+'}, or by transitions from a * sequence of digits to a sequence of characters that are neither digits @@ -740,6 +925,7 @@ * * @see ModuleDescriptor#version() * @since 9 + * @spec JPMS */ public final static class Version @@ -1009,36 +1195,26 @@ } - + private final String name; private final Version version; - private final boolean open; - - // Indicates if synthesised for a JAR file found on the module path - private final boolean automatic; - - // Not generated from a module-info.java - private final boolean synthetic; - + private final Set modifiers; + private final boolean open; // true if modifiers contains OPEN + private final boolean automatic; // true if modifiers contains AUTOMATIC private final Set requires; private final Set exports; private final Set opens; private final Set uses; private final Set provides; - - // Added post-compilation by tools private final Set packages; private final String mainClass; private final String osName; private final String osArch; private final String osVersion; - private ModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -1052,10 +1228,9 @@ { this.name = name; this.version = version; - this.open = open; - this.automatic = automatic; - this.synthetic = synthetic; - + this.modifiers = emptyOrUnmodifiableSet(modifiers); + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); assert (requires.stream().map(Requires::name).distinct().count() == requires.size()); this.requires = emptyOrUnmodifiableSet(requires); @@ -1072,40 +1247,12 @@ } /** - * Clones the given module descriptor with an augmented set of packages - */ - ModuleDescriptor(ModuleDescriptor md, Set pkgs) { - this.name = md.name; - this.version = md.version; - this.open = md.open; - this.automatic = md.automatic; - this.synthetic = md.synthetic; - - this.requires = md.requires; - this.exports = md.exports; - this.opens = md.opens; - this.uses = md.uses; - this.provides = md.provides; - - Set packages = new HashSet<>(md.packages); - packages.addAll(pkgs); - this.packages = emptyOrUnmodifiableSet(packages); - - this.mainClass = md.mainClass; - this.osName = md.osName; - this.osArch = md.osArch; - this.osVersion = md.osVersion; - } - - /** * Creates a module descriptor from its components. * The arguments are pre-validated and sets are unmodifiable sets. */ ModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -1120,9 +1267,9 @@ boolean unused) { this.name = name; this.version = version; - this.open = open; - this.automatic = automatic; - this.synthetic = synthetic; + this.modifiers = modifiers; + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); this.requires = requires; this.exports = exports; this.opens = opens; @@ -1137,7 +1284,7 @@ } /** - *

        The module name.

        + *

        Returns the module name.

        * * @return The module name */ @@ -1146,11 +1293,19 @@ } /** + *

        Returns the set of module modifiers.

        + * + * @return A possibly-empty unmodifiable set of modifiers + */ + public Set modifiers() { + return modifiers; + } + + /** *

        Returns {@code true} if this is an open module.

        * - *

        An open module does not declare any open packages (the {@link #opens() - * opens} method returns an empty set) but the resulting module is treated - * as if all packages are open.

        + *

        This method is equivalent to testing if the set of {@link #modifiers + * modifiers} contains the {@link Modifier#OPEN OPEN} modifier.

        * * @return {@code true} if this is an open module */ @@ -1161,12 +1316,8 @@ /** *

        Returns {@code true} if this is an automatic module.

        * - *

        An automatic module is defined implicitly rather than explicitly - * and therefore does not have a module declaration. JAR files located on - * the application module path, or by the {@link ModuleFinder} returned by - * {@link ModuleFinder#of(java.nio.file.Path[]) ModuleFinder.of}, are - * treated as automatic modules if they do have not have a module - * declaration.

        + *

        This method is equivalent to testing if the set of {@link #modifiers + * modifiers} contains the {@link Modifier#OPEN AUTOMATIC} modifier.

        * * @return {@code true} if this is an automatic module */ @@ -1175,20 +1326,12 @@ } /** - *

        Returns {@code true} if this module descriptor was not generated - * from an explicit module declaration ({@code module-info.java}) - * or an implicit module declaration (an {@link #isAutomatic() automatic} - * module).

        + *

        Returns the set of module dependences.

        * - * @return {@code true} if this module descriptor was not generated by - * an explicit or implicit module declaration - */ - public boolean isSynthetic() { - return synthetic; - } - - /** - *

        The dependences of this module.

        + *

        The set includes a dependency on "{@code java.base}" when this + * module is not named "{@code java.base}". If this module is an automatic + * module then it does not have a dependency on any module other than + * "{@code java.base}".

        * * @return A possibly-empty unmodifiable set of {@link Requires} objects */ @@ -1197,7 +1340,10 @@ } /** - *

        The module exports.

        + *

        Returns the set of exported packages.

        + * + *

        If this module is an automatic module then the set of exports + * is empty.

        * * @return A possibly-empty unmodifiable set of exported packages */ @@ -1206,16 +1352,10 @@ } /** - *

        The module opens directives.

        + *

        Returns the set of open packages.

        * - *

        Each {@code Opens} object in the set represents a package (and - * the set of target module names when qualified) where all types in the - * package, and all their members, not just public types and their public - * members, can be reflected on when using APIs that bypass or suppress - * default Java language access control checks.

        - * - *

        This method returns an empty set when invoked on {@link #isOpen() - * open} module.

        + *

        If this module is an open module or an automatic module then the + * set of open packages is empty.

        * * @return A possibly-empty unmodifiable set of open packages */ @@ -1224,7 +1364,10 @@ } /** - *

        The service dependences of this module.

        + *

        Returns the set of service dependences.

        + * + *

        If this module is an automatic module then the set of service + * dependences is empty.

        * * @return A possibly-empty unmodifiable set of the fully qualified class * names of the service types used @@ -1234,7 +1377,7 @@ } /** - *

        The services that this module provides.

        + *

        Returns the set of services that the module provides.

        * * @return The possibly-empty unmodifiable set of the services that this * module provides @@ -1244,7 +1387,7 @@ } /** - * Returns this module's version. + *

        Returns the module version.

        * * @return This module's version */ @@ -1253,10 +1396,10 @@ } /** - * Returns a string containing this module's name and, if present, its - * version. + *

        Returns a string containing the module name and, if present, its + * version.

        * - * @return A string containing this module's name and, if present, its + * @return A string containing the module name and, if present, its * version. */ public String toNameAndVersion() { @@ -1268,51 +1411,51 @@ } /** - * Returns the module's main class. + *

        Returns the module main class.

        * - * @return The fully qualified class name of this module's main class + * @return The fully qualified class name of the module's main class */ public Optional mainClass() { return Optional.ofNullable(mainClass); } /** - * Returns the operating system name if this module is operating system + * Returns the operating system name if the module is operating system * specific. * * @return The operating system name or an empty {@code Optional} - * if this module is not operating system specific + * if the module is not operating system specific */ public Optional osName() { return Optional.ofNullable(osName); } /** - * Returns the operating system architecture if this module is operating + * Returns the operating system architecture if the module is operating * system architecture specific. * * @return The operating system architecture or an empty {@code Optional} - * if this module is not operating system architecture specific + * if the module is not operating system architecture specific */ public Optional osArch() { return Optional.ofNullable(osArch); } /** - * Returns the operating system version if this module is operating + * Returns the operating system version if the module is operating * system version specific. * * @return The operating system version or an empty {@code Optional} - * if this module is not operating system version specific + * if the module is not operating system version specific */ public Optional osVersion() { return Optional.ofNullable(osVersion); } /** - * Returns the names of all packages in this module. + * Returns the set of packages in the module. * - * @return A possibly-empty unmodifiable set of all packages in the module + * @return A possibly-empty unmodifiable set of the packages in the module */ public Set packages() { return packages; @@ -1320,37 +1463,53 @@ /** - * A builder used for building {@link ModuleDescriptor} objects. + * A builder for building {@link ModuleDescriptor} objects. * - *

        {@code ModuleDescriptor} defines the {@link #module module}, {@link - * #openModule openModule}, and {@link #automaticModule automaticModule} - * methods to create builders for building different kinds of modules.

        + *

        {@code ModuleDescriptor} defines the {@link #newModule newModule}, + * {@link #newOpenModule newOpenModule}, and {@link #newAutomaticModule + * newAutomaticModule} methods to create builders for building + * normal, open, and automatic modules.

        + * + *

        The set of packages in the module are accumulated by the {@code + * Builder} as the {@link ModuleDescriptor.Builder#exports(String) exports}, + * {@link ModuleDescriptor.Builder#opens(String) opens}, + * {@link ModuleDescriptor.Builder#packages(Set) packages}, + * {@link ModuleDescriptor.Builder#provides(String,List) provides}, and + * {@link ModuleDescriptor.Builder#mainClass(String) mainClass} methods are + * invoked.

        + * + *

        The module names, package names, and class names that are parameters + * specified to the builder methods are the module names, package names, + * and qualified names of classes (in named packages) as defined in the + * The Java™ Language Specification.

        * *

        Example usage:

        - *
        {@code    ModuleDescriptor descriptor = ModuleDescriptor.module("m1")
        -     *         .exports("p")
        -     *         .requires("m2")
        +     * 
        {@code    ModuleDescriptor descriptor = ModuleDescriptor.newModule("stats.core")
        +     *         .requires("java.base")
        +     *         .exports("org.acme.stats.core.clustering")
        +     *         .exports("org.acme.stats.core.regression")
        +     *         .packages(Set.of("org.acme.stats.core.internal"))
              *         .build();
              * }
        * * @apiNote A {@code Builder} checks the components and invariants as - * components are added to the builder. The rational for this is to detect + * components are added to the builder. The rationale for this is to detect * errors as early as possible and not defer all validation to the - * {@link #build build} method. A {@code Builder} cannot be used to create - * a {@link ModuleDescriptor#isSynthetic() synthetic} module. + * {@link #build build} method. * * @since 9 + * @spec JPMS */ public static final class Builder { final String name; - final boolean strict; // true if module names are checked + final boolean strict; + final Set modifiers; final boolean open; - final boolean synthetic; - boolean automatic; + final boolean automatic; + final Set packages = new HashSet<>(); final Map requires = new HashMap<>(); final Map exports = new HashMap<>(); final Map opens = new HashMap<>(); - final Set concealedPackages = new HashSet<>(); final Set uses = new HashSet<>(); final Map provides = new HashMap<>(); Version version; @@ -1362,35 +1521,25 @@ /** * Initializes a new builder with the given module name. * - * @param strict - * Indicates whether module names are checked or not + * If {@code strict} is {@code true} then module, package, and class + * names are checked to ensure they are legal names. In addition, the + * {@link #build buid} method will add "{@code requires java.base}" if + * the dependency is not declared. */ - Builder(String name, boolean strict, boolean open, boolean synthetic) { + Builder(String name, boolean strict, Set modifiers) { this.name = (strict) ? requireModuleName(name) : name; this.strict = strict; - this.open = open; - this.synthetic = synthetic; - } - - /* package */ Builder automatic(boolean automatic) { - this.automatic = automatic; - return this; + this.modifiers = modifiers; + this.open = modifiers.contains(Modifier.OPEN); + this.automatic = modifiers.contains(Modifier.AUTOMATIC); + assert !open || !automatic; } /** - * Returns the set of packages that are exported (unconditionally or - * unconditionally). + * Returns a snapshot of the packages in the module. */ - /* package */ Set exportedPackages() { - return exports.keySet(); - } - - /** - * Returns the set of packages that are opened (unconditionally or - * unconditionally). - */ - /* package */Set openPackages() { - return opens.keySet(); + /* package */ Set packages() { + return Collections.unmodifiableSet(packages); } /** @@ -1406,8 +1555,12 @@ * initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Requires req) { + if (automatic) + throw new IllegalStateException("Automatic modules cannot declare" + + " dependences"); String mn = req.name(); if (name.equals(mn)) throw new IllegalArgumentException("Dependence on self"); @@ -1433,11 +1586,12 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Set ms, String mn, @@ -1448,6 +1602,23 @@ return requires(new Requires(ms, mn, compiledVersion)); } + /* package */Builder requires(Set ms, + String mn, + String compiledVersion) { + Version v = null; + try { + v = Version.parse(compiledVersion); + } catch (IllegalArgumentException e) { + // for now, drop un-parsable version when non-strict + if (strict) throw e; + } + if (v == null) { + return requires(ms, mn); + } else { + return requires(ms, mn, v); + } + } + /** * Adds a dependence on a module with the given (and possibly empty) * set of modifiers. @@ -1460,11 +1631,12 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(Set ms, String mn) { if (strict) @@ -1481,18 +1653,19 @@ * @return This builder * * @throws IllegalArgumentException - * If the module name is {@code null}, is not a legal Java - * identifier, or is equal to the module name that this builder + * If the module name is {@code null}, is not a legal module + * name, or is equal to the module name that this builder * was initialized to build * @throws IllegalStateException * If the dependence on the module has already been declared + * or this builder is for an automatic module */ public Builder requires(String mn) { return requires(EnumSet.noneOf(Requires.Modifier.class), mn); } /** - * Adds an export. + * Adds an exported package. * * @param e * The export @@ -1500,29 +1673,27 @@ * @return This builder * * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the {@link Exports#source package} is already declared as + * exported or this builder is for an automatic module */ public Builder exports(Exports e) { - // can't be exported and concealed - String source = e.source(); - if (concealedPackages.contains(source)) { - throw new IllegalStateException("Package " + source - + " already declared"); + if (automatic) { + throw new IllegalStateException("Automatic modules cannot declare" + + " exported packages"); } + String source = e.source(); if (exports.containsKey(source)) { throw new IllegalStateException("Exported package " + source + " already declared"); } - exports.put(source, e); + packages.add(source); return this; } /** - * Adds an export, with the given (and possibly empty) set of modifiers, - * to export a package to a set of target modules. + * Adds an exported package with the given (and possibly empty) set of + * modifiers. The package is exported to a set of target modules. * * @param ms * The set of modifiers @@ -1534,33 +1705,34 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(Set ms, String pn, Set targets) { - Exports e = new Exports(ms, requirePackageName(pn), targets); + Exports e = new Exports(ms, pn, targets); // check targets targets = e.targets(); if (targets.isEmpty()) throw new IllegalArgumentException("Empty target set"); - if (strict) + if (strict) { + requirePackageName(e.source()); targets.stream().forEach(Checks::requireModuleName); - + } return exports(e); } /** - * Adds an unqualified export with the given (and possibly empty) set - * of modifiers. + * Adds an exported package with the given (and possibly empty) set of + * modifiers. The package is exported to all modules. * * @param ms * The set of modifiers @@ -1570,20 +1742,23 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(Set ms, String pn) { - Exports e = new Exports(ms, requirePackageName(pn), Collections.emptySet()); + if (strict) { + requirePackageName(pn); + } + Exports e = new Exports(ms, pn, Collections.emptySet()); return exports(e); } /** - * Adds an export to export a package to a set of target modules. + * Adds an exported package. The package is exported to a set of target + * modules. * * @param pn * The package name @@ -1593,20 +1768,20 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(String pn, Set targets) { return exports(Collections.emptySet(), pn, targets); } /** - * Adds an unqualified export. + * Adds an exported package. The package is exported to all modules. * * @param pn * The package name @@ -1614,19 +1789,18 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method or the package is already - * declared as exported + * If the package is already declared as exported + * or this builder is for an automatic module */ public Builder exports(String pn) { return exports(Collections.emptySet(), pn); } /** - * Adds an opens directive. + * Adds an open package. * * @param obj * The {@code Opens} object @@ -1634,35 +1808,28 @@ * @return This builder * * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Opens obj) { - if (open) { - throw new IllegalStateException("open modules cannot declare" - + " open packages"); + if (open || automatic) { + throw new IllegalStateException("Open or automatic modules cannot" + + " declare open packages"); } - - // can't be open and concealed String source = obj.source(); - if (concealedPackages.contains(source)) { - throw new IllegalStateException("Package " + source - + " already declared"); - } if (opens.containsKey(source)) { throw new IllegalStateException("Open package " + source + " already declared"); } - opens.put(source, obj); + packages.add(source); return this; } /** - * Adds an opens directive, with the given (and possibly empty) - * set of modifiers, to open a package to a set of target modules. + * Adds an open package with the given (and possibly empty) set of + * modifiers. The package is open to a set of target modules. * * @param ms * The set of modifiers @@ -1674,33 +1841,34 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Set ms, String pn, Set targets) { - Opens e = new Opens(ms, requirePackageName(pn), targets); + Opens opens = new Opens(ms, pn, targets); // check targets - targets = e.targets(); + targets = opens.targets(); if (targets.isEmpty()) throw new IllegalArgumentException("Empty target set"); - if (strict) + if (strict) { + requirePackageName(opens.source()); targets.stream().forEach(Checks::requireModuleName); - - return opens(e); + } + return opens(opens); } /** - * Adds an opens directive to open a package with the given (and - * possibly empty) set of modifiers. + * Adds an open package with the given (and possibly empty) set of + * modifiers. The package is open to all modules. * * @param ms * The set of modifiers @@ -1710,21 +1878,22 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(Set ms, String pn) { - Opens e = new Opens(ms, requirePackageName(pn), Collections.emptySet()); + if (strict) { + requirePackageName(pn); + } + Opens e = new Opens(ms, pn, Collections.emptySet()); return opens(e); } /** - * Adds an opens directive to open a package to a set of target - * modules. + * Adds an open package. The package is open to a set of target modules. * * @param pn * The package name @@ -1734,20 +1903,20 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name or any of the target modules is {@code - * null} or is not a legal Java identifier, or the set of - * targets is empty + * If the package name is {@code null} or is not a legal + * package name, the set of target modules is empty, or the set + * of target modules contains a name that is not a legal module + * name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(String pn, Set targets) { return opens(Collections.emptySet(), pn, targets); } /** - * Adds an opens directive to open a package. + * Adds an open package. The package is open to all modules. * * @param pn * The package name @@ -1755,12 +1924,11 @@ * @return This builder * * @throws IllegalArgumentException - * If the package name is {@code null} or is not a legal Java - * identifier + * If the package name is {@code null} or is not a legal + * package name * @throws IllegalStateException - * If the package is already declared as a package with the - * {@link #contains contains} method, the package is already - * declared as open, or this is a builder for an open module + * If the package is already declared as open, or this is a + * builder for an open module or automatic module */ public Builder opens(String pn) { return opens(Collections.emptySet(), pn); @@ -1775,12 +1943,16 @@ * @return This builder * * @throws IllegalArgumentException - * If the service type is {@code null} or is not a legal Java - * identifier + * If the service type is {@code null} or not a qualified name of + * 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 */ public Builder uses(String service) { + if (automatic) + throw new IllegalStateException("Automatic modules can not declare" + + " service dependences"); if (uses.contains(requireServiceTypeName(service))) throw new IllegalStateException("Dependence upon service " + service + " already declared"); @@ -1789,7 +1961,9 @@ } /** - * Provides a service with one or more implementations. + * Provides a service with one or more implementations. The package for + * each {@link Provides#providers provider} (or provider factory) is + * added to the module if not already added. * * @param p * The provides @@ -1801,16 +1975,18 @@ * declared */ public Builder provides(Provides p) { - String st = p.service(); - if (provides.containsKey(st)) + String service = p.service(); + if (provides.containsKey(service)) throw new IllegalStateException("Providers of service " - + st + " already declared"); - provides.put(st, p); + + service + " already declared"); + provides.put(service, p); + p.providers().forEach(name -> packages.add(packageName(name))); return this; } /** - * Provides implementations of a service. + * Provides implementations of a service. The package for each provider + * (or provider factory) is added to the module if not already added. * * @param service * The service type @@ -1821,103 +1997,59 @@ * * @throws IllegalArgumentException * If the service type or any of the provider class names is - * {@code null} or is not a legal Java identifier, or the list - * of provider class names is empty + * {@code null} or not a qualified name of a class in a named + * package, or the list of provider class names is empty * @throws IllegalStateException * If the providers for the service type have already been * declared */ public Builder provides(String service, List providers) { - if (provides.containsKey(service)) - throw new IllegalStateException("Providers of service " - + service + " already declared by " + name); - - Provides p = new Provides(requireServiceTypeName(service), providers); + Provides p = new Provides(service, providers); // check providers after the set has been copied. List providerNames = p.providers(); if (providerNames.isEmpty()) throw new IllegalArgumentException("Empty providers set"); - providerNames.forEach(Checks::requireServiceProviderName); - provides.put(service, p); - return this; - } - - /** - * Provides an implementation of a service. - * - * @param service - * The service type - * @param provider - * The provider or provider factory class name - * - * @return This builder - * - * @throws IllegalArgumentException - * If the service type or the provider class name is {@code - * null} or is not a legal Java identifier - * @throws IllegalStateException - * If the providers for the service type have already been - * declared - */ - public Builder provides(String service, String provider) { - if (provider == null) - throw new IllegalArgumentException("'provider' is null"); - return provides(service, List.of(provider)); + if (strict) { + requireServiceTypeName(p.service()); + providerNames.forEach(Checks::requireServiceProviderName); + } else { + // Disallow service/providers in unnamed package + String pn = packageName(service); + if (pn.isEmpty()) { + throw new IllegalArgumentException(service + + ": unnamed package"); + } + for (String name : providerNames) { + pn = packageName(name); + if (pn.isEmpty()) { + throw new IllegalArgumentException(name + + ": unnamed package"); + } + } + } + return provides(p); } /** - * Adds a (possible empty) set of packages to the module + * Adds packages to the module. All packages in the set of package names + * that are not in the module are added to module. * * @param pns - * The set of package names + * The (possibly empty) set of package names * * @return This builder * * @throws IllegalArgumentException * If any of the package names is {@code null} or is not a - * legal Java identifier - * @throws IllegalStateException - * If any of packages are already declared as packages in - * the module. This includes packages that are already - * declared as exported or open packages. + * legal package name */ - public Builder contains(Set pns) { - pns.forEach(this::contains); - return this; - } - - /** - * Adds a package to the module. - * - * @param pn - * The package name - * - * @return This builder - * - * @throws IllegalArgumentException - * If the package name is {@code null}, or is not a legal Java - * identifier - * @throws IllegalStateException - * If the package is already declared as a package in the - * module. This includes the package already declared as an - * exported or open package. - */ - public Builder contains(String pn) { - Checks.requirePackageName(pn); - if (concealedPackages.contains(pn)) { - throw new IllegalStateException("Package " + pn - + " already declared"); + public Builder packages(Set pns) { + if (strict) { + pns = new HashSet<>(pns); + pns.forEach(Checks::requirePackageName); } - if (exports.containsKey(pn)) { - throw new IllegalStateException("Exported package " - + pn + " already declared"); - } - if (opens.containsKey(pn)) { - throw new IllegalStateException("Open package " - + pn + " already declared"); - } - concealedPackages.add(pn); + this.packages.addAll(pns); return this; } @@ -1937,22 +2069,35 @@ /** * Sets the module version. * - * @param v + * @param vs * The version string to parse * * @return This builder * * @throws IllegalArgumentException - * If {@code v} is null or cannot be parsed as a version string + * If {@code vs} is {@code null} or cannot be parsed as a + * version string * * @see Version#parse(String) */ - public Builder version(String v) { - return version(Version.parse(v)); + 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; + } + } + return version(v); } /** - * Sets the module main class. + * Sets the module main class. The package for the main class is added + * to the module if not already added. * * @param mc * The module main class @@ -1960,10 +2105,24 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code mainClass} is null or is not a legal Java identifier + * If {@code mainClass} is {@code null} or not a qualified + * name of a class in a named package */ public Builder mainClass(String mc) { - mainClass = requireBinaryName("main class name", mc); + String pn; + if (strict) { + mc = requireQualifiedClassName("main class name", mc); + pn = packageName(mc); + assert !pn.isEmpty(); + } else { + // Disallow main class in unnamed package + pn = packageName(mc); + if (pn.isEmpty()) { + throw new IllegalArgumentException(mc + ": unnamed package"); + } + } + mainClass = mc; + packages.add(pn); return this; } @@ -1976,7 +2135,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osName(String name) { if (name == null || name.isEmpty()) @@ -1994,7 +2153,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osArch(String arch) { if (arch == null || arch.isEmpty()) @@ -2012,7 +2171,7 @@ * @return This builder * * @throws IllegalArgumentException - * If {@code name} is null or the empty String + * If {@code name} is {@code null} or the empty String */ public Builder osVersion(String version) { if (version == null || version.isEmpty()) @@ -2024,26 +2183,34 @@ /** * Builds and returns a {@code ModuleDescriptor} from its components. * + *

        The module will require "{@code java.base}" even if the dependence + * has not been declared (the exception is when building a module named + * "{@code java.base}" as it cannot require itself). The dependence on + * "{@code java.base}" will have the {@link + * java.lang.module.ModuleDescriptor.Requires.Modifier#MANDATED MANDATED} + * modifier if the dependence was not declared.

        + * * @return The module descriptor */ public ModuleDescriptor build() { Set requires = new HashSet<>(this.requires.values()); - - Set packages = new HashSet<>(); - packages.addAll(exports.keySet()); - packages.addAll(opens.keySet()); - packages.addAll(concealedPackages); - Set exports = new HashSet<>(this.exports.values()); Set opens = new HashSet<>(this.opens.values()); + // add dependency on java.base + if (strict + && !name.equals("java.base") + && !this.requires.containsKey("java.base")) { + requires.add(new Requires(Set.of(Requires.Modifier.MANDATED), + "java.base", + null)); + } + Set provides = new HashSet<>(this.provides.values()); return new ModuleDescriptor(name, version, - open, - automatic, - synthetic, + modifiers, requires, exports, opens, @@ -2062,16 +2229,20 @@ * Compares this module descriptor to another. * *

        Two {@code ModuleDescriptor} objects are compared by comparing their - * module name lexicographically. Where the module names are equal then - * the versions, if present, are compared.

        - * - * @apiNote For now, the natural ordering is not consistent with equals. - * If two module descriptors have equal module names, equal versions if - * present, but their corresponding components are not equal, then they - * will be considered equal by this method. + * 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 + * a binary value 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 + * are also equal then the other components of the module descriptors are + * compared in a manner that is consistent with {@code equals}.

        * * @param that - * The object to which this module descriptor is to be compared + * The module descriptor to compare * * @return A negative integer, zero, or a positive integer if this module * descriptor is less than, equal to, or greater than the given @@ -2079,16 +2250,50 @@ */ @Override public int compareTo(ModuleDescriptor that) { + if (this == that) return 0; + int c = this.name().compareTo(that.name()); if (c != 0) return c; - if (version == null) { - if (that.version == null) - return 0; - return -1; - } - if (that.version == null) - return +1; - return version.compareTo(that.version); + + c = compare(this.version, that.version); + if (c != 0) return c; + + long v1 = modsValue(this.modifiers()); + long v2 = modsValue(that.modifiers()); + c = Long.compare(v1, v2); + if (c != 0) return c; + + c = compare(this.requires, that.requires); + if (c != 0) return c; + + c = compare(this.packages, that.packages); + if (c != 0) return c; + + c = compare(this.exports, that.exports); + if (c != 0) return c; + + c = compare(this.opens, that.opens); + if (c != 0) return c; + + c = compare(this.uses, that.uses); + if (c != 0) return c; + + c = compare(this.provides, that.provides); + if (c != 0) return c; + + 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; } /** @@ -2115,10 +2320,9 @@ return false; ModuleDescriptor that = (ModuleDescriptor)ob; return (name.equals(that.name) - && open == that.open - && automatic == that.automatic - && synthetic == that.synthetic + && modifiers.equals(that.modifiers) && requires.equals(that.requires) + && Objects.equals(packages, that.packages) && exports.equals(that.exports) && opens.equals(that.opens) && uses.equals(that.uses) @@ -2127,12 +2331,9 @@ && Objects.equals(mainClass, that.mainClass) && Objects.equals(osName, that.osName) && Objects.equals(osArch, that.osArch) - && Objects.equals(osVersion, that.osVersion) - && Objects.equals(packages, that.packages)); + && Objects.equals(osVersion, that.osVersion)); } - private transient int hash; // cached hash code - /** * Computes a hash code for this module descriptor. * @@ -2147,10 +2348,9 @@ int hc = hash; if (hc == 0) { hc = name.hashCode(); - hc = hc * 43 + Boolean.hashCode(open); - hc = hc * 43 + Boolean.hashCode(automatic); - hc = hc * 43 + Boolean.hashCode(synthetic); + hc = hc * 43 + Objects.hashCode(modifiers); hc = hc * 43 + requires.hashCode(); + hc = hc * 43 + Objects.hashCode(packages); hc = hc * 43 + exports.hashCode(); hc = hc * 43 + opens.hashCode(); hc = hc * 43 + uses.hashCode(); @@ -2160,18 +2360,18 @@ hc = hc * 43 + Objects.hashCode(osName); hc = hc * 43 + Objects.hashCode(osArch); hc = hc * 43 + Objects.hashCode(osVersion); - hc = hc * 43 + Objects.hashCode(packages); if (hc == 0) hc = -1; hash = hc; } return hc; } + private transient int hash; // cached hash code /** - * Returns a string describing this descriptor. + *

        Returns a string describing the module.

        * - * @return A string describing this descriptor + * @return A string describing the module */ @Override public String toString() { @@ -2201,31 +2401,44 @@ * * @param name * The module name + * @param ms + * The set of module modifiers + * + * @return A new builder + * + * @throws IllegalArgumentException + * If the module name is {@code null} or is not a legal module + * name, or the set of modifiers contains both {@link Modifier#OPEN + * OPEN} and {@link Modifier#AUTOMATIC AUTOMATIC} + */ + public static Builder newModule(String name, Set ms) { + Set mods = new HashSet<>(ms); + if (mods.contains(Modifier.OPEN) && mods.contains(Modifier.AUTOMATIC)) + throw new IllegalArgumentException("OPEN and AUTOMATIC not allowed"); + return new Builder(name, true, mods); + } + + /** + * Instantiates a builder to build a module descriptor. + * + * @param name + * The module name * * @return A new builder * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name */ - public static Builder module(String name) { - return new Builder(name, true, false, false); + public static Builder newModule(String name) { + return new Builder(name, true, Set.of()); } /** * Instantiates a builder to build a module descriptor for an open module. - * An open module does not declare any open packages but the resulting - * module is treated as if all packages are open. * - *

        As an example, the following creates a module descriptor for an open - * name "{@code m}" containing two packages, one of which is exported.

        - *
        {@code
        -     *     ModuleDescriptor descriptor = ModuleDescriptor.openModule("m")
        -     *         .requires("java.base")
        -     *         .exports("p")
        -     *         .contains("q")
        -     *         .build();
        -     * }
        + *

        The builder for an open module cannot be used to declare any open + * packages.

        * * @param name * The module name @@ -2233,19 +2446,20 @@ * @return A new builder that builds an open module * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name */ - public static Builder openModule(String name) { - return new Builder(name, true, true, false); + public static Builder newOpenModule(String name) { + return new Builder(name, true, Set.of(Modifier.OPEN)); } /** * Instantiates a builder to build a module descriptor for an automatic - * module. Automatic modules receive special treatment during resolution - * (see {@link Configuration}) so that they read all other modules. When - * Instantiated in the Java virtual machine as a {@link java.lang.reflect.Module} - * then the Module reads every unnamed module in the Java virtual machine. + * module. + * + *

        The builder for an automatic module cannot be used to declare module + * or service dependences. It also cannot be used to declare any exported + * or open packages.

        * * @param name * The module name @@ -2253,13 +2467,13 @@ * @return A new builder that builds an automatic module * * @throws IllegalArgumentException - * If the module name is {@code null} or is not a legal Java - * identifier + * If the module name is {@code null} or is not a legal module + * name * * @see ModuleFinder#of(Path[]) */ - public static Builder automaticModule(String name) { - return new Builder(name, true, false, false).automatic(true); + public static Builder newAutomaticModule(String name) { + return new Builder(name, true, Set.of(Modifier.AUTOMATIC)); } @@ -2269,8 +2483,12 @@ * *

        If the descriptor encoded in the input stream does not indicate a * set of packages in the module then the {@code packageFinder} will be - * invoked. If the {@code packageFinder} throws an {@link UncheckedIOException} - * then {@link IOException} cause will be re-thrown.

        + * invoked. The set of packages that the {@code packageFinder} returns + * must include all the packages that the module exports, opens, as well + * as the packages of the service implementations that the module provides, + * and the package of the main class (if the module has a main class). If + * the {@code packageFinder} throws an {@link UncheckedIOException} then + * {@link IOException} cause will be re-thrown.

        * *

        If there are bytes following the module descriptor then it is * implementation specific as to whether those bytes are read, ignored, @@ -2292,7 +2510,9 @@ * @return The module descriptor * * @throws InvalidModuleDescriptorException - * If an invalid module descriptor is detected + * If an invalid module descriptor is detected or the set of + * packages returned by the {@code packageFinder} does not include + * all of the packages obtained from the module descriptor * @throws IOException * If an I/O error occurs reading from the input stream or {@code * UncheckedIOException} is thrown by the package finder @@ -2305,8 +2525,12 @@ } /** - * Reads the binary form of a module declaration from an input stream - * as a module descriptor. + * Reads the binary form of a module declaration from an input stream as a + * module descriptor. This method works exactly as specified by the 2-arg + * {@link #read(InputStream,Supplier) read} method with the exception that + * a packager finder is not used to find additional packages when the + * module descriptor read from the stream does not indicate the set of + * packages. * * @param in * The input stream @@ -2327,7 +2551,13 @@ * as a module descriptor. * *

        If the descriptor encoded in the byte buffer does not indicate a - * set of packages then the {@code packageFinder} will be invoked.

        + * set of packages in the module then the {@code packageFinder} will be + * invoked. The set of packages that the {@code packageFinder} returns + * must include all the packages that the module exports, opens, as well + * as the packages of the service implementations that the module provides, + * and the package of the main class (if the module has a main class). If + * the {@code packageFinder} throws an {@link UncheckedIOException} then + * {@link IOException} cause will be re-thrown.

        * *

        The module descriptor is read from the buffer stating at index * {@code p}, where {@code p} is the buffer's {@link ByteBuffer#position() @@ -2353,7 +2583,9 @@ * @return The module descriptor * * @throws InvalidModuleDescriptorException - * If an invalid module descriptor is detected + * If an invalid module descriptor is detected or the set of + * packages returned by the {@code packageFinder} does not include + * all of the packages obtained from the module descriptor */ public static ModuleDescriptor read(ByteBuffer bb, Supplier> packageFinder) @@ -2362,8 +2594,11 @@ } /** - * Reads the binary form of a module declaration from a byte buffer - * as a module descriptor. + * Reads the binary form of a module declaration from a byte buffer as a + * module descriptor. This method works exactly as specified by the 2-arg + * {@link #read(ByteBuffer,Supplier) read} method with the exception that a + * packager finder is not used to find additional packages when the module + * descriptor encoded in the buffer does not indicate the set of packages. * * @param bb * The byte buffer @@ -2398,6 +2633,11 @@ } } + private static String packageName(String cn) { + int index = cn.lastIndexOf('.'); + return (index == -1) ? "" : cn.substring(0, index); + } + /** * Returns a string containing the given set of modifiers and label. */ @@ -2407,6 +2647,46 @@ .collect(Collectors.joining(" ")); } + private static > + int compare(T obj1, T obj2) { + if (obj1 != null) { + return (obj2 != null) ? obj1.compareTo(obj2) : 1; + } else { + return (obj2 == null) ? 0 : -1; + } + } + + /** + * Compares two sets of {@code Comparable} objects. + */ + private static > + int compare(Set s1, Set s2) { + Iterator iterator1 = new TreeSet<>(s1).iterator(); + Iterator iterator2 = new TreeSet<>(s2).iterator(); + while (iterator1.hasNext()) { + if (!iterator2.hasNext()) + return 1; // s1 has more elements + T e1 = iterator1.next(); + T e2 = iterator2.next(); + int c = e1.compareTo(e2); + if (c != 0) + return c; + } + if (iterator2.hasNext()) { + return -1; // s2 has more elements + } else { + return 0; + } + } + + private static > long modsValue(Set set) { + long value = 0; + for (Enum e : set) { + value += 1 << e.ordinal(); + } + return value; + } + static { /** * Setup the shared secret to allow code in other packages access @@ -2417,19 +2697,21 @@ @Override public Builder newModuleBuilder(String mn, boolean strict, - boolean open, - boolean synthetic) { - return new Builder(mn, strict, open, synthetic); + Set modifiers) { + return new Builder(mn, strict, modifiers); } @Override - public Set exportedPackages(ModuleDescriptor.Builder builder) { - return builder.exportedPackages(); + public Set packages(ModuleDescriptor.Builder builder) { + return builder.packages(); } @Override - public Set openPackages(ModuleDescriptor.Builder builder) { - return builder.openPackages(); + public void requires(ModuleDescriptor.Builder builder, + Set ms, + String mn, + String compiledVersion) { + builder.requires(ms, mn, compiledVersion); } @Override @@ -2467,22 +2749,9 @@ } @Override - public Version newVersion(String v) { - return new Version(v); - } - - @Override - public ModuleDescriptor newModuleDescriptor(ModuleDescriptor md, - Set pkgs) { - return new ModuleDescriptor(md, pkgs); - } - - @Override public ModuleDescriptor newModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set modifiers, Set requires, Set exports, Set opens, @@ -2496,9 +2765,7 @@ int hashCode) { return new ModuleDescriptor(name, version, - open, - automatic, - synthetic, + modifiers, requires, exports, opens, @@ -2514,12 +2781,12 @@ } @Override - public Configuration resolveRequiresAndUses(ModuleFinder finder, - Collection roots, - boolean check, - PrintStream traceOutput) + public Configuration resolveAndBind(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) { - return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput); + return Configuration.resolveAndBind(finder, roots, check, traceOutput); } }); } --- old/src/java.base/share/classes/java/lang/module/ModuleFinder.java 2017-02-07 13:13:27.984870568 +0000 +++ new/src/java.base/share/classes/java/lang/module/ModuleFinder.java 2017-02-07 13:13:27.815858961 +0000 @@ -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 @@ -48,8 +48,8 @@ /** * A finder of modules. A {@code ModuleFinder} is used to find modules during - * resolution or - * service binding. + * resolution or + * service binding. * *

        A {@code ModuleFinder} can only find one module with a given name. A * {@code ModuleFinder} that finds modules in a sequence of directories, for @@ -85,6 +85,7 @@ *

        A {@code ModuleFinder} is not required to be thread safe.

        * * @since 9 + * @spec JPMS */ public interface ModuleFinder { @@ -124,8 +125,8 @@ * to find that module.

        * * @apiNote This is important to have for methods such as {@link - * Configuration#resolveRequiresAndUses resolveRequiresAndUses} that need - * to scan the module path to find modules that provide a specific service. + * Configuration#resolveAndBind resolveAndBind} that need to scan the + * module path to find modules that provide a specific service. * * @return The set of all module references that this finder locates * @@ -198,13 +199,9 @@ * *

        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. The module finder's {@link #find(String) find} or - * {@link #findAll() findAll} methods throw {@link FindException} if a - * directory containing more than one module with the same name is - * encountered.

        - * - *

        If an element in the array is a path to a directory, and that - * directory contains a file named {@code module-info.class}, then the + * exploded module. It it 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 * modules.

        * @@ -214,9 +211,8 @@ * entry in a {@link java.util.jar.JarFile#isMultiRelease() multi-release} * JAR file) is a modular JAR and is an explicit module. * A JAR file that does not have a {@code module-info.class} in the - * top-level directory is an {@link ModuleDescriptor#isAutomatic automatic} - * module. The {@link ModuleDescriptor} for an automatic module is created as - * follows: + * top-level directory is created as an automatic module. The components + * for the automatic module are derived as follows: * *
          * @@ -248,46 +244,48 @@ * *
      • * - *
      • It {@link ModuleDescriptor#requires() requires} {@code - * java.base}.

      • - * - *
      • The set of packages in the module is derived from the names - * of non-directory entries in the JAR file. A candidate package name - * is derived from an entry using the characters up to, but not - * including, the last forward slash. All remaining forward slashes are - * replaced with dot ({@code "."}). If the resulting string is a valid - * Java identifier then it is assumed to be a package name. For example, - * if the JAR file contains an entry "{@code p/q/Foo.class}" then the - * package name derived is "{@code p.q}". All packages are {@link - * ModuleDescriptor#exports() exported}.

      • + *
      • The set of packages in the module is derived from the + * non-directory entries in the JAR file that have names ending in + * "{@code .class}". A candidate package name is derived from the name + * using the characters up to, but not including, the last forward slash. + * All remaining forward slashes are replaced with dot ({@code "."}). If + * the resulting string is a legal package name then it is assumed to be + * a package name. For example, if the JAR file contains the entry + * "{@code p/q/Foo.class}" then the package name derived is + * "{@code p.q}".

      • * *
      • The contents of entries starting with {@code * META-INF/services/} are assumed to be service configuration files * (see {@link java.util.ServiceLoader}). If the name of a file - * (that follows {@code META-INF/services/}) is a legal Java identifier - * then it is assumed to be the fully-qualified binary name of a - * service type. The entries in the file are assumed to be the - * fully-qualified binary names of provider classes.

      • + * (that follows {@code META-INF/services/}) is a legal class name + * then it is assumed to be the fully-qualified class name of a service + * type. The entries in the file are assumed to be the fully-qualified + * class names of provider classes.

        * *
      • If the JAR file has a {@code Main-Class} attribute in its - * main manifest then its value is the {@link + * main manifest then its value is the module {@link * ModuleDescriptor#mainClass() main class}.

      • * *
      * *

      If a {@code ModuleDescriptor} cannot be created (by means of the * {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an - * automatic module then {@code FindException} is thrown. This can arise, - * for example, when a legal Java identifier name cannot be derived from - * the file name of the JAR file or where the JAR file contains a {@code - * .class} in the top-level directory of the JAR file.

      + * automatic module then {@code FindException} is thrown. This can arise + * when a legal module name cannot be derived from the file name of the JAR + * file, where the JAR file contains a {@code .class} in the top-level + * directory of the JAR file, where an entry in a service configuration + * file is not a legal class name or its package name is not in the set of + * packages derived for the module, or where the module main class is not + * a legal class name or its package is not in the module.

      * *

      In addition to JAR files, an implementation may also support modules - * that are packaged in other implementation specific module formats. When - * a file is encountered that is not recognized as a packaged module then - * {@code FindException} is thrown. An implementation may choose to ignore - * some files, {@link java.nio.file.Files#isHidden hidden} files for - * example. Paths to files that do not exist are always ignored.

      + * that are packaged in other implementation specific module formats. If + * an element in the array specified to this method is a path to a directory + * of modules then entries in the directory that not recognized as modules + * are ignored. If an element in the array is a path to a packaged module + * that is not recognized then a {@code FindException} is thrown when the + * file is encountered. Paths to files that do not exist are always ignored. + *

      * *

      As with automatic modules, the contents of a packaged or exploded * module may need to be scanned in order to determine the packages --- old/src/java.base/share/classes/java/lang/module/ModuleReader.java 2017-02-07 13:13:28.458903122 +0000 +++ new/src/java.base/share/classes/java/lang/module/ModuleReader.java 2017-02-07 13:13:28.292891721 +0000 @@ -45,7 +45,7 @@ * module. A module reader is also intended to be used by {@code ClassLoader} * implementations that load classes and resources from modules.

      * - *

      A resource in a module is identified by a name that is a + *

      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}.

      @@ -61,8 +61,18 @@ * open}, {@link #read read}, and {@link #list list} methods may throw {@code * SecurityException} if access is denied by the security manager.

      * + * @implSpec Implementations of {@code ModuleReader} should take great care + * when translating an abstract resource name to the location of a resource in + * a packaged module or on the file system. Implementations are advised to + * treat resource names with elements such as '{@code .}, '{@code ..}', + * elements containing file separators, or empty elements as "not found". More + * generally, if the resource name is not in the stream of elements that the + * {@code list} method returns then the resource should be treated as "not + * found" to avoid inconsistencies. + * * @see ModuleReference * @since 9 + * @spec JPMS */ public interface ModuleReader extends Closeable { @@ -148,6 +158,9 @@ * If an I/O error occurs or the module reader is closed * @throws SecurityException * If denied by the security manager + * @throws OutOfMemoryError + * If the resource is larger than {@code Integer.MAX_VALUE}, + * the maximum capacity of a byte buffer * * @see ClassLoader#defineClass(String, ByteBuffer, java.security.ProtectionDomain) */ --- old/src/java.base/share/classes/java/lang/module/ModuleReference.java 2017-02-07 13:13:28.935935881 +0000 +++ new/src/java.base/share/classes/java/lang/module/ModuleReference.java 2017-02-07 13:13:28.767924343 +0000 @@ -44,6 +44,7 @@ * @see ModuleFinder * @see ModuleReader * @since 9 + * @spec JPMS */ public abstract class ModuleReference { @@ -76,7 +77,7 @@ /** * Returns the location of this module's content, if known. * - *

      This URI, when present, is used as the {@linkplain + *

      This URI, when present, can be used as the {@linkplain * java.security.CodeSource#getLocation location} value of a {@link * java.security.CodeSource CodeSource} so that a module's classes can be * granted specific permissions when loaded by a {@link --- old/src/java.base/share/classes/java/lang/module/ResolutionException.java 2017-02-07 13:13:29.401967884 +0000 +++ new/src/java.base/share/classes/java/lang/module/ResolutionException.java 2017-02-07 13:13:29.233956347 +0000 @@ -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 @@ -26,10 +26,12 @@ package java.lang.module; /** - * Thrown when resolving a set of modules or binding fails. + * Thrown when resolving a set of modules, or resolving a set of modules with + * service binding, fails. * * @see Configuration * @since 9 + * @spec JPMS */ public class ResolutionException extends RuntimeException { private static final long serialVersionUID = -1031186845316729450L; --- old/src/java.base/share/classes/java/lang/module/ResolvedModule.java 2017-02-07 13:13:29.862999545 +0000 +++ new/src/java.base/share/classes/java/lang/module/ResolvedModule.java 2017-02-07 13:13:29.693987938 +0000 @@ -37,6 +37,7 @@ * module's content. * * @since 9 + * @spec JPMS * @see Configuration#modules() */ public final class ResolvedModule { --- old/src/java.base/share/classes/java/lang/module/Resolver.java 2017-02-07 13:13:30.349032922 +0000 +++ new/src/java.base/share/classes/java/lang/module/Resolver.java 2017-02-07 13:13:30.183021521 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -49,8 +49,8 @@ import jdk.internal.module.ModuleReferenceImpl; /** - * The resolver used by {@link Configuration#resolveRequires} and - * {@link Configuration#resolveRequiresAndUses}. + * The resolver used by {@link Configuration#resolve} and {@link + * Configuration#resolveAndBind}. * * @implNote The resolver is used at VM startup and so deliberately avoids * using lambda and stream usages in code paths used during startup. @@ -66,7 +66,19 @@ // maps module name to module reference private final Map nameToReference = new HashMap<>(); + // 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 + * the constraints on the target platform conflict + */ Resolver(ModuleFinder beforeFinder, List parents, ModuleFinder afterFinder, @@ -75,15 +87,54 @@ this.parents = parents; this.afterFinder = afterFinder; this.traceOutput = traceOutput; + + // record constraints on target platform, checking that they don't conflict + for (Configuration parent : parents) { + String value = parent.osName(); + if (value != null) { + if (osName == null) { + osName = value; + } else { + if (!value.equals(osName)) { + failParentConflict("Operating System", osName, value); + } + } + } + value = parent.osArch(); + if (value != null) { + if (osArch == null) { + osArch = value; + } else { + if (!value.equals(osArch)) { + failParentConflict("OS architecture", osArch, value); + } + } + } + value = parent.osVersion(); + if (value != null) { + if (osVersion == null) { + osVersion = value; + } else { + if (!value.equals(osVersion)) { + failParentConflict("OS version", osVersion, value); + } + } + } + } } + private void failParentConflict(String constraint, String s1, String s2) { + String msg = "Parents have conflicting constraints on target " + + constraint + ": " + s1 + ", " + s2; + throw new IllegalArgumentException(msg); + } /** * Resolves the given named modules. * * @throws ResolutionException */ - Resolver resolveRequires(Collection roots) { + Resolver resolve(Collection roots) { // create the visit stack to get us started Deque q = new ArrayDeque<>(); @@ -100,7 +151,7 @@ mref = findWithAfterFinder(root); if (mref == null) { - fail("Module %s not found", root); + findFail("Module %s not found", root); } } @@ -109,8 +160,7 @@ mref.location().ifPresent(uri -> trace(" (%s)", uri)); } - assert mref.descriptor().name().equals(root); - nameToReference.put(root, mref); + addFoundModule(mref); q.push(mref.descriptor()); } @@ -152,19 +202,19 @@ mref = findWithAfterFinder(dn); if (mref == null) { - fail("Module %s not found, required by %s", - dn, descriptor.name()); + findFail("Module %s not found, required by %s", + dn, descriptor.name()); } } if (!nameToReference.containsKey(dn)) { - nameToReference.put(dn, mref); + addFoundModule(mref); q.offer(mref.descriptor()); resolved.add(mref.descriptor()); if (isTracing()) { trace("Module %s located, required by %s", - dn, descriptor.name()); + dn, descriptor.name()); mref.location().ifPresent(uri -> trace(" (%s)", uri)); } } @@ -181,7 +231,7 @@ * Augments the set of resolved modules with modules induced by the * service-use relation. */ - Resolver resolveUses() { + Resolver bind() { // Scan the finders for all available service provider modules. As // java.base uses services then then module finders will be scanned @@ -246,7 +296,7 @@ mref.location() .ifPresent(uri -> trace(" (%s)", uri)); } - nameToReference.put(pn, mref); + addFoundModule(mref); q.push(provider); } } @@ -264,6 +314,81 @@ /** + * Add the module to the nameToReference map. Also check any constraints on + * the target platform with the constraints of other modules. + */ + private void addFoundModule(ModuleReference mref) { + ModuleDescriptor descriptor = mref.descriptor(); + nameToReference.put(descriptor.name(), mref); + + if (descriptor.osName().isPresent() + || descriptor.osArch().isPresent() + || descriptor.osVersion().isPresent()) + checkTargetConstraints(descriptor); + } + + /** + * Check that the module's constraints on the target platform do not + * 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); + if (value != null) { + if (osName == null) { + osName = value; + } else { + if (!value.equals(osName)) { + failTargetConstraint(descriptor); + } + } + } + value = descriptor.osArch().orElse(null); + 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); + } + } + } + } + + 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 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(String osName, String osArch, String osVersion) { + return new StringJoiner("-") + .add(Objects.toString(osName, "*")) + .add(Objects.toString(osArch, "*")) + .add(Objects.toString(osVersion, "*")) + .toString(); + } + + + /** * Execute post-resolution checks and returns the module graph of resolved * modules as {@code Map}. The resolved modules will be in the given * configuration. @@ -281,7 +406,6 @@ if (check) { detectCycles(); - checkPlatformConstraints(); checkHashes(); } @@ -319,8 +443,7 @@ if (!visited.contains(descriptor)) { boolean added = visitPath.add(descriptor); if (!added) { - throw new ResolutionException("Cycle detected: " + - cycleAsString(descriptor)); + resolveFail("Cycle detected: %s", cycleAsString(descriptor)); } for (ModuleDescriptor.Requires requires : descriptor.requires()) { String dn = requires.name(); @@ -354,86 +477,6 @@ /** - * If there are platform specific modules then check that the OS name, - * architecture and version match. - * - * @apiNote This method does not currently check if the OS matches - * platform specific modules in parent configurations. - */ - private void checkPlatformConstraints() { - - // first module encountered that is platform specific - String savedModuleName = null; - String savedOsName = null; - String savedOsArch = null; - String savedOsVersion = null; - - for (ModuleReference mref : nameToReference.values()) { - ModuleDescriptor descriptor = mref.descriptor(); - - String osName = descriptor.osName().orElse(null); - String osArch = descriptor.osArch().orElse(null); - String osVersion = descriptor.osVersion().orElse(null); - - if (osName != null || osArch != null || osVersion != null) { - - if (savedModuleName == null) { - - savedModuleName = descriptor.name(); - savedOsName = osName; - savedOsArch = osArch; - savedOsVersion = osVersion; - - } else { - - boolean matches = platformMatches(osName, savedOsName) - && platformMatches(osArch, savedOsArch) - && platformMatches(osVersion, savedOsVersion); - - if (!matches) { - String s1 = platformAsString(savedOsName, - savedOsArch, - savedOsVersion); - - String s2 = platformAsString(osName, osArch, osVersion); - fail("Mismatching constraints on target platform: " - + savedModuleName + ": " + s1 - + ", " + descriptor.name() + ": " + s2); - } - - } - - } - } - - } - - /** - * Returns true if the s1 and s2 are equal or one of them is null. - */ - private boolean platformMatches(String s1, String s2) { - if (s1 == null || s2 == null) - return true; - else - return Objects.equals(s1, s2); - } - - /** - * Return a string that encodes the OS name/arch/version. - */ - private String platformAsString(String osName, - String osArch, - String osVersion) { - - return new StringJoiner("-") - .add(Objects.toString(osName, "*")) - .add(Objects.toString(osArch, "*")) - .add(Objects.toString(osVersion, "*")) - .toString(); - - } - - /** * Checks the hashes in the module descriptor to ensure that they match * any recorded hashes. */ @@ -460,7 +503,7 @@ continue; if (!(mref2 instanceof ModuleReferenceImpl)) { - fail("Unable to compute the hash of module %s", dn); + findFail("Unable to compute the hash of module %s", dn); } // skip checking the hash if the module has been patched @@ -469,11 +512,11 @@ byte[] recordedHash = hashes.hashFor(dn); byte[] actualHash = other.computeHash(algorithm); if (actualHash == null) - fail("Unable to compute the hash of module %s", dn); + findFail("Unable to compute the hash of module %s", dn); if (!Arrays.equals(recordedHash, actualHash)) { - fail("Hash of %s (%s) differs to expected hash (%s)" + - " recorded in %s", dn, toHexString(actualHash), - toHexString(recordedHash), descriptor.name()); + findFail("Hash of %s (%s) differs to expected hash (%s)" + + " recorded in %s", dn, toHexString(actualHash), + toHexString(recordedHash), descriptor.name()); } } } @@ -694,37 +737,38 @@ for (ResolvedModule endpoint : reads) { ModuleDescriptor descriptor2 = endpoint.descriptor(); - for (ModuleDescriptor.Exports export : descriptor2.exports()) { + if (descriptor2.isAutomatic()) { + // automatic modules read self and export all packages + if (descriptor2 != descriptor1){ + for (String source : descriptor2.packages()) { + ModuleDescriptor supplier + = packageToExporter.putIfAbsent(source, descriptor2); + + // descriptor2 and 'supplier' export source to descriptor1 + if (supplier != null) { + failTwoSuppliers(descriptor1, source, descriptor2, supplier); + } + } - if (export.isQualified()) { - if (!export.targets().contains(descriptor1.name())) - continue; - } - - // source is exported to descriptor2 - String source = export.source(); - ModuleDescriptor other - = packageToExporter.putIfAbsent(source, descriptor2); - - if (other != null && other != descriptor2) { - // package might be local to descriptor1 - if (other == descriptor1) { - fail("Module %s contains package %s" - + ", module %s exports package %s to %s", - descriptor1.name(), - source, - descriptor2.name(), - source, - descriptor1.name()); - } else { - fail("Modules %s and %s export package %s to module %s", - descriptor2.name(), - other.name(), - source, - descriptor1.name()); + } + } else { + for (ModuleDescriptor.Exports export : descriptor2.exports()) { + if (export.isQualified()) { + if (!export.targets().contains(descriptor1.name())) + continue; } + // source is exported by descriptor2 + String source = export.source(); + ModuleDescriptor supplier + = packageToExporter.putIfAbsent(source, descriptor2); + + // descriptor2 and 'supplier' export source to descriptor1 + if (supplier != null) { + failTwoSuppliers(descriptor1, source, descriptor2, supplier); + } } + } } @@ -735,8 +779,8 @@ for (String service : descriptor1.uses()) { String pn = packageName(service); if (!packageToExporter.containsKey(pn)) { - fail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); + resolveFail("Module %s does not read a module that exports %s", + descriptor1.name(), pn); } } @@ -744,15 +788,8 @@ for (ModuleDescriptor.Provides provides : descriptor1.provides()) { String pn = packageName(provides.service()); if (!packageToExporter.containsKey(pn)) { - fail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); - } - - for (String provider : provides.providers()) { - if (!packages.contains(packageName(provider))) { - fail("Provider %s not in module %s", - provider, descriptor1.name()); - } + resolveFail("Module %s does not read a module that exports %s", + descriptor1.name(), pn); } } @@ -763,6 +800,42 @@ } /** + * Fail because a module in the configuration exports the same package to + * a module that reads both. This includes the case where a module M + * containing a package p reads another module that exports p to at least + * module M. + */ + private void failTwoSuppliers(ModuleDescriptor descriptor, + String source, + ModuleDescriptor supplier1, + ModuleDescriptor supplier2) { + + if (supplier2 == descriptor) { + ModuleDescriptor tmp = supplier1; + supplier1 = supplier2; + supplier2 = tmp; + } + + if (supplier1 == descriptor) { + resolveFail("Module %s contains package %s" + + ", module %s exports package %s to %s", + descriptor.name(), + source, + supplier2.name(), + source, + descriptor.name()); + } else { + resolveFail("Modules %s and %s export package %s to module %s", + supplier1.name(), + supplier2.name(), + source, + descriptor.name()); + } + + } + + + /** * Find a module of the given name in the parent configurations */ private ResolvedModule findInParent(String mn) { @@ -779,24 +852,16 @@ * Invokes the beforeFinder to find method to find the given module. */ private ModuleReference findWithBeforeFinder(String mn) { - try { - return beforeFinder.find(mn).orElse(null); - } catch (FindException e) { - // unwrap - throw new ResolutionException(e.getMessage(), e.getCause()); - } + + return beforeFinder.find(mn).orElse(null); + } /** * Invokes the afterFinder to find method to find the given module. */ private ModuleReference findWithAfterFinder(String mn) { - try { - return afterFinder.find(mn).orElse(null); - } catch (FindException e) { - // unwrap - throw new ResolutionException(e.getMessage(), e.getCause()); - } + return afterFinder.find(mn).orElse(null); } /** @@ -804,34 +869,27 @@ * and after ModuleFinders. */ private Set findAll() { - try { + Set beforeModules = beforeFinder.findAll(); + Set afterModules = afterFinder.findAll(); - Set beforeModules = beforeFinder.findAll(); - Set afterModules = afterFinder.findAll(); + if (afterModules.isEmpty()) + return beforeModules; - if (afterModules.isEmpty()) - return beforeModules; + if (beforeModules.isEmpty() + && parents.size() == 1 + && parents.get(0) == Configuration.empty()) + return afterModules; - if (beforeModules.isEmpty() - && parents.size() == 1 - && parents.get(0) == Configuration.empty()) - return afterModules; - - Set result = new HashSet<>(beforeModules); - for (ModuleReference mref : afterModules) { - String name = mref.descriptor().name(); - if (!beforeFinder.find(name).isPresent() - && findInParent(name) == null) { - result.add(mref); - } + Set result = new HashSet<>(beforeModules); + for (ModuleReference mref : afterModules) { + String name = mref.descriptor().name(); + if (!beforeFinder.find(name).isPresent() + && findInParent(name) == null) { + result.add(mref); } - - return result; - - } catch (FindException e) { - // unwrap - throw new ResolutionException(e.getMessage(), e.getCause()); } + + return result; } /** @@ -843,9 +901,17 @@ } /** + * Throw FindException with the given format string and arguments + */ + private static void findFail(String fmt, Object ... args) { + String msg = String.format(fmt, args); + throw new FindException(msg); + } + + /** * Throw ResolutionException with the given format string and arguments */ - private static void fail(String fmt, Object ... args) { + private static void resolveFail(String fmt, Object ... args) { String msg = String.format(fmt, args); throw new ResolutionException(msg); } --- old/src/java.base/share/classes/java/lang/module/package-info.java 2017-02-07 13:13:30.844066917 +0000 +++ new/src/java.base/share/classes/java/lang/module/package-info.java 2017-02-07 13:13:30.676055380 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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,6 +27,112 @@ * Classes to support module descriptors and creating configurations of modules * by means of resolution and service binding. * + *

      Resolution

      + * + *

      Resolution is the process of computing the transitive closure of a set + * of root modules over a set of observable modules by resolving the + * dependences expressed by {@link + * java.lang.module.ModuleDescriptor.Requires requires} clauses. + * The dependence graph is augmented with edges that take account of + * implicitly declared dependences ({@code requires transitive}) to create a + * readability graph. The result of resolution is a {@link + * java.lang.module.Configuration Configuration} that encapsulates the + * readability graph.

      + * + *

      As an example, suppose we have the following observable modules:

      + *
       {@code
      + *     module m1 { requires m2; }
      + *     module m2 { requires transitive m3; }
      + *     module m3 { }
      + *     module m4 { }
      + * } 
      + * + *

      If the module {@code m1} is resolved then the resulting configuration + * contains three modules ({@code m1}, {@code m2}, {@code m3}). The edges in + * its readability graph are:

      + *
       {@code
      + *     m1 --> m2  (meaning m1 reads m2)
      + *     m1 --> m3
      + *     m2 --> m3
      + * } 
      + * + *

      Resolution is an additive process. When computing the transitive closure + * then the dependence relation may include dependences on modules in {@link + * java.lang.module.Configuration#parents() parent} configurations. The result + * is a relative configuration that is relative to one or more parent + * configurations and where the readability graph may have edges from modules + * in the configuration to modules in parent configurations.

      + * + *

      As an example, suppose we have the following observable modules:

      + *
       {@code
      + *     module m1 { requires m2; requires java.xml; }
      + *     module m2 { }
      + * } 
      + * + *

      If module {@code m1} is resolved with the configuration for the {@link + * java.lang.reflect.Layer#boot() boot} layer as the parent then the resulting + * configuration contains two modules ({@code m1}, {@code m2}). The edges in + * its readability graph are: + *

       {@code
      + *     m1 --> m2
      + *     m1 --> java.xml
      + * } 
      + * where module {@code java.xml} is in the parent configuration. For + * simplicity, this example omits the implicitly declared dependence on the + * {@code java.base} module. + * + *

      Requires clauses that are "{@code requires static}" express an optional + * dependence (except at compile-time). If a module declares that it + * "{@code requires static M}" then resolution does not search the observable + * modules for "{@code M}". However, if "{@code M}" is resolved (because resolution + * resolves a module that requires "{@code M}" without the {@link + * java.lang.module.ModuleDescriptor.Requires.Modifier#STATIC static} modifier) + * then the readability graph will contain read edges for each module that + * "{@code requires static M}".

      + * + *

      {@link java.lang.module.ModuleDescriptor#isAutomatic() Automatic} modules + * receive special treatment during resolution. Each automatic module is resolved + * so that it reads all other modules in the configuration and all parent + * configurations. Each automatic module is also resolved as if it + * "{@code requires transitive}" all other automatic modules in the configuration + * (and all automatic modules in parent configurations).

      + * + *

      Service binding

      + * + *

      Service binding is the process of augmenting a graph of resolved modules + * from the set of observable modules induced by the service-use dependence + * ({@code uses} and {@code provides} clauses). Any module that was not + * previously in the graph requires resolution to compute its transitive + * closure. Service binding is an iterative process in that adding a module + * that satisfies some service-use dependence may introduce new service-use + * dependences.

      + * + *

      Suppose we have the following observable modules:

      + *
       {@code
      + *     module m1 { exports p; uses p.S; }
      + *     module m2 { requires m1; provides p.S with p2.S2; }
      + *     module m3 { requires m1; requires m4; provides p.S with p3.S3; }
      + *     module m4 { }
      + * } 
      + * + *

      If the module {@code m1} is resolved then the resulting graph of modules + * has one module ({@code m1}). If the graph is augmented with modules induced + * by the service-use dependence relation then the configuration will contain + * four modules ({@code m1}, {@code m2}, {@code m3}, {@code m4}). The edges in + * its readability graph are:

      + *
       {@code
      + *     m2 --> m1
      + *     m3 --> m1
      + *     m3 --> m4
      + * } 
      + *

      The edges in the conceptual service-use graph are:

      + *
       {@code
      + *     m1 --> m2  (meaning m1 uses a service that is provided by m2)
      + *     m1 --> m3
      + * } 
      + * + *

      General Exceptions

      + * *

      Unless otherwise noted, passing a {@code null} argument to a constructor * or method of any class or interface in this package will cause a {@link * java.lang.NullPointerException NullPointerException} to be thrown. Additionally, @@ -34,6 +140,7 @@ * will cause a {@code NullPointerException}, unless otherwise specified.

      * * @since 9 + * @spec JPMS */ package java.lang.module; --- old/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java 2017-02-07 13:13:31.308098783 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java 2017-02-07 13:13:31.141087315 +0000 @@ -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 @@ -28,38 +28,44 @@ import java.lang.annotation.Annotation; import java.security.AccessController; -import jdk.internal.misc.VM; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import jdk.internal.reflect.ReflectionFactory; -import sun.security.action.GetPropertyAction; /** - * The AccessibleObject class is the base class for Field, Method and - * Constructor objects. It provides the ability to flag a reflected - * object as suppressing default Java language access control checks - * when it is used. The access checks -- module boundaries, - * public, default (package) access, protected, and private members -- - * are performed when Fields, Methods or Constructors are used to set - * or get fields, to invoke methods or to create and initialize new - * instances of classes, respectively. Unlike access control specified - * in the The Java™ Language Specification and - * The Java Virtual Machine Specification, access checks - * with reflected objects assume {@link Module#canRead readability}. + * The {@code AccessibleObject} class is the base class for {@code Field}, + * {@code Method}, and {@code Constructor} objects (known as reflected + * objects). It provides the ability to flag a reflected object as + * suppressing checks for Java language access control when it is used. This + * permits sophisticated applications with sufficient privilege, such as Java + * Object Serialization or other persistence mechanisms, to manipulate objects + * in a manner that would normally be prohibited. * - *

      Setting the {@code accessible} flag in a reflected object - * permits sophisticated applications with sufficient privilege, such - * as Java Object Serialization or other persistence mechanisms, to - * manipulate objects in a manner that would normally be prohibited. + *

      Java language access control prevents use of private members outside + * their class; package access members outside their package; protected members + * outside their package or subclasses; and public members outside their + * module unless they are declared in an {@link Module#isExported(String,Module) + * exported} package and the user {@link Module#canRead reads} their module. By + * default, Java language access control is enforced (with one variation) when + * {@code Field}s, {@code Method}s, or {@code Constructor}s are used to get or + * set fields, to invoke methods, or to create and initialize new instances of + * classes, respectively. Every reflected object checks that the code using it + * is in an appropriate class, package, or module.

      * - *

      By default, a reflected object is not accessible. + *

      The one variation from Java language access control is that the checks + * by reflected objects assume readability. That is, the module containing + * the use of a reflected object is assumed to read the module in which + * the underlying field, method, or constructor is declared.

      * - * @see Field - * @see Method - * @see Constructor - * @see ReflectPermission + *

      Whether the checks for Java language access control can be suppressed + * (and thus, whether access can be enabled) depends on whether the reflected + * object corresponds to a member in an exported or open package + * (see {@link #setAccessible(boolean)}).

      * + * @jls 6.6 * @since 1.2 + * @revised 9 + * @spec JPMS */ public class AccessibleObject implements AnnotatedElement { @@ -78,15 +84,11 @@ /** * Convenience method to set the {@code accessible} flag for an - * array of objects with a single security check (for efficiency). + * array of reflected objects with a single security check (for efficiency). * - *

      This method cannot be used to enable access to an object that is a - * {@link Member member} of a class in a different module to the caller and - * where the class is in a package that is not exported to the caller's - * module. Additionally, if the member is non-public or its declaring - * class is non-public, then this method can only be used to enable access - * if the package is {@link Module#isOpen(String,Module) open} to at least - * the caller's module. + *

      This method may be used to enable access to all reflected objects in + * the array when access to each reflected object can be enabled as + * specified by {@link #setAccessible(boolean) setAccessible(boolean)}.

      * *

      If there is a security manager, its * {@code checkPermission} method is first called with a @@ -99,10 +101,15 @@ * @param array the array of AccessibleObjects * @param flag the new value for the {@code accessible} flag * in each object - * @throws InaccessibleObjectException if access cannot be enabled - * @throws SecurityException if the request is denied. + * @throws InaccessibleObjectException if access cannot be enabled for all + * objects in the array + * @throws SecurityException if the request is denied by the security manager + * or an element in the array is a constructor for {@code + * java.lang.Class} * @see SecurityManager#checkPermission * @see ReflectPermission + * @revised 9 + * @spec JPMS */ @CallerSensitive public static void setAccessible(AccessibleObject[] array, boolean flag) { @@ -120,41 +127,143 @@ } /** - * Set the {@code accessible} flag for this object to + * Set the {@code accessible} flag for this reflected object to * the indicated boolean value. A value of {@code true} indicates that - * the reflected object should suppress Java language access - * checking when it is used. A value of {@code false} indicates - * that the reflected object should enforce Java language access checks - * while assuming readability (as noted in the class description). - * - *

      This method cannot be used to enable access to an object that is a - * {@link Member member} of a class in a different module to the caller and - * where the class is in a package that is not exported to the caller's - * module. Additionally, if the member is non-public or its declaring - * class is non-public, then this method can only be used to enable access - * if the package is {@link Module#isOpen(String,Module) open} to at least - * the caller's module. + * the reflected object should suppress checks for Java language access + * control when it is used. A value of {@code false} indicates that + * the reflected object should enforce checks for Java language access + * control when it is used, with the variation noted in the class description. * - *

      If there is a security manager, its + *

      This method may be used by a caller in class {@code C} to enable + * access to a {@link Member member} of {@link Member#getDeclaringClass() + * declaring class} {@code D} if any of the following hold:

      + * + *
        + *
      • {@code C} and {@code D} are in the same module.
      • + * + *
      • The member is {@code public} and {@code D} is {@code public} in + * a package that the module containing {@code D} {@link + * Module#isExported(String,Module) exports} to at least the module + * containing {@code C}.
      • + * + *
      • The member is {@code protected} {@code static}, {@code D} is + * {@code public} in a package that the module containing {@code D} + * exports to at least the module containing {@code C}, and {@code C} + * is a subclass of {@code D}.
      • + * + *
      • {@code D} is in a package that the module containing {@code D} + * {@link Module#isOpen(String,Module) opens} to at least the module + * containing {@code C}. + * All packages in unnamed and open modules are open to all modules and + * so this method always succeeds when {@code D} is in an unnamed or + * open module.
      • + *
      + * + *

      This method cannot be used to enable access to private members, + * members with default (package) access, protected instance members, or + * protected constructors when the declaring class is in a different module + * to the caller and the package containing the declaring class is not open + * to the caller's module.

      + * + *

      If there is a security manager, its * {@code checkPermission} method is first called with a * {@code ReflectPermission("suppressAccessChecks")} permission. * * @param flag the new value for the {@code accessible} flag * @throws InaccessibleObjectException if access cannot be enabled - * @throws SecurityException if the request is denied - * @see SecurityManager#checkPermission - * @see ReflectPermission + * @throws SecurityException if the request is denied by the security manager + * @see #trySetAccessible * @see java.lang.invoke.MethodHandles#privateLookupIn + * @revised 9 + * @spec JPMS */ public void setAccessible(boolean flag) { AccessibleObject.checkPermission(); setAccessible0(flag); } - void setAccessible0(boolean flag) { + /** + * Sets the accessible flag and returns the new value + */ + boolean setAccessible0(boolean flag) { this.override = flag; + return flag; + } + + /** + * Set the {@code accessible} flag for this reflected object to {@code true} + * if possible. This method sets the {@code accessible} flag, as if by + * invoking {@link #setAccessible(boolean) setAccessible(true)}, and returns + * the possibly-updated value for the {@code accessible} flag. If access + * cannot be enabled, i.e. the checks or Java language access control cannot + * be suppressed, this method returns {@code false} (as opposed to {@code + * setAccessible(true)} throwing {@code InaccessibleObjectException} when + * it fails). + * + *

      This method is a no-op if the {@code accessible} flag for + * this reflected object is {@code true}. + * + *

      For example, a caller can invoke {@code trySetAccessible} + * on a {@code Method} object for a private instance method + * {@code p.T::privateMethod} to suppress the checks for Java language access + * control when the {@code Method} is invoked. + * If {@code p.T} class is in a different module to the caller and + * package {@code p} is open to at least the caller's module, + * the code below successfully sets the {@code accessible} flag + * to {@code true}. + * + *

      +     * {@code
      +     *     p.T obj = ....;  // instance of p.T
      +     *     :
      +     *     Method m = p.T.class.getDeclaredMethod("privateMethod");
      +     *     if (m.trySetAccessible()) {
      +     *         m.invoke(obj);
      +     *     } else {
      +     *         // package p is not opened to the caller to access private member of T
      +     *         ...
      +     *     }
      +     * }
      + * + *

      If there is a security manager, its {@code checkPermission} method + * is first called with a {@code ReflectPermission("suppressAccessChecks")} + * permission.

      + * + * @return {@code true} if the {@code accessible} flag is set to {@code true}; + * {@code false} if access cannot be enabled. + * @throws SecurityException if the request is denied by the security manager + * + * @since 9 + * @spec JPMS + * @see java.lang.invoke.MethodHandles#privateLookupIn + */ + @CallerSensitive + public final boolean trySetAccessible() { + AccessibleObject.checkPermission(); + + if (override == true) return true; + + // if it's not a Constructor, Method, Field then no access check + if (!Member.class.isInstance(this)) { + return setAccessible0(true); + } + + // does not allow to suppress access check for Class's constructor + Class declaringClass = ((Member) this).getDeclaringClass(); + if (declaringClass == Class.class && this instanceof Constructor) { + return false; + } + + if (checkCanSetAccessible(Reflection.getCallerClass(), + declaringClass, + false)) { + return setAccessible0(true); + } else { + return false; + } } + /** * If the given AccessibleObject is a {@code Constructor}, {@code Method} * or {@code Field} then checks that its declaring class is in a package @@ -164,22 +273,29 @@ // do nothing, needs to be overridden by Constructor, Method, Field } + void checkCanSetAccessible(Class caller, Class declaringClass) { + checkCanSetAccessible(caller, declaringClass, true); + } + + private boolean checkCanSetAccessible(Class caller, + Class declaringClass, + boolean throwExceptionIfDenied) { Module callerModule = caller.getModule(); Module declaringModule = declaringClass.getModule(); - if (callerModule == declaringModule) return; - if (callerModule == Object.class.getModule()) return; - if (!declaringModule.isNamed()) return; + if (callerModule == declaringModule) return true; + 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)) { - printStackTraceIfOpenedReflectively(declaringModule, pn, callerModule); - return; + dumpStackIfOpenedReflectively(declaringModule, pn, callerModule); + return true; } - // package is exported to caller and class/member is public + // package is exported to caller boolean isExported = declaringModule.isExported(pn, callerModule); boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers()); int modifiers; @@ -188,48 +304,72 @@ } else { modifiers = ((Field) this).getModifiers(); } - boolean isMemberPublic = Modifier.isPublic(modifiers); - if (isExported && isClassPublic && isMemberPublic) { - printStackTraceIfExportedReflectively(declaringModule, pn, callerModule); - return; - } - - // not accessible - String msg = "Unable to make "; - if (this instanceof Field) - msg += "field "; - msg += this + " accessible: " + declaringModule + " does not \""; - if (isClassPublic && isMemberPublic) - msg += "exports"; - else - msg += "opens"; - msg += " " + pn + "\" to " + callerModule; - InaccessibleObjectException e = new InaccessibleObjectException(msg); - if (Reflection.printStackTraceWhenAccessFails()) { - e.printStackTrace(System.err); - } - throw e; - } - - private void printStackTraceIfOpenedReflectively(Module module, - String pn, - Module other) { - printStackTraceIfExposedReflectively(module, pn, other, true); - } - - private void printStackTraceIfExportedReflectively(Module module, - String pn, - Module other) { - printStackTraceIfExposedReflectively(module, pn, other, false); - } - - private void printStackTraceIfExposedReflectively(Module module, - String pn, - Module other, - boolean open) + if (isExported && isClassPublic) { + + // member is public + if (Modifier.isPublic(modifiers)) { + dumpStackIfExportedReflectively(declaringModule, pn, callerModule); + return true; + } + + // member is protected-static + if (Modifier.isProtected(modifiers) + && Modifier.isStatic(modifiers) + && isSubclassOf(caller, declaringClass)) { + dumpStackIfExportedReflectively(declaringModule, pn, callerModule); + return true; + } + } + + if (throwExceptionIfDenied) { + // not accessible + String msg = "Unable to make "; + if (this instanceof Field) + msg += "field "; + msg += this + " accessible: " + declaringModule + " does not \""; + if (isClassPublic && Modifier.isPublic(modifiers)) + msg += "exports"; + else + msg += "opens"; + msg += " " + pn + "\" to " + callerModule; + InaccessibleObjectException e = new InaccessibleObjectException(msg); + if (Reflection.printStackTraceWhenAccessFails()) { + e.printStackTrace(System.err); + } + throw e; + } + return false; + } + + private boolean isSubclassOf(Class queryClass, Class ofClass) { + while (queryClass != null) { + if (queryClass == ofClass) { + return true; + } + queryClass = queryClass.getSuperclass(); + } + return false; + } + + private void dumpStackIfOpenedReflectively(Module module, + String pn, + Module other) { + dumpStackIfExposedReflectively(module, pn, other, true); + } + + 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)) + && !module.isStaticallyExportedOrOpen(pn, other, open)) { String msg = other + " allowed to invoke setAccessible on "; if (this instanceof Field) @@ -256,15 +396,100 @@ } /** - * Get the value of the {@code accessible} flag for this object. + * Get the value of the {@code accessible} flag for this reflected object. * * @return the value of the object's {@code accessible} flag + * + * @deprecated + * This method is deprecated because its name hints that it checks + * if the reflected object is accessible when it actually indicates + * if the checks for Java language access control are suppressed. + * This method may return {@code false} on a reflected object that is + * accessible to the caller. To test if this reflected object is accessible, + * it should use {@link #canAccess(Object)}. + * + * @revised 9 */ + @Deprecated(since="9") public boolean isAccessible() { return override; } /** + * Test if the caller can access this reflected object. If this reflected + * object corresponds to an instance method or field then this method tests + * if the caller can access the given {@code obj} with the reflected object. + * For instance methods or fields then the {@code obj} argument must be an + * instance of the {@link Member#getDeclaringClass() declaring class}. For + * static members and constructors then {@code obj} must be {@code null}. + * + *

      This method returns {@code true} if the {@code accessible} flag + * is set to {@code true}, i.e. the checks for Java language access control + * are suppressed, or if the caller can access the member as + * specified in The Java™ Language Specification, + * with the variation noted in the class description.

      + * + * @param obj an instance object of the declaring class of this reflected + * object if it is an instance method or field + * + * @return {@code true} if the caller can access this reflected object. + * + * @throws IllegalArgumentException + *
        + *
      • if this reflected object is a static member or constructor and + * the given {@code obj} is non-{@code null}, or
      • + *
      • if this reflected object is an instance method or field + * and the given {@code obj} is {@code null} or of type + * that is not a subclass of the {@link Member#getDeclaringClass() + * declaring class} of the member.
      • + *
      + * + * @since 9 + * @spec JPMS + * + * @see #trySetAccessible + * @see #setAccessible(boolean) + */ + @CallerSensitive + public final boolean canAccess(Object obj) { + if (!Member.class.isInstance(this)) { + return override; + } + + Class declaringClass = ((Member) this).getDeclaringClass(); + int modifiers = ((Member) this).getModifiers(); + if (!Modifier.isStatic(modifiers) && + (this instanceof Method || this instanceof Field)) { + if (obj == null) { + throw new IllegalArgumentException("null object for " + this); + } + // if this object is an instance member, the given object + // must be a subclass of the declaring class of this reflected object + if (!declaringClass.isAssignableFrom(obj.getClass())) { + throw new IllegalArgumentException("object is not an instance of " + + declaringClass.getName()); + } + } else if (obj != null) { + throw new IllegalArgumentException("non-null object for " + this); + } + + // access check is suppressed + if (override) return true; + + Class caller = Reflection.getCallerClass(); + Class targetClass; + if (this instanceof Constructor) { + targetClass = declaringClass; + } else { + targetClass = Modifier.isStatic(modifiers) ? null : obj.getClass(); + } + return Reflection.verifyMemberAccess(caller, + declaringClass, + targetClass, + modifiers); + } + + /** * Constructor: only used by the Java Virtual Machine. */ protected AccessibleObject() {} --- old/src/java.base/share/classes/java/lang/reflect/Constructor.java 2017-02-07 13:13:31.807133054 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Constructor.java 2017-02-07 13:13:31.637121378 +0000 @@ -168,6 +168,13 @@ * is true.

      * * @param flag {@inheritDoc} + * + * @throws InaccessibleObjectException {@inheritDoc} + * @throws SecurityException if the request is denied by the security manager + * or this is a constructor for {@code java.lang.Class} + * + * @since 9 + * @spec JPMS */ @Override @CallerSensitive --- old/src/java.base/share/classes/java/lang/reflect/Field.java 2017-02-07 13:13:32.284165813 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Field.java 2017-02-07 13:13:32.115154206 +0000 @@ -158,6 +158,10 @@ return res; } + /** + * @throws InaccessibleObjectException {@inheritDoc} + * @throws SecurityException {@inheritDoc} + */ @Override @CallerSensitive public void setAccessible(boolean flag) { --- old/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java 2017-02-07 13:13:32.775199533 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java 2017-02-07 13:13:32.609188133 +0000 @@ -30,6 +30,7 @@ * * @see AccessibleObject#setAccessible(boolean) * @since 9 + * @spec JPMS */ public class InaccessibleObjectException extends RuntimeException { --- old/src/java.base/share/classes/java/lang/reflect/Layer.java 2017-02-07 13:13:33.233230988 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Layer.java 2017-02-07 13:13:33.064219381 +0000 @@ -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 @@ -56,20 +56,19 @@ /** * A layer of modules in the Java virtual machine. * - *

      A layer is created from a graph of modules that is the {@link - * Configuration} and a function that maps each module to a {@link ClassLoader}. + *

      A layer is created from a graph of modules in a {@link Configuration} + * and a function that maps each module to a {@link ClassLoader}. * Creating a layer informs the Java virtual machine about the classes that - * may be loaded from modules so that the Java virtual machine knows which - * module that each class is a member of. Each layer, except the {@link - * #empty() empty} layer, has at least one {@link #parents() parent}.

      + * may be loaded from the modules so that the Java virtual machine knows which + * module that each class is a member of.

      * *

      Creating a layer creates a {@link Module} object for each {@link * 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 parent layer. The {@code Module} {@link - * Module#isExported(String) exports} the packages described by its {@link - * ModuleDescriptor}.

      + * 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}.

      * *

      The {@link #defineModulesWithOneLoader defineModulesWithOneLoader} and * {@link #defineModulesWithManyLoaders defineModulesWithManyLoaders} methods @@ -80,7 +79,7 @@ * a function specified to the method. Each of these methods has an instance * and static variant. The instance methods create a layer with the receiver * as the parent layer. The static methods are for more advanced cases where - * there can be more than one parent layer or a {@link Layer.Controller + * there can be more than one parent layer or where a {@link Layer.Controller * Controller} is needed to control modules in the layer.

      * *

      A Java virtual machine has at least one non-empty layer, the {@link @@ -93,9 +92,8 @@ * the {@link #parents() parent} when creating additional layers.

      * *

      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 + * {@link ModuleDescriptor#isAutomatic() automatic} modules receive special + * treatment when creating a layer. An automatic module is created in the * Java virtual machine as a {@code Module} that reads every unnamed {@code * Module} in the Java virtual machine.

      * @@ -115,8 +113,7 @@ * * Layer parent = Layer.boot(); * - * Configuration cf = parent.configuration() - * .resolveRequires(finder, ModuleFinder.of(), Set.of("myapp")); + * Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("myapp")); * * ClassLoader scl = ClassLoader.getSystemClassLoader(); * @@ -126,6 +123,7 @@ * } * * @since 9 + * @spec JPMS * @see Module#getLayer() */ @@ -168,10 +166,15 @@ * module layers return a {@code Controller} that can be used to control * modules in the layer. * + *

      Unless otherwise specified, passing a {@code null} argument to a + * method in this class causes a {@link NullPointerException + * NullPointerException} to be thrown.

      + * * @apiNote Care should be taken with {@code Controller} objects, they * should never be shared with untrusted code. * * @since 9 + * @spec JPMS */ public static final class Controller { private final Layer layer; @@ -281,10 +284,8 @@ * If the parent of the given configuration is not the configuration * for this layer * @throws LayerInstantiationException - * If all modules cannot be defined to the same class loader for any - * of the reasons listed above or 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.}" + * If the layer cannot be created for any of the reasons specified + * by the static {@code defineModulesWithOneLoader} method * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by @@ -325,9 +326,8 @@ * If the parent of the given configuration is not the configuration * for this layer * @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.}" + * If the layer cannot be created for any of the reasons specified + * by the static {@code defineModulesWithManyLoaders} method * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by @@ -365,14 +365,8 @@ * If the parent of the given configuration is not the configuration * for this layer * @throws LayerInstantiationException - * If creating the {@code Layer} fails for any of the reasons - * listed above, the layer cannot be created because the - * configuration contains a module named "{@code java.base}", - * 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} + * If the layer cannot be created for any of the reasons specified + * by the static {@code defineModules} method * @throws SecurityException * If {@code RuntimePermission("getClassLoader")} is denied by * the security manager @@ -396,7 +390,6 @@ * exported to one or more of the modules in this layer. The class * loader delegates to the class loader of the module, throwing {@code * ClassNotFoundException} if not found by that class loader. - * * When {@code loadClass} is invoked to load classes that do not map to a * module then it delegates to the parent class loader.

      * @@ -414,6 +407,10 @@ * *
    * + *

    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.}".

    + * *

    If there is a security manager then the class loader created by * this method will load classes and resources with privileges that are * restricted by the calling context of this method.

    @@ -433,9 +430,7 @@ * the parent layers, including order * @throws LayerInstantiationException * If all modules cannot be defined to the same class loader for any - * of the reasons listed above or 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.}" + * of the reasons listed above * @throws SecurityException * If {@code RuntimePermission("createClassLoader")} or * {@code RuntimePermission("getClassLoader")} is denied by @@ -480,7 +475,6 @@ * module in a parent layer. The class loader delegates to the class loader * of the module, throwing {@code ClassNotFoundException} if not found by * that class loader. - * * When {@code loadClass} is invoked to load classes that do not map to a * module then it delegates to the parent class loader.

    * @@ -533,15 +527,19 @@ /** * Creates a new layer by defining the modules in the given {@code - * Configuration} to the Java virtual machine. - * Each module is mapped, by name, to its class loader by means of the - * given function. The class loader delegation implemented by these class - * loaders must respect module readability. The class loaders should be + * Configuration} to the Java virtual machine. The given function maps each + * module in the configuration, by name, to a class loader. Creating the + * layer informs the Java virtual machine about the classes that may be + * loaded so that the Java virtual machine knows which module that each + * class is a member of. + * + *

    The class loader delegation implemented by the class loaders must + * respect module readability. The class loaders should be * {@link ClassLoader#registerAsParallelCapable parallel-capable} so as to * avoid deadlocks during class loading. In addition, the entity creating - * a new layer with this method should arrange that the class loaders are + * a new layer with this method should arrange that the class loaders be * ready to load from these modules before there are any attempts to load - * classes or resources. + * classes or resources.

    * *

    Creating a {@code Layer} can fail for the following reasons:

    * @@ -558,6 +556,13 @@ * *
* + *

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}.

+ * *

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. *

@@ -565,7 +570,7 @@ * @apiNote It is implementation specific as to whether creating a Layer * with this method is an atomic operation or not. Consequentially it is * possible for this method to fail with some modules, but not all, defined - * to Java virtual machine. + * to the Java virtual machine. * * @param cf * The configuration for the layer @@ -580,14 +585,7 @@ * If the parent configurations do not match the configuration of * the parent layers, including order * @throws LayerInstantiationException - * If creating the {@code Layer} fails for any of the reasons - * listed above, the layer cannot be created because the - * configuration contains a module named "{@code java.base}", - * 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} + * If creating the layer fails for any of the reasons listed above * @throws SecurityException * If {@code RuntimePermission("getClassLoader")} is denied by * the security manager @@ -764,7 +762,7 @@ /** * Returns the module with the given name in this layer, or if not in this - * layer, the {@linkplain #parents parents} layers. Finding a module in + * layer, the {@linkplain #parents parent} layers. Finding a module in * parent layers is equivalent to invoking {@code findModule} on each * parent, in search order, until the module is found or all parents have * been searched. In a tree of layers then this is equivalent to --- old/src/java.base/share/classes/java/lang/reflect/LayerInstantiationException.java 2017-02-07 13:13:33.727264914 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/LayerInstantiationException.java 2017-02-07 13:13:33.563253651 +0000 @@ -31,6 +31,7 @@ * @see Layer * * @since 9 + * @spec JPMS */ public class LayerInstantiationException extends RuntimeException { private static final long serialVersionUID = -906239691613568347L; --- old/src/java.base/share/classes/java/lang/reflect/Method.java 2017-02-07 13:13:34.187296506 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Method.java 2017-02-07 13:13:34.022285174 +0000 @@ -179,6 +179,10 @@ return res; } + /** + * @throws InaccessibleObjectException {@inheritDoc} + * @throws SecurityException {@inheritDoc} + */ @Override @CallerSensitive public void setAccessible(boolean flag) { --- old/src/java.base/share/classes/java/lang/reflect/Module.java 2017-02-07 13:13:34.665329334 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Module.java 2017-02-07 13:13:34.495317659 +0000 @@ -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 @@ -39,7 +39,6 @@ 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.Map; @@ -74,16 +73,15 @@ * Java Virtual Machine when a graph of modules is defined to the Java virtual * machine to create a module {@link Layer Layer}.

* - *

An unnamed module does not have a name. There is an unnamed module - * per {@link ClassLoader ClassLoader} that is obtained by invoking the class - * loader's {@link ClassLoader#getUnnamedModule() getUnnamedModule} method. The - * {@link Class#getModule() getModule} method of all types defined by a class - * loader that are not in a named module return the class loader's unnamed + *

An unnamed module does not have a name. There is an unnamed module for + * each {@link ClassLoader ClassLoader}, obtained by invoking its {@link + * ClassLoader#getUnnamedModule() getUnnamedModule} method. All types that are + * not in a named module are members of their defining class loader's unnamed * module.

* *

The package names that are parameters or returned by methods defined in * this class are the fully-qualified names of the packages as defined in - * section 6.5.3 of The Java™ Language Specification , for + * section 6.5.3 of The Java™ Language Specification, for * example, {@code "java.lang"}.

* *

Unless otherwise specified, passing a {@code null} argument to a method @@ -91,6 +89,7 @@ * be thrown.

* * @since 9 + * @spec JPMS * @see java.lang.Class#getModule */ @@ -128,14 +127,8 @@ Version version = descriptor.version().orElse(null); String vs = Objects.toString(version, null); String loc = Objects.toString(uri, null); - Set packages = descriptor.packages(); - int n = packages.size(); - String[] array = new String[n]; - int i = 0; - for (String pn : packages) { - array[i++] = pn.replace('.', '/'); - } - defineModule0(this, isOpen, vs, loc, array); + String[] packages = descriptor.packages().toArray(new String[0]); + defineModule0(this, isOpen, vs, loc, packages); } @@ -333,8 +326,9 @@ * * @return this module * - * @throws IllegalStateException - * If this is a named module and the caller is not this module + * @throws IllegalCallerException + * If this is a named module and the caller's module is not this + * module * * @see #canRead */ @@ -344,7 +338,7 @@ if (this.isNamed()) { Module caller = Reflection.getCallerClass().getModule(); if (caller != this) { - throw new IllegalStateException(caller + " != " + this); + throw new IllegalCallerException(caller + " != " + this); } implAddReads(other, true); } @@ -539,8 +533,8 @@ if (other == this && containsPackage(pn)) return true; - // all packages in open modules are open - if (descriptor.isOpen()) + // all packages in open and automatic modules are open + if (descriptor.isOpen() || descriptor.isAutomatic()) return containsPackage(pn); // exported/opened via module declaration/descriptor @@ -640,8 +634,7 @@ * the given package to the given module. * *

This method has no effect if the package is already exported (or - * open) to the given module. It also has no effect if - * invoked on an {@link ModuleDescriptor#isOpen open} module.

+ * open) to the given module.

* * @apiNote As specified in section 5.4.3 of the The Java™ * Virtual Machine Specification , if an attempt to resolve a @@ -659,8 +652,9 @@ * @throws IllegalArgumentException * If {@code pn} is {@code null}, or this is a named module and the * package {@code pn} is not a package in this module - * @throws IllegalStateException - * If this is a named module and the caller is not this module + * @throws IllegalCallerException + * If this is a named module and the caller's module is not this + * module * * @jvms 5.4.3 Resolution * @see #isExported(String,Module) @@ -671,10 +665,10 @@ throw new IllegalArgumentException("package is null"); Objects.requireNonNull(other); - if (isNamed() && !descriptor.isOpen()) { + if (isNamed()) { Module caller = Reflection.getCallerClass().getModule(); if (caller != this) { - throw new IllegalStateException(caller + " != " + this); + throw new IllegalCallerException(caller + " != " + this); } implAddExportsOrOpens(pn, other, /*open*/false, /*syncVM*/true); } @@ -692,8 +686,7 @@ * access control checks. * *

This method has no effect if the package is already open - * to the given module. It also has no effect if invoked on an {@link - * ModuleDescriptor#isOpen open} module.

+ * to the given module.

* * @param pn * The package name @@ -705,9 +698,9 @@ * @throws IllegalArgumentException * If {@code pn} is {@code null}, or this is a named module and the * package {@code pn} is not a package in this module - * @throws IllegalStateException + * @throws IllegalCallerException * If this is a named module and this module has not opened the - * package to at least the caller + * package to at least the caller's module * * @see #isOpen(String,Module) * @see AccessibleObject#setAccessible(boolean) @@ -719,10 +712,10 @@ throw new IllegalArgumentException("package is null"); Objects.requireNonNull(other); - if (isNamed() && !descriptor.isOpen()) { + if (isNamed()) { Module caller = Reflection.getCallerClass().getModule(); if (caller != this && !isOpen(pn, caller)) - throw new IllegalStateException(pn + " is not open to " + caller); + throw new IllegalCallerException(pn + " is not open to " + caller); implAddExportsOrOpens(pn, other, /*open*/true, /*syncVM*/true); } @@ -773,8 +766,8 @@ Objects.requireNonNull(other); Objects.requireNonNull(pn); - // all packages are open in unnamed and open modules - if (!isNamed() || descriptor.isOpen()) + // all packages are open in unnamed, open, and automatic modules + if (!isNamed() || descriptor.isOpen() || descriptor.isAutomatic()) return; // nothing to do if already exported/open to other @@ -789,13 +782,12 @@ // update VM first, just in case it fails if (syncVM) { - String pkgInternalForm = pn.replace('.', '/'); if (other == EVERYONE_MODULE) { - addExportsToAll0(this, pkgInternalForm); + addExportsToAll0(this, pn); } else if (other == ALL_UNNAMED_MODULE) { - addExportsToAllUnnamed0(this, pkgInternalForm); + addExportsToAllUnnamed0(this, pn); } else { - addExports0(this, pkgInternalForm, other); + addExports0(this, pn, other); } } @@ -826,17 +818,17 @@ * passed a reference to the service type by other code. This method is * a no-op when invoked on an unnamed module or an automatic module. * - *

This method does not cause {@link - * Configuration#resolveRequiresAndUses resolveRequiresAndUses} to be - * re-run.

+ *

This method does not cause {@link Configuration#resolveAndBind + * resolveAndBind} to be re-run.

* * @param service * The service type * * @return this module * - * @throws IllegalStateException - * If this is a named module and the caller is not this module + * @throws IllegalCallerException + * If this is a named module and the caller's module is not this + * module * * @see #canUse(Class) * @see ModuleDescriptor#uses() @@ -848,7 +840,7 @@ if (isNamed() && !descriptor.isAutomatic()) { Module caller = Reflection.getCallerClass().getModule(); if (caller != this) { - throw new IllegalStateException(caller + " != " + this); + throw new IllegalCallerException(caller + " != " + this); } implAddUses(service); } @@ -901,14 +893,13 @@ // -- packages -- // Additional packages that are added to the module at run-time. - // The field is volatile as it may be replaced at run-time - private volatile Set extraPackages; + private volatile Map extraPackages; private boolean containsPackage(String pn) { if (descriptor.packages().contains(pn)) return true; - Set extraPackages = this.extraPackages; - if (extraPackages != null && extraPackages.contains(pn)) + Map extraPackages = this.extraPackages; + if (extraPackages != null && extraPackages.containsKey(pn)) return true; return false; } @@ -922,7 +913,7 @@ * added to the module, dynamic modules * for example, after it was loaded. * - *

For unnamed modules, this method is the equivalent of invoking the + *

For unnamed modules, this method is the equivalent to invoking the * {@link ClassLoader#getDefinedPackages() getDefinedPackages} method of * this module's class loader and returning the array of package names.

* @@ -937,12 +928,12 @@ if (isNamed()) { Set packages = descriptor.packages(); - Set extraPackages = this.extraPackages; + Map extraPackages = this.extraPackages; if (extraPackages == null) { return packages.toArray(new String[0]); } else { return Stream.concat(packages.stream(), - extraPackages.stream()) + extraPackages.keySet().stream()) .toArray(String[]::new); } @@ -962,10 +953,6 @@ * Add a package to this module. * * @apiNote This method is for Proxy use. - * - * @apiNote This is an expensive operation, not expected to be used often. - * At this time then it does not validate that the package name is a - * valid java identifier. */ void addPackage(String pn) { implAddPackage(pn, true); @@ -983,49 +970,52 @@ /** * Add a package to this module. * - * If {@code syncVM} is {@code true} then the VM is notified. + * If {@code syncVM} is {@code true} then the VM is notified. This method is + * a no-op if this is an unnamed module or the module already contains the + * package. + * + * @throws IllegalArgumentException if the package name is not legal + * @throws IllegalStateException if the package is defined to another module */ private void implAddPackage(String pn, boolean syncVM) { + // no-op if unnamed module if (!isNamed()) - throw new InternalError("adding package to unnamed module?"); - if (descriptor.isOpen()) - throw new InternalError("adding package to open module?"); - if (pn.isEmpty()) - throw new InternalError("adding package to module?"); - - if (descriptor.packages().contains(pn)) { - // already in module return; - } - Set extraPackages = this.extraPackages; - if (extraPackages != null && extraPackages.contains(pn)) { - // already added + // no-op if module contains the package + if (containsPackage(pn)) return; - } - synchronized (this) { - // recheck under lock - extraPackages = this.extraPackages; - if (extraPackages != null) { - if (extraPackages.contains(pn)) { - // already added - return; - } - // copy the set - extraPackages = new HashSet<>(extraPackages); - extraPackages.add(pn); - } else { - extraPackages = Collections.singleton(pn); + // check package name is legal for named modules + if (pn.isEmpty()) + throw new IllegalArgumentException("Cannot add package"); + for (int i=0; i extraPackages = this.extraPackages; + if (extraPackages == null) { + synchronized (this) { + extraPackages = this.extraPackages; + if (extraPackages == null) + this.extraPackages = extraPackages = new ConcurrentHashMap<>(); + } + } - // replace with new set - this.extraPackages = extraPackages; // volatile write + // update VM first in case it fails. This is a no-op if another thread + // beats us to add the package first + if (syncVM) { + // throws IllegalStateException if defined to another module + addPackage0(this, pn); + if (descriptor.isOpen() || descriptor.isAutomatic()) { + addExportsToAll0(this, pn); + } } + extraPackages.putIfAbsent(pn, Boolean.TRUE); } @@ -1176,12 +1166,12 @@ Map nameToModule, Module m) { - // The VM doesn't know about open modules so need to export all packages - if (descriptor.isOpen()) { + // The VM doesn't special case open or automatic modules so need to + // export all packages + if (descriptor.isOpen() || descriptor.isAutomatic()) { assert descriptor.opens().isEmpty(); for (String source : descriptor.packages()) { - String sourceInternalForm = source.replace('.', '/'); - addExportsToAll0(m, sourceInternalForm); + addExportsToAll0(m, source); } return; } @@ -1192,7 +1182,6 @@ // process the open packages first for (Opens opens : descriptor.opens()) { String source = opens.source(); - String sourceInternalForm = source.replace('.', '/'); if (opens.isQualified()) { // qualified opens @@ -1201,7 +1190,7 @@ // only open to modules that are in this configuration Module m2 = nameToModule.get(target); if (m2 != null) { - addExports0(m, sourceInternalForm, m2); + addExports0(m, source, m2); targets.add(m2); } } @@ -1210,7 +1199,7 @@ } } else { // unqualified opens - addExportsToAll0(m, sourceInternalForm); + addExportsToAll0(m, source); openPackages.put(source, EVERYONE_SET); } } @@ -1218,7 +1207,6 @@ // next the exports, skipping exports when the package is open for (Exports exports : descriptor.exports()) { String source = exports.source(); - String sourceInternalForm = source.replace('.', '/'); // skip export if package is already open to everyone Set openToTargets = openPackages.get(source); @@ -1234,7 +1222,7 @@ if (m2 != null) { // skip qualified export if already open to m2 if (openToTargets == null || !openToTargets.contains(m2)) { - addExports0(m, sourceInternalForm, m2); + addExports0(m, source, m2); targets.add(m2); } } @@ -1245,7 +1233,7 @@ } else { // unqualified exports - addExportsToAll0(m, sourceInternalForm); + addExportsToAll0(m, source); exportedPackages.put(source, EVERYONE_SET); } } @@ -1385,35 +1373,44 @@ /** - * Returns an input stream for reading a resource in this module. The - * {@code name} parameter is a {@code '/'}-separated path name that - * identifies the resource. + * Returns an input stream for reading a resource in this module. + * The {@code name} parameter is a {@code '/'}-separated path name that + * identifies the resource. As with {@link Class#getResourceAsStream + * Class.getResourceAsStream}, this method delegates to the module's class + * loader {@link ClassLoader#findResource(String,String) + * findResource(String,String)} method, invoking it with the module name + * (or {@code null} when the module is unnamed) and the name of the + * resource. If the resource name has a leading slash then it is dropped + * before delegation. * - *

A resource in a named modules may be encapsulated so that + *

A resource in a named module may be encapsulated so that * it cannot be located by code in other modules. Whether a resource can be - * located or not is determined as follows: + * located or not is determined as follows:

* *
    - *
  • The package name of the resource is derived from the - * subsequence of characters that precedes the last {@code '/'} and then - * replacing each {@code '/'} character in the subsequence with - * {@code '.'}. For example, the package name derived for a resource - * named "{@code a/b/c/foo.properties}" is "{@code a.b.c}".
  • - * - *
  • If the package name is a package in the module then the package - * must be {@link #isOpen open} the module of the caller of this method. - * If the package is not in the module then the resource is not - * encapsulated. Resources in the unnamed package or "{@code META-INF}", - * for example, are never encapsulated because they can never be - * packages in a named module.
  • + *
  • If the resource name ends with "{@code .class}" then it is not + * encapsulated.
  • * - *
  • As a special case, resources ending with "{@code .class}" are - * never encapsulated.
  • + *
  • A package name is derived from the resource name. If + * the package name is a {@link #getPackages() package} in the module + * then the resource can only be located by the caller of this method + * when the package is {@link #isOpen(String,Module) open} to at least + * the caller's module. If the resource is not in a package in the module + * then the resource is not encapsulated.
  • *
* + *

In the above, the package name for a resource is derived + * from the subsequence of characters that precedes the last {@code '/'} in + * the name and then replacing each {@code '/'} character in the subsequence + * with {@code '.'}. A leading slash is ignored when deriving the package + * name. As an example, the package name derived for a resource named + * "{@code a/b/c/foo.properties}" is "{@code a.b.c}". A resource name + * with the name "{@code META-INF/MANIFEST.MF}" is never encapsulated + * because "{@code META-INF}" is not a legal package name.

+ * *

This method returns {@code null} if the resource is not in this * module, the resource is encapsulated and cannot be located by the caller, - * or access to the resource is denied by the security manager. + * or access to the resource is denied by the security manager.

* * @param name * The resource name @@ -1423,11 +1420,13 @@ * @throws IOException * If an I/O error occurs * - * @see java.lang.module.ModuleReader#open(String) + * @see Class#getResourceAsStream(String) */ @CallerSensitive public InputStream getResourceAsStream(String name) throws IOException { - Objects.requireNonNull(name); + if (name.startsWith("/")) { + name = name.substring(1); + } if (isNamed() && !ResourceHelper.isSimpleResource(name)) { Module caller = Reflection.getCallerClass().getModule(); --- old/src/java.base/share/classes/java/lang/reflect/Proxy.java 2017-02-07 13:13:35.198365939 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/Proxy.java 2017-02-07 13:13:35.028354264 +0000 @@ -164,7 +164,8 @@ * methods is specified as follows: * *
    - *
  1. If all the proxy interfaces are in exported packages: + *
  2. If all the proxy interfaces are in exported or open + * packages: *
      *
    1. if all the proxy interfaces are public, then the proxy class is * public in a package exported by the @@ -178,10 +179,11 @@ * not possible.
    2. *
    *
  3. - *
  4. If at least one proxy interface is a non-exported package: + *
  5. If at least one proxy interface is in a package that is + * non-exported and non-open: *
      *
    1. if all the proxy interfaces are public, then the proxy class is - * public in a non-exported package of + * public in a non-exported, non-open package of * dynamic module. * The names of the package and the module are unspecified.
    2. * @@ -195,21 +197,22 @@ *
    * *

    - * Note that if proxy interfaces with a mix of accessibilities -- - * exported public, exported non-public, non-exported public, non-exported non-public -- - * are proxied by the same instance, then the proxy class's accessibility is + * Note that if proxy interfaces with a mix of accessibilities -- for example, + * an exported public interface and a non-exported non-public interface -- are + * proxied by the same instance, then the proxy class's accessibility is * governed by the least accessible proxy interface. *

    * Note that it is possible for arbitrary code to obtain access to a proxy class - * in an exported package with {@link AccessibleObject#setAccessible setAccessible}, - * whereas a proxy class in a non-exported package is never accessible to + * in an open package with {@link AccessibleObject#setAccessible setAccessible}, + * whereas a proxy class in a non-open package is never accessible to * code outside the module of the proxy class. * *

    - * Throughout this specification, a "non-exported package" refers to a package that - * is not exported to all modules. Specifically, it refers to a package that - * either is not exported at all by its containing module or is exported in a - * qualified fashion by its containing module. + * Throughout this specification, a "non-exported package" refers to a package + * that is not exported to all modules, and a "non-open package" refers to + * a package that is not open to all modules. Specifically, these terms refer to + * a package that either is not exported/open by its containing module or is + * exported/open in a qualified fashion by its containing module. * *

    Dynamic Modules

    *

    @@ -272,6 +275,8 @@ * @author Peter Jones * @see InvocationHandler * @since 1.3 + * @revised 9 + * @spec JPMS */ public class Proxy implements java.io.Serializable { private static final long serialVersionUID = -2222568056686623797L; @@ -358,6 +363,8 @@ * to create a proxy instance instead. * * @see Package and Module Membership of Proxy Class + * @revised 9 + * @spec JPMS */ @Deprecated @CallerSensitive @@ -955,6 +962,8 @@ * {@code null} * * @see Package and Module Membership of Proxy Class + * @revised 9 + * @spec JPMS */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, @@ -1039,6 +1048,9 @@ * @return {@code true} if the class is a proxy class and * {@code false} otherwise * @throws NullPointerException if {@code cl} is {@code null} + * + * @revised 9 + * @spec JPMS */ public static boolean isProxyClass(Class cl) { return Proxy.class.isAssignableFrom(cl) && ProxyBuilder.isProxyClass(cl); --- old/src/java.base/share/classes/java/lang/reflect/package-info.java 2017-02-07 13:13:35.683399247 +0000 +++ new/src/java.base/share/classes/java/lang/reflect/package-info.java 2017-02-07 13:13:35.518387916 +0000 @@ -45,5 +45,7 @@ * members declared by a given class. * * @since 1.1 + * @revised 9 + * @spec JPMS */ package java.lang.reflect; --- old/src/java.base/share/classes/java/net/URLClassLoader.java 2017-02-07 13:13:36.143430839 +0000 +++ new/src/java.base/share/classes/java/net/URLClassLoader.java 2017-02-07 13:13:35.976419370 +0000 @@ -228,6 +228,7 @@ * allow creation of a class loader. * * @since 9 + * @spec JPMS */ public URLClassLoader(String name, URL[] urls, @@ -262,6 +263,7 @@ * creation of a class loader. * * @since 9 + * @spec JPMS */ public URLClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { @@ -558,6 +560,9 @@ * @throws IllegalArgumentException if the package name is * already defined by this class loader * @return the newly defined {@code Package} object + * + * @revised 9 + * @spec JPMS */ protected Package definePackage(String name, Manifest man, URL url) { String path = name.replace('.', '/').concat("/"); --- old/src/java.base/share/classes/java/security/SecureClassLoader.java 2017-02-07 13:13:36.628464147 +0000 +++ new/src/java.base/share/classes/java/security/SecureClassLoader.java 2017-02-07 13:13:36.462452747 +0000 @@ -125,6 +125,7 @@ * doesn't allow creation of a class loader. * * @since 9 + * @spec JPMS */ protected SecureClassLoader(String name, ClassLoader parent) { super(name, parent); --- old/src/java.base/share/classes/java/security/Security.java 2017-02-07 13:13:37.093496082 +0000 +++ new/src/java.base/share/classes/java/security/Security.java 2017-02-07 13:13:36.927484682 +0000 @@ -25,11 +25,12 @@ package java.security; -import java.lang.reflect.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.io.*; import java.net.URL; + +import jdk.internal.misc.SharedSecrets; import sun.security.util.Debug; import sun.security.util.PropertyExpander; @@ -800,9 +801,6 @@ * "package.definition", we need to signal to the SecurityManager * class that the value has just changed, and that it should * invalidate it's local cache values. - * - * Rather than create a new API entry for this function, - * we use reflection to set a private variable. */ private static void invalidateSMCache(String key) { @@ -810,42 +808,8 @@ final boolean pd = key.equals("package.definition"); if (pa || pd) { - AccessController.doPrivileged(new PrivilegedAction<>() { - public Void run() { - try { - /* Get the class via the bootstrap class loader. */ - Class cl = Class.forName( - "java.lang.SecurityManager", false, null); - Field f = null; - boolean accessible = false; - - if (pa) { - f = cl.getDeclaredField("packageAccessValid"); - accessible = f.isAccessible(); - f.setAccessible(true); - } else { - f = cl.getDeclaredField("packageDefinitionValid"); - accessible = f.isAccessible(); - f.setAccessible(true); - } - f.setBoolean(f, false); - f.setAccessible(accessible); - } - catch (Exception e1) { - /* If we couldn't get the class, it hasn't - * been loaded yet. If there is no such - * field, we shouldn't try to set it. There - * shouldn't be a security execption, as we - * are loaded by boot class loader, and we - * are inside a doPrivileged() here. - * - * NOOP: don't do anything... - */ - } - return null; - } /* run */ - }); /* PrivilegedAction */ - } /* if */ + SharedSecrets.getJavaLangAccess().invalidatePackageAccessCache(); + } } private static void check(String directive) { --- old/src/java.base/share/classes/java/util/ResourceBundle.java 2017-02-07 13:13:37.585529872 +0000 +++ new/src/java.base/share/classes/java/util/ResourceBundle.java 2017-02-07 13:13:37.417518334 +0000 @@ -350,6 +350,8 @@ * @see MissingResourceException * @see ResourceBundleProvider * @since 1.1 + * @revised 9 + * @spec JPMS */ public abstract class ResourceBundle { @@ -870,6 +872,8 @@ * @throws UnsupportedOperationException * if this method is called in a named module * @since 1.6 + * @revised 9 + * @spec JPMS */ @CallerSensitive public static final ResourceBundle getBundle(String baseName, @@ -938,6 +942,7 @@ * specified module * @return a resource bundle for the given base name and the default locale * @since 9 + * @spec JPMS * @see ResourceBundleProvider */ @CallerSensitive @@ -991,6 +996,7 @@ * be found in the specified {@code module} * @return a resource bundle for the given base name and locale in the module * @since 9 + * @spec JPMS */ @CallerSensitive public static ResourceBundle getBundle(String baseName, Locale targetLocale, Module module) { @@ -1036,6 +1042,8 @@ * @throws UnsupportedOperationException * if this method is called in a named module * @since 1.6 + * @revised 9 + * @spec JPMS */ @CallerSensitive public static final ResourceBundle getBundle(String baseName, Locale targetLocale, @@ -1243,6 +1251,8 @@ * @exception MissingResourceException * if no resource bundle for the specified base name can be found * @since 1.2 + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ResourceBundle getBundle(String baseName, Locale locale, @@ -1465,6 +1475,8 @@ * @throws UnsupportedOperationException * if this method is called in a named module * @since 1.6 + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ResourceBundle getBundle(String baseName, Locale targetLocale, @@ -2194,6 +2206,8 @@ * by the caller's module. * * @since 1.6 + * @revised 9 + * @spec JPMS * @see ResourceBundle.Control#getTimeToLive(String,Locale) */ @CallerSensitive @@ -2475,6 +2489,8 @@ * of {@link ResourceBundleControlProvider} are ignored in named modules. * * @since 1.6 + * @revised 9 + * @spec JPMS * @see java.util.spi.ResourceBundleProvider */ public static class Control { @@ -3103,6 +3119,8 @@ * if an error occurred when reading resources using * any I/O operations * @see java.util.spi.ResourceBundleProvider#getBundle(String, Locale) + * @revised 9 + * @spec JPMS */ public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) @@ -3379,6 +3397,8 @@ * if baseName or locale * is null * @see java.util.spi.AbstractResourceBundleProvider#toBundleName(String, Locale) + * @revised 9 + * @spec JPMS */ public String toBundleName(String baseName, Locale locale) { if (locale == Locale.ROOT) { --- old/src/java.base/share/classes/java/util/ServiceLoader.java 2017-02-07 13:13:38.132567438 +0000 +++ new/src/java.base/share/classes/java/util/ServiceLoader.java 2017-02-07 13:13:37.962555763 +0000 @@ -119,7 +119,7 @@ * and deployed as an explicit module must have an appropriate uses * clause in its module descriptor to declare that the module uses * implementations of the service. A corresponding requirement is that a - * provider deployed as a named module must have an appropriate + * provider deployed as an explicit module must have an appropriate * provides clause in its module descriptor to declare that the module * provides an implementation of the service. The uses and * provides allow consumers of a service to be linked to modules @@ -203,8 +203,11 @@ * ordering of modules in a layer, is not defined.

  6. * *
  7. If a named module declares more than one provider then the providers - * are located in the order that they appear in the {@code provides} table of - * the {@code Module} class file attribute ({@code module-info.class}).
  8. + * are located in the iteration order of the {@link + * java.lang.module.ModuleDescriptor.Provides#providers() providers} list. + * Providers added dynamically by instrumentation agents ({@link + * java.lang.instrument.Instrumentation#redefineModule redefineModule}) + * are always located after providers declared by the module. * *
  9. When locating providers in unnamed modules then the ordering is * based on the order that the class loader's {@link @@ -335,6 +338,8 @@ * * @author Mark Reinhold * @since 1.6 + * @revised 9 + * @spec JPMS */ public final class ServiceLoader @@ -386,6 +391,7 @@ * * @param The service type * @since 9 + * @spec JPMS */ public static interface Provider extends Supplier { /** @@ -927,26 +933,28 @@ } else { catalog = ServicesCatalog.getServicesCatalogOrNull(loader); } - Stream stream1; + List providers; if (catalog == null) { - stream1 = Stream.empty(); + providers = List.of(); } else { - stream1 = catalog.findServices(serviceName).stream(); + providers = catalog.findServices(serviceName); } // modules in custom layers that define modules to the class loader - Stream stream2; if (loader == null) { - stream2 = Stream.empty(); + return providers.iterator(); } else { + List allProviders = new ArrayList<>(providers); Layer bootLayer = Layer.boot(); - stream2 = JLRM_ACCESS.layers(loader) - .filter(l -> (l != bootLayer)) - .map(l -> providers(l)) - .flatMap(List::stream); + Iterator iterator = JLRM_ACCESS.layers(loader).iterator(); + while (iterator.hasNext()) { + Layer layer = iterator.next(); + if (layer != bootLayer) { + allProviders.addAll(providers(layer)); + } + } + return allProviders.iterator(); } - - return Stream.concat(stream1, stream2).iterator(); } @Override @@ -1214,6 +1222,9 @@ * * @return An iterator that lazily loads providers for this loader's * service + * + * @revised 9 + * @spec JPMS */ public Iterator iterator() { @@ -1279,8 +1290,10 @@ * provider to be loaded.

    * *

    If this loader's provider caches are cleared by invoking the {@link - * #reload() reload} method then existing streams for this service - * loader should be discarded.

    + * #reload() reload} method then existing streams for this service loader + * should be discarded. The returned stream's source {@code Spliterator} is + * fail-fast and will throw {@link ConcurrentModificationException} + * if the provider cache has been cleared.

    * *

    The following examples demonstrate usage. The first example * creates a stream of providers, the second example is the same except @@ -1300,6 +1313,7 @@ * @return A stream that lazily loads providers for this loader's service * * @since 9 + * @spec JPMS */ public Stream> stream() { // use cached providers as the source when all providers loaded @@ -1414,6 +1428,9 @@ * if the service type is not accessible to the caller or the * caller is in an explicit module and its module descriptor does * not declare that it uses {@code service} + * + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ServiceLoader load(Class service, @@ -1457,6 +1474,9 @@ * if the service type is not accessible to the caller or the * caller is in an explicit module and its module descriptor does * not declare that it uses {@code service} + * + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ServiceLoader load(Class service) { @@ -1490,6 +1510,9 @@ * if the service type is not accessible to the caller or the * caller is in an explicit module and its module descriptor does * not declare that it uses {@code service} + * + * @revised 9 + * @spec JPMS */ @CallerSensitive public static ServiceLoader loadInstalled(Class service) { @@ -1522,6 +1545,7 @@ * not declare that it uses {@code service} * * @since 9 + * @spec JPMS */ @CallerSensitive public static ServiceLoader load(Layer layer, Class service) { @@ -1551,6 +1575,7 @@ * or error is thrown when locating or instantiating the provider. * * @since 9 + * @spec JPMS */ public Optional findFirst() { Iterator iterator = iterator(); --- old/src/java.base/share/classes/java/util/spi/AbstractResourceBundleProvider.java 2017-02-07 13:13:38.635601983 +0000 +++ new/src/java.base/share/classes/java/util/spi/AbstractResourceBundleProvider.java 2017-02-07 13:13:38.469590582 +0000 @@ -81,6 +81,7 @@ * ResourceBundleProvider Service Providers * * @since 9 + * @spec JPMS */ public abstract class AbstractResourceBundleProvider implements ResourceBundleProvider { private static final JavaUtilResourceBundleAccess RB_ACCESS = --- old/src/java.base/share/classes/java/util/spi/ResourceBundleControlProvider.java 2017-02-07 13:13:39.134636253 +0000 +++ new/src/java.base/share/classes/java/util/spi/ResourceBundleControlProvider.java 2017-02-07 13:13:38.955623960 +0000 @@ -44,6 +44,8 @@ * * @author Masayoshi Okutsu * @since 1.8 + * @revised 9 + * @spec JPMS * @see ResourceBundle#getBundle(String, java.util.Locale, ClassLoader, ResourceBundle.Control) * ResourceBundle.getBundle * @see java.util.ServiceLoader#load(Class) --- old/src/java.base/share/classes/java/util/spi/ResourceBundleProvider.java 2017-02-07 13:13:39.602668394 +0000 +++ new/src/java.base/share/classes/java/util/spi/ResourceBundleProvider.java 2017-02-07 13:13:39.434656856 +0000 @@ -57,6 +57,7 @@ * @see * ResourceBundleProvider Service Providers * @since 9 + * @spec JPMS */ public interface ResourceBundleProvider { /** --- old/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java 2017-02-07 13:13:40.069700466 +0000 +++ new/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java 2017-02-07 13:13:39.900688860 +0000 @@ -186,7 +186,7 @@ public Entry getEntry(Section section, String name) { String entry = section.jmodDir() + "/" + name; ZipEntry ze = zipfile.getEntry(entry); - return (ze != null) ? new Entry(ze) : null; + return (ze == null || ze.isDirectory()) ? null : new Entry(ze); } /** @@ -201,7 +201,7 @@ { String entry = section.jmodDir() + "/" + name; ZipEntry e = zipfile.getEntry(entry); - if (e == null) { + if (e == null || e.isDirectory()) { throw new IOException(name + " not found: " + file); } return zipfile.getInputStream(e); --- old/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java 2017-02-07 13:13:40.535732470 +0000 +++ new/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java 2017-02-07 13:13:40.369721069 +0000 @@ -57,8 +57,9 @@ import java.util.jar.Manifest; import java.util.stream.Stream; -import jdk.internal.module.ModulePatcher.PatchedModuleReader; import jdk.internal.misc.VM; +import jdk.internal.module.ModulePatcher.PatchedModuleReader; +import jdk.internal.module.SystemModules; /** @@ -135,7 +136,7 @@ // maps package name to loaded module for modules in the boot layer private static final Map packageToModule - = new ConcurrentHashMap<>(1024); + = new ConcurrentHashMap<>(SystemModules.PACKAGES_IN_BOOT_LAYER); // maps a module name to a module reference private final Map nameToModule; @@ -922,7 +923,7 @@ * Returns the ModuleReader for the given module. */ private ModuleReader moduleReaderFor(ModuleReference mref) { - return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); + return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(m)); } /** --- old/src/java.base/share/classes/jdk/internal/loader/ResourceHelper.java 2017-02-07 13:13:41.026766190 +0000 +++ new/src/java.base/share/classes/jdk/internal/loader/ResourceHelper.java 2017-02-07 13:13:40.857754584 +0000 @@ -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 @@ -24,6 +24,10 @@ */ package jdk.internal.loader; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + import jdk.internal.module.Checks; /** @@ -34,7 +38,8 @@ private ResourceHelper() { } /** - * Returns the package name for a resource. + * Returns the package name 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('/'); @@ -46,19 +51,75 @@ } /** - * Returns true if the resource is a simple resource that can - * never be encapsulated. Resources ending in "{@code .class}" or where - * the package name is not a Java identifier are resources that can - * never be encapsulated. + * Returns true if the resource is a simple resource. 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.isJavaIdentifier(getPackageName(name))) { + 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; + } + } --- old/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java 2017-02-07 13:13:41.496798469 +0000 +++ new/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java 2017-02-07 13:13:41.330787068 +0000 @@ -174,4 +174,9 @@ * Invokes Long.fastUUID */ String fastUUID(long lsb, long msb); + + /** + * Invalidate package access cache + */ + void invalidatePackageAccessCache(); } --- old/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java 2017-02-07 13:13:41.966830747 +0000 +++ new/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java 2017-02-07 13:13:41.798819209 +0000 @@ -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 @@ -59,20 +59,21 @@ */ ModuleDescriptor.Builder newModuleBuilder(String mn, boolean strict, - boolean open, - boolean synthetic); + Set ms); /** - * Returns the set of packages that are exported (unconditionally or - * unconditionally). + * Returns a snapshot of the packages in the module. */ - Set exportedPackages(ModuleDescriptor.Builder builder); + Set packages(ModuleDescriptor.Builder builder); /** - * Returns the set of packages that are opened (unconditionally or - * unconditionally). + * Adds a dependence on a module with the given (possibly un-parsable) + * version string. */ - Set openPackages(ModuleDescriptor.Builder builder); + void requires(ModuleDescriptor.Builder builder, + Set ms, + String mn, + String compiledVersion); /** * Returns a {@code ModuleDescriptor.Requires} of the given modifiers @@ -114,23 +115,11 @@ Provides newProvides(String service, List providers); /** - * Returns a {@code ModuleDescriptor.Version} of the given version. - */ - Version newVersion(String v); - - /** - * Clones the given module descriptor with an augmented set of packages - */ - ModuleDescriptor newModuleDescriptor(ModuleDescriptor md, Set pkgs); - - /** * Returns a new {@code ModuleDescriptor} instance. */ ModuleDescriptor newModuleDescriptor(String name, Version version, - boolean open, - boolean automatic, - boolean synthetic, + Set ms, Set requires, Set exports, Set opens, @@ -148,9 +137,9 @@ * and the empty configuration as the parent. The post resolution * checks are optionally run. */ - Configuration resolveRequiresAndUses(ModuleFinder finder, - Collection roots, - boolean check, - PrintStream traceOutput); + Configuration resolveAndBind(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput); } --- old/src/java.base/share/classes/jdk/internal/module/Builder.java 2017-02-07 13:13:42.437863094 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/Builder.java 2017-02-07 13:13:42.269851556 +0000 @@ -31,6 +31,7 @@ import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Version; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -137,7 +138,6 @@ final String name; boolean open; - boolean automatic; boolean synthetic; Set requires; Set exports; @@ -165,11 +165,6 @@ return this; } - Builder automatic(boolean value) { - this.automatic = value; - return this; - } - Builder synthetic(boolean value) { this.synthetic = value; return this; @@ -228,13 +223,10 @@ * * @throws IllegalArgumentException if {@code v} is null or cannot be * parsed as a version string - * @throws IllegalStateException if the module version is already set * * @see Version#parse(String) */ public Builder version(String v) { - if (version != null) - throw new IllegalStateException("module version already set"); Version ver = cachedVersion; if (ver != null && v.equals(ver.toString())) { version = ver; @@ -246,36 +238,24 @@ /** * Sets the module main class. - * - * @throws IllegalStateException if already set */ public Builder mainClass(String mc) { - if (mainClass != null) - throw new IllegalStateException("main class already set"); mainClass = mc; return this; } /** * Sets the OS name. - * - * @throws IllegalStateException if already set */ public Builder osName(String name) { - if (osName != null) - throw new IllegalStateException("OS name already set"); this.osName = name; return this; } /** * Sets the OS arch. - * - * @throws IllegalStateException if already set */ public Builder osArch(String arch) { - if (osArch != null) - throw new IllegalStateException("OS arch already set"); this.osArch = arch; return this; } @@ -286,8 +266,6 @@ * @throws IllegalStateException if already set */ public Builder osVersion(String version) { - if (osVersion != null) - throw new IllegalStateException("OS version already set"); this.osVersion = version; return this; } @@ -298,11 +276,19 @@ public ModuleDescriptor build(int hashCode) { assert name != null; + Set modifiers; + if (open || synthetic) { + modifiers = new HashSet<>(); + if (open) modifiers.add(ModuleDescriptor.Modifier.OPEN); + if (synthetic) modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC); + modifiers = Collections.unmodifiableSet(modifiers); + } else { + modifiers = Collections.emptySet(); + } + return JLMA.newModuleDescriptor(name, version, - open, - automatic, - synthetic, + modifiers, requires, exports, opens, --- old/src/java.base/share/classes/jdk/internal/module/Checks.java 2017-02-07 13:13:42.916895990 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/Checks.java 2017-02-07 13:13:42.748884453 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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,7 +26,7 @@ package jdk.internal.module; /** - * Utility class for checking module name and binary names. + * Utility class for checking module, package, and class names. */ public final class Checks { @@ -58,8 +58,6 @@ throw new IllegalArgumentException(name + ": Invalid module name" + ": '" + id + "' is not a Java identifier"); } - //if (!Character.isJavaIdentifierStart(last)) - // throw new IllegalArgumentException(name + ": Module name ends in digit"); return name; } @@ -77,8 +75,6 @@ int last = isJavaIdentifier(name, off, name.length() - off); if (last == -1) return false; - //if (!Character.isJavaIdentifierStart(last)) - // return false; return true; } @@ -89,40 +85,62 @@ * package name */ public static String requirePackageName(String name) { - return requireBinaryName("package name", name); + return requireTypeName("package name", name); } /** - * Checks a name to ensure that it's a legal type name. + * Returns {@code true} if the given name is a legal package name. + */ + public static boolean isPackageName(String name) { + return isTypeName(name); + } + + /** + * Checks a name to ensure that it's a legal qualified class name * * @throws IllegalArgumentException if name is null or not a legal - * type name + * qualified class name */ public static String requireServiceTypeName(String name) { - return requireBinaryName("service type name", name); + return requireQualifiedClassName("service type name", name); } /** - * Checks a name to ensure that it's a legal type name. + * Checks a name to ensure that it's a legal qualified class name. * * @throws IllegalArgumentException if name is null or not a legal - * type name + * qualified class name */ public static String requireServiceProviderName(String name) { - return requireBinaryName("service provider name", name); + return requireQualifiedClassName("service provider name", name); + } + + /** + * Checks a name to ensure that it's a legal qualified class name in + * a named package. + * + * @throws IllegalArgumentException if name is null or not a legal + * qualified class name in a named package + */ + public static String requireQualifiedClassName(String what, String name) { + requireTypeName(what, name); + if (name.indexOf('.') == -1) + throw new IllegalArgumentException(name + ": is not a qualified name of" + + " a Java class in a named package"); + return name; } /** - * Returns {@code true} if the given name is a legal binary name. + * Returns {@code true} if the given name is a legal class name. */ - public static boolean isJavaIdentifier(String name) { - return isBinaryName(name); + public static boolean isClassName(String name) { + return isTypeName(name); } /** - * Returns {@code true} if the given name is a legal binary name. + * Returns {@code true} if the given name is a legal type name. */ - public static boolean isBinaryName(String name) { + private static boolean isTypeName(String name) { int next; int off = 0; while ((next = name.indexOf('.', off)) != -1) { @@ -135,12 +153,12 @@ } /** - * Checks if the given name is a legal binary name. + * Checks if the given name is a legal type name. * * @throws IllegalArgumentException if name is null or not a legal - * binary name + * type name */ - public static String requireBinaryName(String what, String name) { + private static String requireTypeName(String what, String name) { if (name == null) throw new IllegalArgumentException("Null " + what); int next; --- old/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java 2017-02-07 13:13:43.397929024 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java 2017-02-07 13:13:43.227917349 +0000 @@ -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 @@ -26,6 +26,7 @@ package jdk.internal.module; import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Builder; import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Opens; @@ -98,14 +99,17 @@ // module_flags int module_flags = cr.readUnsignedShort(off); - boolean open = ((module_flags & ACC_OPEN) != 0); - boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0); off += 2; - ModuleDescriptor.Builder builder = JLMA.newModuleBuilder(mn, - false, - open, - synthetic); + Set modifiers = new HashSet<>(); + if ((module_flags & ACC_OPEN) != 0) + modifiers.add(ModuleDescriptor.Modifier.OPEN); + if ((module_flags & ACC_SYNTHETIC) != 0) + modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC); + if ((module_flags & ACC_MANDATED) != 0) + modifiers.add(ModuleDescriptor.Modifier.MANDATED); + + Builder builder = JLMA.newModuleBuilder(mn, false, modifiers); // module_version String module_version = cr.readUTF8(off, buf); @@ -142,19 +146,13 @@ mods.add(Requires.Modifier.MANDATED); } - // requires_version - Version compiledVersion = null; String requires_version = cr.readUTF8(off, buf); off += 2; - if (requires_version != null) { - compiledVersion = Version.parse(requires_version); - } - - if (compiledVersion == null) { + if (requires_version == null) { builder.requires(mods, dn); } else { - builder.requires(mods, dn, compiledVersion); + JLMA.requires(builder, mods, dn, requires_version); } } @@ -283,11 +281,14 @@ attr.putShort(module_name_index); // module_flags + Set modifiers = descriptor.modifiers(); int module_flags = 0; - if (descriptor.isOpen()) + if (modifiers.contains(ModuleDescriptor.Modifier.OPEN)) module_flags |= ACC_OPEN; - if (descriptor.isSynthetic()) + if (modifiers.contains(ModuleDescriptor.Modifier.SYNTHETIC)) module_flags |= ACC_SYNTHETIC; + if (modifiers.contains(ModuleDescriptor.Modifier.MANDATED)) + module_flags |= ACC_MANDATED; attr.putShort(module_flags); // module_version --- old/src/java.base/share/classes/jdk/internal/module/ClassFileConstants.java 2017-02-07 13:13:43.872961646 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ClassFileConstants.java 2017-02-07 13:13:43.706950245 +0000 @@ -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 @@ -46,8 +46,8 @@ // access, requires, exports, and opens flags public static final int ACC_MODULE = 0x8000; public static final int ACC_OPEN = 0x0020; - public static final int ACC_TRANSITIVE = 0x0010; - public static final int ACC_STATIC_PHASE = 0x0020; + public static final int ACC_TRANSITIVE = 0x0020; + public static final int ACC_STATIC_PHASE = 0x0040; public static final int ACC_SYNTHETIC = 0x1000; public static final int ACC_MANDATED = 0x8000; --- old/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2017-02-07 13:13:44.335993443 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java 2017-02-07 13:13:44.167981906 +0000 @@ -26,7 +26,9 @@ package jdk.internal.module; import java.io.File; +import java.io.IOException; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -35,6 +37,7 @@ import java.lang.reflect.Layer; import java.lang.reflect.Module; import java.net.URI; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -46,6 +49,7 @@ 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; @@ -114,7 +118,12 @@ long t0 = System.nanoTime(); // system modules (may be patched) - ModuleFinder systemModules = ModuleFinder.ofSystem(); + ModuleFinder systemModules; + if (SystemModules.MODULE_NAMES.length > 0) { + systemModules = SystemModuleFinder.getInstance(); + } else { + systemModules = ModuleFinder.ofSystem(); + } PerfCounters.systemModulesTime.addElapsedTimeFrom(t0); @@ -275,10 +284,10 @@ // run the resolver to create the configuration Configuration cf = SharedSecrets.getJavaLangModuleAccess() - .resolveRequiresAndUses(finder, - roots, - needPostResolutionChecks, - traceOutput); + .resolveAndBind(finder, + roots, + needPostResolutionChecks, + traceOutput); // time to create configuration PerfCounters.resolveTime.addElapsedTimeFrom(t3); @@ -318,20 +327,20 @@ // if needed check that there are no split packages in the set of // resolved modules for the boot layer if (SystemModules.hasSplitPackages() || needPostResolutionChecks) { - Map packageToModule = new HashMap<>(); - for (ResolvedModule resolvedModule : cf.modules()) { - ModuleDescriptor descriptor = - resolvedModule.reference().descriptor(); - String name = descriptor.name(); - for (String p : descriptor.packages()) { - String other = packageToModule.putIfAbsent(p, name); - if (other != null) { - fail("Package " + p + " in both module " - + name + " and module " + other); - } + Map packageToModule = new HashMap<>(); + for (ResolvedModule resolvedModule : cf.modules()) { + ModuleDescriptor descriptor = + resolvedModule.reference().descriptor(); + String name = descriptor.name(); + for (String p : descriptor.packages()) { + String other = packageToModule.putIfAbsent(p, name); + if (other != null) { + fail("Package " + p + " in both module " + + name + " and module " + other); } } } + } long t4 = System.nanoTime(); @@ -380,10 +389,9 @@ Set otherMods) { // resolve all root modules - Configuration cf = Configuration.empty() - .resolveRequires(finder, - ModuleFinder.of(), - roots); + Configuration cf = Configuration.empty().resolve(finder, + ModuleFinder.of(), + roots); // module name -> reference Map map = new HashMap<>(); @@ -528,8 +536,48 @@ if (!extraOpens.isEmpty()) { addExtraExportsOrOpens(bootLayer, extraOpens, true); } + + // DEBUG_ADD_OPENS is for debugging purposes only + String home = System.getProperty("java.home"); + Path file = Paths.get(home, "conf", "DEBUG_ADD_OPENS"); + if (Files.exists(file)) { + warn(file + " detected; may break encapsulation"); + try (Stream lines = Files.lines(file)) { + lines.map(line -> line.trim()) + .filter(line -> (!line.isEmpty() && !line.startsWith("#"))) + .forEach(line -> { + String[] s = line.split("/"); + if (s.length != 2) { + fail("Unable to parse as /: " + line); + } else { + String mn = s[0]; + String pkg = s[1]; + openPackage(bootLayer, mn, pkg); + } + }); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + } + + private static void openPackage(Layer bootLayer, String mn, String pkg) { + if (mn.equals("ALL-RESOLVED") && pkg.equals("ALL-PACKAGES")) { + bootLayer.modules().stream().forEach(m -> + m.getDescriptor().packages().forEach(pn -> openPackage(m, pn))); + } else { + bootLayer.findModule(mn) + .filter(m -> m.getDescriptor().packages().contains(pkg)) + .ifPresent(m -> openPackage(m, pkg)); + } + } + + private static void openPackage(Module m, String pn) { + Modules.addOpensToAllUnnamed(m, pn); + warn("Opened for deep reflection: " + m.getName() + "/" + pn); } + private static void addExtraExportsOrOpens(Layer bootLayer, Map> map, boolean opens) --- old/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java 2017-02-07 13:13:44.826027095 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java 2017-02-07 13:13:44.658015557 +0000 @@ -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 @@ -37,7 +37,6 @@ import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Opens; -import java.lang.module.ModuleDescriptor.Version; import java.nio.ByteBuffer; import java.nio.BufferUnderflowException; import java.util.ArrayList; @@ -51,7 +50,6 @@ import jdk.internal.misc.JavaLangModuleAccess; import jdk.internal.misc.SharedSecrets; -import jdk.internal.module.ModuleResolution; import static jdk.internal.module.ClassFileConstants.*; @@ -221,7 +219,7 @@ Set attributes = new HashSet<>(); Builder builder = null; - Set packages = null; + Set allPackages = null; String mainClass = null; String[] osValues = null; ModuleHashes hashes = null; @@ -245,7 +243,7 @@ break; case MODULE_PACKAGES : - packages = readModulePackagesAttribute(in, cpool); + allPackages = readModulePackagesAttribute(in, cpool); break; case MODULE_MAIN_CLASS : @@ -284,51 +282,44 @@ throw invalidModuleDescriptor(MODULE + " attribute not found"); } + // ModuleMainClass and ModuleTarget attributes + 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 boolean usedPackageFinder = false; - if (packages == null && packageFinder != null) { + if (allPackages == null && packageFinder != null) { try { - packages = new HashSet<>(packageFinder.get()); + allPackages = packageFinder.get(); } catch (UncheckedIOException x) { throw x.getCause(); } usedPackageFinder = true; } - if (packages != null) { - Set exportedPackages = JLMA.exportedPackages(builder); - Set openPackages = JLMA.openPackages(builder); - if (packages.containsAll(exportedPackages) - && packages.containsAll(openPackages)) { - packages.removeAll(exportedPackages); - packages.removeAll(openPackages); - } else { - // the set of packages is not complete - Set exportedAndOpenPackages = new HashSet<>(); - exportedAndOpenPackages.addAll(exportedPackages); - exportedAndOpenPackages.addAll(openPackages); - for (String pn : exportedAndOpenPackages) { - if (!packages.contains(pn)) { - String tail; - if (usedPackageFinder) { - tail = " not found by package finder"; - } else { - tail = " missing from ModulePackages attribute"; - } - throw invalidModuleDescriptor("Package " + pn + tail); - } + if (allPackages != null) { + Set knownPackages = JLMA.packages(builder); + if (!allPackages.containsAll(knownPackages)) { + Set missingPackages = new HashSet<>(knownPackages); + missingPackages.removeAll(allPackages); + assert !missingPackages.isEmpty(); + String missingPackage = missingPackages.iterator().next(); + String tail; + if (usedPackageFinder) { + tail = " not found in module"; + } else { + tail = " missing from ModulePackages class file attribute"; } - assert false; // should not get here - } - builder.contains(packages); - } + throw invalidModuleDescriptor("Package " + missingPackage + tail); - 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]); + } + builder.packages(allPackages); } ModuleDescriptor descriptor = builder.build(); @@ -347,10 +338,17 @@ String mn = cpool.getModuleName(module_name_index); int module_flags = in.readUnsignedShort(); + + Set modifiers = new HashSet<>(); boolean open = ((module_flags & ACC_OPEN) != 0); - boolean synthetic = ((module_flags & ACC_SYNTHETIC) != 0); + if (open) + modifiers.add(ModuleDescriptor.Modifier.OPEN); + if ((module_flags & ACC_SYNTHETIC) != 0) + modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC); + if ((module_flags & ACC_MANDATED) != 0) + modifiers.add(ModuleDescriptor.Modifier.MANDATED); - Builder builder = JLMA.newModuleBuilder(mn, false, open, synthetic); + Builder builder = JLMA.newModuleBuilder(mn, false, modifiers); int module_version_index = in.readUnsignedShort(); if (module_version_index != 0) { @@ -381,16 +379,11 @@ } int requires_version_index = in.readUnsignedShort(); - Version compiledVersion = null; - if (requires_version_index != 0) { - String vs = cpool.getUtf8(requires_version_index); - compiledVersion = Version.parse(vs); - } - - if (compiledVersion == null) { + if (requires_version_index == 0) { builder.requires(mods, dn); } else { - builder.requires(mods, dn, compiledVersion); + String vs = cpool.getUtf8(requires_version_index); + JLMA.requires(builder, mods, dn, vs); } if (dn.equals("java.base")) @@ -629,10 +622,7 @@ /** * Return true if the given attribute name is the name of a pre-defined - * attribute that is not allowed in the class file. - * - * Except for Module, InnerClasses, SourceFile, SourceDebugExtension, and - * Deprecated, none of the pre-defined attributes in JVMS 4.7 may appear. + * attribute in JVMS 4.7 that is not allowed in a module-info class. */ private static boolean isAttributeDisallowed(String name) { Set notAllowed = predefinedNotAllowed; @@ -640,6 +630,7 @@ notAllowed = Set.of( "ConstantValue", "Code", + "Deprecated", "StackMapTable", "Exceptions", "EnclosingMethod", --- old/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java 2017-02-07 13:13:45.322061159 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java 2017-02-07 13:13:45.159049965 +0000 @@ -56,7 +56,7 @@ // the packages in the ModulePackages attribute private Set packages; - // the value of the module_version in Module attribute + // the value for the module version in the Module attribute private Version version; // the value of the ModuleMainClass attribute @@ -78,7 +78,11 @@ } /** - * Sets the set of packages for the ModulePackages attribute + * Sets the packages for the ModulePackages attribute + * + * @apiNote This method does not check that the package names are legal + * package names or that the set of packages is a super set of the + * packages in the module. */ public ModuleInfoExtender packages(Set packages) { this.packages = Collections.unmodifiableSet(packages); @@ -86,7 +90,7 @@ } /** - * Sets the value of the module_version in Module attribute. + * Sets the value for the module version in the Module attribute */ public ModuleInfoExtender version(Version version) { this.version = version; @@ -95,6 +99,9 @@ /** * Sets the value of the ModuleMainClass attribute. + * + * @apiNote This method does not check that the main class is a legal + * class name in a named package. */ public ModuleInfoExtender mainClass(String mainClass) { this.mainClass = mainClass; @@ -133,7 +140,7 @@ /** * A ClassVisitor that supports adding class file attributes. If an - * attribute already exists then the first occurence of the attribute + * attribute already exists then the first occurrence of the attribute * is replaced. */ private static class AttributeAddingClassVisitor extends ClassVisitor { --- old/src/java.base/share/classes/jdk/internal/module/ModuleLoaderMap.java 2017-02-07 13:13:45.800093987 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleLoaderMap.java 2017-02-07 13:13:45.630082312 +0000 @@ -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 @@ -41,13 +41,6 @@ * are generated at build time. */ final class ModuleLoaderMap { - /* - * The list of boot modules and platform modules are generated at build time. - */ - private static final String[] BOOT_MODULES - = new String[] { "@@BOOT_MODULE_NAMES@@" }; - private static final String[] PLATFORM_MODULES - = new String[] { "@@PLATFORM_MODULE_NAMES@@" }; /** * Returns the function to map modules in the given configuration to the @@ -55,6 +48,10 @@ */ static Function mappingFunction(Configuration cf) { + // The list of boot modules and platform modules are generated at build time. + final String[] BOOT_MODULES = new String[] { "@@BOOT_MODULE_NAMES@@" }; + final String[] PLATFORM_MODULES = new String[] { "@@PLATFORM_MODULE_NAMES@@" }; + Set bootModules = new HashSet<>(BOOT_MODULES.length); for (String mn : BOOT_MODULES) { bootModules.add(mn); --- old/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java 2017-02-07 13:13:46.264125853 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java 2017-02-07 13:13:46.095114247 +0000 @@ -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 @@ -31,6 +31,7 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Builder; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.net.MalformedURLException; @@ -54,6 +55,7 @@ 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; @@ -108,8 +110,11 @@ if (paths == null) return mref; - // scan the JAR file or directory tree to get the set of packages + // Scan the JAR file or directory tree to get the set of packages. + // For automatic modules then packages that do not contain class files + // must be ignored. Set packages = new HashSet<>(); + boolean isAutomatic = descriptor.isAutomatic(); try { for (Path file : paths) { if (Files.isRegularFile(file)) { @@ -118,8 +123,10 @@ // is not supported by the boot class loader try (JarFile jf = new JarFile(file.toFile())) { jf.stream() + .filter(e -> !e.isDirectory() + && (!isAutomatic || e.getName().endsWith(".class"))) .map(e -> toPackageName(file, e)) - .filter(Checks::isJavaIdentifier) + .filter(Checks::isPackageName) .forEach(packages::add); } @@ -129,8 +136,10 @@ Path top = file; Files.find(top, Integer.MAX_VALUE, ((path, attrs) -> attrs.isRegularFile())) + .filter(path -> !isAutomatic + || path.toString().endsWith(".class")) .map(path -> toPackageName(top, path)) - .filter(Checks::isJavaIdentifier) + .filter(Checks::isPackageName) .forEach(packages::add); } @@ -141,10 +150,30 @@ } // if there are new packages then we need a new ModuleDescriptor - Set original = descriptor.packages(); - packages.addAll(original); - if (packages.size() > original.size()) { - descriptor = JLMA.newModuleDescriptor(descriptor, packages); + packages.removeAll(descriptor.packages()); + if (!packages.isEmpty()) { + Builder builder = JLMA.newModuleBuilder(descriptor.name(), + /*strict*/ false, + descriptor.modifiers()); + if (!descriptor.isAutomatic()) { + descriptor.requires().forEach(builder::requires); + descriptor.exports().forEach(builder::exports); + descriptor.opens().forEach(builder::opens); + descriptor.uses().forEach(builder::uses); + } + descriptor.provides().forEach(builder::provides); + + 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()); + builder.packages(packages); + + descriptor = builder.build(); } // return a module reference to the patched module @@ -471,23 +500,14 @@ @Override public Resource find(String name) throws IOException { - Path file = Paths.get(name.replace('/', File.separatorChar)); - if (file.getRoot() == null) { - file = dir.resolve(file); - } else { - // drop the root component so that the resource is - // located relative to the module directory - int n = file.getNameCount(); - if (n == 0) - return null; - file = dir.resolve(file.subpath(0, n)); - } - - if (Files.isRegularFile(file)) { - return newResource(name, dir, file); - } else { - return null; + Path path = ResourceHelper.toFilePath(name); + if (path != null) { + Path file = dir.resolve(path); + if (Files.isRegularFile(file)) { + return newResource(name, dir, file); + } } + return null; } private Resource newResource(String name, Path top, Path file) { --- old/src/java.base/share/classes/jdk/internal/module/ModulePath.java 2017-02-07 13:13:46.748159093 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModulePath.java 2017-02-07 13:13:46.577147349 +0000 @@ -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 @@ -195,8 +195,7 @@ if (attrs.isDirectory()) { Path mi = entry.resolve(MODULE_INFO); if (!Files.exists(mi)) { - // does not exist or unable to determine so assume a - // directory of modules + // assume a directory of modules return scanDirectory(entry); } } @@ -206,10 +205,16 @@ if (mref != null) { String name = mref.descriptor().name(); return Collections.singletonMap(name, mref); + } + + // not recognized + String msg; + if (!isLinkPhase && entry.toString().endsWith(".jmod")) { + msg = "JMOD format not supported at execution time"; } else { - // skipped - return Collections.emptyMap(); + msg = "Module format not recognized"; } + throw new FindException(msg + ": " + entry); } catch (IOException ioe) { throw new FindException(ioe); @@ -266,14 +271,11 @@ /** - * Locates a packaged or exploded module, returning a {@code ModuleReference} - * to the module. Returns {@code null} if the entry is skipped because it is - * to a directory that does not contain a module-info.class or it's a hidden - * file. + * Reads a packaged or exploded module, returning a {@code ModuleReference} + * to the module. Returns {@code null} if the entry is not recognized. * * @throws IOException if an I/O error occurs - * @throws FindException if the file is not recognized as a module or an - * error occurs parsing its module descriptor + * @throws FindException if an error occurs parsing its module descriptor */ private ModuleReference readModule(Path entry, BasicFileAttributes attrs) throws IOException @@ -282,24 +284,16 @@ if (attrs.isDirectory()) { return readExplodedModule(entry); // may return null - } - - String fn = entry.getFileName().toString(); - if (attrs.isRegularFile()) { - if (fn.endsWith(".jar")) { - return readJar(entry); - } else if (fn.endsWith(".jmod")) { - if (isLinkPhase) + } else { + String fn = entry.getFileName().toString(); + if (attrs.isRegularFile()) { + if (fn.endsWith(".jar")) { + return readJar(entry); + } else if (isLinkPhase && fn.endsWith(".jmod")) { return readJMod(entry); - throw new FindException("JMOD files not supported: " + entry); + } } - } - - // skip hidden files - if (fn.startsWith(".") || Files.isHidden(entry)) { return null; - } else { - throw new FindException("Unrecognized module: " + entry); } } catch (InvalidModuleDescriptorException e) { @@ -362,7 +356,7 @@ /** * Returns the service type corresponding to the name of a services - * configuration file if it is a valid Java identifier. + * configuration file if it is a legal type name. * * For example, if called with "META-INF/services/p.S" then this method * returns a container with the value "p.S". @@ -374,7 +368,7 @@ String prefix = cf.substring(0, index); if (prefix.equals(SERVICES_PREFIX)) { String sn = cf.substring(index); - if (Checks.isJavaIdentifier(sn)) + if (Checks.isClassName(sn)) return Optional.of(sn); } } @@ -403,11 +397,10 @@ * * 1. The module name (and optionally the version) is derived from the file * name of the JAR file - * 2. All packages are exported and open - * 3. It has no non-exported/non-open packages - * 4. The contents of any META-INF/services configuration files are mapped + * 2. All packages are derived from the .class files in the JAR file + * 3. The contents of any META-INF/services configuration files are mapped * to "provides" declarations - * 5. The Main-Class attribute in the main attributes of the JAR manifest + * 4. The Main-Class attribute in the main attributes of the JAR manifest * is mapped to the module descriptor mainClass */ private ModuleDescriptor deriveModuleDescriptor(JarFile jf) @@ -443,9 +436,7 @@ mn = cleanModuleName(mn); // Builder throws IAE if module name is empty or invalid - ModuleDescriptor.Builder builder - = ModuleDescriptor.automaticModule(mn) - .requires(Set.of(Requires.Modifier.MANDATED), "java.base"); + ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(mn); if (vs != null) builder.version(vs); @@ -453,17 +444,22 @@ Map> map = VersionedStream.stream(jf) .filter(e -> !e.isDirectory()) .map(JarEntry::getName) + .filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX))) .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX), Collectors.toSet())); - Set resources = map.get(Boolean.FALSE); + Set classFiles = map.get(Boolean.FALSE); Set configFiles = map.get(Boolean.TRUE); - // all packages are exported and open - resources.stream() + + // the packages containing class files + Set packages = classFiles.stream() .map(this::toPackageName) .flatMap(Optional::stream) .distinct() - .forEach(pn -> builder.exports(pn).opens(pn)); + .collect(Collectors.toSet()); + + // all packages are exported and open + builder.packages(packages); // map names of service configuration files to service names Set serviceNames = configFiles.stream() @@ -481,6 +477,11 @@ String cn; while ((cn = nextLine(reader)) != null) { if (cn.length() > 0) { + String pn = packageName(cn); + if (!packages.contains(pn)) { + String msg = "Provider class " + cn + " not in module"; + throw new IOException(msg); + } providerClasses.add(cn); } } @@ -494,8 +495,15 @@ if (man != null) { Attributes attrs = man.getMainAttributes(); String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS); - if (mainClass != null) - builder.mainClass(mainClass.replace("/", ".")); + if (mainClass != null) { + mainClass = mainClass.replace("/", "."); + String pn = packageName(mainClass); + if (!packages.contains(pn)) { + String msg = "Main-Class " + mainClass + " not in module"; + throw new IOException(msg); + } + builder.mainClass(mainClass); + } } return builder.build(); @@ -569,10 +577,10 @@ try { ModuleDescriptor md = deriveModuleDescriptor(jf); attrs = new ModuleInfo.Attributes(md, null, null); - } catch (IllegalArgumentException iae) { + } catch (IllegalArgumentException e) { throw new FindException( "Unable to derive module descriptor for: " - + jf.getName(), iae); + + jf.getName(), e); } } else { @@ -621,6 +629,14 @@ } /** + * Maps a type name to its package name. + */ + private static String packageName(String cn) { + int index = cn.lastIndexOf('.'); + return (index == -1) ? "" : cn.substring(0, index); + } + + /** * 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 @@ -629,19 +645,18 @@ */ private Optional 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:" + + " found in top-level directory" + " (unnamed package not allowed in module)"); } return Optional.empty(); } String pn = name.substring(0, index).replace('/', '.'); - if (Checks.isJavaIdentifier(pn)) { + if (Checks.isPackageName(pn)) { return Optional.of(pn); } else { // not a valid package name @@ -654,7 +669,7 @@ * name. * * @throws IllegalArgumentException if the name is a class file in - * the top-level directory (and it's not module-info.class) + * the top-level directory (and it's not module-info.class) */ private Optional toPackageName(Path file) { assert file.getRoot() == null; @@ -664,14 +679,14 @@ String name = file.toString(); if (name.endsWith(".class") && !name.equals(MODULE_INFO)) { throw new IllegalArgumentException(name - + " found in in top-level directory" + + " found in top-level directory" + " (unnamed package not allowed in module)"); } return Optional.empty(); } String pn = parent.toString().replace(File.separatorChar, '.'); - if (Checks.isJavaIdentifier(pn)) { + if (Checks.isPackageName(pn)) { return Optional.of(pn); } else { // not a valid package name --- old/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java 2017-02-07 13:13:47.217191302 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleReferenceImpl.java 2017-02-07 13:13:47.053180039 +0000 @@ -163,7 +163,14 @@ @Override public String toString() { - return super.toString(); + StringBuilder sb = new StringBuilder(); + sb.append("[module "); + sb.append(descriptor().name()); + sb.append(", location="); + sb.append(location().orElseThrow(() -> new InternalError())); + if (isPatched()) sb.append(" (patched)"); + sb.append("]"); + return sb.toString(); } } --- old/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java 2017-02-07 13:13:47.678222963 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java 2017-02-07 13:13:47.513211631 +0000 @@ -51,6 +51,7 @@ import java.util.zip.ZipFile; import jdk.internal.jmod.JmodFile; +import jdk.internal.loader.ResourceHelper; import jdk.internal.misc.JavaLangAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.module.ModuleHashes.HashSupplier; @@ -243,7 +244,8 @@ } private JarEntry getEntry(String name) { - return jf.getJarEntry(Objects.requireNonNull(name)); + JarEntry entry = jf.getJarEntry(Objects.requireNonNull(name)); + return (entry == null || entry.isDirectory()) ? null : entry; } @Override @@ -370,32 +372,33 @@ } /** - * Returns a Path to access to the given resource. + * Throws IOException if the module reader is closed; */ - private Path toPath(String name) { - Path path = Paths.get(name.replace('/', File.separatorChar)); - if (path.getRoot() == null) { - return dir.resolve(path); - } else { - // drop the root component so that the resource is - // located relative to the module directory - int n = path.getNameCount(); - return (n > 0) ? dir.resolve(path.subpath(0, n)) : null; - } + private void ensureOpen() throws IOException { + if (closed) throw new IOException("ModuleReader is closed"); } /** - * Throws IOException if the module reader 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 void ensureOpen() throws IOException { - if (closed) throw new IOException("ModuleReader is closed"); + 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 find(String name) throws IOException { ensureOpen(); - Path path = toPath(name); - if (path != null && Files.isRegularFile(path)) { + Path path = toFilePath(name); + if (path != null) { try { return Optional.of(path.toUri()); } catch (IOError e) { @@ -409,8 +412,8 @@ @Override public Optional open(String name) throws IOException { ensureOpen(); - Path path = toPath(name); - if (path != null && Files.isRegularFile(path)) { + Path path = toFilePath(name); + if (path != null) { return Optional.of(Files.newInputStream(path)); } else { return Optional.empty(); @@ -420,8 +423,8 @@ @Override public Optional read(String name) throws IOException { ensureOpen(); - Path path = toPath(name); - if (path != null && Files.isRegularFile(path)) { + Path path = toFilePath(name); + if (path != null) { return Optional.of(ByteBuffer.wrap(Files.readAllBytes(path))); } else { return Optional.empty(); --- old/src/java.base/share/classes/jdk/internal/module/Modules.java 2017-02-07 13:13:48.158255928 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/Modules.java 2017-02-07 13:13:47.989244321 +0000 @@ -82,8 +82,8 @@ String name, Set packages) { - ModuleDescriptor descriptor = ModuleDescriptor.module(name) - .contains(packages) + ModuleDescriptor descriptor = ModuleDescriptor.newModule(name) + .packages(packages) .build(); return JLRMA.defineModule(loader, descriptor, null); @@ -185,7 +185,8 @@ /** * Adds a package to a module's content. * - * This method is a no-op if the module already contains the package. + * 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); --- old/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java 2017-02-07 13:13:48.615287313 +0000 +++ new/src/java.base/share/classes/jdk/internal/module/SystemModuleFinder.java 2017-02-07 13:13:48.449275913 +0000 @@ -80,8 +80,6 @@ = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages"); private static final PerfCounter exportsCount = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports"); - // ImageReader used to access all modules in the image - private static final ImageReader imageReader; // singleton finder to find modules in the run-time images private static final SystemModuleFinder INSTANCE; @@ -96,13 +94,28 @@ */ static { long t0 = System.nanoTime(); - imageReader = ImageReaderFactory.getImageReader(); INSTANCE = new SystemModuleFinder(); initTime.addElapsedTimeFrom(t0); } + /** + * Holder class for the ImageReader + */ + private static class SystemImage { + static final ImageReader READER; + static { + long t0 = System.nanoTime(); + READER = ImageReaderFactory.getImageReader(); + initTime.addElapsedTimeFrom(t0); + } + + static ImageReader reader() { + return READER; + } + } + private static boolean isFastPathSupported() { return SystemModules.MODULE_NAMES.length > 0; } @@ -114,7 +127,7 @@ // this happens when java.base is patched with java.base // from an exploded image - return imageReader.getModuleNames(); + return SystemImage.reader().getModuleNames(); } // the set of modules in the run-time image @@ -151,6 +164,7 @@ descriptors = new ModuleDescriptor[n]; recordedHashes = new ModuleHashes[n]; moduleResolutions = new ModuleResolution[n]; + ImageReader imageReader = SystemImage.reader(); for (int i = 0; i < names.length; i++) { String mn = names[i]; ImageLocation loc = imageReader.findLocation(mn, "module-info.class"); @@ -291,6 +305,7 @@ Objects.requireNonNull(name); if (closed) throw new IOException("ModuleReader is closed"); + ImageReader imageReader = SystemImage.reader(); if (imageReader != null) { return imageReader.findLocation(module, name); } else { @@ -330,7 +345,7 @@ public Optional read(String name) throws IOException { ImageLocation location = findImageLocation(name); if (location != null) { - return Optional.of(imageReader.getResourceBuffer(location)); + return Optional.of(SystemImage.reader().getResourceBuffer(location)); } else { return Optional.empty(); } @@ -372,7 +387,7 @@ stack = new ArrayDeque<>(); // push the root node to the stack to get started - ImageReader.Node dir = imageReader.findNode(moduleRoot); + ImageReader.Node dir = SystemImage.reader().findNode(moduleRoot); if (dir == null || !dir.isDirectory()) throw new IOException(moduleRoot + " not a directory"); stack.push(dir); @@ -390,7 +405,7 @@ String name = node.getName(); if (node.isDirectory()) { // build node - ImageReader.Node dir = imageReader.findNode(name); + ImageReader.Node dir = SystemImage.reader().findNode(name); assert dir.isDirectory(); stack.push(dir); } else { --- old/src/java.base/share/classes/jdk/internal/reflect/Reflection.java 2017-02-07 13:13:49.085319591 +0000 +++ new/src/java.base/share/classes/jdk/internal/reflect/Reflection.java 2017-02-07 13:13:48.918308122 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, 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 @@ -211,15 +211,7 @@ if (currentModule == memberModule) return true; // same module (named or unnamed) - // memberClass may be primitive or array class - Class c = memberClass; - while (c.isArray()) { - c = c.getComponentType(); - } - if (c.isPrimitive()) - return true; - - String pkg = c.getPackageName(); + String pkg = memberClass.getPackageName(); boolean allowed = memberModule.isExported(pkg, currentModule); if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) { if (!SharedSecrets.getJavaLangReflectModuleAccess() @@ -237,10 +229,6 @@ private static boolean isSameClassPackage(Class c1, Class c2) { if (c1.getClassLoader() != c2.getClassLoader()) return false; - while (c1.isArray()) - c1 = c1.getComponentType(); - while (c2.isArray()) - c2 = c2.getComponentType(); return Objects.equals(c1.getPackageName(), c2.getPackageName()); } @@ -378,12 +366,6 @@ } } - public static void enableStackTraces() { - printStackWhenAccessFails = true; - printStackWhenAccessSucceeds = true; - printStackPropertiesSet = true; - } - public static boolean printStackTraceWhenAccessFails() { ensurePrintStackPropertiesSet(); return printStackWhenAccessFails; @@ -413,11 +395,7 @@ if (m2.isNamed()) memberSuffix = " (in " + m2 + ")"; - Class c = memberClass; - while (c.isArray()) { - c = c.getComponentType(); - } - String memberPackageName = c.getPackageName(); + String memberPackageName = memberClass.getPackageName(); String msg = currentClass + currentSuffix + " cannot access "; if (m2.isExported(memberPackageName, m1)) { --- old/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java 2017-02-07 13:13:49.559352144 +0000 +++ new/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java 2017-02-07 13:13:49.390340538 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -39,6 +39,7 @@ private VerifyAccess() { } // cannot instantiate + private static final int UNCONDITIONAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.UNCONDITIONAL; private static final int MODULE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.MODULE; private static final int PACKAGE_ONLY = 0; private static final int PACKAGE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.PACKAGE; @@ -92,7 +93,7 @@ int allowedModes) { if (allowedModes == 0) return false; assert((allowedModes & PUBLIC) != 0 && - (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED)) == 0); + (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0); // The symbolic reference class (refc) must always be fully verified. if (!isClassAccessible(refc, lookupClass, allowedModes)) { return false; @@ -173,7 +174,7 @@ int allowedModes) { if (allowedModes == 0) return false; assert((allowedModes & PUBLIC) != 0 && - (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED)) == 0); + (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0); int mods = getClassModifiers(refc); if (isPublic(mods)) { @@ -191,22 +192,17 @@ (lookupModule == refModule)) return true; - // check readability - if (lookupModule.canRead(refModule)) { + // check readability when UNCONDITIONAL not allowed + if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0) + || lookupModule.canRead(refModule)) { // check that refc is in an exported package - Class c = refc; - while (c.isArray()) { - c = c.getComponentType(); - } - if (c.isPrimitive()) - return true; if ((allowedModes & MODULE_ALLOWED) != 0) { - if (refModule.isExported(c.getPackageName(), lookupModule)) + if (refModule.isExported(refc.getPackageName(), lookupModule)) return true; } else { // exported unconditionally - if (refModule.isExported(c.getPackageName())) + if (refModule.isExported(refc.getPackageName())) return true; } --- old/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java 2017-02-07 13:13:50.038385041 +0000 +++ new/src/java.base/share/classes/sun/net/www/protocol/jrt/JavaRuntimeURLConnection.java 2017-02-07 13:13:49.869373434 +0000 @@ -32,7 +32,9 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.security.AccessController; import java.security.Permission; +import java.security.PrivilegedAction; import jdk.internal.jimage.ImageLocation; import jdk.internal.jimage.ImageReader; @@ -51,7 +53,11 @@ public class JavaRuntimeURLConnection extends URLConnection { // ImageReader to access resources in jimage - private static final ImageReader reader = ImageReaderFactory.getImageReader(); + private static final ImageReader reader; + static { + PrivilegedAction pa = ImageReaderFactory::getImageReader; + reader = AccessController.doPrivileged(pa); + } // the module and resource name in the URL private final String module; --- old/src/java.base/share/native/include/jvm.h 2017-02-07 13:13:50.509417388 +0000 +++ new/src/java.base/share/native/include/jvm.h 2017-02-07 13:13:50.340405781 +0000 @@ -401,30 +401,67 @@ * Module support funcions */ +/* + * Define a module with the specified packages and bind the module to the + * given class loader. + * module: module to define + * is_open: specifies if module is open (currently ignored) + * version: the module version + * location: the module location + * packages: list of packages in the module + * num_packages: number of packages in the module + */ JNIEXPORT void JNICALL JVM_DefineModule(JNIEnv *env, jobject module, jboolean is_open, jstring version, - jstring location, jobjectArray packages); + jstring location, const char* const* packages, jsize num_packages); +/* + * Set the boot loader's unnamed module. + * module: boot loader's unnamed module + */ JNIEXPORT void JNICALL JVM_SetBootLoaderUnnamedModule(JNIEnv *env, jobject module); +/* + * Do a qualified export of a package. + * from_module: module containing the package to export + * package: name of the package to export + * to_module: module to export the package to + */ JNIEXPORT void JNICALL -JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject to_module); +JVM_AddModuleExports(JNIEnv *env, jobject from_module, const char* package, jobject to_module); +/* + * Do an export of a package to all unnamed modules. + * from_module: module containing the package to export + * package: name of the package to export to all unnamed modules + */ JNIEXPORT void JNICALL -JVM_AddModuleExports(JNIEnv *env, jobject from_module, jstring package, jobject to_module); +JVM_AddModuleExportsToAllUnnamed(JNIEnv *env, jobject from_module, const char* package); +/* + * Do an unqualified export of a package. + * from_module: module containing the package to export + * package: name of the package to export + */ JNIEXPORT void JNICALL -JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, jstring package); +JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, const char* package); +/* + * Add a module to the list of modules that a given module can read. + * from_module: module requesting read access + * source_module: module that from_module wants to read + */ JNIEXPORT void JNICALL -JVM_AddModuleExportsToAllUnnamed(JNIEnv *env, jobject from_module, jstring package); +JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module); +/* + * Add a package to a module. + * module: module that will contain the package + * package: package to add to the module + */ JNIEXPORT void JNICALL -JVM_AddModulePackage(JNIEnv* env, jobject module, jstring package); - -JNIEXPORT jobject JNICALL -JVM_GetModuleByPackageName(JNIEnv *env, jobject cl, jstring pkg); +JVM_AddModulePackage(JNIEnv* env, jobject module, const char* package); /* * Reflection support functions --- old/src/java.base/share/native/libjava/Module.c 2017-02-07 13:13:51.003451314 +0000 +++ new/src/java.base/share/native/libjava/Module.c 2017-02-07 13:13:50.836439845 +0000 @@ -22,18 +22,88 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +#include +#include #include "jni.h" +#include "jni_util.h" #include "jvm.h" #include "java_lang_reflect_Module.h" +/* + * Gets the UTF-8 chars for the string and translates '.' to '/'. Does no + * further validation, assumption being that both calling code in + * java.lang.reflect.Module and VM will do deeper validation. + */ +static char* +GetInternalPackageName(JNIEnv *env, jstring pkg, char* buf, jsize buf_size) +{ + jsize len; + jsize unicode_len; + char* p; + char* utf_str; + + len = (*env)->GetStringUTFLength(env, pkg); + unicode_len = (*env)->GetStringLength(env, pkg); + if (len >= buf_size) { + utf_str = malloc(len + 1); + if (utf_str == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return NULL; + } + } else { + utf_str = buf; + } + (*env)->GetStringUTFRegion(env, pkg, 0, unicode_len, utf_str); + + p = utf_str; + while (*p != '\0') { + if (*p == '.') { + *p = '/'; + } + p++; + } + return utf_str; +} + JNIEXPORT void JNICALL Java_java_lang_reflect_Module_defineModule0(JNIEnv *env, jclass cls, jobject module, jboolean is_open, jstring version, jstring location, jobjectArray packages) { - JVM_DefineModule(env, module, is_open, version, location, 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; + 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; + break; + } + } + + if (valid != 0) { + JVM_DefineModule(env, module, is_open, version, location, + (const char* const*)pkgs, num_packages); + } + } + + if (num_packages > 0) { + for (idx = 0; idx < num_packages; idx++) { + if (pkgs[idx] != NULL) { + free(pkgs[idx]); + } + } + free(pkgs); + } } JNIEXPORT void JNICALL @@ -46,25 +116,81 @@ Java_java_lang_reflect_Module_addExports0(JNIEnv *env, jclass cls, jobject from, jstring pkg, jobject to) { - JVM_AddModuleExports(env, from, pkg, to); + char buf[128]; + char* pkg_name; + + if (pkg == NULL) { + JNU_ThrowNullPointerException(env, "package is null"); + return; + } + + pkg_name = GetInternalPackageName(env, pkg, buf, (jsize)sizeof(buf)); + if (pkg_name != NULL) { + JVM_AddModuleExports(env, from, pkg_name, to); + if (pkg_name != buf) { + free(pkg_name); + } + } } JNIEXPORT void JNICALL Java_java_lang_reflect_Module_addExportsToAll0(JNIEnv *env, jclass cls, jobject from, jstring pkg) { - JVM_AddModuleExportsToAll(env, from, pkg); + char buf[128]; + char* pkg_name; + + if (pkg == NULL) { + JNU_ThrowNullPointerException(env, "package is null"); + return; + } + + pkg_name = GetInternalPackageName(env, pkg, buf, (jsize)sizeof(buf)); + if (pkg_name != NULL) { + JVM_AddModuleExportsToAll(env, from, pkg_name); + if (pkg_name != buf) { + free(pkg_name); + } + } } JNIEXPORT void JNICALL Java_java_lang_reflect_Module_addExportsToAllUnnamed0(JNIEnv *env, jclass cls, jobject from, jstring pkg) { - JVM_AddModuleExportsToAllUnnamed(env, from, pkg); + char buf[128]; + char* pkg_name; + + if (pkg == NULL) { + JNU_ThrowNullPointerException(env, "package is null"); + return; + } + + pkg_name = GetInternalPackageName(env, pkg, buf, (jsize)sizeof(buf)); + if (pkg_name != NULL) { + JVM_AddModuleExportsToAllUnnamed(env, from, pkg_name); + if (pkg_name != buf) { + free(pkg_name); + } + } } JNIEXPORT void JNICALL Java_java_lang_reflect_Module_addPackage0(JNIEnv *env, jclass cls, jobject m, jstring pkg) { - JVM_AddModulePackage(env, m, pkg); + char buf[128]; + char* pkg_name; + + if (pkg == NULL) { + JNU_ThrowNullPointerException(env, "package is null"); + return; + } + + pkg_name = GetInternalPackageName(env, pkg, buf, (jsize)sizeof(buf)); + if (pkg_name != NULL) { + JVM_AddModulePackage(env, m, pkg_name); + if (pkg_name != buf) { + free(pkg_name); + } + } } --- old/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java 2017-02-07 13:13:51.467483180 +0000 +++ new/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java 2017-02-07 13:13:51.302471848 +0000 @@ -229,6 +229,7 @@ * or {@code null} if no transform is performed * * @since 9 + * @spec JPMS */ default byte[] transform( Module module, --- old/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java 2017-02-07 13:13:51.938515527 +0000 +++ new/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java 2017-02-07 13:13:51.766503715 +0000 @@ -714,6 +714,7 @@ * @throws NullPointerException if any of the arguments are {@code null} or * any of the Sets or Maps contains a {@code null} key or value * @since 9 + * @spec JPMS */ void redefineModule(Module module, Set extraReads, --- old/src/jdk.jartool/share/classes/sun/tools/jar/Main.java 2017-02-07 13:13:52.419548561 +0000 +++ new/src/jdk.jartool/share/classes/sun/tools/jar/Main.java 2017-02-07 13:13:52.251537023 +0000 @@ -699,7 +699,7 @@ } String pn = toPackageName(name); // add if this is a class or resource in a package - if (Checks.isJavaIdentifier(pn)) { + if (Checks.isPackageName(pn)) { packages.add(pn); } } @@ -1995,7 +1995,7 @@ } // get a resolved module graph Configuration config = - Configuration.empty().resolveRequires(system, finder, roots); + Configuration.empty().resolve(system, finder, roots); // filter modules resolved from the system module finder this.modules = config.modules().stream() --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java 2017-02-07 13:13:52.918582831 +0000 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java 2017-02-07 13:13:52.753571499 +0000 @@ -29,6 +29,7 @@ import java.io.PrintWriter; import java.io.UncheckedIOException; import java.lang.module.Configuration; +import java.lang.module.FindException; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; @@ -232,7 +233,7 @@ return EXIT_OK; } catch (PluginException | IllegalArgumentException | - UncheckedIOException |IOException | ResolutionException e) { + UncheckedIOException |IOException | FindException | ResolutionException e) { log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage()); if (DEBUG) { e.printStackTrace(log); @@ -418,9 +419,9 @@ } Configuration cf = Configuration.empty() - .resolveRequires(finder, - ModuleFinder.of(), - roots); + .resolve(finder, + ModuleFinder.of(), + roots); // emit warning for modules that end with a digit cf.modules().stream() @@ -458,9 +459,9 @@ // resolve all root modules Configuration cf = Configuration.empty() - .resolveRequires(finder, - ModuleFinder.of(), - roots); + .resolve(finder, + ModuleFinder.of(), + roots); // module name -> reference Map map = new HashMap<>(); --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java 2017-02-07 13:13:53.398615796 +0000 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolConfiguration.java 2017-02-07 13:13:53.230604258 +0000 @@ -51,7 +51,7 @@ ModuleDescriptor md = mod.descriptor(); // drop hashes - ModuleDescriptor.Builder builder = ModuleDescriptor.module(md.name()); + ModuleDescriptor.Builder builder = ModuleDescriptor.newModule(md.name()); md.requires().stream() .forEach(builder::requires); md.exports().stream() @@ -62,12 +62,7 @@ .forEach(builder::uses); md.provides().stream() .forEach(builder::provides); - - // build the proper concealed packages - Set concealed = new HashSet<>(mod.packages()); - md.exports().stream().map(ModuleDescriptor.Exports::source).forEach(concealed::remove); - md.opens().stream().map(ModuleDescriptor.Opens::source).forEach(concealed::remove); - concealed.stream().forEach(builder::contains); + builder.packages(md.packages()); md.version().ifPresent(builder::version); md.mainClass().ifPresent(builder::mainClass); @@ -124,7 +119,7 @@ } }; - return Configuration.empty().resolveRequires( + return Configuration.empty().resolve( finder, ModuleFinder.of(), nameToModRef.keySet()); } } --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java 2017-02-07 13:13:53.867648005 +0000 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java 2017-02-07 13:13:53.698636399 +0000 @@ -728,9 +728,9 @@ Configuration bootConfiguration = Layer.boot().configuration(); try { Configuration cf = bootConfiguration - .resolveRequiresAndUses(ModuleFinder.of(), - finder, - Collections.emptySet()); + .resolveAndBind(ModuleFinder.of(), + finder, + Collections.emptySet()); ClassLoader scl = ClassLoader.getSystemClassLoader(); return Layer.boot().defineModulesWithOneLoader(cf, scl); } catch (Exception ex) { --- old/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java 2017-02-07 13:13:54.355681520 +0000 +++ new/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java 2017-02-07 13:13:54.189670119 +0000 @@ -771,7 +771,7 @@ if (md.isOpen()) { setModuleBit("open", true); } - if (md.isSynthetic()) { + if (md.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) { setModuleBit("synthetic", true); } } --- old/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 2017-02-07 13:13:54.850715515 +0000 +++ new/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 2017-02-07 13:13:54.681703908 +0000 @@ -34,6 +34,7 @@ import java.io.PrintWriter; import java.io.UncheckedIOException; import java.lang.module.Configuration; +import java.lang.module.FindException; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.module.ModuleFinder; @@ -851,8 +852,8 @@ // get a resolved module graph Configuration config = null; try { - config = Configuration.empty().resolveRequires(system, finder, roots); - } catch (ResolutionException e) { + config = Configuration.empty().resolve(system, finder, roots); + } catch (FindException | ResolutionException e) { throw new CommandException("err.module.resolution.fail", e.getMessage()); } --- old/test/ProblemList.txt 2017-02-07 13:13:55.337748960 +0000 +++ new/test/ProblemList.txt 2017-02-07 13:13:55.172737629 +0000 @@ -298,6 +298,8 @@ sun/tools/jcmd/TestJcmdSanity.java 8031482 windows-all +sun/tools/jstat/jstatClassloadOutput1.sh 8173942 generic-all + sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java 8057732 generic-all demo/jvmti/compiledMethodLoad/CompiledMethodLoadTest.java 8151899 generic-all --- old/test/java/io/FilePermission/ReadFileOnPath.java 2017-02-07 13:13:55.884786527 +0000 +++ new/test/java/io/FilePermission/ReadFileOnPath.java 2017-02-07 13:13:55.720775264 +0000 @@ -57,7 +57,7 @@ "module-info.class", "base", "p/App.class", "p/child"); // exploded module - test("--module-path", "modules", "-m", "m/p.App", "SS+++++"); + test("--module-path", "modules", "-m", "m/p.App", "SS++++0"); // module in jar test("--module-path", "new.jar", "-m", "m/p.App", "SSSS++0"); --- old/test/java/lang/Class/forName/modules/TestLayer.java 2017-02-07 13:13:56.437824505 +0000 +++ new/test/java/lang/Class/forName/modules/TestLayer.java 2017-02-07 13:13:56.269812967 +0000 @@ -46,9 +46,9 @@ ModuleFinder finder = ModuleFinder.of(MODS_DIR); Configuration parent = Layer.boot().configuration(); - Configuration cf = parent.resolveRequiresAndUses(ModuleFinder.of(), - finder, - modules); + Configuration cf = parent.resolveAndBind(ModuleFinder.of(), + finder, + modules); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = Layer.boot().defineModulesWithManyLoaders(cf, scl); --- old/test/java/lang/Class/forName/modules/src/m3/p3/NoAccess.java 2017-02-07 13:13:56.906856715 +0000 +++ new/test/java/lang/Class/forName/modules/src/m3/p3/NoAccess.java 2017-02-07 13:13:56.738845177 +0000 @@ -50,9 +50,9 @@ Layer bootLayer = Layer.boot(); Configuration parent = bootLayer.configuration(); - Configuration cf = parent.resolveRequiresAndUses(finder, - ModuleFinder.of(), - Set.of("m1", "m2")); + Configuration cf = parent.resolveAndBind(finder, + ModuleFinder.of(), + Set.of("m1", "m2")); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithManyLoaders(cf, scl); --- old/test/java/lang/Class/getPackageName/Basic.java 2017-02-07 13:13:57.366888306 +0000 +++ new/test/java/lang/Class/getPackageName/Basic.java 2017-02-07 13:13:57.202877043 +0000 @@ -154,8 +154,8 @@ return new Object[][] { { Basic.class, TEST_PACKAGE }, - { Basic[].class, null }, - { Basic[][].class, null }, + { Basic[].class, TEST_PACKAGE }, + { Basic[][].class, TEST_PACKAGE }, { getNestedClass1(), TEST_PACKAGE }, { getNestedClass2(), TEST_PACKAGE }, @@ -174,14 +174,14 @@ { getAnonymousClass6(), TEST_PACKAGE }, { Object.class, "java.lang" }, - { Object[].class, null }, - { Object[][].class, null }, + { Object[].class, "java.lang" }, + { Object[][].class, "java.lang" }, - { int.class, null }, - { int[].class, null }, - { int[][].class, null }, + { int.class, "java.lang" }, + { int[].class, "java.lang" }, + { int[][].class, "java.lang" }, - { void.class, null }, + { void.class, "java.lang" }, }; } --- old/test/java/lang/StackWalker/CallerFromMain.java 2017-02-07 13:13:57.827919966 +0000 +++ new/test/java/lang/StackWalker/CallerFromMain.java 2017-02-07 13:13:57.662908635 +0000 @@ -51,7 +51,7 @@ try { Class c = sw.getCallerClass(); throw new RuntimeException("UOE not thrown. Caller: " + c); - } catch (IllegalStateException e) {} + } catch (IllegalCallerException e) {} // StackWalker::getCallerClass // Runnable::run --- old/test/java/lang/invoke/AccessControlTest.java 2017-02-07 13:13:58.291951832 +0000 +++ new/test/java/lang/invoke/AccessControlTest.java 2017-02-07 13:13:58.122940226 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -118,6 +118,8 @@ suffix = "/noaccess"; else if (lookupModes == PUBLIC) suffix = "/public"; + else if (lookupModes == (PUBLIC|UNCONDITIONAL)) + suffix = "/publicLookup"; else if (lookupModes == (PUBLIC|MODULE)) suffix = "/module"; else if (lookupModes == (PUBLIC|MODULE|PACKAGE)) @@ -140,23 +142,24 @@ * [A2] However, the resulting {@code Lookup} object is guaranteed * to have no more access capabilities than the original. * In particular, access capabilities can be lost as follows:

      - *
    • [A3] If the new lookup class differs from the old one, - * protected members will not be accessible by virtue of inheritance. + *
    • [A3] If the old lookup class is in a named module, and the new + * lookup class is in a different module {@code M}, then no members, not + * even public members in {@code M}'s exported packages, will be accessible. + * The exception to this is when this lookup is publicLookup, in which case + * public access is not lost. + *
    • [A4] If the old lookup class is in an unnamed module, and the new + * lookup class is a different module then module access is lost. + *
    • [A5] If the new lookup class differs from the old one then UNCONDITIONAL + * is lost. If the new lookup class is not within the same package member as the + * old one, protected members will not be accessible by virtue of inheritance. * (Protected members may continue to be accessible because of package sharing.) - *
    • [A4] If the new lookup class is in a different package - * than the old one, protected and default (package) members will not be accessible. - *
    • [A5] If the new lookup class is not within the same package member + *
    • [A6] If the new lookup class is in a different package than the old one, + * protected and default (package) members will not be accessible. + *
    • [A7] If the new lookup class is not within the same package member * as the old one, private members will not be accessible. - *
    • [A6] If the new lookup class is not accessible to the old lookup class, - * using the original access modes, + *
    • [A8] If the new lookup class is not accessible to the old lookup class, * then no members, not even public members, will be accessible. - *
    • [A7] If the new lookup class for this {@code Lookup} is in the unnamed module, - * and the new lookup class is in a named module {@code M}, then no members in - * {@code M}'s non-exported packages will be accessible. - *
    • [A8] If the lookup for this {@code Lookup} is in a named module, and the - * new lookup class is in a different module, then no members, not even - * public members in {@code M}'s exported packages, will be accessible. - * [A8] (In all other cases, public members will continue to be accessible.) + *
    • [A9] (In all other cases, public members will continue to be accessible.) *
    * Other than the above cases, the new lookup will have the same * access capabilities as the original. [A10] @@ -171,36 +174,35 @@ boolean sameModule = (c1.getModule() == c2.getModule()) || (!c1.getModule().isNamed() && !c2.getModule().isNamed()); boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() && - packagePrefix(c1).equals(packagePrefix(c2))); + c1.getPackageName().equals(c2.getPackageName())); boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2)); boolean sameClass = (c1 == c2); assert(samePackage || !sameTopLevel); assert(sameTopLevel || !sameClass); - boolean accessible = sameClass; // [A6] + boolean accessible = sameClass; if ((m1 & PACKAGE) != 0) accessible |= samePackage; if ((m1 & PUBLIC ) != 0) accessible |= (c2.getModifiers() & PUBLIC) != 0; if (!sameModule) { - if (c1.getModule().isNamed()) { - accessible = false; // [A8] + if (c1.getModule().isNamed() && (m1 & UNCONDITIONAL) == 0) { + accessible = false; // [A3] } else { - // Different module; loose MODULE and lower access. - changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED); // [A7] + changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED); // [A3] [A4] } } if (!accessible) { // Different package and no access to c2; lose all access. - changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A6] + changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8] } if (!samePackage) { // Different package; loose PACKAGE and lower access. - changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4] + changed |= (PACKAGE|PRIVATE|PROTECTED); // [A6] } if (!sameTopLevel) { - // Different top-level class. Lose PRIVATE and lower access. - changed |= (PRIVATE|PROTECTED); // [A5] + // Different top-level class. Lose PRIVATE and PROTECTED access. + changed |= (PRIVATE|PROTECTED); // [A5] [A7] } if (!sameClass) { - changed |= (PROTECTED); // [A3] + changed |= (UNCONDITIONAL); // [A5] } else { assert(changed == 0); // [A10] (no deprivation if same class) } @@ -228,11 +230,10 @@ Class c1 = lookupClass(); Class c2 = m.getDeclaringClass(); - // if the lookup class is in a loose module with PUBLIC access then - // public members of public types in all unnamed modules can be accessed - if (isLooseModule(c1.getModule()) + // publicLookup has access to all public types/members of types in unnamed modules + if ((lookupModes & UNCONDITIONAL) != 0 && (lookupModes & PUBLIC) != 0 - && (!c2.getModule().isNamed()) + && !c2.getModule().isNamed() && Modifier.isPublic(c2.getModifiers()) && Modifier.isPublic(m.getModifiers())) return true; @@ -261,17 +262,12 @@ if (load && c2.getClassLoader() != null) { if (c1.getClassLoader() == null) { // not visible - return false; - } - if (c1 == publicLookup().lookupClass()) { - // not visible as lookup class is defined by child of the boot loader return false; } } - // if the lookup class is in a loose module with PUBLIC access then - // public types in all unnamed modules can be accessed - if (isLooseModule(c1.getModule()) + // publicLookup has access to all public types/members of types in unnamed modules + if ((lookupModes & UNCONDITIONAL) != 0 && (lookupModes & PUBLIC) != 0 && (!c2.getModule().isNamed()) && Modifier.isPublic(c2.getModifiers())) @@ -295,11 +291,6 @@ } return r; } - - private boolean isLooseModule(Module m) { - ClassLoader cl = new ClassLoader() { }; - return m.canRead(cl.getUnnamedModule()); - } } private static Class topLevelClass(Class cls) { @@ -311,14 +302,6 @@ return c; } - private static String packagePrefix(Class c) { - while (c.isArray()) c = c.getComponentType(); - String s = c.getName(); - assert(s.indexOf('/') < 0); - return s.substring(0, s.lastIndexOf('.')+1); - } - - private final TreeSet CASES = new TreeSet<>(); private final TreeMap> CASE_EDGES = new TreeMap<>(); private final ArrayList LOADERS = new ArrayList<>(); --- old/test/java/lang/invoke/DropLookupModeTest.java 2017-02-07 13:13:58.772984865 +0000 +++ new/test/java/lang/invoke/DropLookupModeTest.java 2017-02-07 13:13:58.607973534 +0000 @@ -65,6 +65,10 @@ lookup = fullPowerLookup.dropLookupMode(PUBLIC); assertTrue(lookup.lookupClass() == lc); assertTrue(lookup.lookupModes() == 0); + + lookup = fullPowerLookup.dropLookupMode(UNCONDITIONAL); + assertTrue(lookup.lookupClass() == lc); + assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PRIVATE)); } /** @@ -108,7 +112,7 @@ public void testPublicLookup() { final Lookup publicLookup = MethodHandles.publicLookup(); final Class lc = publicLookup.lookupClass(); - assertTrue(publicLookup.lookupModes() == PUBLIC); + assertTrue(publicLookup.lookupModes() == (PUBLIC|UNCONDITIONAL)); Lookup lookup = publicLookup.dropLookupMode(PRIVATE); assertTrue(lookup.lookupClass() == lc); @@ -129,6 +133,10 @@ lookup = publicLookup.dropLookupMode(PUBLIC); assertTrue(lookup.lookupClass() == lc); assertTrue(lookup.lookupModes() == 0); + + lookup = publicLookup.dropLookupMode(UNCONDITIONAL); + assertTrue(lookup.lookupClass() == lc); + assertTrue(lookup.lookupModes() == PUBLIC); } @DataProvider(name = "badInput") --- old/test/java/lang/invoke/MethodHandles/privateLookupIn/Driver.java 2017-02-07 13:13:59.232016388 +0000 +++ new/test/java/lang/invoke/MethodHandles/privateLookupIn/Driver.java 2017-02-07 13:13:59.068005125 +0000 @@ -23,7 +23,7 @@ /** * @test - * @build test/* m1/* m2/* m3/* + * @build test/* m1/* m2/* m3/* Unnamed * @run testng/othervm test/p.PrivateLookupInTests * @summary Unit tests for MethodHandles.privateLookupIn */ --- old/test/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java 2017-02-07 13:13:59.688047705 +0000 +++ new/test/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java 2017-02-07 13:13:59.523036374 +0000 @@ -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 @@ -126,6 +126,26 @@ Object obj = mh.invokeExact(); } + // test target class in unnamed module + public void testTargetClassInUnnamedModule() throws Throwable { + Class clazz = Class.forName("Unnamed"); + assertFalse(clazz.getModule().isNamed()); + + // thisModule does not read the unnamed module + Module thisModule = getClass().getModule(); + assertFalse(thisModule.canRead(clazz.getModule())); + try { + MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + // thisModule reads the unnamed module + thisModule.addReads(clazz.getModule()); + Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()); + assertTrue(lookup.lookupClass() == clazz); + assertTrue(lookup.hasPrivateAccess()); + } + // test does not read m2, m2 opens p2 to test @Test(expectedExceptions = {IllegalAccessException.class}) public void testCallerDoesNotRead() throws Throwable { --- old/test/java/lang/module/AutomaticModulesTest.java 2017-02-07 13:14:00.158079983 +0000 +++ new/test/java/lang/module/AutomaticModulesTest.java 2017-02-07 13:13:59.986068171 +0000 @@ -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 @@ -33,10 +33,10 @@ import java.lang.module.Configuration; import java.lang.module.FindException; import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Requires.Modifier; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; +import java.lang.module.ResolutionException; import java.lang.module.ResolvedModule; import java.lang.reflect.Layer; import java.lang.reflect.Module; @@ -137,6 +137,7 @@ assertTrue(mref.isPresent(), mn + " not found"); ModuleDescriptor descriptor = mref.get().descriptor(); + assertTrue(descriptor.isAutomatic()); assertEquals(descriptor.name(), mn); if (vs == null) { assertFalse(descriptor.version().isPresent()); @@ -175,17 +176,14 @@ assertTrue(mref.isPresent(), "m not found"); ModuleDescriptor descriptor = mref.get().descriptor(); + assertTrue(descriptor.isAutomatic()); assertTrue(descriptor.packages().size() == 2); assertTrue(descriptor.packages().contains("p")); assertTrue(descriptor.packages().contains("q")); - Set exports = descriptor.exports().stream() - .map(Exports::source) - .collect(Collectors.toSet()); - assertTrue(exports.size() == 2); - assertTrue(exports.contains("p")); - assertTrue(exports.contains("q")); + assertTrue(descriptor.exports().isEmpty()); + assertTrue(descriptor.opens().isEmpty()); } /** @@ -201,15 +199,13 @@ assertTrue(mref.isPresent(), "m not found"); ModuleDescriptor descriptor = mref.get().descriptor(); + assertTrue(descriptor.isAutomatic()); assertTrue(descriptor.packages().size() == 1); assertTrue(descriptor.packages().contains("p")); - Set exports = descriptor.exports().stream() - .map(Exports::source) - .collect(Collectors.toSet()); - assertTrue(exports.size() == 1); - assertTrue(exports.contains("p")); + assertTrue(descriptor.exports().isEmpty()); + assertTrue(descriptor.opens().isEmpty()); } /** @@ -229,10 +225,10 @@ assertTrue(mref.isPresent(), "m not found"); ModuleDescriptor descriptor = mref.get().descriptor(); + assertTrue(descriptor.isAutomatic()); - assertTrue(descriptor.packages().size() == 2); + assertTrue(descriptor.packages().size() == 1); assertTrue(descriptor.packages().contains("p")); - assertTrue(descriptor.packages().contains("p.resources")); } /** @@ -254,9 +250,17 @@ String provider = "p.S1"; Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); + + // provider class + Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); + Files.createDirectories(providerClass.getParent()); + Files.createFile(providerClass); + + // services configuration file Path services = tmpdir.resolve("META-INF").resolve("services"); Files.createDirectories(services); Files.write(services.resolve(service), Set.of(provider)); + Path dir = Files.createTempDirectory(USER_DIR, "mods"); JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); @@ -314,7 +318,7 @@ // service type provider type { "p.S", "-" }, - { "p.S", ".S1" }, + { "p.S", "p..S1" }, { "p.S", "S1." }, }; } @@ -324,13 +328,41 @@ * values or names. */ @Test(dataProvider = "badproviders", expectedExceptions = FindException.class) - public void testBadProvideNames(String service, String provider) + public void testBadProviderNames(String service, String provider) throws IOException { Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); + + // provider class + Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); + Files.createDirectories(providerClass.getParent()); + Files.createFile(providerClass); + + // services configuration file Path services = tmpdir.resolve("META-INF").resolve("services"); Files.createDirectories(services); Files.write(services.resolve(service), Set.of(provider)); + + Path dir = Files.createTempDirectory(USER_DIR, "mods"); + JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); + + // should throw FindException + ModuleFinder.of(dir).findAll(); + } + + /** + * Test JAR file with META-INF/services configuration file listing a + * provider that is not in the module. + */ + @Test(expectedExceptions = FindException.class) + public void testMissingProviderPackage() throws IOException { + Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); + + // services configuration file + Path services = tmpdir.resolve("META-INF").resolve("services"); + Files.createDirectories(services); + Files.write(services.resolve("p.S"), Set.of("q.P")); + Path dir = Files.createTempDirectory(USER_DIR, "mods"); JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); @@ -352,7 +384,8 @@ attrs.put(Attributes.Name.MAIN_CLASS, mainClass); Path dir = Files.createTempDirectory(USER_DIR, "mods"); - createDummyJarFile(dir.resolve("m.jar"), man); + String entry = mainClass.replace('.', '/') + ".class"; + createDummyJarFile(dir.resolve("m.jar"), man, entry); ModuleFinder finder = ModuleFinder.of(dir); @@ -366,20 +399,21 @@ } - // Main-Class files that do not map to a legal Java identifier + // Main-Class files that do not map to a legal qualified type name @DataProvider(name = "badmainclass") public Object[][] createBadMainClass() { return new Object[][]{ + { "Main", null }, + { "p..Main", null }, { "p-.Main", null }, - { ".Main", null } }; } /** - * Test that a JAR file with a Main-Class attribute that is not a valid - * Java identifier + * Test that a JAR file with a Main-Class attribute that is not a qualified + * type name. */ @Test(dataProvider = "badmainclass", expectedExceptions = FindException.class) public void testBadMainClass(String mainClass, String ignore) throws IOException { @@ -389,6 +423,24 @@ attrs.put(Attributes.Name.MAIN_CLASS, mainClass); Path dir = Files.createTempDirectory(USER_DIR, "mods"); + String entry = mainClass.replace('.', '/') + ".class"; + createDummyJarFile(dir.resolve("m.jar"), man, entry); + + // should throw FindException + ModuleFinder.of(dir).findAll(); + } + + /** + * Test that a JAR file with a Main-Class attribute that is not in the module + */ + @Test(expectedExceptions = FindException.class) + public void testMissingMainClassPackage() throws IOException { + Manifest man = new Manifest(); + Attributes attrs = man.getMainAttributes(); + attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); + attrs.put(Attributes.Name.MAIN_CLASS, "p.Main"); + + Path dir = Files.createTempDirectory(USER_DIR, "mods"); createDummyJarFile(dir.resolve("m.jar"), man); // should throw FindException @@ -405,7 +457,7 @@ */ public void testConfiguration1() throws Exception { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("a") + = ModuleDescriptor.newModule("a") .requires("b") .requires("c") .requires("java.base") @@ -465,13 +517,13 @@ */ public void testInConfiguration2() throws IOException { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("a") + = ModuleDescriptor.newModule("a") .requires("b") .requires("java.base") .build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.module("b") + = ModuleDescriptor.newModule("b") .requires("c") .requires("java.base") .build(); @@ -538,13 +590,13 @@ */ public void testInConfiguration3() throws IOException { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("a") + = ModuleDescriptor.newModule("a") .requires("b") .requires("java.base") .build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.module("b") + = ModuleDescriptor.newModule("b") .requires(Set.of(Modifier.TRANSITIVE), "c") .requires("java.base") .build(); @@ -609,11 +661,67 @@ /** + * Basic test of a configuration created with automatic modules + * a requires b* and c* + * b* contains p + * c* contains p + */ + @Test(expectedExceptions = { ResolutionException.class }) + public void testDuplicateSuppliers1() throws IOException { + ModuleDescriptor descriptor + = ModuleDescriptor.newModule("a") + .requires("b") + .requires("c") + .build(); + + // c and d are automatic modules with the same package + Path dir = Files.createTempDirectory(USER_DIR, "mods"); + createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); + createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); + + // module finder locates 'a' and the modules in the directory + ModuleFinder finder + = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), + ModuleFinder.of(dir)); + + Configuration parent = Layer.boot().configuration(); + resolve(parent, finder, "a"); + } + + + /** + * Basic test of a configuration created with automatic modules + * a contains p, requires b* + * b* contains p + */ + @Test(expectedExceptions = { ResolutionException.class }) + public void testDuplicateSuppliers2() throws IOException { + ModuleDescriptor descriptor + = ModuleDescriptor.newModule("a") + .packages(Set.of("p")) + .requires("b") + .build(); + + // c and d are automatic modules with the same package + Path dir = Files.createTempDirectory(USER_DIR, "mods"); + createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); + + // module finder locates 'a' and the modules in the directory + ModuleFinder finder + = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), + ModuleFinder.of(dir)); + + Configuration parent = Layer.boot().configuration(); + resolve(parent, finder, "a"); + } + + + /** * Basic test of Layer containing automatic modules */ public void testInLayer() throws IOException { ModuleDescriptor descriptor - = ModuleDescriptor.module("a") + = ModuleDescriptor.newModule("a") .requires("b") .requires("c") .build(); @@ -664,7 +772,7 @@ // test miscellaneous methods assertTrue(m.isAutomatic()); - assertFalse(m.isSynthetic()); + assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)); assertFalse(m.osName().isPresent()); assertFalse(m.osArch().isPresent()); assertFalse(m.osVersion().isPresent()); @@ -672,12 +780,12 @@ /** - * Invokes parent.resolveRequires to resolve the given root modules. + * Invokes parent.resolve to resolve the given root modules. */ static Configuration resolve(Configuration parent, ModuleFinder finder, String... roots) { - return parent.resolveRequires(finder, ModuleFinder.of(), Set.of(roots)); + return parent.resolve(finder, ModuleFinder.of(), Set.of(roots)); } /** --- old/test/java/lang/module/ConfigurationTest.java 2017-02-07 13:14:00.654114047 +0000 +++ new/test/java/lang/module/ConfigurationTest.java 2017-02-07 13:14:00.486102510 +0000 @@ -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 @@ -24,23 +24,26 @@ /** * @test * @library /lib/testlibrary + * @modules java.base/jdk.internal.misc * @build ConfigurationTest ModuleUtils * @run testng ConfigurationTest * @summary Basic tests for java.lang.module.Configuration */ 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.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import jdk.internal.misc.SharedSecrets; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -48,30 +51,35 @@ @Test public class ConfigurationTest { + /** + * Creates a "non-strict" builder for building a module. This allows the + * test the create ModuleDescriptor objects that do not require java.base. + */ + private static ModuleDescriptor.Builder newBuilder(String mn) { + return SharedSecrets.getJavaLangModuleAccess() + .newModuleBuilder(mn, false, Set.of()); + } /** * Basic test of resolver * m1 requires m2, m2 requires m3 */ public void testBasic() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m3") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 3); @@ -110,24 +118,21 @@ */ public void testRequiresTransitive1() { // m1 requires m2, m2 requires transitive m3 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 3); @@ -167,18 +172,16 @@ // cf1: m1 and m2, m2 requires transitive m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); @@ -196,14 +199,13 @@ // cf2: m3, m3 requires m2 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m1").isPresent()); // in parent @@ -231,13 +233,11 @@ // cf1: m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); @@ -250,19 +250,17 @@ // cf2: m2, m3: m2 requires transitive m1, m3 requires m2 - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2, descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); assertTrue(cf2.modules().size() == 2); assertTrue(cf2.findModule("m1").isPresent()); // in parent @@ -297,13 +295,11 @@ // cf1: m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); @@ -316,14 +312,13 @@ // cf2: m2 requires transitive m1 - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2); - Configuration cf2 = resolveRequires(cf1, finder2, "m2"); + Configuration cf2 = resolve(cf1, finder2, "m2"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m1").isPresent()); // in parent @@ -340,14 +335,13 @@ // cf3: m3 requires m2 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder3 = ModuleUtils.finderOf(descriptor3); - Configuration cf3 = resolveRequires(cf2, finder3, "m3"); + Configuration cf3 = resolve(cf2, finder3, "m3"); assertTrue(cf3.modules().size() == 1); assertTrue(cf3.findModule("m1").isPresent()); // in parent @@ -376,18 +370,16 @@ // cf1: m1, m2 requires transitive m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); @@ -408,20 +400,18 @@ // cf2: m3 requires transitive m2, m4 requires m3 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m2") .build(); - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m3") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); - Configuration cf2 = resolveRequires(cf1, finder2, "m3", "m4"); + Configuration cf2 = resolve(cf1, finder2, "m3", "m4"); assertTrue(cf2.modules().size() == 2); assertTrue(cf2.findModule("m1").isPresent()); // in parent @@ -456,28 +446,24 @@ * - Configuration cf3(cf1,cf2): m4 requires m2, m3 */ public void testRequiresTransitive6() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m2") .requires("m3") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); assertTrue(cf1.findModule("m2").isPresent()); @@ -485,7 +471,7 @@ assertTrue(cf1.parents().get(0) == Configuration.empty()); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3); - Configuration cf2 = resolveRequires(finder2, "m3"); + Configuration cf2 = resolve(finder2, "m3"); assertTrue(cf2.modules().size() == 2); assertTrue(cf2.findModule("m3").isPresent()); assertTrue(cf2.findModule("m1").isPresent()); @@ -493,7 +479,7 @@ assertTrue(cf2.parents().get(0) == Configuration.empty()); ModuleFinder finder3 = ModuleUtils.finderOf(descriptor4); - Configuration cf3 = Configuration.resolveRequires(finder3, + Configuration cf3 = Configuration.resolve(finder3, List.of(cf1, cf2), ModuleFinder.of(), Set.of("m4")); @@ -522,14 +508,13 @@ * resolve m1 */ public void testRequiresStatic1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 1); @@ -545,18 +530,16 @@ * resolve m1 */ public void testRequiresStatic2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 1); @@ -572,18 +555,16 @@ * resolve m1, m2 */ public void testRequiresStatic3() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequires(finder, "m1", "m2"); + Configuration cf = resolve(finder, "m1", "m2"); assertTrue(cf.modules().size() == 2); @@ -604,25 +585,22 @@ * m3 */ public void testRequiresStatic4() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .requires("m3") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.STATIC), "m3") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .build(); ModuleFinder finder - = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); + = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 3); @@ -648,31 +626,28 @@ * - Configuration cf2: m3 requires m1, requires static m2 */ public void testRequiresStatic5() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m1", "m2"); + Configuration cf1 = resolve(finder1, "m1", "m2"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); assertTrue(cf1.findModule("m2").isPresent()); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m3").isPresent()); @@ -694,26 +669,24 @@ * - Configuration cf2: m3 requires m1, requires static m2 */ public void testRequiresStatic6() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m3").isPresent()); @@ -735,21 +708,19 @@ public void testRequiresStatic7() { ModuleDescriptor descriptor1 = null; // not observable - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE, Requires.Modifier.STATIC), "m1") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor2, descriptor3); - Configuration cf = resolveRequires(finder, "m3"); + Configuration cf = resolve(finder, "m3"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m2").isPresent()); @@ -770,8 +741,7 @@ public void testRequiresStatic8() { ModuleDescriptor descriptor1 = null; // not observable - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE, Requires.Modifier.STATIC), "m1") @@ -779,21 +749,20 @@ ModuleFinder finder1 = ModuleUtils.finderOf(descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m2").isPresent()); ResolvedModule m2 = cf1.findModule("m2").get(); assertTrue(m2.reads().isEmpty()); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m3").isPresent()); @@ -810,22 +779,19 @@ */ public void testServiceBinding1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequiresAndUses(finder, "m1"); + Configuration cf = resolveAndBind(finder, "m1"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m1").isPresent()); @@ -853,31 +819,26 @@ */ public void testServiceBinding2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .uses("p.S2") - .contains("q") - .provides("p.S1", "q.Service1Impl") + .provides("p.S1", List.of("q.Service1Impl")) .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") - .contains("q") - .provides("p.S2", "q.Service2Impl") + .provides("p.S2", List.of("q.Service2Impl")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - Configuration cf = resolveRequiresAndUses(finder, "m1"); + Configuration cf = resolveAndBind(finder, "m1"); assertTrue(cf.modules().size() == 3); assertTrue(cf.findModule("m1").isPresent()); @@ -912,29 +873,26 @@ */ public void testServiceBindingWithConfigurations1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2); - Configuration cf2 = resolveRequiresAndUses(cf1, finder2); // no roots + Configuration cf2 = resolveAndBind(cf1, finder2); // no roots assertTrue(cf2.parents().size() == 1); assertTrue(cf2.parents().get(0) == cf1); @@ -961,47 +919,39 @@ */ public void testServiceBindingWithConfigurations2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") - .contains("p1") - .provides("p.S", "p1.ServiceImpl") + .provides("p.S", List.of("p1.ServiceImpl")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") - .contains("p2") - .provides("p.S", "p2.ServiceImpl") + .provides("p.S", List.of("p2.ServiceImpl")) .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequiresAndUses(finder1, "m1"); + Configuration cf1 = resolveAndBind(finder1, "m1"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); assertTrue(cf1.findModule("m2").isPresent()); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") - .contains("p3") - .provides("p.S", "p3.ServiceImpl") + .provides("p.S", List.of("p3.ServiceImpl")) .build(); - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m1") - .contains("p4") - .provides("p.S", "p4.ServiceImpl") + .provides("p.S", List.of("p4.ServiceImpl")) .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); - Configuration cf2 = resolveRequiresAndUses(cf1, finder2); // no roots + Configuration cf2 = resolveAndBind(cf1, finder2); // no roots assertTrue(cf2.parents().size() == 1); assertTrue(cf2.parents().get(0) == cf1); @@ -1037,22 +987,19 @@ */ public void testServiceBindingWithConfigurations3() { - ModuleDescriptor service - = ModuleDescriptor.module("s") + ModuleDescriptor service = newBuilder("s") .exports("p") .build(); - ModuleDescriptor provider_v1 - = ModuleDescriptor.module("p") + ModuleDescriptor provider_v1 = newBuilder("p") .version("1.0") .requires("s") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder1 = ModuleUtils.finderOf(service, provider_v1); - Configuration cf1 = resolveRequires(finder1, "p"); + Configuration cf1 = resolve(finder1, "p"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("s").isPresent()); @@ -1063,18 +1010,15 @@ assertEquals(p.reference().descriptor(), provider_v1); - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("s") .uses("p.S") .build(); - ModuleDescriptor provider_v2 - = ModuleDescriptor.module("p") + ModuleDescriptor provider_v2 = newBuilder("p") .version("2.0") .requires("s") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, provider_v2); @@ -1082,7 +1026,7 @@ // finder2 is the before ModuleFinder and so p@2.0 should be located - Configuration cf2 = resolveRequiresAndUses(cf1, finder2, "m1"); + Configuration cf2 = resolveAndBind(cf1, finder2, "m1"); assertTrue(cf2.parents().size() == 1); assertTrue(cf2.parents().get(0) == cf1); @@ -1097,7 +1041,7 @@ // finder2 is the after ModuleFinder and so p@2.0 should not be located // as module p is in parent configuration. - cf2 = resolveRequiresAndUses(cf1, ModuleFinder.of(), finder2, "m1"); + cf2 = resolveAndBind(cf1, ModuleFinder.of(), finder2, "m1"); assertTrue(cf2.parents().size() == 1); assertTrue(cf2.parents().get(0) == cf1); @@ -1117,25 +1061,22 @@ */ public void testWithTwoFinders1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); - ModuleDescriptor descriptor2_v1 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2_v1 = newBuilder("m2") .version("1.0") .build(); - ModuleDescriptor descriptor2_v2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2_v2 = newBuilder("m2") .version("2.0") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor2_v1); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor2_v2); - Configuration cf = resolveRequires(finder1, finder2, "m1"); + Configuration cf = resolve(finder1, finder2, "m1"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m1").isPresent()); @@ -1157,30 +1098,25 @@ */ public void testWithTwoFinders2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); - ModuleDescriptor descriptor2_v1 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2_v1 = newBuilder("m2") .requires("m1") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); - ModuleDescriptor descriptor2_v2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2_v2 = newBuilder("m2") .requires("m1") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2_v1); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2_v2); - Configuration cf = resolveRequiresAndUses(finder1, finder2, "m1"); + Configuration cf = resolveAndBind(finder1, finder2, "m1"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m1").isPresent()); @@ -1200,18 +1136,17 @@ */ public void testResolvedInParent1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder, "m1"); + Configuration cf1 = resolve(finder, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - Configuration cf2 = resolveRequires(cf1, finder, "m1"); + Configuration cf2 = resolve(cf1, finder, "m1"); assertTrue(cf2.modules().size() == 1); } @@ -1223,26 +1158,23 @@ */ public void testResolvedInParent2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2); - Configuration cf2 = resolveRequires(cf1, ModuleFinder.of(), finder2, "m2"); + Configuration cf2 = resolve(cf1, ModuleFinder.of(), finder2, "m2"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m2").isPresent()); @@ -1268,29 +1200,28 @@ public void testResolvedInMultipleParents1() { // Configuration cf1: m1 - ModuleDescriptor descriptor1 = ModuleDescriptor.module("m1").build(); - Configuration cf1 = resolveRequires(ModuleUtils.finderOf(descriptor1), "m1"); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); + Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); assertEquals(cf1.parents(), List.of(Configuration.empty())); assertTrue(cf1.findModule("m1").isPresent()); ResolvedModule m1 = cf1.findModule("m1").get(); assertTrue(m1.configuration() == cf1); // Configuration cf2: m2 - ModuleDescriptor descriptor2 = ModuleDescriptor.module("m2").build(); - Configuration cf2 = resolveRequires(ModuleUtils.finderOf(descriptor2), "m2"); + ModuleDescriptor descriptor2 = newBuilder("m2").build(); + Configuration cf2 = resolve(ModuleUtils.finderOf(descriptor2), "m2"); assertEquals(cf2.parents(), List.of(Configuration.empty())); assertTrue(cf2.findModule("m2").isPresent()); ResolvedModule m2 = cf2.findModule("m2").get(); assertTrue(m2.configuration() == cf2); // Configuration cf3(cf1,cf2): m3 requires m1 and m2 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") .requires("m2") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor3); - Configuration cf3 = Configuration.resolveRequires( + Configuration cf3 = Configuration.resolve( finder, List.of(cf1, cf2), // parents ModuleFinder.of(), @@ -1319,19 +1250,18 @@ */ public void testResolvedInMultipleParents2() { // Configuration cf1: m1 - ModuleDescriptor descriptor1 = ModuleDescriptor.module("m1").build(); - Configuration cf1 = resolveRequires(ModuleUtils.finderOf(descriptor1), "m1"); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); + Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); assertEquals(cf1.parents(), List.of(Configuration.empty())); assertTrue(cf1.findModule("m1").isPresent()); ResolvedModule m1 = cf1.findModule("m1").get(); assertTrue(m1.configuration() == cf1); // Configuration cf2(cf1): m2 requires m1 - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .build(); - Configuration cf2 = Configuration.resolveRequires( + Configuration cf2 = Configuration.resolve( ModuleUtils.finderOf(descriptor2), List.of(cf1), // parents ModuleFinder.of(), @@ -1342,11 +1272,10 @@ assertTrue(m2.configuration() == cf2); // Configuration cf3(cf1): m3 requires m1 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m1") .build(); - Configuration cf3 = Configuration.resolveRequires( + Configuration cf3 = Configuration.resolve( ModuleUtils.finderOf(descriptor3), List.of(cf1), // parents ModuleFinder.of(), @@ -1357,13 +1286,12 @@ assertTrue(m3.configuration() == cf3); // Configuration cf4(cf2,cf3): m4 requires m1,m2,m3 - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m1") .requires("m2") .requires("m3") .build(); - Configuration cf4 = Configuration.resolveRequires( + Configuration cf4 = Configuration.resolve( ModuleUtils.finderOf(descriptor4), List.of(cf2, cf3), // parents ModuleFinder.of(), @@ -1395,35 +1323,34 @@ ModuleDescriptor descriptor1, descriptor2, descriptor3; // Configuration cf1: m1@1 - descriptor1 = ModuleDescriptor.module("m1").version("1").build(); - Configuration cf1 = resolveRequires(ModuleUtils.finderOf(descriptor1), "m1"); + descriptor1 = newBuilder("m1").version("1").build(); + Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); assertEquals(cf1.parents(), List.of(Configuration.empty())); // Configuration cf2: m1@2, m2@2 - descriptor1 = ModuleDescriptor.module("m1").version("2").build(); - descriptor2 = ModuleDescriptor.module("m2").version("2").build(); - Configuration cf2 = resolveRequires( + descriptor1 = newBuilder("m1").version("2").build(); + descriptor2 = newBuilder("m2").version("2").build(); + Configuration cf2 = resolve( ModuleUtils.finderOf(descriptor1, descriptor2), "m1", "m2"); assertEquals(cf2.parents(), List.of(Configuration.empty())); // Configuration cf3: m1@3, m2@3, m3@3 - descriptor1 = ModuleDescriptor.module("m1").version("3").build(); - descriptor2 = ModuleDescriptor.module("m2").version("3").build(); - descriptor3 = ModuleDescriptor.module("m3").version("3").build(); - Configuration cf3 = resolveRequires( + descriptor1 = newBuilder("m1").version("3").build(); + descriptor2 = newBuilder("m2").version("3").build(); + descriptor3 = newBuilder("m3").version("3").build(); + Configuration cf3 = resolve( ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3), "m1", "m2", "m3"); assertEquals(cf3.parents(), List.of(Configuration.empty())); // Configuration cf4(cf1,cf2,cf3): m4 requires m1,m2,m3 - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m1") .requires("m2") .requires("m3") .build(); - Configuration cf4 = Configuration.resolveRequires( + Configuration cf4 = Configuration.resolve( ModuleUtils.finderOf(descriptor4), List.of(cf1, cf2, cf3), // parents ModuleFinder.of(), @@ -1470,17 +1397,15 @@ * configuration. */ public void testOverriding1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder, "m1"); + Configuration cf1 = resolve(finder, "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - Configuration cf2 = resolveRequires(cf1, finder, "m1"); + Configuration cf2 = resolve(cf1, finder, "m1"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m1").isPresent()); } @@ -1490,24 +1415,24 @@ * configuration. */ public void testOverriding2() { - ModuleDescriptor descriptor1 = ModuleDescriptor.module("m1").build(); - Configuration cf1 = resolveRequires(ModuleUtils.finderOf(descriptor1), "m1"); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); + Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); assertTrue(cf1.modules().size() == 1); assertTrue(cf1.findModule("m1").isPresent()); - ModuleDescriptor descriptor2 = ModuleDescriptor.module("m2").build(); - Configuration cf2 = resolveRequires(ModuleUtils.finderOf(descriptor2), "m2"); + ModuleDescriptor descriptor2 = newBuilder("m2").build(); + Configuration cf2 = resolve(ModuleUtils.finderOf(descriptor2), "m2"); assertTrue(cf2.modules().size() == 1); assertTrue(cf2.findModule("m2").isPresent()); - ModuleDescriptor descriptor3 = ModuleDescriptor.module("m3").build(); - Configuration cf3 = resolveRequires(ModuleUtils.finderOf(descriptor3), "m3"); + ModuleDescriptor descriptor3 = newBuilder("m3").build(); + Configuration cf3 = resolve(ModuleUtils.finderOf(descriptor3), "m3"); assertTrue(cf3.modules().size() == 1); assertTrue(cf3.findModule("m3").isPresent()); // override m2, m1 and m3 should be found in parent configurations ModuleFinder finder = ModuleUtils.finderOf(descriptor2); - Configuration cf4 = Configuration.resolveRequires( + Configuration cf4 = Configuration.resolve( finder, List.of(cf1, cf2, cf3), ModuleFinder.of(), @@ -1530,18 +1455,16 @@ */ public void testOverriding3() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); assertTrue(cf1.modules().size() == 2); assertTrue(cf1.findModule("m1").isPresent()); @@ -1549,14 +1472,13 @@ // cf2: m3 requires m2, m1 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m1", "m3"); + Configuration cf2 = resolve(cf1, finder2, "m1", "m3"); assertTrue(cf2.parents().size() == 1); assertTrue(cf2.parents().get(0) == cf1); @@ -1585,64 +1507,58 @@ /** * Root module not found */ - @Test(expectedExceptions = { ResolutionException.class }) + @Test(expectedExceptions = { FindException.class }) public void testRootNotFound() { - resolveRequires(ModuleFinder.of(), "m1"); + resolve(ModuleFinder.of(), "m1"); } /** * Direct dependency not found */ - @Test(expectedExceptions = { ResolutionException.class }) + @Test(expectedExceptions = { FindException.class }) public void testDirectDependencyNotFound() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1").requires("m2").build(); + ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } /** * Transitive dependency not found */ - @Test(expectedExceptions = { ResolutionException.class }) + @Test(expectedExceptions = { FindException.class }) public void testTransitiveDependencyNotFound() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1").requires("m2").build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2").requires("m3").build(); + ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); + ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } /** * Service provider dependency not found */ - @Test(expectedExceptions = { ResolutionException.class }) + @Test(expectedExceptions = { FindException.class }) public void testServiceProviderDependencyNotFound() { // service provider dependency (on m3) not found - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .requires("m3") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // should throw ResolutionException because m3 is not found - Configuration cf = resolveRequiresAndUses(finder, "m1"); + Configuration cf = resolveAndBind(finder, "m1"); } @@ -1651,15 +1567,12 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testSimpleCycle() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1").requires("m2").build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2").requires("m3").build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3").requires("m1").build(); + ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); + ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build(); + ModuleDescriptor descriptor3 = newBuilder("m3").requires("m1").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } /** @@ -1668,20 +1581,16 @@ @Test(expectedExceptions = { ResolutionException.class }) public void testCycleInProvider() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .requires("m3") - .contains("q") - .provides("p.S", "q.T") + .provides("p.S", List.of("q.T")) .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); @@ -1689,7 +1598,7 @@ = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); // should throw ResolutionException because of the m2 <--> m3 cycle - resolveRequiresAndUses(finder, "m1"); + resolveAndBind(finder, "m1"); } @@ -1699,19 +1608,16 @@ @Test(expectedExceptions = { ResolutionException.class }) public void testPackageSuppliedByTwoOthers() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .requires("m3") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .exports("p") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .exports("p", Set.of("m1")) .build(); @@ -1719,7 +1625,7 @@ = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); // m2 and m3 export package p to module m1 - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } @@ -1730,21 +1636,19 @@ @Test(expectedExceptions = { ResolutionException.class }) public void testPackageSuppliedBySelfAndOther() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") - .contains("p") + .packages(Set.of("p")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .exports("p") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m1 contains package p, module m2 exports package p to m1 - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } @@ -1753,20 +1657,18 @@ * a module that also contains a package p. */ public void testContainsPackageInSelfAndOther() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") - .contains("p") + .packages(Set.of("p")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") - .contains("p") + ModuleDescriptor descriptor2 = newBuilder("m2") + .packages(Set.of("p")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m1").isPresent()); @@ -1787,8 +1689,7 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testExportSamePackageAsBootLayer() { - ModuleDescriptor descriptor - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor = newBuilder("m1") .requires("java.base") .exports("java.lang") .build(); @@ -1798,7 +1699,7 @@ Configuration bootConfiguration = Layer.boot().configuration(); // m1 contains package java.lang, java.base exports package java.lang to m1 - resolveRequires(bootConfiguration, finder, "m1"); + resolve(bootConfiguration, finder, "m1"); } @@ -1806,15 +1707,14 @@ * Test "uses p.S" where p is contained in the same module. */ public void testContainsService1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("p") + ModuleDescriptor descriptor1 = newBuilder("m1") + .packages(Set.of("p")) .uses("p.S") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 1); assertTrue(cf.findModule("m1").isPresent()); @@ -1826,13 +1726,11 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testContainsService2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("p") + ModuleDescriptor descriptor1 = newBuilder("m1") + .packages(Set.of("p")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") .uses("p.S") .build(); @@ -1840,7 +1738,7 @@ ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m2 does not read a module that exports p - resolveRequires(finder, "m2"); + resolve(finder, "m2"); } @@ -1848,16 +1746,14 @@ * Test "provides p.S" where p is contained in the same module. */ public void testContainsService3() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("p") - .contains("q") - .provides("p.S", "q.S1") + ModuleDescriptor descriptor1 = newBuilder("m1") + .packages(Set.of("p", "q")) + .provides("p.S", List.of("q.S1")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 1); assertTrue(cf.findModule("m1").isPresent()); @@ -1869,22 +1765,19 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testContainsService4() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("p") + ModuleDescriptor descriptor1 = newBuilder("m1") + .packages(Set.of("p")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m1") - .contains("q") - .provides("p.S", "q.S1") + .provides("p.S", List.of("q.S1")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m2 does not read a module that exports p - resolveRequires(finder, "m2"); + resolve(finder, "m2"); } @@ -1893,15 +1786,14 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testServiceTypePackageNotExported1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .uses("p.S") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); // m1 does not read a module that exports p - resolveRequires(finder, "m1"); + resolve(finder, "m1"); } @@ -1910,40 +1802,14 @@ */ @Test(expectedExceptions = { ResolutionException.class }) public void testServiceTypePackageNotExported2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("q") - .provides("p.S", "q.T") + ModuleDescriptor descriptor1 = newBuilder("m1") + .provides("p.S", List.of("q.T")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); // m1 does not read a module that exports p - resolveRequires(finder, "m1"); - } - - - /** - * Test "provides p.S with q.T" where q.T is not local - */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testProviderPackageNotLocal() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .exports("p") - .exports("q") - .build(); - - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") - .requires("m1") - .provides("p.S", "q.T") - .build(); - - ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - - // q.T not in module m2 - resolveRequires(finder, "m2"); + resolve(finder, "m1"); } @@ -2007,34 +1873,17 @@ @Test(dataProvider = "platformmatch") public void testPlatformMatch(String s1, String s2) { - ModuleDescriptor.Builder builder - = ModuleDescriptor.module("m1").requires("m2"); - - String[] s = s1.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]); - + Builder builder = newBuilder("m1").requires("m2"); + addPlatformConstraints(builder, s1); ModuleDescriptor descriptor1 = builder.build(); - builder = ModuleDescriptor.module("m2"); - - s = s2.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]); - + builder = newBuilder("m2"); + addPlatformConstraints(builder, s2); ModuleDescriptor descriptor2 = builder.build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 2); assertTrue(cf.findModule("m1").isPresent()); @@ -2046,7 +1895,7 @@ * platforms. */ @Test(dataProvider = "platformmismatch", - expectedExceptions = ResolutionException.class ) + expectedExceptions = FindException.class ) public void testPlatformMisMatch(String s1, String s2) { testPlatformMatch(s1, s2); } @@ -2057,16 +1906,67 @@ @Test(expectedExceptions = { IllegalArgumentException.class }) public void testResolveRequiresWithNoParents() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveRequires(empty, List.of(), empty, Set.of()); + Configuration.resolve(empty, List.of(), empty, Set.of()); } @Test(expectedExceptions = { IllegalArgumentException.class }) public void testResolveRequiresAndUsesWithNoParents() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveRequiresAndUses(empty, List.of(), empty, Set.of()); + Configuration.resolveAndBind(empty, List.of(), empty, Set.of()); + } + + + // 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(); + + 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"); + + Configuration cf3 = Configuration.resolve(ModuleFinder.of(), + List.of(cf1, cf2), + ModuleFinder.of(), + Set.of()); + 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()); } + // null handling // finder1, finder2, roots @@ -2074,72 +1974,72 @@ @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull1() { - resolveRequires((ModuleFinder)null, ModuleFinder.of()); + resolve((ModuleFinder)null, ModuleFinder.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull2() { - resolveRequires(ModuleFinder.of(), (ModuleFinder)null); + resolve(ModuleFinder.of(), (ModuleFinder)null); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull3() { Configuration empty = Configuration.empty(); - Configuration.resolveRequires(null, List.of(empty), ModuleFinder.of(), Set.of()); + Configuration.resolve(null, List.of(empty), ModuleFinder.of(), Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull4() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveRequires(empty, null, empty, Set.of()); + Configuration.resolve(empty, null, empty, Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull5() { Configuration cf = Layer.boot().configuration(); - Configuration.resolveRequires(ModuleFinder.of(), List.of(cf), null, Set.of()); + Configuration.resolve(ModuleFinder.of(), List.of(cf), null, Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresWithNull6() { ModuleFinder empty = ModuleFinder.of(); Configuration cf = Layer.boot().configuration(); - Configuration.resolveRequires(empty, List.of(cf), empty, null); + Configuration.resolve(empty, List.of(cf), empty, null); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull1() { - resolveRequiresAndUses((ModuleFinder) null, ModuleFinder.of()); + resolveAndBind((ModuleFinder) null, ModuleFinder.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull2() { - resolveRequiresAndUses(ModuleFinder.of(), (ModuleFinder) null); + resolveAndBind(ModuleFinder.of(), (ModuleFinder) null); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull3() { Configuration empty = Configuration.empty(); - Configuration.resolveRequiresAndUses(null, List.of(empty), ModuleFinder.of(), Set.of()); + Configuration.resolveAndBind(null, List.of(empty), ModuleFinder.of(), Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull4() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveRequiresAndUses(empty, null, empty, Set.of()); + Configuration.resolveAndBind(empty, null, empty, Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull5() { Configuration cf = Layer.boot().configuration(); - Configuration.resolveRequiresAndUses(ModuleFinder.of(), List.of(cf), null, Set.of()); + Configuration.resolveAndBind(ModuleFinder.of(), List.of(cf), null, Set.of()); } @Test(expectedExceptions = { NullPointerException.class }) public void testResolveRequiresAndUsesWithNull6() { ModuleFinder empty = ModuleFinder.of(); Configuration cf = Layer.boot().configuration(); - Configuration.resolveRequiresAndUses(empty, List.of(cf), empty, null); + Configuration.resolveAndBind(empty, List.of(cf), empty, null); } @Test(expectedExceptions = { NullPointerException.class }) @@ -2165,58 +2065,58 @@ /** - * Invokes parent.resolveRequires(...) + * Invokes parent.resolve(...) */ - private Configuration resolveRequires(Configuration parent, - ModuleFinder before, - ModuleFinder after, - String... roots) { - return parent.resolveRequires(before, after, Set.of(roots)); + private Configuration resolve(Configuration parent, + ModuleFinder before, + ModuleFinder after, + String... roots) { + return parent.resolve(before, after, Set.of(roots)); } - private Configuration resolveRequires(Configuration parent, - ModuleFinder before, - String... roots) { - return resolveRequires(parent, before, ModuleFinder.of(), roots); + private Configuration resolve(Configuration parent, + ModuleFinder before, + String... roots) { + return resolve(parent, before, ModuleFinder.of(), roots); } - private Configuration resolveRequires(ModuleFinder before, - ModuleFinder after, - String... roots) { - return resolveRequires(Configuration.empty(), before, after, roots); + private Configuration resolve(ModuleFinder before, + ModuleFinder after, + String... roots) { + return resolve(Configuration.empty(), before, after, roots); } - private Configuration resolveRequires(ModuleFinder before, - String... roots) { - return resolveRequires(Configuration.empty(), before, roots); + private Configuration resolve(ModuleFinder before, + String... roots) { + return resolve(Configuration.empty(), before, roots); } /** - * Invokes parent.resolveRequiresAndUses(...) + * Invokes parent.resolveAndBind(...) */ - private Configuration resolveRequiresAndUses(Configuration parent, - ModuleFinder before, - ModuleFinder after, - String... roots) { - return parent.resolveRequiresAndUses(before, after, Set.of(roots)); + private Configuration resolveAndBind(Configuration parent, + ModuleFinder before, + ModuleFinder after, + String... roots) { + return parent.resolveAndBind(before, after, Set.of(roots)); } - private Configuration resolveRequiresAndUses(Configuration parent, - ModuleFinder before, - String... roots) { - return resolveRequiresAndUses(parent, before, ModuleFinder.of(), roots); + private Configuration resolveAndBind(Configuration parent, + ModuleFinder before, + String... roots) { + return resolveAndBind(parent, before, ModuleFinder.of(), roots); } - private Configuration resolveRequiresAndUses(ModuleFinder before, - ModuleFinder after, - String... roots) { - return resolveRequiresAndUses(Configuration.empty(), before, after, roots); + private Configuration resolveAndBind(ModuleFinder before, + ModuleFinder after, + String... roots) { + return resolveAndBind(Configuration.empty(), before, after, roots); } - private Configuration resolveRequiresAndUses(ModuleFinder before, - String... roots) { - return resolveRequiresAndUses(Configuration.empty(), before, roots); + private Configuration resolveAndBind(ModuleFinder before, + String... roots) { + return resolveAndBind(Configuration.empty(), before, roots); } @@ -2234,5 +2134,17 @@ .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) { + 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]); + } } --- old/test/java/lang/module/ModuleDescriptorTest.java 2017-02-07 13:14:01.267156146 +0000 +++ new/test/java/lang/module/ModuleDescriptorTest.java 2017-02-07 13:14:01.099144608 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,6 +24,7 @@ /** * @test * @modules java.base/jdk.internal.module + * java.base/jdk.internal.misc * @run testng ModuleDescriptorTest * @summary Basic test for java.lang.module.ModuleDescriptor and its builder */ @@ -42,14 +43,19 @@ import java.lang.module.ModuleDescriptor.Version; import java.lang.reflect.Module; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; +import jdk.internal.misc.SharedSecrets; import jdk.internal.module.ModuleInfoWriter; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -84,21 +90,27 @@ // requires private Requires requires(Set mods, String mn) { - return ModuleDescriptor.module("m") - .requires(mods, mn) - .build() - .requires() - .iterator() - .next(); + return requires(mods, mn, null); } private Requires requires(Set mods, String mn, Version v) { - return ModuleDescriptor.module("m") - .requires(mods, mn, v) - .build() - .requires() - .iterator() - .next(); + Builder builder = ModuleDescriptor.newModule("m"); + if (v == null) { + builder.requires(mods, mn); + } else { + builder.requires(mods, mn, v); + } + Set requires = builder.build().requires(); + assertTrue(requires.size() == 2); + Iterator iterator = requires.iterator(); + Requires r = iterator.next(); + if (r.name().equals("java.base")) { + r = iterator.next(); + } else { + Requires other = iterator.next(); + assertEquals(other.name(), "java.base"); + } + return r; } private Requires requires(String mn) { @@ -107,7 +119,7 @@ public void testRequiresWithRequires() { Requires r1 = requires("foo"); - ModuleDescriptor descriptor = ModuleDescriptor.module("m").requires(r1).build(); + ModuleDescriptor descriptor = ModuleDescriptor.newModule("m").requires(r1).build(); Requires r2 = descriptor.requires().iterator().next(); assertEquals(r1, r2); } @@ -162,28 +174,28 @@ @Test(expectedExceptions = IllegalStateException.class) public void testRequiresWithDuplicatesRequires() { Requires r = requires("foo"); - ModuleDescriptor.module("m").requires(r).requires(r); + ModuleDescriptor.newModule("m").requires(r).requires(r); } @Test(expectedExceptions = IllegalArgumentException.class) public void testRequiresSelfWithRequires() { Requires r = requires("foo"); - ModuleDescriptor.module("foo").requires(r); + ModuleDescriptor.newModule("foo").requires(r); } @Test(expectedExceptions = IllegalArgumentException.class) public void testRequiresSelfWithNoModifier() { - ModuleDescriptor.module("m").requires("m"); + ModuleDescriptor.newModule("m").requires("m"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testRequiresSelfWithOneModifier() { - ModuleDescriptor.module("m").requires(Set.of(TRANSITIVE), "m"); + ModuleDescriptor.newModule("m").requires(Set.of(TRANSITIVE), "m"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testRequiresSelfWithAllModifiers() { - ModuleDescriptor.module("m").requires(EnumSet.allOf(Modifier.class), "m"); + ModuleDescriptor.newModule("m").requires(EnumSet.allOf(Modifier.class), "m"); } @Test(dataProvider = "invalidjavaidentifiers", @@ -194,17 +206,17 @@ @Test(expectedExceptions = NullPointerException.class) public void testRequiresWithNullRequires() { - ModuleDescriptor.module("m").requires((Requires) null); + ModuleDescriptor.newModule("m").requires((Requires) null); } @Test(expectedExceptions = NullPointerException.class) public void testRequiresWithNullModifiers() { - ModuleDescriptor.module("m").requires(null, "foo"); + ModuleDescriptor.newModule("m").requires(null, "foo"); } @Test(expectedExceptions = NullPointerException.class) public void testRequiresWithNullVersion() { - ModuleDescriptor.module("m").requires(Set.of(), "foo", null); + ModuleDescriptor.newModule("m").requires(Set.of(), "foo", null); } public void testRequiresCompare() { @@ -284,7 +296,7 @@ // exports private Exports exports(Set mods, String pn) { - return ModuleDescriptor.module("foo") + return ModuleDescriptor.newModule("foo") .exports(mods, pn) .build() .exports() @@ -297,7 +309,7 @@ } private Exports exports(Set mods, String pn, String target) { - return ModuleDescriptor.module("foo") + return ModuleDescriptor.newModule("foo") .exports(mods, pn, Set.of(target)) .build() .exports() @@ -312,7 +324,7 @@ public void testExportsExports() { Exports e1 = exports("p"); - ModuleDescriptor descriptor = ModuleDescriptor.module("m").exports(e1).build(); + ModuleDescriptor descriptor = ModuleDescriptor.newModule("m").exports(e1).build(); Exports e2 = descriptor.exports().iterator().next(); assertEquals(e1, e2); } @@ -341,7 +353,7 @@ targets.add("bar"); targets.add("gus"); Exports e - = ModuleDescriptor.module("foo") + = ModuleDescriptor.newModule("foo") .exports("p", targets) .build() .exports() @@ -380,69 +392,80 @@ @Test(expectedExceptions = IllegalStateException.class) public void testExportsWithDuplicate1() { Exports e = exports("p"); - ModuleDescriptor.module("foo").exports(e).exports(e); + ModuleDescriptor.newModule("foo").exports(e).exports(e); } @Test(expectedExceptions = IllegalStateException.class) public void testExportsWithDuplicate2() { - ModuleDescriptor.module("foo").exports("p").exports("p"); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testExportsOnContainedPackage() { - ModuleDescriptor.module("foo").contains("p").exports("p"); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testExportsToTargetOnContainedPackage() { - ModuleDescriptor.module("foo").contains("p").exports("p", Set.of("bar")); + ModuleDescriptor.newModule("foo").exports("p").exports("p"); } @Test(expectedExceptions = IllegalArgumentException.class ) public void testExportsWithEmptySet() { - ModuleDescriptor.module("foo").exports("p", Collections.emptySet()); + ModuleDescriptor.newModule("foo").exports("p", Collections.emptySet()); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testExportsWithBadName(String pn, String ignore) { - ModuleDescriptor.module("foo").exports(pn); + ModuleDescriptor.newModule("foo").exports(pn); } @Test(expectedExceptions = NullPointerException.class ) public void testExportsWithNullExports() { - ModuleDescriptor.module("foo").exports((Exports) null); + ModuleDescriptor.newModule("foo").exports((Exports) null); } @Test(expectedExceptions = NullPointerException.class ) public void testExportsWithNullTargets() { - ModuleDescriptor.module("foo").exports("p", (Set) null); + ModuleDescriptor.newModule("foo").exports("p", (Set) null); } - public void testExportsEqualsAndHashCode() { - Exports e1, e2; - - e1 = exports("p"); - e2 = exports("p"); + public void testExportsCompare() { + Exports e1 = exports("p"); + Exports e2 = exports("p"); assertEquals(e1, e2); assertTrue(e1.hashCode() == e2.hashCode()); + assertTrue(e1.compareTo(e2) == 0); + assertTrue(e2.compareTo(e1) == 0); + } - e1 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); - e2 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); + public void testExportsCompareWithSameModifiers() { + Exports e1 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); + Exports e2 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); assertEquals(e1, e2); assertTrue(e1.hashCode() == e2.hashCode()); + assertTrue(e1.compareTo(e2) == 0); + assertTrue(e2.compareTo(e1) == 0); + } - e1 = exports("p"); - e2 = exports("q"); + public void testExportsCompareWithDifferentModifiers() { + Exports e1 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); + Exports e2 = exports("p"); assertNotEquals(e1, e2); + assertTrue(e1.compareTo(e2) == 1); + assertTrue(e2.compareTo(e1) == -1); + } + + public void testExportsCompareWithSameTargets() { + Exports e1 = exports("p", "x"); + Exports e2 = exports("p", "x"); + assertEquals(e1, e2); + assertTrue(e1.hashCode() == e2.hashCode()); + assertTrue(e1.compareTo(e2) == 0); + assertTrue(e2.compareTo(e1) == 0); + } - e1 = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); - e2 = exports(Set.of(), "p"); + public void testExportsCompareWithDifferentTargets() { + Exports e1 = exports("p", "y"); + Exports e2 = exports("p", "x"); assertNotEquals(e1, e2); + assertTrue(e1.compareTo(e2) == 1); + assertTrue(e2.compareTo(e1) == -1); } public void testExportsToString() { - String s = ModuleDescriptor.module("foo") + String s = ModuleDescriptor.newModule("foo") .exports("p1", Set.of("bar")) .build() .exports() @@ -457,7 +480,7 @@ // opens private Opens opens(Set mods, String pn) { - return ModuleDescriptor.module("foo") + return ModuleDescriptor.newModule("foo") .opens(mods, pn) .build() .opens() @@ -470,7 +493,7 @@ } private Opens opens(Set mods, String pn, String target) { - return ModuleDescriptor.module("foo") + return ModuleDescriptor.newModule("foo") .opens(mods, pn, Set.of(target)) .build() .opens() @@ -484,7 +507,7 @@ public void testOpensOpens() { Opens o1 = opens("p"); - ModuleDescriptor descriptor = ModuleDescriptor.module("m").opens(o1).build(); + ModuleDescriptor descriptor = ModuleDescriptor.newModule("m").opens(o1).build(); Opens o2 = descriptor.opens().iterator().next(); assertEquals(o1, o2); } @@ -513,7 +536,7 @@ Set targets = new HashSet<>(); targets.add("bar"); targets.add("gus"); - Opens o = ModuleDescriptor.module("foo") + Opens o = ModuleDescriptor.newModule("foo") .opens("p", targets) .build() .opens() @@ -528,98 +551,83 @@ assertTrue(o.targets().contains("gus")); } - /* - - public void testOpensToAllWithModifier() { - Exports e = exports(Set.of(Exports.Modifier.SYNTHETIC), "p"); - assertEquals(e, e); - assertTrue(e.modifiers().size() == 1); - assertTrue(e.modifiers().contains(Exports.Modifier.SYNTHETIC)); - assertEquals(e.source(), "p"); - assertFalse(e.isQualified()); - assertTrue(e.targets().isEmpty()); - } - - public void testOpensToTargetWithModifier() { - Exports e = exports(Set.of(Exports.Modifier.SYNTHETIC), "p", Set.of("bar")); - assertEquals(e, e); - assertTrue(e.modifiers().size() == 1); - assertTrue(e.modifiers().contains(Exports.Modifier.SYNTHETIC)); - assertEquals(e.source(), "p"); - assertTrue(e.isQualified()); - assertTrue(e.targets().size() == 1); - assertTrue(e.targets().contains("bar")); - } - - - */ - @Test(expectedExceptions = IllegalStateException.class) public void testOpensWithDuplicate1() { Opens o = opens("p"); - ModuleDescriptor.module("foo").opens(o).opens(o); + ModuleDescriptor.newModule("foo").opens(o).opens(o); } @Test(expectedExceptions = IllegalStateException.class) public void testOpensWithDuplicate2() { - ModuleDescriptor.module("foo").opens("p").opens("p"); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testOpensOnContainedPackage() { - ModuleDescriptor.module("foo").contains("p").opens("p"); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testOpensToTargetOnContainedPackage() { - ModuleDescriptor.module("foo").contains("p").opens("p", Set.of("bar")); + ModuleDescriptor.newModule("foo").opens("p").opens("p"); } @Test(expectedExceptions = IllegalArgumentException.class ) public void testOpensWithEmptySet() { - ModuleDescriptor.module("foo").opens("p", Collections.emptySet()); + ModuleDescriptor.newModule("foo").opens("p", Collections.emptySet()); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testOpensWithBadName(String pn, String ignore) { - ModuleDescriptor.module("foo").opens(pn); + ModuleDescriptor.newModule("foo").opens(pn); } @Test(expectedExceptions = NullPointerException.class ) public void testOpensWithNullExports() { - ModuleDescriptor.module("foo").opens((Opens) null); + ModuleDescriptor.newModule("foo").opens((Opens) null); } @Test(expectedExceptions = NullPointerException.class ) public void testOpensWithNullTargets() { - ModuleDescriptor.module("foo").opens("p", (Set) null); + ModuleDescriptor.newModule("foo").opens("p", (Set) null); } - public void testOpensEqualsAndHashCode() { - Opens o1, o2; - - o1 = opens("p"); - o2 = opens("p"); + public void testOpensCompare() { + Opens o1 = opens("p"); + Opens o2 = opens("p"); assertEquals(o1, o2); - assertTrue(o1.hashCode() == o1.hashCode()); + assertTrue(o1.hashCode() == o2.hashCode()); + assertTrue(o1.compareTo(o2) == 0); + assertTrue(o2.compareTo(o1) == 0); + } - o1 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); - o2 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); + public void testOpensCompareWithSameModifiers() { + Opens o1 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); + Opens o2 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); assertEquals(o1, o2); assertTrue(o1.hashCode() == o2.hashCode()); + assertTrue(o1.compareTo(o2) == 0); + assertTrue(o2.compareTo(o1) == 0); + } - o1 = opens("p"); - o2 = opens("q"); + public void testOpensCompareWithDifferentModifiers() { + Opens o1 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); + Opens o2 = opens("p"); assertNotEquals(o1, o2); + assertTrue(o1.compareTo(o2) == 1); + assertTrue(o2.compareTo(o1) == -1); + } + + public void testOpensCompareWithSameTargets() { + Opens o1 = opens("p", "x"); + Opens o2 = opens("p", "x"); + assertEquals(o1, o2); + assertTrue(o1.hashCode() == o2.hashCode()); + assertTrue(o1.compareTo(o2) == 0); + assertTrue(o2.compareTo(o1) == 0); + } - o1 = opens(Set.of(Opens.Modifier.SYNTHETIC), "p"); - o2 = opens(Set.of(), "p"); + public void testOpensCompareWithDifferentTargets() { + Opens o1 = opens("p", "y"); + Opens o2 = opens("p", "x"); assertNotEquals(o1, o2); + assertTrue(o1.compareTo(o2) == 1); + assertTrue(o2.compareTo(o1) == -1); } public void testOpensToString() { - String s = ModuleDescriptor.module("foo") + String s = ModuleDescriptor.newModule("foo") .opens("p1", Set.of("bar")) .build() .opens() @@ -635,7 +643,7 @@ public void testUses() { Set uses - = ModuleDescriptor.module("foo") + = ModuleDescriptor.newModule("foo") .uses("p.S") .uses("q.S") .build() @@ -647,30 +655,44 @@ @Test(expectedExceptions = IllegalStateException.class) public void testUsesWithDuplicate() { - ModuleDescriptor.module("foo").uses("p.S").uses("p.S"); + ModuleDescriptor.newModule("foo").uses("p.S").uses("p.S"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testUsesWithSimpleIdentifier() { + ModuleDescriptor.newModule("foo").uses("S"); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testUsesWithBadName(String service, String ignore) { - ModuleDescriptor.module("foo").uses(service); + ModuleDescriptor.newModule("foo").uses(service); } // provides private Provides provides(String st, String pc) { - return ModuleDescriptor.module("foo") - .provides(st, pc) + return ModuleDescriptor.newModule("foo") + .provides(st, List.of(pc)) .build() .provides() .iterator() .next(); } + private Provides provides(String st, List pns) { + return ModuleDescriptor.newModule("foo") + .provides(st, pns) + .build() + .provides() + .iterator() + .next(); + } + public void testProvidesWithProvides() { Provides p1 = provides("p.S", "q.S1"); - ModuleDescriptor descriptor = ModuleDescriptor.module("m") + ModuleDescriptor descriptor = ModuleDescriptor.newModule("m") .provides(p1) .build(); Provides p2 = descriptor.provides().iterator().next(); @@ -679,7 +701,7 @@ public void testProvides() { - Set set = ModuleDescriptor.module("foo") + Set set = ModuleDescriptor.newModule("foo") .provides("p.S", List.of("q.P1", "q.P2")) .build() .provides(); @@ -696,59 +718,97 @@ @Test(expectedExceptions = IllegalStateException.class ) public void testProvidesWithDuplicateProvides() { Provides p = provides("p.S", "q.S2"); - ModuleDescriptor.module("m").provides("p.S", "q.S1").provides(p); + ModuleDescriptor.newModule("m").provides("p.S", List.of("q.S1")).provides(p); } @Test(expectedExceptions = IllegalArgumentException.class ) public void testProvidesWithEmptySet() { - ModuleDescriptor.module("foo").provides("p.Service", Collections.emptyList()); + ModuleDescriptor.newModule("foo").provides("p.Service", Collections.emptyList()); + } + + @Test(expectedExceptions = IllegalArgumentException.class ) + public void testProvidesWithSimpleIdentifier1() { + ModuleDescriptor.newModule("foo").provides("S", List.of("q.P")); + } + + @Test(expectedExceptions = IllegalArgumentException.class ) + public void testProvidesWithSimpleIdentifier2() { + ModuleDescriptor.newModule("foo").provides("p.S", List.of("P")); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testProvidesWithBadService(String service, String ignore) { - ModuleDescriptor.module("foo").provides(service, "p.Provider"); + ModuleDescriptor.newModule("foo").provides(service, List.of("p.Provider")); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testProvidesWithBadProvider(String provider, String ignore) { - ModuleDescriptor.module("foo").provides("p.Service", provider); + List names = new ArrayList<>(); // allows nulls + names.add(provider); + ModuleDescriptor.newModule("foo").provides("p.Service", names); } @Test(expectedExceptions = NullPointerException.class ) public void testProvidesWithNullProvides() { - ModuleDescriptor.module("foo").provides((Provides) null); + ModuleDescriptor.newModule("foo").provides((Provides) null); } @Test(expectedExceptions = NullPointerException.class ) public void testProvidesWithNullProviders() { - ModuleDescriptor.module("foo").provides("p.S", (List) null); + ModuleDescriptor.newModule("foo").provides("p.S", (List) null); } - public void testProvidesEqualsAndHashCode() { - Provides p1, p2; - - p1 = provides("p.S", "q.S1"); - p2 = provides("p.S", "q.S1"); + public void testProvidesCompare() { + Provides p1 = provides("p.S", "q.S1"); + Provides p2 = provides("p.S", "q.S1"); assertEquals(p1, p2); assertTrue(p1.hashCode() == p2.hashCode()); + assertTrue(p1.compareTo(p2) == 0); + assertTrue(p2.compareTo(p1) == 0); + } + + public void testProvidesCompareWithDifferentService() { + Provides p1 = provides("p.S2", "q.S1"); + Provides p2 = provides("p.S1", "q.S1"); + assertNotEquals(p1, p2); + assertTrue(p1.compareTo(p2) == 1); + assertTrue(p2.compareTo(p1) == -1); + } - p1 = provides("p.S", "q.S1"); - p2 = provides("p.S", "q.S2"); + public void testProvidesCompareWithDifferentProviders1() { + Provides p1 = provides("p.S", "q.S2"); + Provides p2 = provides("p.S", "q.S1"); assertNotEquals(p1, p2); + assertTrue(p1.compareTo(p2) == 1); + assertTrue(p2.compareTo(p1) == -1); + } - p1 = provides("p.S", "q.S1"); - p2 = provides("p.S2", "q.S1"); + public void testProvidesCompareWithDifferentProviders2() { + Provides p1 = provides("p.S", List.of("q.S1", "q.S2")); + Provides p2 = provides("p.S", "q.S1"); assertNotEquals(p1, p2); + assertTrue(p1.compareTo(p2) == 1); + assertTrue(p2.compareTo(p1) == -1); } - // contains + // packages + + public void testPackages1() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p", "q")) + .build() + .packages(); + assertTrue(packages.size() == 2); + assertTrue(packages.contains("p")); + assertTrue(packages.contains("q")); + } - public void testContains() { - Set packages = ModuleDescriptor.module("foo") - .contains("p") - .contains("q") + public void testPackages2() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .packages(Set.of("q")) .build() .packages(); assertTrue(packages.size() == 2); @@ -756,56 +816,136 @@ assertTrue(packages.contains("q")); } - public void testContainsWithEmptySet() { - Set packages = ModuleDescriptor.module("foo") - .contains(Collections.emptySet()) + + public void testPackagesWithEmptySet() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Collections.emptySet()) .build() .packages(); assertTrue(packages.size() == 0); } - @Test(expectedExceptions = IllegalStateException.class) - public void testContainsWithDuplicate() { - ModuleDescriptor.module("foo").contains("p").contains("p"); + public void testPackagesDuplicate() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .packages(Set.of("p")) + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); } - @Test(expectedExceptions = IllegalStateException.class) - public void testContainsWithExportedPackage() { - ModuleDescriptor.module("foo").exports("p").contains("p"); + public void testPackagesAndExportsPackage1() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .exports("p") + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); } - @Test(dataProvider = "invalidjavaidentifiers", - expectedExceptions = IllegalArgumentException.class ) - public void testContainsWithBadName(String pn, String ignore) { - ModuleDescriptor.module("foo").contains(pn); + public void testPackagesAndExportsPackage2() { + Set packages = ModuleDescriptor.newModule("foo") + .exports("p") + .packages(Set.of("p")) + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); } + public void testPackagesAndOpensPackage1() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .opens("p") + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); + } - // packages + public void testPackagesAndOpensPackage2() { + Set packages = ModuleDescriptor.newModule("foo") + .opens("p") + .packages(Set.of("p")) + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); + } - public void testPackages() { - Set packages = ModuleDescriptor.module("foo") - .exports("p") - .contains("q") + public void testPackagesAndProvides1() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .provides("q.S", List.of("p.T")) .build() .packages(); - assertTrue(packages.size() == 2); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); + } + + public void testPackagesAndProvides2() { + Set packages = ModuleDescriptor.newModule("foo") + .provides("q.S", List.of("p.T")) + .packages(Set.of("p")) + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); + } + + public void testPackagesAndMainClass1() { + Set packages = ModuleDescriptor.newModule("foo") + .packages(Set.of("p")) + .mainClass("p.Main") + .build() + .packages(); + assertTrue(packages.size() == 1); + assertTrue(packages.contains("p")); + } + + public void testPackagesAndMainClass2() { + Set packages = ModuleDescriptor.newModule("foo") + .mainClass("p.Main") + .packages(Set.of("p")) + .build() + .packages(); + assertTrue(packages.size() == 1); assertTrue(packages.contains("p")); - assertTrue(packages.contains("q")); } + public void testPackagesAndAll() { + Set packages = ModuleDescriptor.newModule("foo") + .exports("p1") + .opens("p2") + .packages(Set.of("p3")) + .provides("q.S", List.of("p4.T")) + .mainClass("p5.Main") + .build() + .packages(); + assertTrue(Objects.equals(packages, Set.of("p1", "p2", "p3", "p4", "p5"))); + } + + @Test(dataProvider = "invalidjavaidentifiers", + expectedExceptions = IllegalArgumentException.class ) + public void testPackagesWithBadName(String pn, String ignore) { + Set pkgs = new HashSet<>(); // allows nulls + pkgs.add(pn); + ModuleDescriptor.newModule("foo").packages(pkgs); + } // name public void testModuleName() { - String mn = ModuleDescriptor.module("foo").build().name(); + String mn = ModuleDescriptor.newModule("foo").build().name(); assertEquals(mn, "foo"); } @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testBadModuleName(String mn, String ignore) { - ModuleDescriptor.module(mn); + ModuleDescriptor.newModule(mn); } @@ -813,7 +953,7 @@ public void testVersion1() { Version v1 = Version.parse("1.0"); - Version v2 = ModuleDescriptor.module("foo") + Version v2 = ModuleDescriptor.newModule("foo") .version(v1) .build() .version() @@ -823,7 +963,7 @@ public void testVersion2() { String vs = "1.0"; - Version v1 = ModuleDescriptor.module("foo") + Version v1 = ModuleDescriptor.newModule("foo") .version(vs) .build() .version() @@ -834,86 +974,183 @@ @Test(expectedExceptions = NullPointerException.class ) public void testNullVersion1() { - ModuleDescriptor.module("foo").version((Version) null); + ModuleDescriptor.newModule("foo").version((Version) null); } @Test(expectedExceptions = IllegalArgumentException.class ) public void testNullVersion2() { - ModuleDescriptor.module("foo").version((String) null); + ModuleDescriptor.newModule("foo").version((String) null); } @Test(expectedExceptions = IllegalArgumentException.class ) public void testEmptyVersion() { - ModuleDescriptor.module("foo").version(""); + ModuleDescriptor.newModule("foo").version(""); } // toNameAndVersion public void testToNameAndVersion() { - ModuleDescriptor md1 = ModuleDescriptor.module("foo").build(); + ModuleDescriptor md1 = ModuleDescriptor.newModule("foo").build(); assertEquals(md1.toNameAndVersion(), "foo"); - ModuleDescriptor md2 = ModuleDescriptor.module("foo").version("1.0").build(); + ModuleDescriptor md2 = ModuleDescriptor.newModule("foo").version("1.0").build(); assertEquals(md2.toNameAndVersion(), "foo@1.0"); } // open modules - public void testOpenModules() { - ModuleDescriptor descriptor = ModuleDescriptor.openModule("m") - .requires("java.base") - .contains("p") + public void testOpenModule() { + ModuleDescriptor descriptor = ModuleDescriptor.newOpenModule("foo") + .requires("bar") + .exports("p") + .provides("p.Service", List.of("q.ServiceImpl")) .build(); + + // modifiers + assertTrue(descriptor.modifiers().contains(ModuleDescriptor.Modifier.OPEN)); assertTrue(descriptor.isOpen()); - assertTrue(descriptor.packages().size() == 1); - assertTrue(descriptor.packages().contains("p")); - assertTrue(descriptor.exports().isEmpty()); + + // requires + assertTrue(descriptor.requires().size() == 2); + Set names = descriptor.requires() + .stream() + .map(Requires::name) + .collect(Collectors.toSet()); + assertEquals(names, Set.of("bar", "java.base")); + + // packages + assertEquals(descriptor.packages(), Set.of("p", "q")); + + // exports + assertTrue(descriptor.exports().size() == 1); + names = descriptor.exports() + .stream() + .map(Exports::source) + .collect(Collectors.toSet()); + assertEquals(names, Set.of("p")); + + // opens + assertTrue(descriptor.opens().isEmpty()); } @Test(expectedExceptions = IllegalStateException.class) - public void testOpensOnWeakModule1() { - ModuleDescriptor.openModule("foo").opens("p"); + public void testOpensOnOpenModule1() { + ModuleDescriptor.newOpenModule("foo").opens("p"); } @Test(expectedExceptions = IllegalStateException.class) - public void testOpensOnWeakModule2() { - ModuleDescriptor.openModule("foo").opens("p", Set.of("bar")); + public void testOpensOnOpenModule2() { + ModuleDescriptor.newOpenModule("foo").opens("p", Set.of("bar")); } public void testIsOpen() { - assertFalse(ModuleDescriptor.module("m").build().isOpen()); - assertFalse(ModuleDescriptor.automaticModule("m").build().isOpen()); - assertTrue(ModuleDescriptor.openModule("m").build().isOpen()); + assertFalse(ModuleDescriptor.newModule("m").build().isOpen()); + assertFalse(ModuleDescriptor.newAutomaticModule("m").build().isOpen()); + assertTrue(ModuleDescriptor.newOpenModule("m").build().isOpen()); } // automatic modules + public void testAutomaticModule() { + ModuleDescriptor descriptor = ModuleDescriptor.newAutomaticModule("foo") + .packages(Set.of("p")) + .provides("p.Service", List.of("q.ServiceImpl")) + .build(); + + // modifiers + assertTrue(descriptor.modifiers().contains(ModuleDescriptor.Modifier.AUTOMATIC)); + assertTrue(descriptor.isAutomatic()); + + // requires + assertTrue(descriptor.requires().size() == 1); + Set names = descriptor.requires() + .stream() + .map(Requires::name) + .collect(Collectors.toSet()); + assertEquals(names, Set.of("java.base")); + + // packages + assertEquals(descriptor.packages(), Set.of("p", "q")); + assertTrue(descriptor.exports().isEmpty()); + assertTrue(descriptor.opens().isEmpty()); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testRequiresOnAutomaticModule() { + ModuleDescriptor.newAutomaticModule("foo").requires("java.base"); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testExportsOnAutomaticModule1() { + ModuleDescriptor.newAutomaticModule("foo").exports("p"); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testExportsOnAutomaticModule2() { + ModuleDescriptor.newAutomaticModule("foo").exports("p", Set.of("bar")); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testOpensOnAutomaticModule1() { + ModuleDescriptor.newAutomaticModule("foo").opens("p"); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testOpensOnAutomaticModule2() { + ModuleDescriptor.newAutomaticModule("foo").opens("p", Set.of("bar")); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testUsesOnAutomaticModule() { + ModuleDescriptor.newAutomaticModule("foo").uses("p.Service"); + } + public void testIsAutomatic() { - ModuleDescriptor descriptor1 = ModuleDescriptor.module("foo").build(); + ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("foo").build(); assertFalse(descriptor1.isAutomatic()); - ModuleDescriptor descriptor2 = ModuleDescriptor.openModule("foo").build(); + ModuleDescriptor descriptor2 = ModuleDescriptor.newOpenModule("foo").build(); assertFalse(descriptor2.isAutomatic()); - ModuleDescriptor descriptor3 = ModuleDescriptor.automaticModule("foo").build(); + ModuleDescriptor descriptor3 = ModuleDescriptor.newAutomaticModule("foo").build(); assertTrue(descriptor3.isAutomatic()); } - // isSynthetic - public void testIsSynthetic() { - assertFalse(Object.class.getModule().getDescriptor().isSynthetic()); - ModuleDescriptor descriptor1 = ModuleDescriptor.module("foo").build(); - assertFalse(descriptor1.isSynthetic()); + // newModule with modifiers + + public void testNewModuleToBuildAutomaticModule() { + Set ms = Set.of(ModuleDescriptor.Modifier.AUTOMATIC); + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo", ms).build(); + assertTrue(descriptor.modifiers().equals(ms)); + assertTrue(descriptor.isAutomatic()); + + ms = Set.of(ModuleDescriptor.Modifier.AUTOMATIC, ModuleDescriptor.Modifier.SYNTHETIC); + descriptor = ModuleDescriptor.newModule("foo", ms).build(); + assertTrue(descriptor.modifiers().equals(ms)); + assertTrue(descriptor.isAutomatic()); + } + + public void testNewModuleToBuildOpenModule() { + Set ms = Set.of(ModuleDescriptor.Modifier.OPEN); + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo", ms).build(); + assertTrue(descriptor.modifiers().equals(ms)); + assertTrue(descriptor.isOpen()); - ModuleDescriptor descriptor2 = ModuleDescriptor.openModule("foo").build(); - assertFalse(descriptor2.isSynthetic()); + ms = Set.of(ModuleDescriptor.Modifier.OPEN, ModuleDescriptor.Modifier.SYNTHETIC); + descriptor = ModuleDescriptor.newModule("foo", ms).build(); + assertTrue(descriptor.modifiers().equals(ms)); + assertTrue(descriptor.isOpen()); + } - ModuleDescriptor descriptor3 = ModuleDescriptor.automaticModule("foo").build(); - assertFalse(descriptor3.isSynthetic()); + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNewModuleToBuildAutomaticAndOpenModule() { + Set ms = Set.of(ModuleDescriptor.Modifier.AUTOMATIC, + ModuleDescriptor.Modifier.OPEN); + ModuleDescriptor.newModule("foo", ms); } @@ -921,14 +1158,19 @@ public void testMainClass() { String mainClass - = ModuleDescriptor.module("foo").mainClass("p.Main").build().mainClass().get(); + = ModuleDescriptor.newModule("foo").mainClass("p.Main").build().mainClass().get(); assertEquals(mainClass, "p.Main"); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMainClassWithSimpleIdentifier() { + ModuleDescriptor.newModule("foo").mainClass("Main"); + } + @Test(dataProvider = "invalidjavaidentifiers", expectedExceptions = IllegalArgumentException.class ) public void testMainClassWithBadName(String mainClass, String ignore) { - Builder builder = ModuleDescriptor.module("foo"); + Builder builder = ModuleDescriptor.newModule("foo"); builder.mainClass(mainClass); } @@ -936,54 +1178,54 @@ // osName public void testOsName() { - String osName = ModuleDescriptor.module("foo").osName("Linux").build().osName().get(); + String osName = ModuleDescriptor.newModule("foo").osName("Linux").build().osName().get(); assertEquals(osName, "Linux"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullOsName() { - ModuleDescriptor.module("foo").osName(null); + ModuleDescriptor.newModule("foo").osName(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testEmptyOsName() { - ModuleDescriptor.module("foo").osName(""); + ModuleDescriptor.newModule("foo").osName(""); } // osArch public void testOsArch() { - String osArch = ModuleDescriptor.module("foo").osName("arm").build().osName().get(); + String osArch = ModuleDescriptor.newModule("foo").osName("arm").build().osName().get(); assertEquals(osArch, "arm"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullOsArch() { - ModuleDescriptor.module("foo").osArch(null); + ModuleDescriptor.newModule("foo").osArch(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testEmptyOsArch() { - ModuleDescriptor.module("foo").osArch(""); + ModuleDescriptor.newModule("foo").osArch(""); } // osVersion public void testOsVersion() { - String osVersion = ModuleDescriptor.module("foo").osName("11.2").build().osName().get(); + String osVersion = ModuleDescriptor.newModule("foo").osName("11.2").build().osName().get(); assertEquals(osVersion, "11.2"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullOsVersion() { - ModuleDescriptor.module("foo").osVersion(null); + ModuleDescriptor.newModule("foo").osVersion(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testEmptyOsVersion() { - ModuleDescriptor.module("foo").osVersion(""); + ModuleDescriptor.newModule("foo").osVersion(""); } // reads @@ -1023,7 +1265,7 @@ * Test ModuleDescriptor with a packager finder */ public void testReadsWithPackageFinder() throws Exception { - ModuleDescriptor descriptor = ModuleDescriptor.module("foo") + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") .requires("java.base") .build(); @@ -1044,7 +1286,7 @@ */ @Test(expectedExceptions = InvalidModuleDescriptorException.class) public void testReadsWithBadPackageFinder() throws Exception { - ModuleDescriptor descriptor = ModuleDescriptor.module("foo") + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") .requires("java.base") .exports("p") .build(); @@ -1077,7 +1319,7 @@ @Test(expectedExceptions = InvalidModuleDescriptorException.class) public void testReadOfJavaBaseWithRequires() { ModuleDescriptor descriptor - = ModuleDescriptor.module("java.base") + = ModuleDescriptor.newModule("java.base") .requires("other") .build(); ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor); @@ -1087,7 +1329,8 @@ // The requires table must have an entry for java.base @Test(expectedExceptions = InvalidModuleDescriptorException.class) public void testReadWithEmptyRequires() { - ModuleDescriptor descriptor = ModuleDescriptor.module("m1").build(); + ModuleDescriptor descriptor = SharedSecrets.getJavaLangModuleAccess() + .newModuleBuilder("m1", false, Set.of()).build(); ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor); ModuleDescriptor.read(bb); } @@ -1095,10 +1338,8 @@ // The requires table must have an entry for java.base @Test(expectedExceptions = InvalidModuleDescriptorException.class) public void testReadWithNoRequiresBase() { - ModuleDescriptor descriptor - = ModuleDescriptor.module("m1") - .requires("m2") - .build(); + ModuleDescriptor descriptor = SharedSecrets.getJavaLangModuleAccess() + .newModuleBuilder("m1", false, Set.of()).requires("m2").build(); ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor); ModuleDescriptor.read(bb); } @@ -1138,22 +1379,50 @@ // equals/hashCode/compareTo/toString public void testEqualsAndHashCode() { - ModuleDescriptor md1 = ModuleDescriptor.module("foo").build(); - ModuleDescriptor md2 = ModuleDescriptor.module("foo").build(); + ModuleDescriptor md1 = ModuleDescriptor.newModule("m").build(); + ModuleDescriptor md2 = ModuleDescriptor.newModule("m").build(); assertEquals(md1, md1); assertEquals(md1.hashCode(), md2.hashCode()); + assertTrue(md1.compareTo(md2) == 0); + assertTrue(md2.compareTo(md1) == 0); } - public void testCompare() { - ModuleDescriptor md1 = ModuleDescriptor.module("foo").build(); - ModuleDescriptor md2 = ModuleDescriptor.module("bar").build(); - int n = "foo".compareTo("bar"); - assertTrue(md1.compareTo(md2) == n); - assertTrue(md2.compareTo(md1) == -n); + @DataProvider(name = "sortedModuleDescriptors") + public Object[][] sortedModuleDescriptors() { + return new Object[][]{ + + { ModuleDescriptor.newModule("m2").build(), + ModuleDescriptor.newModule("m1").build() + }, + + { ModuleDescriptor.newModule("m").version("2").build(), + ModuleDescriptor.newModule("m").version("1").build() + }, + + { ModuleDescriptor.newModule("m").version("1").build(), + ModuleDescriptor.newModule("m").build() + }, + + { ModuleDescriptor.newOpenModule("m").build(), + ModuleDescriptor.newModule("m").build() + }, + + }; + } + + @Test(dataProvider = "sortedModuleDescriptors") + public void testCompare(ModuleDescriptor md1, ModuleDescriptor md2) { + assertNotEquals(md1, md2); + assertTrue(md1.compareTo(md2) == 1); + assertTrue(md2.compareTo(md1) == -1); } public void testToString() { - String s = ModuleDescriptor.module("m1").requires("m2").exports("p1").build().toString(); + String s = ModuleDescriptor.newModule("m1") + .requires("m2") + .exports("p1") + .build() + .toString(); assertTrue(s.contains("m1")); assertTrue(s.contains("m2")); assertTrue(s.contains("p1")); --- old/test/java/lang/module/ModuleFinderTest.java 2017-02-07 13:14:01.809193369 +0000 +++ new/test/java/lang/module/ModuleFinderTest.java 2017-02-07 13:14:01.639181694 +0000 @@ -471,41 +471,33 @@ * Test ModuleFinder.of with a file path to a directory containing a file * that will not be recognized as a module. */ - public void testOfWithUnrecognizedEntryInDirectory() throws Exception { + public void testOfWithUnrecognizedEntryInDirectory1() throws Exception { Path dir = Files.createTempDirectory(USER_DIR, "mods"); Files.createTempFile(dir, "m", ".junk"); ModuleFinder finder = ModuleFinder.of(dir); - try { - finder.find("java.rhubarb"); - assertTrue(false); - } catch (FindException e) { - // expected - } + assertFalse(finder.find("java.rhubarb").isPresent()); finder = ModuleFinder.of(dir); - try { - finder.findAll(); - assertTrue(false); - } catch (FindException e) { - // expected - } + assertTrue(finder.findAll().isEmpty()); } /** * Test ModuleFinder.of with a file path to a directory containing a file - * starting with ".", the file should be ignored. + * that will not be recognized as a module. */ - public void testOfWithHiddenEntryInDirectory() throws Exception { + public void testOfWithUnrecognizedEntryInDirectory2() throws Exception { Path dir = Files.createTempDirectory(USER_DIR, "mods"); - Files.createTempFile(dir, ".marker", ""); + createModularJar(dir.resolve("m1.jar"), "m1"); + Files.createTempFile(dir, "m2", ".junk"); ModuleFinder finder = ModuleFinder.of(dir); - assertFalse(finder.find("java.rhubarb").isPresent()); + assertTrue(finder.find("m1").isPresent()); + assertFalse(finder.find("m2").isPresent()); finder = ModuleFinder.of(dir); - assertTrue(finder.findAll().isEmpty()); + assertTrue(finder.findAll().size() == 1); } @@ -748,7 +740,7 @@ vs = mid.substring(i+1); } ModuleDescriptor.Builder builder - = ModuleDescriptor.module(mn).requires("java.base"); + = ModuleDescriptor.newModule(mn).requires("java.base"); if (vs != null) builder.version(vs); return builder.build(); --- old/test/java/lang/module/ModuleNamesTest.java 2017-02-07 13:14:02.296226815 +0000 +++ new/test/java/lang/module/ModuleNamesTest.java 2017-02-07 13:14:02.127215208 +0000 @@ -235,7 +235,7 @@ */ private Builder newBuilder(String mn) { return SharedSecrets.getJavaLangModuleAccess() - .newModuleBuilder(mn, false, false, false); + .newModuleBuilder(mn, false, Set.of()); } /** --- old/test/java/lang/module/ModuleReader/ModuleReaderTest.java 2017-02-07 13:14:02.767259161 +0000 +++ new/test/java/lang/module/ModuleReader/ModuleReaderTest.java 2017-02-07 13:14:02.600247692 +0000 @@ -79,14 +79,52 @@ "java/lang/Object.class" }; + // resource names that should not be found in the base module + private static final String[] BAD_BASE_RESOURCES = { + "NotFound", + "java", + "/java", + "//java", + "java/", + "java/lang", + "/java/lang", + "//java/lang", + "java/lang/", + "java//lang", + "/java/lang/Object.class", + "//java/lang/Object.class", + "java/lang/Object.class/", + "java//lang//Object.class", + "./java/lang/Object.class", + "java/./lang/Object.class", + "java/lang/./Object.class", + "../java/lang/Object.class", + "java/../lang/Object.class", + "java/lang/../Object.class", + }; + // resources in test module (can't use module-info.class as a test // resource as it will be modified by the jmod tool) private static final String[] TEST_RESOURCES = { "p/Main.class" }; - // a resource that is not in the base or test module - private static final String NOT_A_RESOURCE = "NotAResource"; + // resource names that should not be found in the test module + private static final String[] BAD_TEST_RESOURCES = { + "NotFound", + "p", + "/p", + "//p", + "p/", + "/p/Main.class", + "//p/Main.class", + "p/Main.class/", + "p//Main.class", + "./p/Main.class", + "p/./Main.class", + "../p/Main.class", + "p/../p/Main.class" + }; @BeforeTest @@ -126,10 +164,11 @@ } // test "not found" - assertFalse(reader.find(NOT_A_RESOURCE).isPresent()); - assertFalse(reader.open(NOT_A_RESOURCE).isPresent()); - assertFalse(reader.read(NOT_A_RESOURCE).isPresent()); - + for (String name : BAD_BASE_RESOURCES) { + assertFalse(reader.find(name).isPresent()); + assertFalse(reader.open(name).isPresent()); + assertFalse(reader.read(name).isPresent()); + } // test nulls try { @@ -236,9 +275,11 @@ } // test "not found" - assertFalse(reader.find(NOT_A_RESOURCE).isPresent()); - assertFalse(reader.open(NOT_A_RESOURCE).isPresent()); - assertFalse(reader.read(NOT_A_RESOURCE).isPresent()); + for (String name : BAD_TEST_RESOURCES) { + assertFalse(reader.find(name).isPresent()); + assertFalse(reader.open(name).isPresent()); + assertFalse(reader.read(name).isPresent()); + } // test nulls try { --- old/test/java/lang/module/ModuleReferenceTest.java 2017-02-07 13:14:03.238291508 +0000 +++ new/test/java/lang/module/ModuleReferenceTest.java 2017-02-07 13:14:03.071280039 +0000 @@ -31,6 +31,7 @@ import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.net.URI; +import java.util.Set; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -49,10 +50,10 @@ public void testBasic() throws Exception { ModuleDescriptor descriptor - = ModuleDescriptor.module("m") + = ModuleDescriptor.newModule("m") .exports("p") .exports("q") - .contains("p.internal") + .packages(Set.of("p.internal")) .build(); URI uri = URI.create("module:/m"); @@ -71,7 +72,7 @@ public void testNullLocation() { ModuleDescriptor descriptor - = ModuleDescriptor.module("m") + = ModuleDescriptor.newModule("m") .exports("p") .build(); ModuleReference mref = newModuleReference(descriptor, null); --- old/test/java/lang/module/MultiReleaseJarTest.java 2017-02-07 13:14:03.701323306 +0000 +++ new/test/java/lang/module/MultiReleaseJarTest.java 2017-02-07 13:14:03.536311974 +0000 @@ -65,7 +65,7 @@ private static final String MODULE_INFO = "module-info.class"; - private static final int RELEASE = Runtime.version().major(); + private static final int VERSION = Runtime.version().major(); // are multi-release JARs enabled? private static final boolean MULTI_RELEASE; @@ -80,7 +80,7 @@ public void testBasic() throws Exception { String name = "m1"; - ModuleDescriptor descriptor = ModuleDescriptor.module(name) + ModuleDescriptor descriptor = ModuleDescriptor.newModule(name) .requires("java.base") .build(); @@ -88,8 +88,8 @@ .moduleInfo("module-info.class", descriptor) .resource("p/Main.class") .resource("p/Helper.class") - .resource("META-INF/versions/9/p/Helper.class") - .resource("META-INF/versions/9/p/internal/Helper9.class") + .resource("META-INF/versions/" + VERSION + "/p/Helper.class") + .resource("META-INF/versions/" + VERSION + "/p/internal/Helper.class") .build(); // find the module @@ -117,12 +117,12 @@ public void testModuleInfoInVersionedSection() throws Exception { String name = "m1"; - ModuleDescriptor descriptor1 = ModuleDescriptor.module(name) + ModuleDescriptor descriptor1 = ModuleDescriptor.newModule(name) .requires("java.base") .build(); // module descriptor for versioned section - ModuleDescriptor descriptor2 = ModuleDescriptor.module(name) + ModuleDescriptor descriptor2 = ModuleDescriptor.newModule(name) .requires("java.base") .requires("jdk.unsupported") .build(); @@ -131,9 +131,9 @@ .moduleInfo(MODULE_INFO, descriptor1) .resource("p/Main.class") .resource("p/Helper.class") - .moduleInfo("META-INF/versions/9/" + MODULE_INFO, descriptor2) - .resource("META-INF/versions/9/p/Helper.class") - .resource("META-INF/versions/9/p/internal/Helper9.class") + .moduleInfo("META-INF/versions/" + VERSION + "/" + MODULE_INFO, descriptor2) + .resource("META-INF/versions/" + VERSION + "/p/Helper.class") + .resource("META-INF/versions/" + VERSION + "/p/internal/Helper.class") .build(); // find the module @@ -161,8 +161,8 @@ Path jar = new JarBuilder(name) .resource("p/Main.class") .resource("p/Helper.class") - .resource("META-INF/versions/9/p/Helper.class") - .resource("META-INF/versions/9/p/internal/Helper9.class") + .resource("META-INF/versions/" + VERSION + "/p/Helper.class") + .resource("META-INF/versions/" + VERSION + "/p/internal/Helper.class") .build(); // find the module @@ -188,19 +188,19 @@ public void testModuleReader() throws Exception { String name = "m1"; - ModuleDescriptor descriptor1 = ModuleDescriptor.module(name) + ModuleDescriptor descriptor1 = ModuleDescriptor.newModule(name) .requires("java.base") .build(); // module descriptor for versioned section - ModuleDescriptor descriptor2 = ModuleDescriptor.module(name) + ModuleDescriptor descriptor2 = ModuleDescriptor.newModule(name) .requires("java.base") .requires("jdk.unsupported") .build(); Path jar = new JarBuilder(name) .moduleInfo(MODULE_INFO, descriptor1) - .moduleInfo("META-INF/versions/9/" + MODULE_INFO, descriptor2) + .moduleInfo("META-INF/versions/" + VERSION + "/" + MODULE_INFO, descriptor2) .build(); // find the module @@ -243,7 +243,7 @@ String expectedTail = "!/"; if (MULTI_RELEASE) - expectedTail += "META-INF/versions/" + RELEASE + "/"; + expectedTail += "META-INF/versions/" + VERSION + "/"; expectedTail += MODULE_INFO; assertTrue(uri.toString().endsWith(expectedTail)); --- old/test/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java 2017-02-07 13:14:04.174355790 +0000 +++ new/test/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java 2017-02-07 13:14:04.006344252 +0000 @@ -30,7 +30,6 @@ * @summary Test java.lang.reflect.AccessibleObject with modules */ -import java.lang.module.ModuleDescriptor; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; --- old/test/java/lang/reflect/Layer/BasicLayerTest.java 2017-02-07 13:14:04.638387656 +0000 +++ new/test/java/lang/reflect/Layer/BasicLayerTest.java 2017-02-07 13:14:04.473376324 +0000 @@ -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 @@ -24,6 +24,7 @@ /** * @test * @library /lib/testlibrary + * @modules java.base/jdk.internal.misc * @build BasicLayerTest ModuleUtils * @compile layertest/Test.java * @run testng BasicLayerTest @@ -43,6 +44,7 @@ import java.util.Set; import java.util.stream.Collectors; +import jdk.internal.misc.SharedSecrets; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -50,6 +52,15 @@ public class BasicLayerTest { /** + * Creates a "non-strict" builder for building a module. This allows the + * test the create ModuleDescriptor objects that do not require java.base. + */ + private static ModuleDescriptor.Builder newBuilder(String mn) { + return SharedSecrets.getJavaLangModuleAccess() + .newModuleBuilder(mn, false, Set.of()); + } + + /** * Exercise Layer.empty() */ public void testEmpty() { @@ -109,25 +120,22 @@ * Exercise Layer defineModules, created with empty layer as parent */ public void testLayerOnEmpty() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .exports("p1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("m3") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); // map each module to its own class loader for this test ClassLoader loader1 = new ClassLoader() { }; @@ -191,15 +199,13 @@ * Exercise Layer defineModules, created with boot layer as parent */ public void testLayerOnBoot() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .requires("java.base") .exports("p1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires("java.base") .build(); @@ -207,7 +213,7 @@ = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration parent = Layer.boot().configuration(); - Configuration cf = resolveRequires(parent, finder, "m1"); + Configuration cf = resolve(parent, finder, "m1"); ClassLoader loader = new ClassLoader() { }; @@ -256,21 +262,19 @@ * have the same module-private package. */ public void testPackageContainedInSelfAndOther() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") - .contains("p") + .packages(Set.of("p")) .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") - .contains("p") + ModuleDescriptor descriptor2 = newBuilder("m2") + .packages(Set.of("p")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); assertTrue(cf.modules().size() == 2); // one loader per module, should be okay @@ -292,22 +296,18 @@ public void testSameExportInPartitionedGraph() { // m1 reads m2, m2 exports p to m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .exports("p", Set.of("m1")) .build(); // m3 reads m4, m4 exports p to m3 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m4") .build(); - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .exports("p", Set.of("m3")) .build(); @@ -317,7 +317,7 @@ descriptor3, descriptor4); - Configuration cf = resolveRequires(finder, "m1", "m3"); + Configuration cf = resolve(finder, "m1", "m3"); assertTrue(cf.modules().size() == 4); // one loader per module @@ -353,16 +353,15 @@ ModuleDescriptor base = Object.class.getModule().getDescriptor(); assertTrue(base.packages().contains("sun.launcher")); - ModuleDescriptor descriptor - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor = newBuilder("m1") .requires("java.base") - .contains("sun.launcher") + .packages(Set.of("sun.launcher")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor); Configuration parent = Layer.boot().configuration(); - Configuration cf = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("m1")); assertTrue(cf.modules().size() == 1); ClassLoader loader = new ClassLoader() { }; @@ -382,18 +381,16 @@ // cf1: m1 and m2, m2 requires transitive m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); ClassLoader cl1 = new ClassLoader() { }; Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1); @@ -401,14 +398,13 @@ // cf2: m3, m3 requires m2 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); ClassLoader cl2 = new ClassLoader() { }; Layer layer2 = layer1.defineModules(cf2, mn -> cl2); @@ -456,13 +452,11 @@ // cf1: m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); ClassLoader cl1 = new ClassLoader() { }; Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1); @@ -470,19 +464,17 @@ // cf2: m2, m3: m2 requires transitive m1, m3 requires m2 - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2, descriptor3); - Configuration cf2 = resolveRequires(cf1, finder2, "m3"); + Configuration cf2 = resolve(cf1, finder2, "m3"); ClassLoader cl2 = new ClassLoader() { }; Layer layer2 = layer1.defineModules(cf2, mn -> cl2); @@ -527,13 +519,11 @@ // cf1: m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .build(); + ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); - Configuration cf1 = resolveRequires(finder1, "m1"); + Configuration cf1 = resolve(finder1, "m1"); ClassLoader cl1 = new ClassLoader() { }; Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1); @@ -541,14 +531,13 @@ // cf2: m2 requires transitive m1 - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2); - Configuration cf2 = resolveRequires(cf1, finder2, "m2"); + Configuration cf2 = resolve(cf1, finder2, "m2"); ClassLoader cl2 = new ClassLoader() { }; Layer layer2 = layer1.defineModules(cf2, mn -> cl2); @@ -556,14 +545,13 @@ // cf3: m3 requires m2 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires("m2") .build(); ModuleFinder finder3 = ModuleUtils.finderOf(descriptor3); - Configuration cf3 = resolveRequires(cf2, finder3, "m3"); + Configuration cf3 = resolve(cf2, finder3, "m3"); ClassLoader cl3 = new ClassLoader() { }; Layer layer3 = layer2.defineModules(cf3, mn -> cl3); @@ -610,18 +598,16 @@ // cf1: m1, m2 requires transitive m1 - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); - ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + ModuleDescriptor descriptor2 = newBuilder("m2") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1") .build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); - Configuration cf1 = resolveRequires(finder1, "m2"); + Configuration cf1 = resolve(finder1, "m2"); ClassLoader cl1 = new ClassLoader() { }; Layer layer1 = Layer.empty().defineModules(cf1, mn -> cl1); @@ -629,20 +615,18 @@ // cf2: m3 requires transitive m2, m4 requires m3 - ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3") + ModuleDescriptor descriptor3 = newBuilder("m3") .requires(Set.of(Requires.Modifier.TRANSITIVE), "m2") .build(); - ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4") + ModuleDescriptor descriptor4 = newBuilder("m4") .requires("m3") .build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); - Configuration cf2 = resolveRequires(cf1, finder2, "m3", "m4"); + Configuration cf2 = resolve(cf1, finder2, "m3", "m4"); ClassLoader cl2 = new ClassLoader() { }; Layer layer2 = layer1.defineModules(cf2, mn -> cl2); @@ -693,8 +677,7 @@ @Test(expectedExceptions = { LayerInstantiationException.class }) public void testModuleAlreadyDefinedToLoader() { - ModuleDescriptor md - = ModuleDescriptor.module("m") + ModuleDescriptor md = newBuilder("m") .requires("java.base") .build(); @@ -702,7 +685,7 @@ Configuration parent = Layer.boot().configuration(); - Configuration cf = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m")); + Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("m")); ClassLoader loader = new ClassLoader() { }; @@ -722,15 +705,13 @@ @Test(expectedExceptions = { LayerInstantiationException.class }) public void testPackageAlreadyInNamedModule() { - ModuleDescriptor md1 - = ModuleDescriptor.module("m1") - .contains("p") + ModuleDescriptor md1 = newBuilder("m1") + .packages(Set.of("p")) .requires("java.base") .build(); - ModuleDescriptor md2 - = ModuleDescriptor.module("m2") - .contains("p") + ModuleDescriptor md2 = newBuilder("m2") + .packages(Set.of("p")) .requires("java.base") .build(); @@ -742,13 +723,13 @@ Configuration parent = Layer.boot().configuration(); - Configuration cf1 = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + Configuration cf1 = parent.resolve(finder, ModuleFinder.of(), Set.of("m1")); Layer layer1 = Layer.boot().defineModules(cf1, mn -> loader); // attempt to define m2 containing package p to class loader - Configuration cf2 = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m2")); + Configuration cf2 = parent.resolve(finder, ModuleFinder.of(), Set.of("m2")); // should throw exception because p already in m1 Layer layer2 = Layer.boot().defineModules(cf2, mn -> loader); @@ -767,16 +748,15 @@ Class c = layertest.Test.class; assertFalse(c.getModule().isNamed()); // in unnamed module - ModuleDescriptor md - = ModuleDescriptor.module("m") - .contains(c.getPackageName()) + ModuleDescriptor md = newBuilder("m") + .packages(Set.of(c.getPackageName())) .requires("java.base") .build(); ModuleFinder finder = ModuleUtils.finderOf(md); Configuration parent = Layer.boot().configuration(); - Configuration cf = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m")); + Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("m")); Layer.boot().defineModules(cf, mn -> c.getClassLoader()); } @@ -786,8 +766,7 @@ * Attempt to create a Layer with a module named "java.base". */ public void testLayerWithJavaBase() { - ModuleDescriptor descriptor - = ModuleDescriptor.module("java.base") + ModuleDescriptor descriptor = newBuilder("java.base") .exports("java.lang") .build(); @@ -795,7 +774,7 @@ Configuration cf = Layer.boot() .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of("java.base")); + .resolve(finder, ModuleFinder.of(), Set.of("java.base")); assertTrue(cf.modules().size() == 1); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -824,16 +803,15 @@ */ @Test(enabled = false) public void testLayerWithJavaPackage() { - ModuleDescriptor descriptor - = ModuleDescriptor.module("foo") - .contains("java.foo") + ModuleDescriptor descriptor = newBuilder("foo") + .packages(Set.of("java.foo")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor); Configuration cf = Layer.boot() .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of("foo")); + .resolve(finder, ModuleFinder.of(), Set.of("foo")); assertTrue(cf.modules().size() == 1); ClassLoader pcl = ClassLoader.getPlatformClassLoader(); @@ -870,15 +848,14 @@ */ @Test(expectedExceptions = { LayerInstantiationException.class }) public void testLayerWithBootLoader() { - ModuleDescriptor descriptor - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor = newBuilder("m1") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor); Configuration cf = Layer.boot() .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + .resolve(finder, ModuleFinder.of(), Set.of("m1")); assertTrue(cf.modules().size() == 1); Layer.boot().defineModules(cf, mn -> null ); @@ -891,15 +868,14 @@ @Test(expectedExceptions = { IllegalArgumentException.class }) public void testIncorrectParent1() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .requires("java.base") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); Configuration parent = Layer.boot().configuration(); - Configuration cf = parent.resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("m1")); ClassLoader loader = new ClassLoader() { }; Layer.empty().defineModules(cf, mn -> loader); @@ -912,13 +888,12 @@ @Test(expectedExceptions = { IllegalArgumentException.class }) public void testIncorrectParent2() { - ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") + ModuleDescriptor descriptor1 = newBuilder("m1") .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - Configuration cf = resolveRequires(finder, "m1"); + Configuration cf = resolve(finder, "m1"); ClassLoader loader = new ClassLoader() { }; Layer.boot().defineModules(cf, mn -> loader); @@ -935,7 +910,7 @@ @Test(expectedExceptions = { NullPointerException.class }) public void testCreateWithNull2() { - Configuration cf = resolveRequires(Layer.boot().configuration(), ModuleFinder.of()); + Configuration cf = resolve(Layer.boot().configuration(), ModuleFinder.of()); Layer.boot().defineModules(cf, null); } @@ -975,14 +950,14 @@ * Resolve the given modules, by name, and returns the resulting * Configuration. */ - private static Configuration resolveRequires(Configuration cf, - ModuleFinder finder, - String... roots) { - return cf.resolveRequires(finder, ModuleFinder.of(), Set.of(roots)); + private static Configuration resolve(Configuration cf, + ModuleFinder finder, + String... roots) { + return cf.resolve(finder, ModuleFinder.of(), Set.of(roots)); } - private static Configuration resolveRequires(ModuleFinder finder, - String... roots) { - return resolveRequires(Configuration.empty(), finder, roots); + private static Configuration resolve(ModuleFinder finder, + String... roots) { + return resolve(Configuration.empty(), finder, roots); } } --- old/test/java/lang/reflect/Layer/LayerAndLoadersTest.java 2017-02-07 13:14:05.149422750 +0000 +++ new/test/java/lang/reflect/Layer/LayerAndLoadersTest.java 2017-02-07 13:14:04.981411212 +0000 @@ -81,7 +81,7 @@ */ public void testWithOneLoader() throws Exception { - Configuration cf = resolveRequires("m1"); + Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -110,7 +110,7 @@ */ public void testWithManyLoaders() throws Exception { - Configuration cf = resolveRequires("m1"); + Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -145,7 +145,7 @@ */ public void testServicesWithOneLoader() throws Exception { - Configuration cf = resolveRequiresAndUses("m1"); + Configuration cf = resolveAndBind("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -186,7 +186,7 @@ */ public void testServicesWithManyLoaders() throws Exception { - Configuration cf = resolveRequiresAndUses("m1"); + Configuration cf = resolveAndBind("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -233,7 +233,7 @@ */ public void testDelegationToParent() throws Exception { - Configuration cf = resolveRequires("m1"); + Configuration cf = resolve("m1"); ClassLoader parent = this.getClass().getClassLoader(); String cn = this.getClass().getName(); @@ -267,16 +267,16 @@ public void testOverlappingPackages() { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1").exports("p").build(); + = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2").exports("p").build(); + = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf = Layer.boot() .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of("m1", "m2")); + .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2")); // cannot define both module m1 and m2 to the same class loader try { @@ -301,29 +301,29 @@ public void testSplitDelegation() { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1").exports("p").build(); + = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2").exports("p").build(); + = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf1 = Layer.boot() .configuration() - .resolveRequires(finder1, ModuleFinder.of(), Set.of("m1", "m2")); + .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2")); Layer layer1 = Layer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2"); ModuleDescriptor descriptor3 - = ModuleDescriptor.module("m3").requires("m1").build(); + = ModuleDescriptor.newModule("m3").requires("m1").build(); ModuleDescriptor descriptor4 - = ModuleDescriptor.module("m4").requires("m2").build(); + = ModuleDescriptor.newModule("m4").requires("m2").build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); - Configuration cf2 = cf1.resolveRequires(finder2, ModuleFinder.of(), + Configuration cf2 = cf1.resolve(finder2, ModuleFinder.of(), Set.of("m3", "m4")); // package p cannot be supplied by two class loaders @@ -349,13 +349,13 @@ */ public void testOverriding1() throws Exception { - Configuration cf1 = resolveRequires("m1"); + Configuration cf1 = resolve("m1"); Layer layer1 = Layer.boot().defineModulesWithOneLoader(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = ModuleFinder.of(MODS_DIR); - Configuration cf2 = cf1.resolveRequires(finder, ModuleFinder.of(), + Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); Layer layer2 = layer1.defineModulesWithOneLoader(cf2, null); @@ -398,13 +398,13 @@ */ public void testOverriding2() throws Exception { - Configuration cf1 = resolveRequires("m1"); + Configuration cf1 = resolve("m1"); Layer layer1 = Layer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = ModuleFinder.of(MODS_DIR); - Configuration cf2 = cf1.resolveRequires(finder, ModuleFinder.of(), + Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); Layer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); @@ -492,14 +492,14 @@ */ public void testOverriding3() throws Exception { - Configuration cf1 = resolveRequires("m1"); + Configuration cf1 = resolve("m1"); Layer layer1 = Layer.boot().defineModulesWithOneLoader(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = finderFor("m1", "m3"); - Configuration cf2 = cf1.resolveRequires(finder, ModuleFinder.of(), + Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); Layer layer2 = layer1.defineModulesWithOneLoader(cf2, null); @@ -529,14 +529,14 @@ */ public void testOverriding4() throws Exception { - Configuration cf1 = resolveRequires("m1"); + Configuration cf1 = resolve("m1"); Layer layer1 = Layer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = finderFor("m1", "m3"); - Configuration cf2 = cf1.resolveRequires(finder, ModuleFinder.of(), + Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); Layer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); @@ -577,7 +577,7 @@ * Layer.defineModulesWithOneLoader. */ public void testResourcesOneLoader() throws Exception { - Configuration cf = resolveRequires("m1"); + Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = Layer.boot().defineModulesWithOneLoader(cf, scl); ClassLoader loader = layer.findLoader("m1"); @@ -589,7 +589,7 @@ * Layer.defineModulesWithOneLoader. */ public void testResourcesManyLoaders() throws Exception { - Configuration cf = resolveRequires("m1"); + Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = Layer.boot().defineModulesWithManyLoaders(cf, scl); ClassLoader loader = layer.findLoader("m1"); @@ -621,22 +621,22 @@ * Resolve the given modules, by name, and returns the resulting * Configuration. */ - private static Configuration resolveRequires(String... roots) { + private static Configuration resolve(String... roots) { ModuleFinder finder = ModuleFinder.of(MODS_DIR); return Layer.boot() .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of(roots)); + .resolve(finder, ModuleFinder.of(), Set.of(roots)); } /** * Resolve the given modules, by name, and returns the resulting * Configuration. */ - private static Configuration resolveRequiresAndUses(String... roots) { + private static Configuration resolveAndBind(String... roots) { ModuleFinder finder = ModuleFinder.of(MODS_DIR); return Layer.boot() .configuration() - .resolveRequiresAndUses(finder, ModuleFinder.of(), Set.of(roots)); + .resolveAndBind(finder, ModuleFinder.of(), Set.of(roots)); } --- old/test/java/lang/reflect/Layer/LayerControllerTest.java 2017-02-07 13:14:05.631455852 +0000 +++ new/test/java/lang/reflect/Layer/LayerControllerTest.java 2017-02-07 13:14:05.466444520 +0000 @@ -50,22 +50,22 @@ */ private Layer.Controller createTestLayer() { ModuleDescriptor descriptor1 - = ModuleDescriptor.module("m1") - .contains("p1") + = ModuleDescriptor.newModule("m1") + .packages(Set.of("p1")) .requires("java.base") .build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.module("m2") + = ModuleDescriptor.newModule("m2") .requires("java.base") - .contains("p2") + .packages(Set.of("p2")) .build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); Layer bootLayer = Layer.boot(); Configuration cf = bootLayer.configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of("m1", "m2")); + .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2")); ClassLoader scl = ClassLoader.getSystemClassLoader(); @@ -193,4 +193,4 @@ assertTrue(false); } catch (NullPointerException expected) { } } -} \ No newline at end of file +} --- old/test/java/lang/reflect/Module/AnnotationsTest.java 2017-02-07 13:14:06.098487924 +0000 +++ new/test/java/lang/reflect/Module/AnnotationsTest.java 2017-02-07 13:14:05.930476386 +0000 @@ -144,7 +144,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer.configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of(name)); + .resolve(finder, ModuleFinder.of(), Set.of(name)); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl); --- old/test/java/lang/reflect/Module/BasicModuleTest.java 2017-02-07 13:14:06.569520271 +0000 +++ new/test/java/lang/reflect/Module/BasicModuleTest.java 2017-02-07 13:14:06.400508664 +0000 @@ -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 @@ -25,8 +25,10 @@ import java.lang.module.ResolvedModule; import java.lang.reflect.Layer; import java.lang.reflect.Module; +import java.nio.file.spi.FileSystemProvider; // service type in java.base import java.util.function.Predicate; import java.util.stream.Stream; +import javax.print.PrintServiceLookup; // service type in java.desktop import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -170,6 +172,14 @@ // canRead assertTrue(base.canRead(base)); + assertFalse(base.canRead(thisModule)); + + // addReads + try { + base.addReads(thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(base.canRead(thisModule)); // isExported assertTrue(base.isExported("java.lang")); @@ -182,6 +192,18 @@ assertFalse(base.isExported("java.wombat", thisModule)); assertFalse(base.isExported("java.wombat", base)); + // addExports + try { + base.addExports("java.lang", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + try { + base.addExports("jdk.internal.misc", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(base.isExported("jdk.internal.misc")); + assertFalse(base.isExported("jdk.internal.misc", thisModule)); + // isOpen assertFalse(base.isOpen("java.lang")); assertFalse(base.isOpen("java.lang", thisModule)); @@ -192,6 +214,29 @@ assertFalse(base.isOpen("java.wombat")); assertFalse(base.isOpen("java.wombat", thisModule)); assertFalse(base.isOpen("java.wombat", base)); + + // addOpens + try { + base.addOpens("jdk.internal.misc", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(base.isOpen("jdk.internal.misc")); + assertFalse(base.isOpen("jdk.internal.misc", thisModule)); + + // canUse + assertTrue(base.canUse(FileSystemProvider.class)); + assertFalse(base.canUse(Thread.class)); + + // addUses + try { + base.addUses(FileSystemProvider.class); + assertTrue(false); + } catch (IllegalCallerException expected) { } + try { + base.addUses(Thread.class); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(base.canUse(Thread.class)); } @@ -226,26 +271,68 @@ assertTrue(desktop.canRead(base)); assertTrue(desktop.canRead(xml)); + // addReads + try { + desktop.addReads(thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(desktop.canRead(thisModule)); + // isExported assertTrue(desktop.isExported("java.awt")); assertTrue(desktop.isExported("java.awt", thisModule)); + assertFalse(desktop.isExported("sun.awt")); + assertFalse(desktop.isExported("sun.awt", thisModule)); + assertTrue(desktop.isExported("sun.awt", desktop)); assertFalse(desktop.isExported("java.wombat")); assertFalse(desktop.isExported("java.wombat", thisModule)); - } + assertFalse(desktop.isExported("java.wombat", base)); + // addExports + try { + desktop.addExports("java.awt", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + try { + desktop.addExports("sun.awt", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(desktop.isExported("sun.awt")); + assertFalse(desktop.isExported("sun.awt", thisModule)); - @Test(expectedExceptions = { NullPointerException.class }) - public void testIsExportedNull() { - Module thisModule = this.getClass().getModule(); - thisModule.isExported(null, thisModule); - } - - - @Test(expectedExceptions = { NullPointerException.class }) - public void testIsExportedToNull() { - Module thisModule = this.getClass().getModule(); - thisModule.isExported("", null); + // isOpen + assertFalse(desktop.isOpen("java.awt")); + assertFalse(desktop.isOpen("java.awt", thisModule)); + assertTrue(desktop.isOpen("java.awt", desktop)); + assertFalse(desktop.isOpen("sun.awt")); + assertFalse(desktop.isOpen("sun.awt", thisModule)); + assertTrue(desktop.isOpen("sun.awt", desktop)); + assertFalse(desktop.isOpen("java.wombat")); + assertFalse(desktop.isOpen("java.wombat", thisModule)); + assertFalse(desktop.isOpen("java.wombat", desktop)); + + // addOpens + try { + base.addOpens("sun.awt", thisModule); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(desktop.isOpen("sun.awt")); + assertFalse(desktop.isOpen("sun.awt", thisModule)); + + // canUse + assertTrue(base.canUse(FileSystemProvider.class)); + assertFalse(base.canUse(Thread.class)); + + // addUses + try { + desktop.addUses(PrintServiceLookup.class); + assertTrue(false); + } catch (IllegalCallerException expected) { } + try { + desktop.addUses(Thread.class); + assertTrue(false); + } catch (IllegalCallerException expected) { } + assertFalse(desktop.canUse(Thread.class)); } - } --- old/test/java/lang/reflect/Module/WithSecurityManager.java 2017-02-07 13:14:07.042552755 +0000 +++ new/test/java/lang/reflect/Module/WithSecurityManager.java 2017-02-07 13:14:06.875541286 +0000 @@ -126,7 +126,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer .configuration() - .resolveRequires(finder, ModuleFinder.of(), Set.of(ANOTHER_MODULE)); + .resolve(finder, ModuleFinder.of(), Set.of(ANOTHER_MODULE)); Layer layer = bootLayer.defineModulesWithOneLoader(cf, null); Optional om = layer.findModule(mn); --- old/test/java/lang/reflect/Proxy/ProxyClassAccessTest.java 2017-02-07 13:14:07.502584346 +0000 +++ new/test/java/lang/reflect/Proxy/ProxyClassAccessTest.java 2017-02-07 13:14:07.335572877 +0000 @@ -91,7 +91,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer .configuration() - .resolveRequiresAndUses(ModuleFinder.of(), finder, modules); + .resolveAndBind(ModuleFinder.of(), finder, modules); ClassLoader parentLoader = this.getClass().getClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, parentLoader); --- old/test/java/lang/reflect/Proxy/ProxyLayerTest.java 2017-02-07 13:14:07.963616006 +0000 +++ new/test/java/lang/reflect/Proxy/ProxyLayerTest.java 2017-02-07 13:14:07.795604469 +0000 @@ -79,7 +79,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer .configuration() - .resolveRequiresAndUses(ModuleFinder.of(), finder, Arrays.asList(modules)); + .resolveAndBind(ModuleFinder.of(), finder, Arrays.asList(modules)); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl); @@ -113,7 +113,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer .configuration() - .resolveRequiresAndUses(ModuleFinder.of(), finder, Arrays.asList(modules)); + .resolveAndBind(ModuleFinder.of(), finder, Arrays.asList(modules)); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl); @@ -143,7 +143,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer .configuration() - .resolveRequiresAndUses(ModuleFinder.of(), finder, Arrays.asList(modules)); + .resolveAndBind(ModuleFinder.of(), finder, Arrays.asList(modules)); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl); --- old/test/java/security/modules/ModularTest.java 2017-02-07 13:14:08.426647804 +0000 +++ new/test/java/security/modules/ModularTest.java 2017-02-07 13:14:08.262636540 +0000 @@ -164,9 +164,9 @@ final Builder builder; if (moduleType == MODULE_TYPE.EXPLICIT) { System.out.format(" %nGenerating ModuleDescriptor object"); - builder = ModuleDescriptor.module(moduleName).exports(pkg); + builder = ModuleDescriptor.newModule(moduleName).exports(pkg); if (isService && serviceInterface != null && serviceImpl != null) { - builder.provides(serviceInterface, serviceImpl); + builder.provides(serviceInterface, List.of(serviceImpl)); } else { if (serviceInterface != null) { builder.uses(serviceInterface); --- old/test/java/util/ServiceLoader/modules/BadProvidersTest.java 2017-02-07 13:14:08.889679601 +0000 +++ new/test/java/util/ServiceLoader/modules/BadProvidersTest.java 2017-02-07 13:14:08.723668200 +0000 @@ -90,7 +90,7 @@ Layer bootLayer = Layer.boot(); Configuration cf = bootLayer.configuration() - .resolveRequiresAndUses(finder, ModuleFinder.of(), Set.of(moduleName)); + .resolveAndBind(finder, ModuleFinder.of(), Set.of(moduleName)); ClassLoader scl = ClassLoader.getSystemClassLoader(); --- old/test/java/util/ServiceLoader/modules/Basic.java 2017-02-07 13:14:09.359711879 +0000 +++ new/test/java/util/ServiceLoader/modules/Basic.java 2017-02-07 13:14:09.187700067 +0000 @@ -311,17 +311,17 @@ ModuleFinder finder = ModuleFinder.of(dir); // layer1 - Configuration cf1 = cf0.resolveRequiresAndUses(finder, ModuleFinder.of(), Set.of()); + Configuration cf1 = cf0.resolveAndBind(finder, ModuleFinder.of(), Set.of()); Layer layer1 = bootLayer.defineModulesWithOneLoader(cf1, scl); assertTrue(layer1.modules().size() == 1); // layer2 - Configuration cf2 = cf0.resolveRequiresAndUses(finder, ModuleFinder.of(), Set.of()); + Configuration cf2 = cf0.resolveAndBind(finder, ModuleFinder.of(), Set.of()); Layer layer2 = bootLayer.defineModulesWithOneLoader(cf2, scl); assertTrue(layer2.modules().size() == 1); // layer3 with layer1 and layer2 as parents - Configuration cf3 = Configuration.resolveRequiresAndUses(finder, + Configuration cf3 = Configuration.resolveAndBind(finder, List.of(cf1, cf2), ModuleFinder.of(), Set.of()); @@ -413,7 +413,7 @@ Collections.addAll(roots, modules); Layer bootLayer = Layer.boot(); Configuration parent = bootLayer.configuration(); - Configuration cf = parent.resolveRequires(finder, ModuleFinder.of(), roots); + Configuration cf = parent.resolve(finder, ModuleFinder.of(), roots); ClassLoader scl = ClassLoader.getSystemClassLoader(); Layer layer = bootLayer.defineModulesWithOneLoader(cf, scl); assertTrue(layer.modules().size() == 1); --- old/test/jdk/modules/etc/VerifyModuleDelegation.java 2017-02-07 13:14:09.836744638 +0000 +++ new/test/jdk/modules/etc/VerifyModuleDelegation.java 2017-02-07 13:14:09.669733169 +0000 @@ -46,7 +46,7 @@ private static final String JAVA_BASE = "java.base"; private static final ModuleDescriptor BASE - = ModuleDescriptor.module(JAVA_BASE).build(); + = ModuleDescriptor.newModule(JAVA_BASE).build(); private static final Set MREFS = Layer.boot().modules().stream().map(Module::getDescriptor) --- old/test/jdk/modules/scenarios/container/src/container/container/Main.java 2017-02-07 13:14:10.307776984 +0000 +++ new/test/jdk/modules/scenarios/container/src/container/container/Main.java 2017-02-07 13:14:10.138765378 +0000 @@ -71,9 +71,9 @@ ModuleFinder finder = ModuleFinder.of(paths); Configuration cf = Layer.boot().configuration() - .resolveRequiresAndUses(finder, - ModuleFinder.of(), - Set.of(appModuleName)); + .resolveAndBind(finder, + ModuleFinder.of(), + Set.of(appModuleName)); System.out.println("Resolved"); cf.modules().stream() --- old/test/tools/jar/mmrjar/Basic.java 2017-02-07 13:14:10.773808988 +0000 +++ new/test/tools/jar/mmrjar/Basic.java 2017-02-07 13:14:10.609797725 +0000 @@ -331,7 +331,7 @@ ModuleInfoExtender mie = ModuleInfoExtender.newExtender( new ByteArrayInputStream(mdBytes)); - mie.mainClass("foo.main"); + mie.mainClass("p.Main"); mie.version(Version.parse("1.0")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -345,7 +345,7 @@ // different main-class mie = ModuleInfoExtender.newExtender(new ByteArrayInputStream(mdBytes)); - mie.mainClass("foo.main2"); + mie.mainClass("p.Main2"); mie.version(Version.parse("1.0")); baos.reset(); mie.write(baos); @@ -360,7 +360,7 @@ // different version mie = ModuleInfoExtender.newExtender(new ByteArrayInputStream(mdBytes)); - mie.mainClass("foo.main"); + mie.mainClass("p.Main"); mie.version(Version.parse("2.0")); baos.reset(); mie.write(baos); @@ -395,7 +395,7 @@ Files.copy(Paths.get("test7-v9", "module-info.class"), Paths.get("test7-v10", "module-info.class")); - int rc = jar("--create --file mmr.jar --main-class=foo.main -C test7 . --release 9 -C test7-v9 . --release 10 -C test7-v10 ."); + int rc = jar("--create --file mmr.jar --main-class=p.Main -C test7 . --release 9 -C test7-v9 . --release 10 -C test7-v10 ."); System.out.println("-----------------------"); System.out.println( new String(errbytes.toByteArray())); @@ -409,7 +409,7 @@ System.out.println("-----------------------"); System.out.println( new String(outbytes.toByteArray())); - Optional exp = Optional.of("foo.main"); + Optional exp = Optional.of("p.Main"); try (ZipFile zf = new ZipFile("mmr.jar")) { Assert.assertTrue(zf.getEntry("module-info.class") == null); --- old/test/tools/jar/modularJar/Basic.java 2017-02-07 13:14:11.242841198 +0000 +++ new/test/tools/jar/modularJar/Basic.java 2017-02-07 13:14:11.073829591 +0000 @@ -328,7 +328,7 @@ .resultChecker(r -> assertModuleData(r, FOO)); } - @Test + @Test(enabled = false) public void partialUpdateFooMainClass() throws IOException { Path mp = Paths.get("partialUpdateFooMainClass"); createTestDir(mp); --- old/test/tools/jlink/JLinkNegativeTest.java 2017-02-07 13:14:11.736875124 +0000 +++ new/test/tools/jlink/JLinkNegativeTest.java 2017-02-07 13:14:11.568863585 +0000 @@ -274,7 +274,7 @@ helper.getJmodSrcDir(), helper.getJmodClassesDir(), moduleName2, classNames); try (OutputStream out = Files.newOutputStream(module2.resolve("module-info.class"))) { - ModuleInfoWriter.write(ModuleDescriptor.module(moduleName1) + ModuleInfoWriter.write(ModuleDescriptor.newModule(moduleName1) .requires("java.base").build(), out); } @@ -332,7 +332,7 @@ helper.getJarSrcDir(), helper.getJarClassesDir(), moduleName2, classNames); try (OutputStream out = Files.newOutputStream(module2.resolve("module-info.class"))) { - ModuleInfoWriter.write(ModuleDescriptor.module(moduleName1) + ModuleInfoWriter.write(ModuleDescriptor.newModule(moduleName1) .requires("java.base").build(), out); } --- old/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java 2017-02-07 13:14:12.204907264 +0000 +++ new/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java 2017-02-07 13:14:12.037895795 +0000 @@ -30,6 +30,8 @@ public final class SystemModules { public static final String[] MODULE_NAMES = new String[0]; + public static int PACKAGES_IN_BOOT_LAYER = 1024; + public static boolean hasSplitPackages() { return true; } --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/src/java.base/share/classes/java/lang/IllegalCallerException.java 2017-02-07 13:14:12.493927112 +0000 @@ -0,0 +1,74 @@ +/* + * 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 java.lang; + +/** + * Thrown to indicate that a method has been called by an inappropriate caller. + * + * @since 9 + */ +public class IllegalCallerException extends RuntimeException { + /** + * Constructs an IllegalCallerException with no detail message. + */ + public IllegalCallerException() { + super(); + } + + /** + * Constructs an IllegalCallerException with the specified detail + * message. + * + * @param s the String that contains a detailed message (can be null) + */ + public IllegalCallerException(String s) { + super(s); + } + + /** + * Constructs a new exception with the specified detail message and + * cause. + * + * @param message the detail message (can be null) + * @param cause the cause (can be null) + */ + public IllegalCallerException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail + * message of {@code (cause==null ? null : cause.toString())} (which + * typically contains the class and detail message of {@code cause}). + * + * @param cause the cause (can be null) + */ + public IllegalCallerException(Throwable cause) { + super(cause); + } + + static final long serialVersionUID = -2349421918363102232L; +} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/MethodHandles/privateLookupIn/Unnamed.java 2017-02-07 13:14:12.955958841 +0000 @@ -0,0 +1,24 @@ +/* + * 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. + */ + +public class Unnamed { } --- old/test/java/lang/invoke/modules/ModuleAccessControlTest.java 2017-02-07 13:14:13.666007601 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; - -import static jdk.testlibrary.ProcessTools.executeTestJava; - -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; - -/** - * @test - * @library /lib/testlibrary - * @modules jdk.compiler - * @build CompilerUtils jdk.testlibrary.* - * @run testng ModuleAccessControlTest - * @summary Driver for testing module access checking by MethodHandles.Lookup - */ - -public class ModuleAccessControlTest { - - private static final String TEST_SRC = System.getProperty("test.src"); - - private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); - private static final Path MODS_DIR = Paths.get("mods"); - - // the names of the modules in this test - private static List modules = Arrays.asList("m1", "m2"); - - - /** - * Compiles all modules used by the test - */ - @BeforeTest - public void compileAll() throws Exception { - for (String mn : modules) { - Path msrc = SRC_DIR.resolve(mn); - assertTrue(CompilerUtils - .compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); - } - } - - /** - * Launch the test - */ - @Test - public void runTest() throws Exception { - int exitValue = executeTestJava("--module-path", MODS_DIR.toString(), - "-m", "m1/p1.Main") - .outputTo(System.out) - .errorTo(System.out) - .getExitValue(); - - assertTrue(exitValue == 0); - } - -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/Driver.java 2017-02-07 13:14:13.417990569 +0000 @@ -0,0 +1,29 @@ +/* + * 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 + * @build m1/* m2/* Unnamed + * @run testng/othervm m1/p1.Main + * @summary Basic test case for module access checks and Lookup.in. + */ --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/Unnamed.java 2017-02-07 13:14:13.966028204 +0000 @@ -0,0 +1,24 @@ +/* + * 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. + */ + +public class Unnamed { } --- old/test/java/lang/invoke/modules/src/m1/module-info.java 2017-02-07 13:14:14.670076552 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -module m1 { - requires m2; - exports p1; -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m1/module-info.java 2017-02-07 13:14:14.434060344 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ +module m1 { + requires m2; + requires testng; + exports p1; +} --- old/test/java/lang/invoke/modules/src/m1/p1/Main.java 2017-02-07 13:14:15.178111440 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ - -package p1; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; - -/** - * Basic test case for module access check, supplements AccessControlTest. - * - * The tests consists of two modules: - * - * module m1 { requires m2; exports p1; } - * module m2 { exports q1; } - * - * Both modules read java.base (as every module reads java.base) - * - * module m1 has public types in packages p1 and p2, p2 is not exported. - * module m2 has public types in packages q1 and q2, q2 is not exported. - */ - -public class Main { - - static final int MODULE = Lookup.MODULE; - - // Use Class.forName to get classes for test because some - // are not accessible at compile-time - - static final Class p1_Type1; // m1, exported - static final Class p2_Type2; // m1, not exported - static final Class q1_Type1; // m2, exported, m1 reads m2 - static final Class q2_Type2; // m2, not exported, m1 reads m2 - static final Class x500NameClass; // java.base, not exported - - static { - try { - p1_Type1 = Class.forName("p1.Type1"); - p2_Type2 = Class.forName("p2.Type2"); - q1_Type1 = Class.forName("q1.Type1"); - q2_Type2 = Class.forName("q2.Type2"); - x500NameClass = Class.forName("sun.security.x509.X500Name"); - } catch (ClassNotFoundException e) { - throw new AssertionError(e); - } - } - - public static void main(String[] args) throws Exception { - Lookup lookup, lookup2; - - /** - * MethodHandles.lookup() - * has module access [A0] - * can access all public types in m1 [A1] - * can access public types in packages exported by modules that m1 reads [A2] - * cannot access public types in non-exported modules of modules that m1 reads [A3] - */ - lookup = MethodHandles.lookup(); - assertTrue((lookup.lookupModes() & MODULE) == MODULE); // [A0] - findConstructor(lookup, p1_Type1, void.class); // [A1] - findConstructor(lookup, p2_Type2, void.class); // [A1] - findConstructor(lookup, q1_Type1, void.class); // [A2] - findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A3] - findConstructor(lookup, Object.class, void.class); // [A2] - findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); // [A3] - - /** - * Teleport from MethodHandles.lookup() to lookup class in the same module - * module access is retained [A0] - * can access all public types in m1 [A1] - * can access public types in packages exported by modules that m1 reads [A2] - * cannot access public types in non-exported modules of modules that m1 reads [A3] - */ - lookup2 = lookup.in(p2_Type2); - assertTrue((lookup2.lookupModes() & MODULE) == MODULE); // [A0] - findConstructor(lookup2, p1_Type1, void.class); // [A1] - findConstructor(lookup2, p2_Type2, void.class); // [A1] - findConstructor(lookup2, q1_Type1, void.class); // [A2] - findConstructorExpectingIAE(lookup2, q2_Type2, void.class); // [A3] - findConstructor(lookup2, Object.class, void.class); // [A2] - findConstructorExpectingIAE(lookup2, x500NameClass, void.class, String.class); // [A3] - - /** - * Teleport from MethodHandles.lookup() to lookup class in another named module - * has no access [A0] - */ - lookup2 = lookup.in(Object.class); - assertTrue(lookup2.lookupModes() == 0); // [A0] - findConstructorExpectingIAE(lookup2, Object.class, void.class); // [A0] - - /** - * Teleport from MethodHandles.lookup() to lookup class in an unnamed module - * has no access [A0] - */ - Class c = MethodHandles.publicLookup().lookupClass(); - assertTrue(!c.getModule().isNamed()); - lookup2 = lookup.in(c); - assertTrue(lookup2.lookupModes() == 0); // [A0] - findConstructorExpectingIAE(lookup2, Object.class, void.class); - - /** - * MethodHandles.publicLookup() - * has no module access [A0] - * can access public types in exported packages [A1] - * cannot access public types in non-exported packages [A2] - */ - lookup = MethodHandles.publicLookup(); - assertTrue((lookup.lookupModes() & MODULE) == 0); // [A0] - findConstructor(lookup, p1_Type1, void.class); // [A1] - findConstructorExpectingIAE(lookup, p2_Type2, void.class); // [A1] - findConstructor(lookup, q1_Type1, void.class); // [A1] - findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A2] - findConstructor(lookup, Object.class, void.class); // [A1] - findConstructorExpectingIAE(lookup, x500NameClass, void.class); // [A2] - - /** - * Teleport from MethodHandles.publicLookup() to lookup class in java.base - * has no module access [A0] - * can access public types in packages exported by java.base [A1] - * cannot access public types in non-exported packages [A2] - * no access to types in other named modules [A3] - */ - lookup2 = lookup.in(Object.class); - assertTrue((lookup2.lookupModes() & MODULE) == 0); // [A0] - findConstructor(lookup2, String.class, void.class); // [A1] - findConstructorExpectingIAE(lookup2, x500NameClass, void.class, String.class); // [A2] - findConstructorExpectingIAE(lookup2, p1_Type1, void.class); // [A3] - findConstructorExpectingIAE(lookup2, q1_Type1, void.class); // [A3] - - /** - * Teleport from MethodHandles.publicLookup() to lookup class in m1 - * has no module access [A0] - * can access public types in packages exported by m1, m2 and java.base [A1] - * cannot access public types is non-exported packages [A2] - */ - lookup2 = lookup.in(p1_Type1); - assertTrue((lookup2.lookupModes() & MODULE) == 0); // [A0] - findConstructor(lookup2, p1_Type1, void.class); // [A1] - findConstructor(lookup2, q1_Type1, void.class); // [A1] - findConstructor(lookup2, Object.class, void.class); // [A1] - findConstructorExpectingIAE(lookup, p2_Type2, void.class); // [A2] - findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A2] - findConstructorExpectingIAE(lookup2, x500NameClass, void.class, String.class); // [A2] - - /** - * Teleport from MethodHandles.publicLookup() to lookup class in m2 - * has no module access [A0] - * can access public types in packages exported by m2 and java.base [A1] - * cannot access public types is non-exported packages or modules that m2 does - * not read [A2] - */ - lookup2 = lookup.in(q1_Type1); - assertTrue((lookup2.lookupModes() & MODULE) == 0); // [A0] - findConstructor(lookup2, q1_Type1, void.class); // [A1] - findConstructor(lookup2, Object.class, void.class); // [A1] - findConstructorExpectingIAE(lookup2, p1_Type1, void.class); // [A2] - findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A2] - findConstructorExpectingIAE(lookup2, x500NameClass, void.class, String.class); // [A2] - - /** - * Teleport from MethodHandles.publicLookup() to lookup class that is not - * in an exported package, should get no access [A0] - */ - lookup2 = lookup.in(p2_Type2); - assertTrue(lookup2.lookupModes() == 0); // [A0] - findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A0] - } - - /** - * Invokes Lookup findConstructor with a method type constructored from the - * given return and parameter types, expecting IllegalAccessException to be - * thrown. - */ - static MethodHandle findConstructorExpectingIAE(Lookup lookup, - Class clazz, - Class rtype, - Class... ptypes) throws Exception { - try { - findConstructor(lookup, clazz, rtype, ptypes); - throw new RuntimeException("IllegalAccessError expected"); - } catch (IllegalAccessException expected) { - return null; - } - } - - /** - * Invokes Lookup findConstructor with a method type constructored from the - * given return and parameter types. - */ - static MethodHandle findConstructor(Lookup lookup, - Class clazz, - Class rtype, - Class... ptypes) throws Exception { - MethodType mt = MethodType.methodType(rtype, ptypes); - return lookup.findConstructor(clazz, mt); - } - - static void assertTrue(boolean condition) { - if (!condition) - throw new RuntimeException(); - } - -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m1/p1/Main.java 2017-02-07 13:14:14.942095232 +0000 @@ -0,0 +1,367 @@ +/* + * 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 + * 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. + */ + +package p1; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Layer; +import java.lang.reflect.Module; + +import static java.lang.invoke.MethodHandles.Lookup.*; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * Basic test case for module access checks and Lookup.in. + */ + +@Test +public class Main { + + private Class p1_Type1; // m1, exported + private Class p2_Type2; // m1, not exported + private Class q1_Type1; // m2, exported + private Class q2_Type2; // m2, not exported + private Class x500NameClass; // java.base, not exported + private Class unnamedClass; // class in unnamed module + + @BeforeTest + public void setup() throws Exception { + try { + p1_Type1 = Class.forName("p1.Type1"); + p2_Type2 = Class.forName("p2.Type2"); + q1_Type1 = Class.forName("q1.Type1"); + q2_Type2 = Class.forName("q2.Type2"); + x500NameClass = Class.forName("sun.security.x509.X500Name"); + unnamedClass = Class.forName("Unnamed"); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + + // check setup + Module m1 = Layer.boot().findModule("m1").orElse(null); + assertNotNull(m1); + assertTrue(p1_Type1.getModule() == m1); + assertTrue(p2_Type2.getModule() == m1); + assertTrue(m1.isExported("p1")); + assertFalse(m1.isExported("p2")); + + Module m2 = Layer.boot().findModule("m2").orElse(null); + assertNotNull(m2); + assertTrue(q1_Type1.getModule() == m2); + assertTrue(q2_Type2.getModule() == m2); + assertTrue(m2.isExported("q1")); + assertFalse(m2.isExported("q2")); + + Module unnamedModule = unnamedClass.getModule(); + assertFalse(unnamedModule.isNamed()); + + // m1 needs to read unnamed module + Main.class.getModule().addReads(unnamedModule); + } + + /** + * MethodHandles.lookup() + * + * [A0] has module access + * [A1] can access all public types in m1 + * [A2] can access public types in packages exported by modules that m1 reads + * [A3] cannot access public types in non-exported modules of modules that m1 reads + */ + public void testLookup() throws Exception { + Lookup lookup = MethodHandles.lookup(); + assertTrue((lookup.lookupModes() & MODULE) == MODULE); // [A0] + + // m1 + findConstructor(lookup, p1_Type1, void.class); // [A1] + findConstructor(lookup, p2_Type2, void.class); // [A1] + + // m2 + findConstructor(lookup, q1_Type1, void.class); // [A2] + findConstructorExpectingIAE(lookup, q2_Type2, void.class); // [A3] + + // java.base + findConstructor(lookup, Object.class, void.class); // [A2] + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); // [A3] + + // unnamed + findConstructor(lookup, unnamedClass, void.class); // [A3] + } + + /** + * Hop to lookup class in the same module + * + * [A0] module and public access is not lost + */ + public void testToSameModule() throws Exception { + Lookup lookup = MethodHandles.lookup().in(p2_Type2); + assertTrue(lookup.lookupModes() == (MODULE|PUBLIC)); // [A0] + + // m1 + findConstructor(lookup, p1_Type1, void.class); + findConstructor(lookup, p2_Type2, void.class); + + // m2 + findConstructor(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructor(lookup, unnamedClass, void.class); + } + + /** + * Hop to lookup class in another named module + * + * [A0] has no access + */ + public void testFromNamedToNamedModule() throws Exception { + Lookup lookup = MethodHandles.lookup().in(q1_Type1); + assertTrue(lookup.lookupModes() == 0); // [A0] + + // m1 + findConstructorExpectingIAE(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructorExpectingIAE(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructorExpectingIAE(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructorExpectingIAE(lookup, unnamedClass, void.class); + } + + /** + * Hop to lookup class in an unnamed module + * + * [A0] has no access + */ + public void testFromNamedToUnnamedModule() throws Exception { + Lookup lookup = MethodHandles.lookup().in(unnamedClass); + assertTrue(lookup.lookupModes() == 0); // [A0] + + // m1 + findConstructorExpectingIAE(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructorExpectingIAE(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructorExpectingIAE(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructorExpectingIAE(lookup, unnamedClass, void.class); + } + + /** + * Hop from unnamed to named module. + * + * [A0] retains PUBLIC access + */ + public void testFromUnnamedToNamedModule() throws Exception { + Lookup lookup = MethodHandles.lookup(); + lookup = MethodHandles.privateLookupIn(unnamedClass, lookup).in(p1_Type1); + assertTrue(lookup.lookupModes() == PUBLIC); // A0 + + // m1 + findConstructor(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructor(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructor(lookup, unnamedClass, void.class); + } + + /** + * MethodHandles.publicLookup() + * + * [A0] has PUBLIC|UNCONDITIONAL access + */ + public void testPublicLookup() throws Exception { + Lookup lookup = MethodHandles.publicLookup(); + assertTrue(lookup.lookupModes() == (PUBLIC|UNCONDITIONAL)); // A0 + + // m1 + findConstructor(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructor(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructor(lookup, unnamedClass, void.class); + } + + /** + * Hop from publicLookup to accessible type in java.base + */ + public void testPublicLookupToBaseModule() throws Exception { + Lookup lookup = MethodHandles.publicLookup().in(String.class); + assertTrue(lookup.lookupModes() == PUBLIC); // A0 + + // m1 + findConstructorExpectingIAE(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructorExpectingIAE(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructorExpectingIAE(lookup, unnamedClass, void.class); + } + + + /** + * Hop from publicLookup to accessible type in named module. + * + * [A0] has PUBLIC access + */ + public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception { + Lookup lookup = MethodHandles.publicLookup().in(p1_Type1); + assertTrue(lookup.lookupModes() == PUBLIC); // A0 + + // m1 + findConstructor(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructor(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructor(lookup, unnamedClass, void.class); + } + + /** + * Teleport from publicLookup to inaccessible type in named module. + * + * [A0] has no access + */ + public void testPublicLookupToInaccessibleTypeInNamedModule() throws Exception { + Lookup lookup = MethodHandles.publicLookup().in(p2_Type2); + assertTrue(lookup.lookupModes() == 0); // A0 + + // m1 + findConstructorExpectingIAE(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructorExpectingIAE(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructorExpectingIAE(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructorExpectingIAE(lookup, unnamedClass, void.class); + } + + /** + * Teleport from publicLookup to public type in unnamed module + * + * [A0] has PUBLIC access + */ + public void testPublicLookupToUnnamedModule() throws Exception { + Lookup lookup = MethodHandles.publicLookup().in(unnamedClass); + assertTrue(lookup.lookupModes() == PUBLIC); // A0 + + // m1 + findConstructor(lookup, p1_Type1, void.class); + findConstructorExpectingIAE(lookup, p2_Type2, void.class); + + // m2 + findConstructor(lookup, q1_Type1, void.class); + findConstructorExpectingIAE(lookup, q2_Type2, void.class); + + // java.base + findConstructor(lookup, Object.class, void.class); + findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); + + // unnamed + findConstructor(lookup, unnamedClass, void.class); + } + + /** + * Invokes Lookup findConstructor with a method type constructored from the + * given return and parameter types, expecting IllegalAccessException to be + * thrown. + */ + static void findConstructorExpectingIAE(Lookup lookup, + Class clazz, + Class rtype, + Class... ptypes) throws Exception { + try { + findConstructor(lookup, clazz, rtype, ptypes); + assertTrue(false); + } catch (IllegalAccessException expected) { } + } + + /** + * Invokes Lookup findConstructor with a method type constructored from the + * given return and parameter types. + */ + static MethodHandle findConstructor(Lookup lookup, + Class clazz, + Class rtype, + Class... ptypes) throws Exception { + MethodType mt = MethodType.methodType(rtype, ptypes); + return lookup.findConstructor(clazz, mt); + } +} --- old/test/java/lang/invoke/modules/src/m1/p1/Type1.java 2017-02-07 13:14:15.722148800 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -package p1; - -public class Type1 { - public Type1() { } -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m1/p1/Type1.java 2017-02-07 13:14:15.478132043 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ +package p1; + +public class Type1 { + public Type1() { } +} --- old/test/java/lang/invoke/modules/src/m1/p2/Type2.java 2017-02-07 13:14:16.123176339 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -package p2; - -public class Type2 { - public Type2() { } -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m1/p2/Type2.java 2017-02-07 13:14:15.879159582 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ +package p2; + +public class Type2 { + public Type2() { } +} --- old/test/java/lang/invoke/modules/src/m2/module-info.java 2017-02-07 13:14:16.516203329 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -module m2 { - exports q1; -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m2/module-info.java 2017-02-07 13:14:16.276186847 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, 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. + */ +module m2 { + exports q1; +} --- old/test/java/lang/invoke/modules/src/m2/q1/Type1.java 2017-02-07 13:14:16.907230182 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -package q1; - -public class Type1 { - public Type1() { } -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m2/q1/Type1.java 2017-02-07 13:14:16.668213768 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ +package q1; + +public class Type1 { + public Type1() { } +} --- old/test/java/lang/invoke/modules/src/m2/q2/Type2.java 2017-02-07 13:14:17.305257515 +0000 +++ /dev/null 2017-01-28 05:02:56.239999980 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -package q2; - -public class Type2 { - public Type2() { } -} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/invoke/modules/m2/q2/Type2.java 2017-02-07 13:14:17.057240483 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015, 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. + */ +package q2; + +public class Type2 { + public Type2() { } +} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/reflect/AccessibleObject/CanAccessTest.java 2017-02-07 13:14:17.461268229 +0000 @@ -0,0 +1,138 @@ +/* + * 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 + * @build CanAccessTest + * @modules java.base/jdk.internal.misc:+open + * @run testng CanAccessTest + * @summary Test AccessibleObject::canAccess method + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.security.SecureClassLoader; + +import jdk.internal.misc.Unsafe; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class CanAccessTest { + private static Unsafe INSTANCE = Unsafe.getUnsafe(); + + /** + * null object parameter for Constructor + */ + public void testConstructor() throws Exception { + Constructor ctor = Unsafe.class.getDeclaredConstructor(); + assertFalse(ctor.canAccess(null)); + assertTrue(ctor.trySetAccessible()); + + try { + // non-null object parameter + ctor.canAccess(INSTANCE); + assertTrue(false); + } catch (IllegalArgumentException expected) {} + } + + /** + * Test protected constructors + */ + public void testProtectedConstructor() throws Exception { + TestLoader.testProtectedConstructorNonOpenedPackage(); + + Constructor ctor = TestLoader.class.getDeclaredConstructor(); + assertTrue(ctor.canAccess(null)); + } + + /** + * null object parameter for static members + */ + public void testStaticMember() throws Exception { + Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); + assertFalse(m.canAccess(null)); + assertTrue(m.trySetAccessible()); + + try { + // non-null object parameter + m.canAccess(INSTANCE); + assertTrue(false); + } catch (IllegalArgumentException expected) { } + } + + /** + * Test protected static + */ + public void testProtectedStatic() throws Exception { + Method m = TestLoader.testProtectedStatic(); + assertFalse(m.canAccess(null)); + } + + /** + * the specified object must be an instance of the declaring class + * for instance members + */ + public void testInstanceMethod() throws Exception { + Method m = Unsafe.class.getDeclaredMethod("addressSize0"); + assertFalse(m.canAccess(INSTANCE)); + + try { + m.canAccess(null); + assertTrue(false); + } catch (IllegalArgumentException expected) { } + } + + /** + * the specified object must be an instance of the declaring class + * for instance members + */ + public void testInvalidInstanceObject() throws Exception { + Class clazz = Class.forName("sun.security.x509.X500Name"); + Method m = clazz.getDeclaredMethod("size"); + + try { + m.canAccess(INSTANCE); + assertTrue(false); + } catch (IllegalArgumentException expected) { } + } + + + static class TestLoader extends SecureClassLoader { + public static Method testProtectedStatic() throws Exception { + Method m = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable"); + assertTrue(m.canAccess(null)); + return m; + } + + protected TestLoader() throws Exception { + Constructor ctor = SecureClassLoader.class.getDeclaredConstructor(); + assertFalse(ctor.canAccess(null)); + assertFalse(ctor.trySetAccessible()); + } + + public static void testProtectedConstructorNonOpenedPackage() throws Exception { + new TestLoader(); + } + } +} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java 2017-02-07 13:14:17.937300919 +0000 @@ -0,0 +1,201 @@ +/* + * 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 + * @build TrySetAccessibleTest + * @modules java.base/java.lang:open + * java.base/jdk.internal.perf + * java.base/jdk.internal.misc:+open + * @run testng TrySetAccessibleTest + * @summary Test AccessibleObject::trySetAccessible method + */ + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import jdk.internal.misc.Unsafe; +import jdk.internal.perf.Perf; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +@Test +public class TrySetAccessibleTest { + /** + * Invoke a private constructor on a public class in an exported package + */ + public void testPrivateConstructorInExportedPackage() throws Exception { + Constructor ctor = Perf.class.getDeclaredConstructor(); + + try { + ctor.newInstance(); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertFalse(ctor.trySetAccessible()); + assertFalse(ctor.canAccess(null)); + } + + /** + * Invoke a private constructor on a public class in an open package + */ + public void testPrivateConstructorInOpenedPackage() throws Exception { + Constructor ctor = Unsafe.class.getDeclaredConstructor(); + + try { + ctor.newInstance(); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertTrue(ctor.trySetAccessible()); + assertTrue(ctor.canAccess(null)); + Unsafe unsafe = (Unsafe) ctor.newInstance(); + } + + /** + * Invoke a private method on a public class in an exported package + */ + public void testPrivateMethodInExportedPackage() throws Exception { + Method m = Perf.class.getDeclaredMethod("getBytes", String.class); + try { + m.invoke(null); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertFalse(m.trySetAccessible()); + assertFalse(m.canAccess(null)); + } + + + /** + * Invoke a private method on a public class in an open package + */ + public void testPrivateMethodInOpenedPackage() throws Exception { + Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); + assertFalse(m.canAccess(null)); + + try { + m.invoke(null); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertTrue(m.trySetAccessible()); + assertTrue(m.canAccess(null)); + try { + m.invoke(null); + assertTrue(false); + } catch (InvocationTargetException e) { + // thrown by throwIllegalAccessError + assertTrue(e.getCause() instanceof IllegalAccessError); + } + } + + /** + * Invoke a private method on a public class in an exported package + */ + public void testPrivateFieldInExportedPackage() throws Exception { + Field f = Perf.class.getDeclaredField("instance"); + try { + f.get(null); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertFalse(f.trySetAccessible()); + assertFalse(f.canAccess(null)); + try { + f.get(null); + assertTrue(false); + } catch (IllegalAccessException expected) {} + } + + /** + * Access a private field in a public class that is an exported package + */ + public void testPrivateFieldInOpenedPackage() throws Exception { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + + try { + f.get(null); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertTrue(f.trySetAccessible()); + assertTrue(f.canAccess(null)); + Unsafe unsafe = (Unsafe) f.get(null); + } + + + /** + * Invoke a public constructor on a public class in a non-exported package + */ + public void testPublicConstructorInNonExportedPackage() throws Exception { + Class clazz = Class.forName("sun.security.x509.X500Name"); + Constructor ctor = clazz.getConstructor(String.class); + + try { + ctor.newInstance("cn=duke"); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertFalse(ctor.trySetAccessible()); + assertFalse(ctor.canAccess(null)); + assertTrue(ctor.trySetAccessible() == ctor.isAccessible()); + } + + + /** + * Access a public field in a public class that in a non-exported package + */ + public void testPublicFieldInNonExportedPackage() throws Exception { + Class clazz = Class.forName("sun.security.x509.X500Name"); + Field f = clazz.getField("SERIALNUMBER_OID"); + + try { + f.get(null); + assertTrue(false); + } catch (IllegalAccessException expected) { } + + assertFalse(f.trySetAccessible()); + assertFalse(f.canAccess(null)); + } + + + /** + * Test that the Class constructor cannot be make accessible. + */ + public void testJavaLangClass() throws Exception { + + // non-public constructor + Constructor ctor + = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class); + AccessibleObject[] ctors = { ctor }; + + assertFalse(ctor.trySetAccessible()); + assertFalse(ctor.canAccess(null)); + } + +} --- /dev/null 2017-01-28 05:02:56.239999980 +0000 +++ new/test/jdk/internal/reflect/CallerSensitive/CheckCSMs.java 2017-02-07 13:14:18.417333884 +0000 @@ -0,0 +1,251 @@ +/* + * 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 com.sun.tools.classfile.*; +import com.sun.tools.jdeps.ClassFileReader; +import static com.sun.tools.classfile.ConstantPool.*; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/* + * @test + * @summary CallerSensitive methods should be static or final instance + * methods except the known list of non-final instance methods + * @modules jdk.jdeps/com.sun.tools.classfile + * jdk.jdeps/com.sun.tools.jdeps + * @build CheckCSMs + * @run main/othervm/timeout=900 CheckCSMs + */ +public class CheckCSMs { + private static int numThreads = 3; + private static boolean listCSMs = false; + private final ExecutorService pool; + + // The goal is to remove this list of Non-final instance @CS methods + // over time. Do not add any new one to this list. + private static Set KNOWN_NON_FINAL_CSMS = + Set.of("java/io/ObjectStreamField#getType ()Ljava/lang/Class;", + "java/io/ObjectStreamClass#forClass ()Ljava/lang/Class;", + "java/lang/Runtime#load (Ljava/lang/String;)V", + "java/lang/Runtime#loadLibrary (Ljava/lang/String;)V", + "java/lang/Thread#getContextClassLoader ()Ljava/lang/ClassLoader;", + "javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;" + ); + + public static void main(String[] args) throws Exception { + if (args.length > 0 && args[0].equals("--list")) { + listCSMs = true; + } + + CheckCSMs checkCSMs = new CheckCSMs(); + Set result = checkCSMs.run(getPlatformClasses()); + if (!KNOWN_NON_FINAL_CSMS.equals(result)) { + Set diff = new HashSet<>(result); + diff.removeAll(KNOWN_NON_FINAL_CSMS); + throw new RuntimeException("Unexpected non-final instance method: " + + result.stream().sorted() + .collect(Collectors.joining("\n", "\n", ""))); + } + } + + private final Set nonFinalCSMs = new ConcurrentSkipListSet<>(); + private final ReferenceFinder finder; + public CheckCSMs() { + this.finder = new ReferenceFinder(getFilter(), getVisitor()); + pool = Executors.newFixedThreadPool(numThreads); + + } + + public Set run(Stream classes) + throws IOException, InterruptedException, ExecutionException, + ConstantPoolException + { + classes.forEach(this::processPath); + waitForCompletion(); + pool.shutdown(); + return nonFinalCSMs; + } + + + private ReferenceFinder.Filter getFilter() { + final String classname = "jdk/internal/reflect/Reflection"; + final String method = "getCallerClass"; + return new ReferenceFinder.Filter() { + public boolean accept(ConstantPool cpool, CPRefInfo cpref) { + try { + CONSTANT_NameAndType_info nat = cpref.getNameAndTypeInfo(); + return cpref.getClassName().equals(classname) && nat.getName().equals(method); + } catch (ConstantPoolException ex) { + throw new RuntimeException(ex); + } + } + }; + } + + private ReferenceFinder.Visitor getVisitor() { + return new ReferenceFinder.Visitor() { + public void visit(ClassFile cf, Method m, List refs) { + try { + // ignore jdk.unsupported/sun.reflect.Reflection.getCallerClass + // which is a "special" delegate to the internal getCallerClass + if (cf.getName().equals("sun/reflect/Reflection") && + m.getName(cf.constant_pool).equals("getCallerClass")) + return; + + String name = String.format("%s#%s %s", cf.getName(), + m.getName(cf.constant_pool), + m.descriptor.getValue(cf.constant_pool)); + if (!CheckCSMs.isStaticOrFinal(cf, m, cf.constant_pool)) { + System.err.println("Unsupported @CallerSensitive: " + name); + nonFinalCSMs.add(name); + } else { + if (listCSMs) { + System.out.format("@CS %s%n", name); + } + } + } catch (ConstantPoolException ex) { + throw new RuntimeException(ex); + } + } + }; + } + + void processPath(Path path) { + try { + ClassFileReader reader = ClassFileReader.newInstance(path); + for (ClassFile cf : reader.getClassFiles()) { + if (cf.access_flags.is(AccessFlags.ACC_MODULE)) + continue; + + String classFileName = cf.getName(); + // for each ClassFile + // parse constant pool to find matching method refs + // parse each method (caller) + // - visit and find method references matching the given method name + pool.submit(getTask(cf)); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } catch (ConstantPoolException x) { + throw new RuntimeException(x); + } + } + + private static final String CALLER_SENSITIVE_ANNOTATION + = "Ljdk/internal/reflect/CallerSensitive;"; + + private static boolean isCallerSensitive(Method m, ConstantPool cp) + throws ConstantPoolException + { + RuntimeAnnotations_attribute attr = + (RuntimeAnnotations_attribute)m.attributes.get(Attribute.RuntimeVisibleAnnotations); + if (attr != null) { + for (int i = 0; i < attr.annotations.length; i++) { + Annotation ann = attr.annotations[i]; + String annType = cp.getUTF8Value(ann.type_index); + if (CALLER_SENSITIVE_ANNOTATION.equals(annType)) { + return true; + } + } + } + return false; + } + + private static boolean isStaticOrFinal(ClassFile cf, Method m, ConstantPool cp) + throws ConstantPoolException + { + if (!isCallerSensitive(m, cp)) + return false; + + // either a static method or a final instance method + return m.access_flags.is(AccessFlags.ACC_STATIC) || + m.access_flags.is(AccessFlags.ACC_FINAL) || + cf.access_flags.is(AccessFlags.ACC_FINAL); + } + + private final List> tasks = new ArrayList>(); + private FutureTask getTask(final ClassFile cf) { + FutureTask task = new FutureTask(new Callable() { + public Void call() throws Exception { + finder.parse(cf); + return null; + } + }); + tasks.add(task); + return task; + } + + private void waitForCompletion() throws InterruptedException, ExecutionException { + for (FutureTask t : tasks) { + t.get(); + } + if (tasks.isEmpty()) { + throw new RuntimeException("No classes found, or specified."); + } + System.out.println("Parsed " + tasks.size() + " classfiles"); + } + + static Stream getPlatformClasses() throws IOException { + Path home = Paths.get(System.getProperty("java.home")); + + // Either an exploded build or an image. + File classes = home.resolve("modules").toFile(); + if (classes.isDirectory()) { + return Stream.of(classes.toPath()); + } else { + return jrtPaths(); + } + } + + static Stream jrtPaths() { + FileSystem jrt = FileSystems.getFileSystem(URI.create("jrt:/")); + Path root = jrt.getPath("/"); + + try { + return Files.walk(root) + .filter(p -> p.getNameCount() > 1) + .filter(p -> p.toString().endsWith(".class")); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + } +}