1 /* 2 * Copyright (c) 2009, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 package com.sun.classanalyzer; 25 26 import com.sun.classanalyzer.AnnotatedDependency.OptionalDependency; 27 import java.io.BufferedReader; 28 import java.io.File; 29 import java.io.FileReader; 30 import java.io.IOException; 31 import java.io.PrintWriter; 32 import java.util.ArrayDeque; 33 import java.util.Collection; 34 import java.util.Collections; 35 import java.util.Deque; 36 import java.util.HashSet; 37 import java.util.LinkedHashMap; 38 import java.util.LinkedHashSet; 39 import java.util.LinkedList; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Properties; 43 import java.util.Set; 44 import java.util.TreeMap; 45 import java.util.TreeSet; 46 47 import static com.sun.classanalyzer.Platform.*; 48 import static com.sun.classanalyzer.Trace.*; 49 50 /** 51 * 52 * @author Mandy Chung 53 */ 54 public class Module implements Comparable<Module> { 55 56 private static final Map<String, Module> modules = new LinkedHashMap<String, Module>(); 57 58 /** 59 * Returns the top-level modules that are defined in 60 * the input module config files. 61 * 62 */ 63 static Collection<Module> getTopLevelModules() { 64 Set<Module> result = new LinkedHashSet<Module>(); 65 // always put the boot module first and then the base 66 if (Platform.bootModule() != null) { 67 result.add(Platform.bootModule()); 68 } 69 result.add(findModule(baseModuleName)); 70 71 for (Module m : modules.values()) { 72 if (m.isTopLevel()) { 73 result.add(m); 74 } 75 } 76 return Collections.unmodifiableCollection(result); 77 } 78 79 public static Module addModule(ModuleConfig config) { 80 String name = config.module; 81 if (modules.containsKey(name)) { 82 throw new RuntimeException("module \"" + name + "\" already exists"); 83 } 84 Module m; 85 if (Platform.isBootModule(config.module)) { 86 m = Platform.createBootModule(config); 87 } else { 88 m = new Module(config); 89 } 90 modules.put(name, m); 91 return m; 92 } 93 94 public static Module findModule(String name) { 95 return modules.get(name); 96 } 97 private static String baseModuleName = "base"; 98 private static String version = "7-ea"; 99 100 static void setBaseModule(String name) { 101 baseModuleName = name; 102 } 103 104 static void setVersion(String ver) { 105 version = ver; 106 } 107 private static Properties moduleProps = new Properties(); 108 109 static void setModuleProperties(String file) throws IOException { 110 File f = new File(file); 111 BufferedReader reader = null; 112 try { 113 reader = new BufferedReader(new FileReader(f)); 114 moduleProps.load(reader); 115 } finally { 116 if (reader != null) { 117 reader.close(); 118 } 119 } 120 } 121 private final String name; 122 private final ModuleConfig config; 123 private final Set<Klass> classes; 124 private final Set<ResourceFile> resources; 125 private final Set<Reference> unresolved; 126 private final Map<String, PackageInfo> packages; 127 private final Set<Dependency> dependents; 128 private final Set<Module> members; 129 private final Set<RequiresModule> requires; 130 // update during the analysis 131 private Set<Module> permits; 132 private Module group; 133 private boolean isBaseModule; 134 private int platformApiCount; 135 136 protected Module(ModuleConfig config) { 137 this.name = config.module; 138 this.isBaseModule = name.equals(baseModuleName); 139 this.classes = new TreeSet<Klass>(); 140 this.resources = new TreeSet<ResourceFile>(); 141 this.config = config; 142 this.unresolved = new HashSet<Reference>(); 143 this.dependents = new TreeSet<Dependency>(); 144 this.packages = new TreeMap<String, PackageInfo>(); 145 this.members = new TreeSet<Module>(); 146 this.requires = new TreeSet<RequiresModule>(config.requires()); 147 this.group = this; // initialize to itself 148 this.platformApiCount = 0; 149 } 150 151 String name() { 152 return name; 153 } 154 155 Module group() { 156 return group; 157 } 158 159 boolean isBase() { 160 return isBaseModule; 161 } 162 163 // requires local for JRE modules that are strongly 164 // connected with the boot module 165 boolean isBootConnected() { 166 for (RequiresModule rm : requires) { 167 if (Platform.isBootModule(rm.modulename)) { 168 return true; 169 } 170 } 171 return false; 172 } 173 private Module moduleForRequires; 174 175 synchronized Module toRequiredModule() { 176 if (moduleForRequires == null) { 177 // create a module for external requires if needed 178 moduleForRequires = Platform.toRequiresModule(this); 179 } 180 return moduleForRequires; 181 } 182 183 Set<Module> members() { 184 return members; 185 } 186 187 boolean hasPlatformAPIs() { 188 return platformApiCount > 0; 189 } 190 191 boolean contains(Klass k) { 192 return k != null && classes.contains(k); 193 } 194 195 boolean isEmpty() { 196 return classes.isEmpty() && 197 resources.isEmpty() && 198 mainClass() == null; 199 } 200 201 boolean allowEmpty() { 202 return moduleProps.getProperty(name + ".allow.empty") != null; 203 } 204 205 Module alias() { 206 String mn = moduleProps.getProperty(name + ".alias"); 207 Module m = this; 208 if (mn != null) { 209 m = findModule(mn); 210 if (m == null) { 211 throw new RuntimeException(name + ".alias = " + mn + " not found"); 212 } 213 } 214 return m; 215 } 216 217 protected boolean isTopLevel() { 218 // module with no class is not included except the base module 219 return this.group == this && 220 (isBase() || !isEmpty() || isAggregator() || allowEmpty()); 221 } 222 223 boolean isAggregator() { 224 // a module is an aggregator if it has no class and resource and no main class 225 // but has a list of requires. 226 if (isEmpty() && requires.size() > 0) { 227 // return false if it requires only jdk.boot 228 if (requires.size() == 1) { 229 for (RequiresModule rm : requires) { 230 if (Platform.isBootModule(rm.modulename)) { 231 return false; 232 } 233 } 234 } 235 return true; 236 } 237 238 return false; 239 } 240 241 // fixup permits and requires set after modules are merged 242 void fixupModuleInfo() { 243 Set<Module> newPermits = new TreeSet<Module>(); 244 for (Module m : permits()) { 245 // in case multiple permits from the same group 246 newPermits.add(m.group()); 247 } 248 permits.clear(); 249 permits.addAll(newPermits); 250 251 // fixup requires set 252 Set<RequiresModule> newRequires = new TreeSet<RequiresModule>(); 253 for (RequiresModule rm : requires) { 254 Module req = rm.module(); 255 if (req.isEmpty() && !req.isAggregator()) { 256 // remove from requires set if empty and not a module aggregator 257 continue; 258 } 259 260 newRequires.add(rm); 261 if (req.requirePermits()) { 262 req.permits().add(this.group()); 263 } 264 } 265 requires.clear(); 266 requires.addAll(newRequires); 267 268 // add this to the permits set of its dependences if needed 269 for (Dependency d : dependences()) { 270 if (d.dynamic && !d.optional) { 271 // ignore dynamic dependencies for now 272 continue; 273 } 274 275 // add permits for all local dependencies 276 Module dm = d.module(); 277 if (dm.requirePermits()) { 278 dm.permits().add(this.group()); 279 } 280 } 281 } 282 283 Klass mainClass() { 284 String cls = config.mainClass(); 285 if (cls == null) { 286 return null; 287 } 288 289 Klass k = Klass.findKlass(cls); 290 return k; 291 } 292 293 synchronized Set<Module> permits() { 294 if (permits == null) { 295 this.permits = new TreeSet<Module>(); 296 // initialize the permits set 297 for (String s : config.permits()) { 298 Module m = findModule(s); 299 if (m != null) { 300 permits.add(m.group()); 301 } else { 302 throw new RuntimeException("module " + s + 303 " specified in the permits rule for " + name + " doesn't exist"); 304 } 305 } 306 } 307 return permits; 308 } 309 310 Set<RequiresModule> requires() { 311 return requires; 312 } 313 314 Collection<Dependency> dependents() { 315 Map<Module, Dependency> deps = new LinkedHashMap<Module, Dependency>(); 316 for (Dependency dep : dependents) { 317 Dependency d = deps.get(dep.module()); 318 if (d == null || dep.compareTo(d) > 0) { 319 deps.put(dep.module(), dep); 320 } 321 } 322 return deps.values(); 323 } 324 325 boolean requires(Module m) { 326 for (RequiresModule rm : requires()) { 327 if (rm.module() == m) 328 return true; 329 } 330 return false; 331 } 332 /** 333 * Returns a Collection of Dependency, only one for each dependent 334 * module of the strongest dependency (i.e. 335 * hard static > hard dynamic > optional static > optional dynamic 336 */ 337 Collection<Dependency> dependences() { 338 Set<Dependency> result = new TreeSet<Dependency>(); 339 for (Dependency d : dependents()) { 340 Module dm = d.module(); 341 Module rm = dm; 342 if (!dm.alias().requires(this)) { 343 // use alias as the dependence except this module 344 // is required by the alias that will result in 345 // a recursive dependence. 346 rm = dm.alias(); 347 } 348 if (!isBootConnected()) { 349 // If it's a local module requiring jdk.boot, retain 350 // the original requires; otherwise, use its external 351 // module 352 rm = rm.toRequiredModule(); 353 } 354 355 result.add(new Dependency(rm, d.optional, d.dynamic)); 356 } 357 return result; 358 } 359 360 @Override 361 public int compareTo(Module o) { 362 if (o == null) { 363 return -1; 364 } 365 return name.compareTo(o.name); 366 } 367 368 @Override 369 public String toString() { 370 return name; 371 } 372 373 void addKlass(Klass k) { 374 classes.add(k); 375 k.setModule(this); 376 if (k.isPlatformAPI()) { 377 platformApiCount++; 378 } 379 380 // update package statistics 381 String pkg = k.getPackageName(); 382 PackageInfo pkginfo = packages.get(pkg); 383 if (pkginfo == null) { 384 pkginfo = new PackageInfo(pkg); 385 packages.put(pkg, pkginfo); 386 } 387 388 if (k.exists()) { 389 // only count the class that is parsed 390 pkginfo.add(k.getFileSize()); 391 } 392 } 393 394 void addResource(ResourceFile res) { 395 resources.add(res); 396 res.setModule(this); 397 } 398 399 void processRootsAndReferences() { 400 // start with the root set 401 Deque<Klass> pending = new ArrayDeque<Klass>(); 402 for (Klass k : Klass.getAllClasses()) { 403 if (k.getModule() != null) { 404 continue; 405 } 406 407 String classname = k.getClassName(); 408 if (config.matchesRoot(classname) && !config.isExcluded(classname)) { 409 addKlass(k); 410 pending.add(k); 411 } 412 } 413 414 // follow all references 415 Klass k; 416 while ((k = pending.poll()) != null) { 417 if (!classes.contains(k)) { 418 addKlass(k); 419 } 420 421 for (Klass other : k.getReferencedClasses()) { 422 Module otherModule = other.getModule(); 423 if (otherModule != null && otherModule != this) { 424 // this module is dependent on otherModule 425 addDependency(k, other); 426 continue; 427 } 428 429 if (!classes.contains(other)) { 430 if (config.isExcluded(other.getClassName())) { 431 // reference to an excluded class 432 unresolved.add(new Reference(k, other)); 433 } else { 434 pending.add(other); 435 } 436 } 437 } 438 } 439 440 // add other matching classes that don't require dependency analysis 441 for (Klass c : Klass.getAllClasses()) { 442 if (c.getModule() == null) { 443 String classname = c.getClassName(); 444 if (config.matchesIncludes(classname) && !config.isExcluded(classname)) { 445 addKlass(c); 446 // dependencies 447 for (Klass other : c.getReferencedClasses()) { 448 Module otherModule = other.getModule(); 449 if (otherModule == null) { 450 unresolved.add(new Reference(c, other)); 451 } else { 452 if (otherModule != this) { 453 // this module is dependent on otherModule 454 addDependency(c, other); 455 } 456 } 457 } 458 } 459 } 460 } 461 462 // add other matching classes that don't require dependency analysis 463 for (ResourceFile res : ResourceFile.getAllResources()) { 464 if (res.getModule() == null) { 465 String name = res.getName(); 466 if (config.matchesIncludes(name) && !config.isExcluded(name)) { 467 addResource(res); 468 } 469 } 470 } 471 } 472 473 void addDependency(Klass from, Klass to) { 474 Dependency dep = new Dependency(from, to); 475 dependents.add(dep); 476 } 477 478 void addRequiresModule(Module m) { 479 addRequiresModule(m, false); 480 } 481 482 void addRequiresModule(Module m, boolean optional) { 483 requires.add(new RequiresModule(m, optional)); 484 if (m.requirePermits()) { 485 m.permits().add(this); 486 } 487 } 488 489 boolean requirePermits() { 490 return (name().startsWith("sun.") || 491 permits().size() > 0); 492 } 493 494 void fixupDependencies() { 495 // update dependencies for classes that were allocated to modules after 496 // this module was processed. 497 for (Reference ref : unresolved) { 498 Module m = ref.referree().getModule(); 499 if (m == null || m != this) { 500 addDependency(ref.referrer, ref.referree); 501 } 502 } 503 504 // add dependency due to the main class 505 Klass k = mainClass(); 506 if (k != null) { 507 dependents.add(new Dependency(k.getModule(), false, false)); 508 } 509 fixupAnnotatedDependencies(); 510 } 511 512 private void fixupAnnotatedDependencies() { 513 // add dependencies that this klass may depend on due to the AnnotatedDependency 514 dependents.addAll(AnnotatedDependency.getDependencies(this)); 515 } 516 517 boolean isModuleDependence(Klass k) { 518 Module m = k.getModule(); 519 return m == null || (!classes.contains(k) && !m.isBase()); 520 } 521 522 Module getModuleDependence(Klass k) { 523 if (isModuleDependence(k)) { 524 Module m = k.getModule(); 525 if (group == this && m != null) { 526 // top-level module 527 return m.group; 528 } else { 529 return m; 530 } 531 532 } 533 return null; 534 } 535 536 <P> void visitMember(Set<Module> visited, Visitor<P> visitor, P p) { 537 if (!visited.contains(this)) { 538 visited.add(this); 539 visitor.preVisit(this, p); 540 for (Module m : members) { 541 m.visitMember(visited, visitor, p); 542 visitor.visited(this, m, p); 543 } 544 visitor.postVisit(this, p); 545 } else { 546 throw new RuntimeException("Cycle detected: module " + this.name); 547 } 548 } 549 550 private Set<Module> getDepModules() { 551 Set<Module> deps = new TreeSet<Module>(); 552 for (Dependency d : dependences()) { 553 if (d.dynamic || d.optional) { 554 // ignore dynamic or optional dependencies for now 555 continue; 556 } 557 deps.add(d.module()); 558 } 559 for (RequiresModule req : requires) { 560 if (req.optional) { 561 // ignore optional dependencies for now 562 continue; 563 } 564 deps.add(req.module()); 565 } 566 return deps; 567 } 568 569 <P> void visitDependence(Set<Module> visited, Visitor<P> visitor, P p) { 570 if (!visited.contains(this)) { 571 visited.add(this); 572 573 visitor.preVisit(this, p); 574 for (Module m : getDepModules()) { 575 m.visitDependence(visited, visitor, p); 576 visitor.visited(this, m, p); 577 } 578 visitor.postVisit(this, p); 579 } 580 } 581 582 void addMember(Module m) { 583 // merge class list 584 for (Klass k : m.classes) { 585 classes.add(k); 586 } 587 588 // merge resource list 589 for (ResourceFile res : m.resources) { 590 resources.add(res); 591 } 592 593 platformApiCount += m.platformApiCount; 594 595 // merge the package statistics 596 for (PackageInfo pinfo : m.getPackageInfos()) { 597 String packageName = pinfo.pkgName; 598 PackageInfo pkginfo = packages.get(packageName); 599 if (pkginfo == null) { 600 pkginfo = new PackageInfo(packageName); 601 packages.put(packageName, pkginfo); 602 } 603 604 pkginfo.add(pinfo); 605 } 606 607 // merge all permits and requires set 608 permits().addAll(m.permits()); 609 requires().addAll(m.requires()); 610 } 611 612 static void buildModuleMembers() { 613 // set up module member relationship 614 for (Module m : modules.values()) { 615 m.group = m; // initialize to itself 616 for (String name : m.config.members()) { 617 Module member = modules.get(name); 618 if (member == null) { 619 throw new RuntimeException("module \"" + name + "\" doesn't exist"); 620 } 621 m.members.add(member); 622 } 623 } 624 625 // set up the top-level module 626 Visitor<Module> groupSetter = new Visitor<Module>() { 627 628 public void preVisit(Module m, Module p) { 629 m.group = p; 630 if (p.isBaseModule) { 631 // all members are also base 632 m.isBaseModule = true; 633 } 634 } 635 636 public void visited(Module m, Module child, Module p) { 637 // nop - breadth-first search 638 } 639 640 public void postVisit(Module m, Module p) { 641 // nop - breadth-first search 642 } 643 }; 644 645 // propagate the top-level module to all its members 646 for (Module p : modules.values()) { 647 for (Module m : p.members) { 648 if (m.group == m) { 649 m.visitMember(new TreeSet<Module>(), groupSetter, p); 650 } 651 } 652 } 653 654 Visitor<Module> mergeClassList = new Visitor<Module>() { 655 656 public void preVisit(Module m, Module p) { 657 // nop - depth-first search 658 } 659 660 public void visited(Module m, Module child, Module p) { 661 m.addMember(child); 662 } 663 664 public void postVisit(Module m, Module p) { 665 } 666 }; 667 668 Set<Module> visited = new TreeSet<Module>(); 669 Set<Module> groups = new TreeSet<Module>(); 670 for (Module m : modules.values()) { 671 if (m.group() == m) { 672 groups.add(m); 673 if (m.members().size() > 0) { 674 // merge class list from all its members 675 m.visitMember(visited, mergeClassList, m); 676 } 677 678 // clear the dependencies before fixup 679 m.dependents.clear(); 680 681 // fixup dependencies 682 for (Klass k : m.classes) { 683 for (Klass other : k.getReferencedClasses()) { 684 if (m.isModuleDependence(other)) { 685 // this module is dependent on otherModule 686 m.addDependency(k, other); 687 } 688 } 689 } 690 691 // add dependency due to the main class 692 Klass k = m.mainClass(); 693 if (k != null && m.isModuleDependence(k)) { 694 m.dependents.add(new Dependency(k.getModule().group(), false, false)); 695 } 696 697 // add dependencies that this klass may depend on due to the AnnotatedDependency 698 m.fixupAnnotatedDependencies(); 699 } 700 } 701 } 702 703 Set<Module> orderedDependencies() { 704 Visitor<Set<Module>> walker = new Visitor<Set<Module>>() { 705 706 public void preVisit(Module m, Set<Module> result) { 707 // nop - depth-first search 708 } 709 710 public void visited(Module m, Module child, Set<Module> result) { 711 } 712 713 public void postVisit(Module m, Set<Module> result) { 714 result.add(m); 715 } 716 }; 717 718 Set<Module> visited = new TreeSet<Module>(); 719 Set<Module> result = new LinkedHashSet<Module>(); 720 721 visitDependence(visited, walker, result); 722 return result; 723 } 724 725 class PackageInfo implements Comparable { 726 727 final String pkgName; 728 int count; 729 long filesize; 730 731 PackageInfo(String name) { 732 this.pkgName = name; 733 this.count = 0; 734 this.filesize = 0; 735 } 736 737 void add(PackageInfo pkg) { 738 this.count += pkg.count; 739 this.filesize += pkg.filesize; 740 } 741 742 void add(long size) { 743 count++; 744 filesize += size; 745 746 } 747 748 @Override 749 public int compareTo(Object o) { 750 return pkgName.compareTo(((PackageInfo) o).pkgName); 751 } 752 } 753 754 Set<PackageInfo> getPackageInfos() { 755 return new TreeSet<PackageInfo>(packages.values()); 756 } 757 758 void printSummaryTo(String output) throws IOException { 759 PrintWriter writer = new PrintWriter(output); 760 try { 761 long total = 0L; 762 int count = 0; 763 int nonCoreAPIs = 0; 764 writer.format("%10s\t%10s\t%s%n", "Bytes", "Classes", "Package name"); 765 for (String pkg : packages.keySet()) { 766 PackageInfo info = packages.get(pkg); 767 if (info.count > 0) { 768 if (Platform.isNonCoreAPI(pkg)) { 769 nonCoreAPIs += info.count; 770 writer.format("%10d\t%10d\t%s (*)%n", 771 info.filesize, info.count, pkg); 772 } else { 773 writer.format("%10d\t%10d\t%s%n", 774 info.filesize, info.count, pkg); 775 } 776 total += info.filesize; 777 count += info.count; 778 } 779 } 780 781 782 writer.format("%nTotal: %d bytes (uncompressed) %d classes%n", 783 total, count); 784 writer.format("APIs: %d core %d non-core (*)%n", 785 platformApiCount, nonCoreAPIs); 786 } finally { 787 writer.close(); 788 } 789 790 } 791 792 void printClassListTo(String output) throws IOException { 793 if (classes.isEmpty()) { 794 return; 795 } 796 797 PrintWriter writer = new PrintWriter(output); 798 try { 799 for (Klass c : classes) { 800 if (c.exists()) { 801 writer.format("%s\n", c.getClassFilePathname()); 802 } else { 803 trace("%s in module %s missing\n", c, this); 804 } 805 } 806 807 } finally { 808 writer.close(); 809 } 810 811 } 812 813 void printResourceListTo(String output) throws IOException { 814 // no file created if the module doesn't have any resource file 815 if (resources.isEmpty()) { 816 return; 817 } 818 819 PrintWriter writer = new PrintWriter(output); 820 try { 821 for (ResourceFile res : resources) { 822 writer.format("%s\n", res.getPathname()); 823 } 824 825 } finally { 826 writer.close(); 827 } 828 829 } 830 831 void printDependenciesTo(String output, boolean showDynamic) throws IOException { 832 PrintWriter writer = new PrintWriter(output); 833 try { 834 // classes that this klass may depend on due to the AnnotatedDependency 835 Map<Reference, Set<AnnotatedDependency>> annotatedDeps = AnnotatedDependency.getReferences(this); 836 837 for (Klass klass : classes) { 838 Set<Klass> references = klass.getReferencedClasses(); 839 for (Klass other : references) { 840 String classname = klass.getClassName(); 841 boolean optional = OptionalDependency.isOptional(klass, other); 842 if (optional) { 843 classname = "[optional] " + classname; 844 } 845 846 Module m = getModuleDependence(other); 847 if (m != null || other.getModule() == null) { 848 writer.format("%-40s -> %s (%s)", classname, other, m); 849 Reference ref = new Reference(klass, other); 850 if (annotatedDeps.containsKey(ref)) { 851 for (AnnotatedDependency ad : annotatedDeps.get(ref)) { 852 writer.format(" %s", ad.getTag()); 853 } 854 // printed; so remove the dependency from the annotated deps list 855 annotatedDeps.remove(ref); 856 } 857 writer.format("\n"); 858 } 859 } 860 } 861 862 // print remaining dependencies specified in AnnotatedDependency list 863 if (annotatedDeps.size() > 0) { 864 for (Map.Entry<Reference, Set<AnnotatedDependency>> entry : annotatedDeps.entrySet()) { 865 Reference ref = entry.getKey(); 866 Module m = getModuleDependence(ref.referree); 867 if (m != null || ref.referree.getModule() == null) { 868 String classname = ref.referrer.getClassName(); 869 boolean optional = true; 870 boolean dynamic = true; 871 String tag = ""; 872 for (AnnotatedDependency ad : entry.getValue()) { 873 if (optional && !ad.isOptional()) { 874 optional = false; 875 tag = ad.getTag(); 876 } 877 878 if (!ad.isDynamic()) { 879 dynamic = false; 880 } 881 } 882 if (!showDynamic && optional && dynamic) { 883 continue; 884 } 885 886 if (optional) { 887 classname = "[optional] " + classname; 888 } else if (dynamic) { 889 classname = "[dynamic] " + classname; 890 } 891 writer.format("%-40s -> %s (%s) %s%n", classname, ref.referree, m, tag); 892 } 893 } 894 } 895 } finally { 896 writer.close(); 897 } 898 899 } 900 901 // print module dependency list 902 void printDepModuleListTo(String output) throws IOException { 903 PrintWriter writer = new PrintWriter(output); 904 try { 905 for (Module m : orderedDependencies()) { 906 writer.format("%s\n", m.name()); 907 } 908 if (Platform.legacyModule() != null && 909 (this == Platform.jdkBaseModule() || 910 this == Platform.jdkModule() || 911 this == Platform.jreModule())) { 912 // add legacy module in the modules.list 913 // so that it will install legacy module as well. 914 writer.format("%s\n", Platform.legacyModule()); 915 } 916 } finally { 917 writer.close(); 918 } 919 } 920 921 void printModuleInfoTo(String output) throws IOException { 922 PrintWriter writer = new PrintWriter(output); 923 try { 924 writer.format("module %s @ %s {%n", name, version); 925 String formatSep = " requires"; 926 Map<String, RequiresModule> reqs = new TreeMap<String, RequiresModule>(); 927 for (RequiresModule rm : requires()) { 928 reqs.put(rm.module().name(), rm); 929 } 930 931 for (Dependency dep : dependences()) { 932 Module dm = dep.module(); 933 if (!isBootConnected()) { 934 // If it's a local module requiring jdk.boot, retain 935 // the original requires 936 dm = dm.toRequiredModule(); 937 } 938 939 if (dm == null) { 940 System.err.format("WARNING: module %s has a dependency on null module%n", name); 941 } 942 943 StringBuilder attributes = new StringBuilder(); 944 RequiresModule rm = reqs.get(dm.name()); 945 946 if (rm != null && rm.reexport) { 947 attributes.append(" public"); 948 } 949 950 if (isBootConnected() || (rm != null && rm.local)) { 951 attributes.append(" local"); 952 } 953 954 if (dep.optional || (rm != null && rm.optional)) { 955 attributes.append(" optional"); 956 } 957 958 // FIXME: ignore dynamic dependencies 959 // Filter out optional dependencies for the boot module 960 // which are addded in the jdk.base module instead 961 if (!dep.dynamic || dep.optional) { 962 reqs.remove(dm.name()); 963 writer.format("%s%s %s @ %s;%n", 964 formatSep, 965 attributes.toString(), 966 dep != null ? dm : "null", version); 967 } 968 969 } 970 // additional requires 971 if (reqs.size() > 0) { 972 for (RequiresModule rm : reqs.values()) { 973 StringBuilder attributes = new StringBuilder(); 974 if (rm.reexport) { 975 attributes.append(" public"); 976 } 977 if (rm.optional) { 978 attributes.append(" optional"); 979 } 980 if (isBootConnected() || rm.local) { 981 attributes.append(" local"); 982 } 983 984 writer.format("%s%s %s @ %s;%n", formatSep, attributes.toString(), rm.module(), version); 985 } 986 } 987 988 // permits 989 if (permits().size() > 0) { 990 formatSep = " permits"; 991 for (Module p : permits()) { 992 writer.format("%s %s", formatSep, p); 993 formatSep = ","; 994 } 995 writer.format(";%n"); 996 } 997 if (mainClass() != null) { 998 writer.format(" class %s;%n", mainClass().getClassName()); 999 } 1000 writer.format("}%n"); 1001 } finally { 1002 writer.close(); 1003 } 1004 } 1005 1006 static class Dependency implements Comparable<Dependency> { 1007 1008 protected Module module; 1009 final boolean optional; 1010 final boolean dynamic; 1011 1012 Dependency(Klass from, Klass to) { 1013 // static dependency 1014 this.module = to.getModule() != null ? to.getModule().group() : null; 1015 this.optional = OptionalDependency.isOptional(from, to); 1016 this.dynamic = false; 1017 } 1018 1019 Dependency(Module m, boolean optional, boolean dynamic) { 1020 this.module = m != null ? m.group() : null; 1021 this.optional = optional; 1022 this.dynamic = dynamic; 1023 } 1024 1025 Module module() { 1026 return module; 1027 } 1028 1029 public boolean isLocal(Module from) { 1030 if (module().isBootConnected()) { 1031 // local requires if the requesting module is the boot module 1032 // or it's an aggregate platform module 1033 return true; 1034 } 1035 1036 for (PackageInfo pkg : from.getPackageInfos()) { 1037 // local dependence if any package this module owns is splitted 1038 // across its dependence 1039 for (PackageInfo p : module().getPackageInfos()) { 1040 if (pkg.pkgName.equals(p.pkgName)) { 1041 return true; 1042 } 1043 } 1044 } 1045 return false; 1046 } 1047 1048 @Override 1049 public boolean equals(Object obj) { 1050 if (!(obj instanceof Dependency)) { 1051 return false; 1052 } 1053 if (this == obj) { 1054 return true; 1055 } 1056 1057 Dependency d = (Dependency) obj; 1058 if (this.module() != d.module()) { 1059 return false; 1060 } else { 1061 return this.optional == d.optional && this.dynamic == d.dynamic; 1062 } 1063 } 1064 1065 @Override 1066 public int hashCode() { 1067 int hash = 3; 1068 hash = 19 * hash + (this.module() != null ? this.module().hashCode() : 0); 1069 hash = 19 * hash + (this.optional ? 1 : 0); 1070 hash = 19 * hash + (this.dynamic ? 1 : 0); 1071 return hash; 1072 } 1073 1074 @Override 1075 public int compareTo(Dependency d) { 1076 if (this.equals(d)) { 1077 return 0; 1078 } 1079 1080 // Hard static > hard dynamic > optional static > optional dynamic 1081 if (this.module() == d.module()) { 1082 if (this.optional == d.optional) { 1083 return this.dynamic ? -1 : 1; 1084 } else { 1085 return this.optional ? -1 : 1; 1086 } 1087 } else if (this.module() != null && d.module() != null) { 1088 return (this.module().compareTo(d.module())); 1089 } else { 1090 return (this.module() == null) ? -1 : 1; 1091 } 1092 } 1093 1094 @Override 1095 public String toString() { 1096 String s = module().name(); 1097 if (optional) { 1098 s += " (optional)"; 1099 } else if (dynamic) { 1100 s += " (dynamic)"; 1101 } 1102 return s; 1103 } 1104 } 1105 1106 static class RequiresModule extends Dependency { 1107 1108 final String modulename; 1109 final boolean reexport; 1110 final boolean local; 1111 1112 public RequiresModule(String name, boolean optional, boolean reexport, boolean local) { 1113 super(null, optional, false /* dynamic */); 1114 this.modulename = name; 1115 this.reexport = reexport; 1116 this.local = local; 1117 } 1118 1119 public RequiresModule(Module m, boolean optional) { 1120 super(m, optional, false); 1121 this.modulename = m.name(); 1122 this.reexport = true; 1123 this.local = false; 1124 } 1125 1126 // deferred initialization until it's called. 1127 // must call after all modules are merged. 1128 synchronized Module fixupModule() { 1129 if (module == null) { 1130 Module m = findModule(modulename); 1131 if (m == null) { 1132 throw new RuntimeException("Required module \"" + modulename + "\" doesn't exist"); 1133 } 1134 module = m.group(); 1135 } 1136 return module; 1137 } 1138 1139 @Override 1140 Module module() { 1141 return fixupModule(); 1142 } 1143 1144 @Override 1145 public int compareTo(Dependency d) { 1146 RequiresModule rm = (RequiresModule) d; 1147 if (this.equals(rm)) { 1148 return 0; 1149 } 1150 return modulename.compareTo(rm.modulename); 1151 } 1152 1153 @Override 1154 public boolean equals(Object obj) { 1155 if (!(obj instanceof RequiresModule)) { 1156 return false; 1157 } 1158 if (this == obj) { 1159 return true; 1160 } 1161 1162 RequiresModule d = (RequiresModule) obj; 1163 return this.modulename.equals(d.modulename); 1164 } 1165 1166 @Override 1167 public int hashCode() { 1168 int hash = 3; 1169 hash = 19 * hash + this.modulename.hashCode(); 1170 return hash; 1171 } 1172 1173 @Override 1174 public String toString() { 1175 String s = reexport ? "public " : ""; 1176 if (optional) { 1177 s += "optional "; 1178 } 1179 s += modulename; 1180 return s; 1181 } 1182 } 1183 1184 static class Reference implements Comparable<Reference> { 1185 1186 private final Klass referrer, referree; 1187 1188 Reference(Klass referrer, Klass referree) { 1189 this.referrer = referrer; 1190 this.referree = referree; 1191 } 1192 1193 Klass referrer() { 1194 return referrer; 1195 } 1196 1197 Klass referree() { 1198 return referree; 1199 } 1200 1201 @Override 1202 public int hashCode() { 1203 return referrer.hashCode() ^ referree.hashCode(); 1204 } 1205 1206 @Override 1207 public boolean equals(Object obj) { 1208 if (!(obj instanceof Reference)) { 1209 return false; 1210 } 1211 if (this == obj) { 1212 return true; 1213 } 1214 1215 Reference r = (Reference) obj; 1216 return (this.referrer.equals(r.referrer) && this.referree.equals(r.referree)); 1217 } 1218 1219 @Override 1220 public int compareTo(Reference r) { 1221 int ret = referrer.compareTo(r.referrer); 1222 if (ret == 0) { 1223 ret = referree.compareTo(r.referree); 1224 } 1225 return ret; 1226 } 1227 } 1228 1229 interface Visitor<P> { 1230 1231 public void preVisit(Module m, P param); 1232 1233 public void visited(Module m, Module child, P param); 1234 1235 public void postVisit(Module m, P param); 1236 } 1237 }