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