30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
31 import java.lang.reflect.Layer;
32 import java.util.ArrayDeque;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Deque;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Optional;
44 import java.util.Set;
45 import java.util.StringJoiner;
46 import java.util.stream.Collectors;
47
48 import jdk.internal.module.ModuleHashes;
49 import jdk.internal.module.ModuleReferenceImpl;
50
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 // module constraints on target platform
70 private String osName;
71 private String osArch;
72 private String osVersion;
73
74 String osName() { return osName; }
75 String osArch() { return osArch; }
76 String osVersion() { return osVersion; }
77
78 /**
79 * @throws IllegalArgumentException if there are more than one parent and
80 * the constraints on the target platform conflict
81 */
82 Resolver(ModuleFinder beforeFinder,
83 List<Configuration> parents,
84 ModuleFinder afterFinder,
85 PrintStream traceOutput) {
86 this.beforeFinder = beforeFinder;
87 this.parents = parents;
88 this.afterFinder = afterFinder;
89 this.traceOutput = traceOutput;
90
91 // record constraints on target platform, checking that they don't conflict
92 for (Configuration parent : parents) {
93 String value = parent.osName();
94 if (value != null) {
95 if (osName == null) {
96 osName = value;
97 } else {
98 if (!value.equals(osName)) {
99 failParentConflict("Operating System", osName, value);
100 }
101 }
102 }
103 value = parent.osArch();
104 if (value != null) {
105 if (osArch == null) {
106 osArch = value;
107 } else {
108 if (!value.equals(osArch)) {
109 failParentConflict("OS architecture", osArch, value);
110 }
111 }
112 }
113 value = parent.osVersion();
114 if (value != null) {
115 if (osVersion == null) {
116 osVersion = value;
117 } else {
118 if (!value.equals(osVersion)) {
119 failParentConflict("OS version", osVersion, value);
120 }
121 }
122 }
123 }
124 }
125
126 private void failParentConflict(String constraint, String s1, String s2) {
127 String msg = "Parents have conflicting constraints on target "
128 + constraint + ": " + s1 + ", " + s2;
129 throw new IllegalArgumentException(msg);
130 }
131
132 /**
133 * Resolves the given named modules.
134 *
135 * @throws ResolutionException
136 */
137 Resolver resolve(Collection<String> roots) {
138
139 // create the visit stack to get us started
140 Deque<ModuleDescriptor> q = new ArrayDeque<>();
141 for (String root : roots) {
142
301 }
302 }
303 }
304 }
305 }
306 }
307 }
308
309 candidateConsumers = resolve(q);
310 } while (!candidateConsumers.isEmpty());
311
312 return this;
313 }
314
315
316 /**
317 * Add the module to the nameToReference map. Also check any constraints on
318 * the target platform with the constraints of other modules.
319 */
320 private void addFoundModule(ModuleReference mref) {
321 ModuleDescriptor descriptor = mref.descriptor();
322 nameToReference.put(descriptor.name(), mref);
323
324 if (descriptor.osName().isPresent()
325 || descriptor.osArch().isPresent()
326 || descriptor.osVersion().isPresent())
327 checkTargetConstraints(descriptor);
328 }
329
330 /**
331 * Check that the module's constraints on the target platform do not
332 * conflict with the constraints of other modules resolved so far or
333 * modules in parent configurations.
334 */
335 private void checkTargetConstraints(ModuleDescriptor descriptor) {
336 String value = descriptor.osName().orElse(null);
337 if (value != null) {
338 if (osName == null) {
339 osName = value;
340 } else {
341 if (!value.equals(osName)) {
342 failTargetConstraint(descriptor);
343 }
344 }
345 }
346 value = descriptor.osArch().orElse(null);
347 if (value != null) {
348 if (osArch == null) {
349 osArch = value;
350 } else {
351 if (!value.equals(osArch)) {
352 failTargetConstraint(descriptor);
353 }
354 }
355 }
356 value = descriptor.osVersion().orElse(null);
357 if (value != null) {
358 if (osVersion == null) {
359 osVersion = value;
360 } else {
361 if (!value.equals(osVersion)) {
362 failTargetConstraint(descriptor);
363 }
364 }
365 }
366 }
367
368 private void failTargetConstraint(ModuleDescriptor md) {
369 String s1 = targetAsString(osName, osArch, osVersion);
370 String s2 = targetAsString(md);
371 findFail("Module %s has constraints on target platform that conflict" +
372 " with other modules: %s, %s", md.name(), s1, s2);
373 }
374
375 private String targetAsString(ModuleDescriptor descriptor) {
376 String osName = descriptor.osName().orElse(null);
377 String osArch = descriptor.osArch().orElse(null);
378 String osVersion = descriptor.osVersion().orElse(null);
379 return targetAsString(osName, osArch, osVersion);
380 }
381
382 private String targetAsString(String osName, String osArch, String osVersion) {
383 return new StringJoiner("-")
384 .add(Objects.toString(osName, "*"))
385 .add(Objects.toString(osArch, "*"))
386 .add(Objects.toString(osVersion, "*"))
387 .toString();
388 }
389
390
391 /**
392 * Execute post-resolution checks and returns the module graph of resolved
393 * modules as {@code Map}. The resolved modules will be in the given
394 * configuration.
395 *
396 * @param check {@true} to execute the post resolution checks
397 */
398 Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
399 boolean check)
400 {
401 if (isTracing()) {
402 trace("Result:");
403 Set<String> names = nameToReference.keySet();
404 names.stream().sorted().forEach(name -> trace(" %s", name));
405 }
406
695 * Equivalent to
696 * <pre>{@code
697 * map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
698 * </pre>}
699 */
700 private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
701 String name,
702 Configuration cf,
703 ModuleReference mref)
704 {
705 ResolvedModule m = map.get(name);
706 if (m == null) {
707 m = new ResolvedModule(cf, mref);
708 map.put(name, m);
709 }
710 return m;
711 }
712
713
714 /**
715 * Checks the readability graph to ensure that no two modules export the
716 * same package to a module. This includes the case where module M has
717 * a local package P and M reads another module that exports P to M.
718 * Also checks the uses/provides of module M to ensure that it reads a
719 * module that exports the package of the service type to M.
720 */
721 private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
722
723 for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
724 ModuleDescriptor descriptor1 = e.getKey().descriptor();
725
726 // the map of packages that are local or exported to descriptor1
727 Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
728
729 // local packages
730 Set<String> packages = descriptor1.packages();
731 for (String pn : packages) {
732 packageToExporter.put(pn, descriptor1);
733 }
734
735 // descriptor1 reads descriptor2
736 Set<ResolvedModule> reads = e.getValue();
737 for (ResolvedModule endpoint : reads) {
738 ModuleDescriptor descriptor2 = endpoint.descriptor();
739
740 if (descriptor2.isAutomatic()) {
741 // automatic modules read self and export all packages
742 if (descriptor2 != descriptor1){
743 for (String source : descriptor2.packages()) {
744 ModuleDescriptor supplier
745 = packageToExporter.putIfAbsent(source, descriptor2);
746
747 // descriptor2 and 'supplier' export source to descriptor1
748 if (supplier != null) {
749 failTwoSuppliers(descriptor1, source, descriptor2, supplier);
750 }
751 }
752
753 }
754 } else {
755 for (ModuleDescriptor.Exports export : descriptor2.exports()) {
756 if (export.isQualified()) {
757 if (!export.targets().contains(descriptor1.name()))
758 continue;
759 }
760
761 // source is exported by descriptor2
762 String source = export.source();
|
30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
31 import java.lang.reflect.Layer;
32 import java.util.ArrayDeque;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Deque;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Objects;
43 import java.util.Optional;
44 import java.util.Set;
45 import java.util.StringJoiner;
46 import java.util.stream.Collectors;
47
48 import jdk.internal.module.ModuleHashes;
49 import jdk.internal.module.ModuleReferenceImpl;
50 import jdk.internal.module.ModuleTarget;
51
52 /**
53 * The resolver used by {@link Configuration#resolve} and {@link
54 * Configuration#resolveAndBind}.
55 *
56 * @implNote The resolver is used at VM startup and so deliberately avoids
57 * using lambda and stream usages in code paths used during startup.
58 */
59
60 final class Resolver {
61
62 private final ModuleFinder beforeFinder;
63 private final List<Configuration> parents;
64 private final ModuleFinder afterFinder;
65 private final PrintStream traceOutput;
66
67 // maps module name to module reference
68 private final Map<String, ModuleReference> nameToReference = new HashMap<>();
69
70 // module constraints on target platform
71 private String osName;
72 private String osArch;
73
74 String osName() { return osName; }
75 String osArch() { return osArch; }
76
77 /**
78 * @throws IllegalArgumentException if there are more than one parent and
79 * the constraints on the target platform conflict
80 */
81 Resolver(ModuleFinder beforeFinder,
82 List<Configuration> parents,
83 ModuleFinder afterFinder,
84 PrintStream traceOutput) {
85 this.beforeFinder = beforeFinder;
86 this.parents = parents;
87 this.afterFinder = afterFinder;
88 this.traceOutput = traceOutput;
89
90 // record constraints on target platform, checking that they don't conflict
91 for (Configuration parent : parents) {
92 String value = parent.osName();
93 if (value != null) {
94 if (osName == null) {
95 osName = value;
96 } else {
97 if (!value.equals(osName)) {
98 failParentConflict("Operating System", osName, value);
99 }
100 }
101 }
102 value = parent.osArch();
103 if (value != null) {
104 if (osArch == null) {
105 osArch = value;
106 } else {
107 if (!value.equals(osArch)) {
108 failParentConflict("OS architecture", osArch, value);
109 }
110 }
111 }
112 }
113 }
114
115 private void failParentConflict(String constraint, String s1, String s2) {
116 String msg = "Parents have conflicting constraints on target "
117 + constraint + ": " + s1 + ", " + s2;
118 throw new IllegalArgumentException(msg);
119 }
120
121 /**
122 * Resolves the given named modules.
123 *
124 * @throws ResolutionException
125 */
126 Resolver resolve(Collection<String> roots) {
127
128 // create the visit stack to get us started
129 Deque<ModuleDescriptor> q = new ArrayDeque<>();
130 for (String root : roots) {
131
290 }
291 }
292 }
293 }
294 }
295 }
296 }
297
298 candidateConsumers = resolve(q);
299 } while (!candidateConsumers.isEmpty());
300
301 return this;
302 }
303
304
305 /**
306 * Add the module to the nameToReference map. Also check any constraints on
307 * the target platform with the constraints of other modules.
308 */
309 private void addFoundModule(ModuleReference mref) {
310 String mn = mref.descriptor().name();
311
312 if (mref instanceof ModuleReferenceImpl) {
313 ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
314 if (target != null)
315 checkTargetConstraints(mn, target);
316 }
317
318 nameToReference.put(mn, mref);
319 }
320
321 /**
322 * Check that the module's constraints on the target platform do not
323 * conflict with the constraints of other modules resolved so far or
324 * modules in parent configurations.
325 */
326 private void checkTargetConstraints(String mn, ModuleTarget target) {
327 String value = target.osName();
328 if (value != null) {
329 if (osName == null) {
330 osName = value;
331 } else {
332 if (!value.equals(osName)) {
333 failTargetConstraint(mn, target);
334 }
335 }
336 }
337 value = target.osArch();
338 if (value != null) {
339 if (osArch == null) {
340 osArch = value;
341 } else {
342 if (!value.equals(osArch)) {
343 failTargetConstraint(mn, target);
344 }
345 }
346 }
347 }
348
349 private void failTargetConstraint(String mn, ModuleTarget target) {
350 String s1 = targetAsString(osName, osArch);
351 String s2 = targetAsString(target.osName(), target.osArch());
352 findFail("Module %s has constraints on target platform (%s) that"
353 + " conflict with other modules: %s", mn, s1, s2);
354 }
355
356 private String targetAsString(ModuleTarget target) {
357 return targetAsString(target.osName(), target.osArch());
358 }
359
360 private String targetAsString(String osName, String osArch) {
361 return new StringJoiner("-")
362 .add(Objects.toString(osName, "*"))
363 .add(Objects.toString(osArch, "*"))
364 .toString();
365 }
366
367
368 /**
369 * Execute post-resolution checks and returns the module graph of resolved
370 * modules as {@code Map}. The resolved modules will be in the given
371 * configuration.
372 *
373 * @param check {@true} to execute the post resolution checks
374 */
375 Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
376 boolean check)
377 {
378 if (isTracing()) {
379 trace("Result:");
380 Set<String> names = nameToReference.keySet();
381 names.stream().sorted().forEach(name -> trace(" %s", name));
382 }
383
672 * Equivalent to
673 * <pre>{@code
674 * map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
675 * </pre>}
676 */
677 private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
678 String name,
679 Configuration cf,
680 ModuleReference mref)
681 {
682 ResolvedModule m = map.get(name);
683 if (m == null) {
684 m = new ResolvedModule(cf, mref);
685 map.put(name, m);
686 }
687 return m;
688 }
689
690
691 /**
692 * Checks the readability graph to ensure that
693 * <ol>
694 * <li><p> A module does not read two or more modules with the same name.
695 * This includes the case where a module reads another another with the
696 * same name as itself. </p></li>
697 * <li><p> Two or more modules in the configuration don't export the same
698 * package to a module that reads both. This includes the case where a
699 * module {@code M} containing package {@code p} reads another module
700 * that exports {@code p} to {@code M}. </p></li>
701 * <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}"
702 * or "{@code provides p.S with ...}" but package {@code p} is neither
703 * in module {@code M} nor exported to {@code M} by any module that
704 * {@code M} reads. </p></li>
705 * </ol>
706 */
707 private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
708
709 for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
710 ModuleDescriptor descriptor1 = e.getKey().descriptor();
711 String name1 = descriptor1.name();
712
713 // the names of the modules that are read (including self)
714 Set<String> names = new HashSet<>();
715 names.add(name1);
716
717 // the map of packages that are local or exported to descriptor1
718 Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
719
720 // local packages
721 Set<String> packages = descriptor1.packages();
722 for (String pn : packages) {
723 packageToExporter.put(pn, descriptor1);
724 }
725
726 // descriptor1 reads descriptor2
727 Set<ResolvedModule> reads = e.getValue();
728 for (ResolvedModule endpoint : reads) {
729 ModuleDescriptor descriptor2 = endpoint.descriptor();
730
731 String name2 = descriptor2.name();
732 if (descriptor2 != descriptor1 && !names.add(name2)) {
733 if (name2.equals(name1)) {
734 resolveFail("Module %s reads another module named %s",
735 name1, name1);
736 } else{
737 resolveFail("Module %s reads more than one module named %s",
738 name1, name2);
739 }
740 }
741
742 if (descriptor2.isAutomatic()) {
743 // automatic modules read self and export all packages
744 if (descriptor2 != descriptor1) {
745 for (String source : descriptor2.packages()) {
746 ModuleDescriptor supplier
747 = packageToExporter.putIfAbsent(source, descriptor2);
748
749 // descriptor2 and 'supplier' export source to descriptor1
750 if (supplier != null) {
751 failTwoSuppliers(descriptor1, source, descriptor2, supplier);
752 }
753 }
754
755 }
756 } else {
757 for (ModuleDescriptor.Exports export : descriptor2.exports()) {
758 if (export.isQualified()) {
759 if (!export.targets().contains(descriptor1.name()))
760 continue;
761 }
762
763 // source is exported by descriptor2
764 String source = export.source();
|