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