jdk/src/java.base/share/classes/java/lang/module/Resolver.java
changeset 43712 5dfd0950317c
parent 42703 20c39ea4a507
child 44359 c6761862ca0b
--- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Feb 09 17:21:47 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Fri Feb 10 09:04:39 2017 +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<String, ModuleReference> 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<Configuration> 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<String> roots) {
+    Resolver resolve(Collection<String> roots) {
 
         // create the visit stack to get us started
         Deque<ModuleDescriptor> 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 (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 (descriptor2.isAutomatic()) {
+                    // automatic modules read self and export all packages
+                    if (descriptor2 != descriptor1){
+                        for (String source : descriptor2.packages()) {
+                            ModuleDescriptor supplier
+                                = 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());
+                            // descriptor2 and 'supplier' export source to descriptor1
+                            if (supplier != null) {
+                                failTwoSuppliers(descriptor1, source, descriptor2, supplier);
+                            }
                         }
 
                     }
+                } 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<ModuleReference> findAll() {
-        try {
-
-            Set<ModuleReference> beforeModules = beforeFinder.findAll();
-            Set<ModuleReference> afterModules = afterFinder.findAll();
+        Set<ModuleReference> beforeModules = beforeFinder.findAll();
+        Set<ModuleReference> 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<ModuleReference> 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<ModuleReference> 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);
     }