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