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