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