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