1 /*
   2  * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.module;
  27 
  28 import java.io.PrintStream;
  29 import java.lang.module.ModuleDescriptor.Provides;
  30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
  31 import java.net.URI;
  32 import java.util.ArrayDeque;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Deque;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.LinkedHashSet;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Optional;
  43 import java.util.Set;
  44 import java.util.stream.Collectors;
  45 
  46 import jdk.internal.module.ModuleHashes;
  47 import jdk.internal.module.ModuleReferenceImpl;
  48 import jdk.internal.module.ModuleTarget;
  49 
  50 /**
  51  * The resolver used by {@link Configuration#resolve} and {@link
  52  * Configuration#resolveAndBind}.
  53  *
  54  * @implNote The resolver is used at VM startup and so deliberately avoids
  55  * using lambda and stream usages in code paths used during startup.
  56  */
  57 
  58 final class Resolver {
  59 
  60     private final ModuleFinder beforeFinder;
  61     private final List<Configuration> parents;
  62     private final ModuleFinder afterFinder;
  63     private final PrintStream traceOutput;
  64 
  65     // maps module name to module reference
  66     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
  67 
  68     // true if all automatic modules have been found
  69     private boolean haveAllAutomaticModules;
  70 
  71     // constraint on target platform
  72     private String targetPlatform;
  73 
  74     String targetPlatform() { return targetPlatform; }
  75 
  76     /**
  77      * @throws IllegalArgumentException if there are more than one parent and
  78      *         the constraints on the target platform conflict
  79      */
  80     Resolver(ModuleFinder beforeFinder,
  81              List<Configuration> parents,
  82              ModuleFinder afterFinder,
  83              PrintStream traceOutput) {
  84         this.beforeFinder = beforeFinder;
  85         this.parents = parents;
  86         this.afterFinder = afterFinder;
  87         this.traceOutput = traceOutput;
  88 
  89         // record constraint on target platform, checking for conflicts
  90         for (Configuration parent : parents) {
  91             String value = parent.targetPlatform();
  92             if (value != null) {
  93                 if (targetPlatform == null) {
  94                     targetPlatform = value;
  95                 } else {
  96                     if (!value.equals(targetPlatform)) {
  97                         String msg = "Parents have conflicting constraints on target" +
  98                                      "  platform: " + targetPlatform + ", " + value;
  99                         throw new IllegalArgumentException(msg);
 100                     }
 101                 }
 102             }
 103         }
 104     }
 105 
 106     /**
 107      * Resolves the given named modules.
 108      *
 109      * @throws ResolutionException
 110      */
 111     Resolver resolve(Collection<String> roots) {
 112 
 113         // create the visit stack to get us started
 114         Deque<ModuleDescriptor> q = new ArrayDeque<>();
 115         for (String root : roots) {
 116 
 117             // find root module
 118             ModuleReference mref = findWithBeforeFinder(root);
 119             if (mref == null) {
 120 
 121                 if (findInParent(root) != null) {
 122                     // in parent, nothing to do
 123                     continue;
 124                 }
 125 
 126                 mref = findWithAfterFinder(root);
 127                 if (mref == null) {
 128                     findFail("Module %s not found", root);
 129                 }
 130             }
 131 
 132             if (isTracing()) {
 133                 trace("root %s", nameAndInfo(mref));
 134             }
 135 
 136             addFoundModule(mref);
 137             q.push(mref.descriptor());
 138         }
 139 
 140         resolve(q);
 141 
 142         return this;
 143     }
 144 
 145     /**
 146      * Resolve all modules in the given queue. On completion the queue will be
 147      * empty and any resolved modules will be added to {@code nameToReference}.
 148      *
 149      * @return The set of module resolved by this invocation of resolve
 150      */
 151     private Set<ModuleDescriptor> resolve(Deque<ModuleDescriptor> q) {
 152         Set<ModuleDescriptor> resolved = new HashSet<>();
 153 
 154         while (!q.isEmpty()) {
 155             ModuleDescriptor descriptor = q.poll();
 156             assert nameToReference.containsKey(descriptor.name());
 157 
 158             // if the module is an automatic module then all automatic
 159             // modules need to be resolved
 160             if (descriptor.isAutomatic() && !haveAllAutomaticModules) {
 161                 addFoundAutomaticModules().forEach(mref -> {
 162                     ModuleDescriptor other = mref.descriptor();
 163                     q.offer(other);
 164                     if (isTracing()) {
 165                         trace("%s requires %s", descriptor.name(), nameAndInfo(mref));
 166                     }
 167                 });
 168                 haveAllAutomaticModules = true;
 169             }
 170 
 171             // process dependences
 172             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 173 
 174                 // only required at compile-time
 175                 if (requires.modifiers().contains(Modifier.STATIC))
 176                     continue;
 177 
 178                 String dn = requires.name();
 179 
 180                 // find dependence
 181                 ModuleReference mref = findWithBeforeFinder(dn);
 182                 if (mref == null) {
 183 
 184                     if (findInParent(dn) != null) {
 185                         // dependence is in parent
 186                         continue;
 187                     }
 188 
 189                     mref = findWithAfterFinder(dn);
 190                     if (mref == null) {
 191                         findFail("Module %s not found, required by %s",
 192                                  dn, descriptor.name());
 193                     }
 194                 }
 195 
 196                 if (isTracing() && !dn.equals("java.base")) {
 197                     trace("%s requires %s", descriptor.name(), nameAndInfo(mref));
 198                 }
 199 
 200                 if (!nameToReference.containsKey(dn)) {
 201                     addFoundModule(mref);
 202                     q.offer(mref.descriptor());
 203                 }
 204 
 205             }
 206 
 207             resolved.add(descriptor);
 208         }
 209 
 210         return resolved;
 211     }
 212 
 213     /**
 214      * Augments the set of resolved modules with modules induced by the
 215      * service-use relation.
 216      */
 217     Resolver bind() {
 218 
 219         // Scan the finders for all available service provider modules. As
 220         // java.base uses services then then module finders will be scanned
 221         // anyway.
 222         Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
 223         for (ModuleReference mref : findAll()) {
 224             ModuleDescriptor descriptor = mref.descriptor();
 225             if (!descriptor.provides().isEmpty()) {
 226 
 227                 for (Provides provides :  descriptor.provides()) {
 228                     String sn = provides.service();
 229 
 230                     // computeIfAbsent
 231                     Set<ModuleReference> providers = availableProviders.get(sn);
 232                     if (providers == null) {
 233                         providers = new HashSet<>();
 234                         availableProviders.put(sn, providers);
 235                     }
 236                     providers.add(mref);
 237                 }
 238 
 239             }
 240         }
 241 
 242         // create the visit stack
 243         Deque<ModuleDescriptor> q = new ArrayDeque<>();
 244 
 245         // the initial set of modules that may use services
 246         Set<ModuleDescriptor> initialConsumers;
 247         if (ModuleLayer.boot() == null) {
 248             initialConsumers = new HashSet<>();
 249         } else {
 250             initialConsumers = parents.stream()
 251                     .flatMap(Configuration::configurations)
 252                     .distinct()
 253                     .flatMap(c -> c.descriptors().stream())
 254                     .collect(Collectors.toSet());
 255         }
 256         for (ModuleReference mref : nameToReference.values()) {
 257             initialConsumers.add(mref.descriptor());
 258         }
 259 
 260         // Where there is a consumer of a service then resolve all modules
 261         // that provide an implementation of that service
 262         Set<ModuleDescriptor> candidateConsumers = initialConsumers;
 263         do {
 264             for (ModuleDescriptor descriptor : candidateConsumers) {
 265                 if (!descriptor.uses().isEmpty()) {
 266 
 267                     // the modules that provide at least one service
 268                     Set<ModuleDescriptor> modulesToBind = null;
 269                     if (isTracing()) {
 270                         modulesToBind = new HashSet<>();
 271                     }
 272 
 273                     for (String service : descriptor.uses()) {
 274                         Set<ModuleReference> mrefs = availableProviders.get(service);
 275                         if (mrefs != null) {
 276                             for (ModuleReference mref : mrefs) {
 277                                 ModuleDescriptor provider = mref.descriptor();
 278                                 if (!provider.equals(descriptor)) {
 279 
 280                                     if (isTracing() && modulesToBind.add(provider)) {
 281                                         trace("%s binds %s", descriptor.name(),
 282                                                 nameAndInfo(mref));
 283                                     }
 284 
 285                                     String pn = provider.name();
 286                                     if (!nameToReference.containsKey(pn)) {
 287                                         addFoundModule(mref);
 288                                         q.push(provider);
 289                                     }
 290                                 }
 291                             }
 292                         }
 293                     }
 294                 }
 295             }
 296 
 297             candidateConsumers = resolve(q);
 298         } while (!candidateConsumers.isEmpty());
 299 
 300         return this;
 301     }
 302 
 303     /**
 304      * Add all automatic modules that have not already been found to the
 305      * nameToReference map.
 306      */
 307     private Set<ModuleReference> addFoundAutomaticModules() {
 308         Set<ModuleReference> result = new HashSet<>();
 309         findAll().forEach(mref -> {
 310             String mn = mref.descriptor().name();
 311             if (mref.descriptor().isAutomatic() && !nameToReference.containsKey(mn)) {
 312                 addFoundModule(mref);
 313                 result.add(mref);
 314             }
 315         });
 316         return result;
 317     }
 318 
 319     /**
 320      * Add the module to the nameToReference map. Also check any constraints on
 321      * the target platform with the constraints of other modules.
 322      */
 323     private void addFoundModule(ModuleReference mref) {
 324         String mn = mref.descriptor().name();
 325 
 326         if (mref instanceof ModuleReferenceImpl) {
 327             ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
 328             if (target != null)
 329                 checkTargetPlatform(mn, target);
 330         }
 331 
 332         nameToReference.put(mn, mref);
 333     }
 334 
 335     /**
 336      * Check that the module's constraints on the target platform does
 337      * conflict with the constraint of other modules resolved so far.
 338      */
 339     private void checkTargetPlatform(String mn, ModuleTarget target) {
 340         String value = target.targetPlatform();
 341         if (value != null) {
 342             if (targetPlatform == null) {
 343                 targetPlatform = value;
 344             } else {
 345                 if (!value.equals(targetPlatform)) {
 346                     findFail("Module %s has constraints on target platform (%s)"
 347                              + " that conflict with other modules: %s", mn,
 348                              value, targetPlatform);
 349                 }
 350             }
 351         }
 352     }
 353 
 354     /**
 355      * Execute post-resolution checks and returns the module graph of resolved
 356      * modules as a map.
 357      */
 358     Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) {
 359         detectCycles();
 360         checkHashes();
 361         Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
 362         checkExportSuppliers(graph);
 363         return graph;
 364     }
 365 
 366     /**
 367      * Checks the given module graph for cycles.
 368      *
 369      * For now the implementation is a simple depth first search on the
 370      * dependency graph. We'll replace this later, maybe with Tarjan.
 371      */
 372     private void detectCycles() {
 373         visited = new HashSet<>();
 374         visitPath = new LinkedHashSet<>(); // preserve insertion order
 375         for (ModuleReference mref : nameToReference.values()) {
 376             visit(mref.descriptor());
 377         }
 378         visited.clear();
 379     }
 380 
 381     // the modules that were visited
 382     private Set<ModuleDescriptor> visited;
 383 
 384     // the modules in the current visit path
 385     private Set<ModuleDescriptor> visitPath;
 386 
 387     private void visit(ModuleDescriptor descriptor) {
 388         if (!visited.contains(descriptor)) {
 389             boolean added = visitPath.add(descriptor);
 390             if (!added) {
 391                 resolveFail("Cycle detected: %s", cycleAsString(descriptor));
 392             }
 393             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 394                 String dn = requires.name();
 395 
 396                 ModuleReference mref = nameToReference.get(dn);
 397                 if (mref != null) {
 398                     ModuleDescriptor other = mref.descriptor();
 399                     if (other != descriptor) {
 400                         // dependency is in this configuration
 401                         visit(other);
 402                     }
 403                 }
 404             }
 405             visitPath.remove(descriptor);
 406             visited.add(descriptor);
 407         }
 408     }
 409 
 410     /**
 411      * Returns a String with a list of the modules in a detected cycle.
 412      */
 413     private String cycleAsString(ModuleDescriptor descriptor) {
 414         List<ModuleDescriptor> list = new ArrayList<>(visitPath);
 415         list.add(descriptor);
 416         int index = list.indexOf(descriptor);
 417         return list.stream()
 418                 .skip(index)
 419                 .map(ModuleDescriptor::name)
 420                 .collect(Collectors.joining(" -> "));
 421     }
 422 
 423 
 424     /**
 425      * Checks the hashes in the module descriptor to ensure that they match
 426      * any recorded hashes.
 427      */
 428     private void checkHashes() {
 429         for (ModuleReference mref : nameToReference.values()) {
 430 
 431             // get the recorded hashes, if any
 432             if (!(mref instanceof ModuleReferenceImpl))
 433                 continue;
 434             ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
 435             if (hashes == null)
 436                 continue;
 437 
 438             ModuleDescriptor descriptor = mref.descriptor();
 439             String algorithm = hashes.algorithm();
 440             for (String dn : hashes.names()) {
 441                 ModuleReference mref2 = nameToReference.get(dn);
 442                 if (mref2 == null) {
 443                     ResolvedModule resolvedModule = findInParent(dn);
 444                     if (resolvedModule != null)
 445                         mref2 = resolvedModule.reference();
 446                 }
 447                 if (mref2 == null)
 448                     continue;
 449 
 450                 if (!(mref2 instanceof ModuleReferenceImpl)) {
 451                     findFail("Unable to compute the hash of module %s", dn);
 452                 }
 453 
 454                 ModuleReferenceImpl other = (ModuleReferenceImpl)mref2;
 455                 if (other != null) {
 456                     byte[] recordedHash = hashes.hashFor(dn);
 457                     byte[] actualHash = other.computeHash(algorithm);
 458                     if (actualHash == null)
 459                         findFail("Unable to compute the hash of module %s", dn);
 460                     if (!Arrays.equals(recordedHash, actualHash)) {
 461                         findFail("Hash of %s (%s) differs to expected hash (%s)" +
 462                                  " recorded in %s", dn, toHexString(actualHash),
 463                                  toHexString(recordedHash), descriptor.name());
 464                     }
 465                 }
 466             }
 467 
 468         }
 469     }
 470 
 471     private static String toHexString(byte[] ba) {
 472         StringBuilder sb = new StringBuilder(ba.length * 2);
 473         for (byte b: ba) {
 474             sb.append(String.format("%02x", b & 0xff));
 475         }
 476         return sb.toString();
 477     }
 478 
 479 
 480     /**
 481      * Computes the readability graph for the modules in the given Configuration.
 482      *
 483      * The readability graph is created by propagating "requires" through the
 484      * "requires transitive" edges of the module dependence graph. So if the
 485      * module dependence graph has m1 requires m2 && m2 requires transitive m3
 486      * then the resulting readability graph will contain m1 reads m2, m1 reads m3,
 487      * and m2 reads m3.
 488      */
 489     private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
 490 
 491         // initial capacity of maps to avoid resizing
 492         int capacity = 1 + (4 * nameToReference.size())/ 3;
 493 
 494         // the "reads" graph starts as a module dependence graph and
 495         // is iteratively updated to be the readability graph
 496         Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity);
 497 
 498         // the "requires transitive" graph, contains requires transitive edges only
 499         Map<ResolvedModule, Set<ResolvedModule>> g2;
 500 
 501         // need "requires transitive" from the modules in parent configurations
 502         // as there may be selected modules that have a dependency on modules in
 503         // the parent configuration.
 504         if (ModuleLayer.boot() == null) {
 505             g2 = new HashMap<>(capacity);
 506         } else {
 507             g2 = parents.stream()
 508                 .flatMap(Configuration::configurations)
 509                 .distinct()
 510                 .flatMap(c ->
 511                     c.modules().stream().flatMap(m1 ->
 512                         m1.descriptor().requires().stream()
 513                             .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE))
 514                             .flatMap(r -> {
 515                                 Optional<ResolvedModule> m2 = c.findModule(r.name());
 516                                 assert m2.isPresent()
 517                                         || r.modifiers().contains(Modifier.STATIC);
 518                                 return m2.stream();
 519                             })
 520                             .map(m2 -> Map.entry(m1, m2))
 521                     )
 522                 )
 523                 // stream of m1->m2
 524                 .collect(Collectors.groupingBy(Map.Entry::getKey,
 525                         HashMap::new,
 526                         Collectors.mapping(Map.Entry::getValue, Collectors.toSet())
 527             ));
 528         }
 529 
 530         // populate g1 and g2 with the dependences from the selected modules
 531 
 532         Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity);
 533 
 534         for (ModuleReference mref : nameToReference.values()) {
 535             ModuleDescriptor descriptor = mref.descriptor();
 536             String name = descriptor.name();
 537 
 538             ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
 539 
 540             Set<ResolvedModule> reads = new HashSet<>();
 541             Set<ResolvedModule> requiresTransitive = new HashSet<>();
 542 
 543             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 544                 String dn = requires.name();
 545 
 546                 ResolvedModule m2 = null;
 547                 ModuleReference mref2 = nameToReference.get(dn);
 548                 if (mref2 != null) {
 549                     // same configuration
 550                     m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
 551                 } else {
 552                     // parent configuration
 553                     m2 = findInParent(dn);
 554                     if (m2 == null) {
 555                         assert requires.modifiers().contains(Modifier.STATIC);
 556                         continue;
 557                     }
 558                 }
 559 
 560                 // m1 requires m2 => m1 reads m2
 561                 reads.add(m2);
 562 
 563                 // m1 requires transitive m2
 564                 if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
 565                     requiresTransitive.add(m2);
 566                 }
 567 
 568             }
 569 
 570             // automatic modules read all selected modules and all modules
 571             // in parent configurations
 572             if (descriptor.isAutomatic()) {
 573 
 574                 // reads all selected modules
 575                 // `requires transitive` all selected automatic modules
 576                 for (ModuleReference mref2 : nameToReference.values()) {
 577                     ModuleDescriptor descriptor2 = mref2.descriptor();
 578                     String name2 = descriptor2.name();
 579 
 580                     if (!name.equals(name2)) {
 581                         ResolvedModule m2
 582                             = computeIfAbsent(nameToResolved, name2, cf, mref2);
 583                         reads.add(m2);
 584                         if (descriptor2.isAutomatic())
 585                             requiresTransitive.add(m2);
 586                     }
 587                 }
 588 
 589                 // reads all modules in parent configurations
 590                 // `requires transitive` all automatic modules in parent
 591                 // configurations
 592                 for (Configuration parent : parents) {
 593                     parent.configurations()
 594                             .map(Configuration::modules)
 595                             .flatMap(Set::stream)
 596                             .forEach(m -> {
 597                                 reads.add(m);
 598                                 if (m.reference().descriptor().isAutomatic())
 599                                     requiresTransitive.add(m);
 600                             });
 601                 }
 602             }
 603 
 604             g1.put(m1, reads);
 605             g2.put(m1, requiresTransitive);
 606         }
 607 
 608         // Iteratively update g1 until there are no more requires transitive
 609         // to propagate
 610         boolean changed;
 611         List<ResolvedModule> toAdd = new ArrayList<>();
 612         do {
 613             changed = false;
 614             for (Set<ResolvedModule> m1Reads : g1.values()) {
 615                 for (ResolvedModule m2 : m1Reads) {
 616                     Set<ResolvedModule> m2RequiresTransitive = g2.get(m2);
 617                     if (m2RequiresTransitive != null) {
 618                         for (ResolvedModule m3 : m2RequiresTransitive) {
 619                             if (!m1Reads.contains(m3)) {
 620                                 // m1 reads m2, m2 requires transitive m3
 621                                 // => need to add m1 reads m3
 622                                 toAdd.add(m3);
 623                             }
 624                         }
 625                     }
 626                 }
 627                 if (!toAdd.isEmpty()) {
 628                     m1Reads.addAll(toAdd);
 629                     toAdd.clear();
 630                     changed = true;
 631                 }
 632             }
 633         } while (changed);
 634 
 635         return g1;
 636     }
 637 
 638     /**
 639      * Equivalent to
 640      * <pre>{@code
 641      *     map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
 642      * </pre>}
 643      */
 644     private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
 645                                            String name,
 646                                            Configuration cf,
 647                                            ModuleReference mref)
 648     {
 649         ResolvedModule m = map.get(name);
 650         if (m == null) {
 651             m = new ResolvedModule(cf, mref);
 652             map.put(name, m);
 653         }
 654         return m;
 655     }
 656 
 657 
 658     /**
 659      * Checks the readability graph to ensure that
 660      * <ol>
 661      *   <li><p> A module does not read two or more modules with the same name.
 662      *   This includes the case where a module reads another another with the
 663      *   same name as itself. </p></li>
 664      *   <li><p> Two or more modules in the configuration don't export the same
 665      *   package to a module that reads both. This includes the case where a
 666      *   module {@code M} containing package {@code p} reads another module
 667      *   that exports {@code p} to {@code M}. </p></li>
 668      *   <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}"
 669      *   or "{@code provides p.S with ...}" but package {@code p} is neither
 670      *   in module {@code M} nor exported to {@code M} by any module that
 671      *   {@code M} reads. </p></li>
 672      * </ol>
 673      */
 674     private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
 675 
 676         for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
 677             ModuleDescriptor descriptor1 = e.getKey().descriptor();
 678             String name1 = descriptor1.name();
 679 
 680             // the names of the modules that are read (including self)
 681             Set<String> names = new HashSet<>();
 682             names.add(name1);
 683 
 684             // the map of packages that are local or exported to descriptor1
 685             Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
 686 
 687             // local packages
 688             Set<String> packages = descriptor1.packages();
 689             for (String pn : packages) {
 690                 packageToExporter.put(pn, descriptor1);
 691             }
 692 
 693             // descriptor1 reads descriptor2
 694             Set<ResolvedModule> reads = e.getValue();
 695             for (ResolvedModule endpoint : reads) {
 696                 ModuleDescriptor descriptor2 = endpoint.descriptor();
 697 
 698                 String name2 = descriptor2.name();
 699                 if (descriptor2 != descriptor1 && !names.add(name2)) {
 700                     if (name2.equals(name1)) {
 701                         resolveFail("Module %s reads another module named %s",
 702                                     name1, name1);
 703                     } else{
 704                         resolveFail("Module %s reads more than one module named %s",
 705                                      name1, name2);
 706                     }
 707                 }
 708 
 709                 if (descriptor2.isAutomatic()) {
 710                     // automatic modules read self and export all packages
 711                     if (descriptor2 != descriptor1) {
 712                         for (String source : descriptor2.packages()) {
 713                             ModuleDescriptor supplier
 714                                 = packageToExporter.putIfAbsent(source, descriptor2);
 715 
 716                             // descriptor2 and 'supplier' export source to descriptor1
 717                             if (supplier != null) {
 718                                 failTwoSuppliers(descriptor1, source, descriptor2, supplier);
 719                             }
 720                         }
 721 
 722                     }
 723                 } else {
 724                     for (ModuleDescriptor.Exports export : descriptor2.exports()) {
 725                         if (export.isQualified()) {
 726                             if (!export.targets().contains(descriptor1.name()))
 727                                 continue;
 728                         }
 729 
 730                         // source is exported by descriptor2
 731                         String source = export.source();
 732                         ModuleDescriptor supplier
 733                             = packageToExporter.putIfAbsent(source, descriptor2);
 734 
 735                         // descriptor2 and 'supplier' export source to descriptor1
 736                         if (supplier != null) {
 737                             failTwoSuppliers(descriptor1, source, descriptor2, supplier);
 738                         }
 739                     }
 740 
 741                 }
 742             }
 743 
 744             // uses/provides checks not applicable to automatic modules
 745             if (!descriptor1.isAutomatic()) {
 746 
 747                 // uses S
 748                 for (String service : descriptor1.uses()) {
 749                     String pn = packageName(service);
 750                     if (!packageToExporter.containsKey(pn)) {
 751                         resolveFail("Module %s does not read a module that exports %s",
 752                                     descriptor1.name(), pn);
 753                     }
 754                 }
 755 
 756                 // provides S
 757                 for (ModuleDescriptor.Provides provides : descriptor1.provides()) {
 758                     String pn = packageName(provides.service());
 759                     if (!packageToExporter.containsKey(pn)) {
 760                         resolveFail("Module %s does not read a module that exports %s",
 761                                     descriptor1.name(), pn);
 762                     }
 763                 }
 764 
 765             }
 766 
 767         }
 768 
 769     }
 770 
 771     /**
 772      * Fail because a module in the configuration exports the same package to
 773      * a module that reads both. This includes the case where a module M
 774      * containing a package p reads another module that exports p to at least
 775      * module M.
 776      */
 777     private void failTwoSuppliers(ModuleDescriptor descriptor,
 778                                   String source,
 779                                   ModuleDescriptor supplier1,
 780                                   ModuleDescriptor supplier2) {
 781 
 782         if (supplier2 == descriptor) {
 783             ModuleDescriptor tmp = supplier1;
 784             supplier1 = supplier2;
 785             supplier2 = tmp;
 786         }
 787 
 788         if (supplier1 == descriptor) {
 789             resolveFail("Module %s contains package %s"
 790                          + ", module %s exports package %s to %s",
 791                     descriptor.name(),
 792                     source,
 793                     supplier2.name(),
 794                     source,
 795                     descriptor.name());
 796         } else {
 797             resolveFail("Modules %s and %s export package %s to module %s",
 798                     supplier1.name(),
 799                     supplier2.name(),
 800                     source,
 801                     descriptor.name());
 802         }
 803 
 804     }
 805 
 806 
 807     /**
 808      * Find a module of the given name in the parent configurations
 809      */
 810     private ResolvedModule findInParent(String mn) {
 811         for (Configuration parent : parents) {
 812             Optional<ResolvedModule> om = parent.findModule(mn);
 813             if (om.isPresent())
 814                 return om.get();
 815         }
 816         return null;
 817     }
 818 
 819 
 820     /**
 821      * Invokes the beforeFinder to find method to find the given module.
 822      */
 823     private ModuleReference findWithBeforeFinder(String mn) {
 824 
 825         return beforeFinder.find(mn).orElse(null);
 826 
 827     }
 828 
 829     /**
 830      * Invokes the afterFinder to find method to find the given module.
 831      */
 832     private ModuleReference findWithAfterFinder(String mn) {
 833         return afterFinder.find(mn).orElse(null);
 834     }
 835 
 836     /**
 837      * Returns the set of all modules that are observable with the before
 838      * and after ModuleFinders.
 839      */
 840     private Set<ModuleReference> findAll() {
 841         Set<ModuleReference> beforeModules = beforeFinder.findAll();
 842         Set<ModuleReference> afterModules = afterFinder.findAll();
 843 
 844         if (afterModules.isEmpty())
 845             return beforeModules;
 846 
 847         if (beforeModules.isEmpty()
 848                 && parents.size() == 1
 849                 && parents.get(0) == Configuration.empty())
 850             return afterModules;
 851 
 852         Set<ModuleReference> result = new HashSet<>(beforeModules);
 853         for (ModuleReference mref : afterModules) {
 854             String name = mref.descriptor().name();
 855             if (!beforeFinder.find(name).isPresent()
 856                     && findInParent(name) == null) {
 857                 result.add(mref);
 858             }
 859         }
 860 
 861         return result;
 862     }
 863 
 864     /**
 865      * Returns the package name
 866      */
 867     private static String packageName(String cn) {
 868         int index = cn.lastIndexOf(".");
 869         return (index == -1) ? "" : cn.substring(0, index);
 870     }
 871 
 872     /**
 873      * Throw FindException with the given format string and arguments
 874      */
 875     private static void findFail(String fmt, Object ... args) {
 876         String msg = String.format(fmt, args);
 877         throw new FindException(msg);
 878     }
 879 
 880     /**
 881      * Throw ResolutionException with the given format string and arguments
 882      */
 883     private static void resolveFail(String fmt, Object ... args) {
 884         String msg = String.format(fmt, args);
 885         throw new ResolutionException(msg);
 886     }
 887 
 888     /**
 889      * Tracing support
 890      */
 891 
 892     private boolean isTracing() {
 893         return traceOutput != null;
 894     }
 895 
 896     private void trace(String fmt, Object ... args) {
 897         if (traceOutput != null) {
 898             traceOutput.format(fmt, args);
 899             traceOutput.println();
 900         }
 901     }
 902 
 903     private String nameAndInfo(ModuleReference mref) {
 904         ModuleDescriptor descriptor = mref.descriptor();
 905         StringBuilder sb = new StringBuilder(descriptor.name());
 906         mref.location().ifPresent(uri -> sb.append(" " + uri));
 907         if (descriptor.isAutomatic())
 908             sb.append(" automatic");
 909         return sb.toString();
 910     }
 911 }