1 /* 2 * Copyright (c) 2009, 2016, 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 27 package com.sun.tools.javac.comp; 28 29 import java.io.IOException; 30 import java.util.Arrays; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.EnumSet; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.LinkedHashMap; 37 import java.util.LinkedHashSet; 38 import java.util.Map; 39 import java.util.Map.Entry; 40 import java.util.Set; 41 import java.util.function.Predicate; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 import java.util.stream.Stream; 45 46 import javax.lang.model.SourceVersion; 47 import javax.tools.JavaFileManager; 48 import javax.tools.JavaFileManager.Location; 49 import javax.tools.JavaFileObject; 50 import javax.tools.JavaFileObject.Kind; 51 import javax.tools.StandardLocation; 52 53 import com.sun.tools.javac.code.Directive; 54 import com.sun.tools.javac.code.Directive.ExportsDirective; 55 import com.sun.tools.javac.code.Directive.RequiresDirective; 56 import com.sun.tools.javac.code.Directive.RequiresFlag; 57 import com.sun.tools.javac.code.Directive.UsesDirective; 58 import com.sun.tools.javac.code.Flags; 59 import com.sun.tools.javac.code.Kinds; 60 import com.sun.tools.javac.code.ModuleFinder; 61 import com.sun.tools.javac.code.Source; 62 import com.sun.tools.javac.code.Symbol; 63 import com.sun.tools.javac.code.Symbol.ClassSymbol; 64 import com.sun.tools.javac.code.Symbol.Completer; 65 import com.sun.tools.javac.code.Symbol.CompletionFailure; 66 import com.sun.tools.javac.code.Symbol.MethodSymbol; 67 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 68 import com.sun.tools.javac.code.Symbol.PackageSymbol; 69 import com.sun.tools.javac.code.Symtab; 70 import com.sun.tools.javac.code.Type; 71 import com.sun.tools.javac.jvm.ClassWriter; 72 import com.sun.tools.javac.jvm.JNIWriter; 73 import com.sun.tools.javac.main.Option; 74 import com.sun.tools.javac.resources.CompilerProperties.Errors; 75 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 76 import com.sun.tools.javac.tree.JCTree; 77 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 78 import com.sun.tools.javac.tree.JCTree.JCExports; 79 import com.sun.tools.javac.tree.JCTree.JCExpression; 80 import com.sun.tools.javac.tree.JCTree.JCModuleDecl; 81 import com.sun.tools.javac.tree.JCTree.JCPackageDecl; 82 import com.sun.tools.javac.tree.JCTree.JCProvides; 83 import com.sun.tools.javac.tree.JCTree.JCRequires; 84 import com.sun.tools.javac.tree.JCTree.JCUses; 85 import com.sun.tools.javac.tree.TreeInfo; 86 import com.sun.tools.javac.util.Assert; 87 import com.sun.tools.javac.util.Context; 88 import com.sun.tools.javac.util.JCDiagnostic; 89 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 90 import com.sun.tools.javac.util.List; 91 import com.sun.tools.javac.util.ListBuffer; 92 import com.sun.tools.javac.util.Log; 93 import com.sun.tools.javac.util.Name; 94 import com.sun.tools.javac.util.Names; 95 import com.sun.tools.javac.util.Options; 96 97 import static com.sun.tools.javac.code.Flags.UNATTRIBUTED; 98 import static com.sun.tools.javac.code.Kinds.Kind.MDL; 99 import static com.sun.tools.javac.code.TypeTag.CLASS; 100 101 import com.sun.tools.javac.tree.JCTree.JCDirective; 102 import com.sun.tools.javac.tree.JCTree.Tag; 103 104 import static com.sun.tools.javac.code.Flags.ABSTRACT; 105 import static com.sun.tools.javac.code.Flags.PUBLIC; 106 import static com.sun.tools.javac.tree.JCTree.Tag.MODULEDEF; 107 108 /** 109 * TODO: fill in 110 * 111 * <p><b>This is NOT part of any supported API. 112 * If you write code that depends on this, you do so at your own risk. 113 * This code and its internal interfaces are subject to change or 114 * deletion without notice.</b> 115 */ 116 public class Modules extends JCTree.Visitor { 117 private static final String ALL_SYSTEM = "ALL-SYSTEM"; 118 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; 119 120 private final Log log; 121 private final Names names; 122 private final Symtab syms; 123 private final Attr attr; 124 private final TypeEnvs typeEnvs; 125 private final JavaFileManager fileManager; 126 private final ModuleFinder moduleFinder; 127 private final boolean allowModules; 128 129 public final boolean multiModuleMode; 130 public final boolean noModules; 131 132 private final String moduleOverride; 133 134 private final Name java_se; 135 private final Name java_; 136 137 ModuleSymbol defaultModule; 138 139 private final String addExportsOpt; 140 private Map<ModuleSymbol, Set<ExportsDirective>> addExports; 141 private final String addReadsOpt; 142 private Map<ModuleSymbol, Set<RequiresDirective>> addReads; 143 private final String addModsOpt; 144 private final String limitModsOpt; 145 146 private Set<ModuleSymbol> rootModules = Collections.emptySet(); 147 148 public static Modules instance(Context context) { 149 Modules instance = context.get(Modules.class); 150 if (instance == null) 151 instance = new Modules(context); 152 return instance; 153 } 154 155 protected Modules(Context context) { 156 context.put(Modules.class, this); 157 log = Log.instance(context); 158 names = Names.instance(context); 159 syms = Symtab.instance(context); 160 attr = Attr.instance(context); 161 typeEnvs = TypeEnvs.instance(context); 162 moduleFinder = ModuleFinder.instance(context); 163 fileManager = context.get(JavaFileManager.class); 164 allowModules = Source.instance(context).allowModules(); 165 Options options = Options.instance(context); 166 167 moduleOverride = options.get(Option.XMODULE); 168 169 // The following is required, for now, to support building 170 // Swing beaninfo via javadoc. 171 noModules = options.isSet("noModules"); 172 173 multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); 174 ClassWriter classWriter = ClassWriter.instance(context); 175 classWriter.multiModuleMode = multiModuleMode; 176 JNIWriter jniWriter = JNIWriter.instance(context); 177 jniWriter.multiModuleMode = multiModuleMode; 178 179 java_se = names.fromString("java.se"); 180 java_ = names.fromString("java."); 181 182 addExportsOpt = options.get(Option.XADDEXPORTS); 183 addReadsOpt = options.get(Option.XADDREADS); 184 addModsOpt = options.get(Option.ADDMODS); 185 limitModsOpt = options.get(Option.LIMITMODS); 186 } 187 188 int depth = -1; 189 private void dprintln(String msg) { 190 for (int i = 0; i < depth; i++) 191 System.err.print(" "); 192 System.err.println(msg); 193 } 194 195 public boolean enter(List<JCCompilationUnit> trees, ClassSymbol c) { 196 if (!allowModules || noModules) { 197 for (JCCompilationUnit tree: trees) { 198 tree.modle = syms.noModule; 199 } 200 defaultModule = syms.noModule; 201 return true; 202 } 203 204 int startErrors = log.nerrors; 205 206 depth++; 207 try { 208 // scan trees for module defs 209 Set<ModuleSymbol> roots = enterModules(trees, c); 210 211 setCompilationUnitModules(trees, roots); 212 213 if (!roots.isEmpty() && this.rootModules.isEmpty()) { 214 this.rootModules = roots; 215 allModules(); //ensure errors reported 216 } 217 218 for (ModuleSymbol msym: roots) { 219 msym.complete(); 220 } 221 } finally { 222 depth--; 223 } 224 225 return (log.nerrors == startErrors); 226 } 227 228 public Completer getCompleter() { 229 return mainCompleter; 230 } 231 232 public ModuleSymbol getDefaultModule() { 233 return defaultModule; 234 } 235 236 private Set<ModuleSymbol> enterModules(List<JCCompilationUnit> trees, ClassSymbol c) { 237 Set<ModuleSymbol> modules = new LinkedHashSet<>(); 238 for (JCCompilationUnit tree : trees) { 239 JavaFileObject prev = log.useSource(tree.sourcefile); 240 try { 241 enterModule(tree, c, modules); 242 } finally { 243 log.useSource(prev); 244 } 245 } 246 return modules; 247 } 248 249 250 private void enterModule(JCCompilationUnit toplevel, ClassSymbol c, Set<ModuleSymbol> modules) { 251 boolean isModuleInfo = toplevel.sourcefile.isNameCompatible("module-info", Kind.SOURCE); 252 boolean isModuleDecl = toplevel.defs.nonEmpty() && toplevel.defs.head.hasTag(MODULEDEF); 253 if (isModuleInfo && isModuleDecl) { 254 JCModuleDecl decl = (JCModuleDecl) toplevel.defs.head; 255 Name name = TreeInfo.fullName(decl.qualId); 256 ModuleSymbol sym; 257 if (c != null) { 258 sym = (ModuleSymbol) c.owner; 259 if (sym.name == null) { 260 //ModuleFinder.findSingleModule creates a stub of a ModuleSymbol without a name, 261 //fill the name here after the module-info.java has been parsed 262 //also enter the ModuleSymbol among modules: 263 syms.enterModule(sym, name); 264 } else { 265 // TODO: validate name 266 } 267 } else { 268 sym = syms.enterModule(name); 269 if (sym.module_info.sourcefile != null && sym.module_info.sourcefile != toplevel.sourcefile) { 270 log.error(decl.pos(), Errors.DuplicateModule(sym)); 271 return; 272 } 273 } 274 sym.completer = getSourceCompleter(toplevel); 275 sym.module_info.sourcefile = toplevel.sourcefile; 276 decl.sym = sym; 277 278 if (multiModuleMode || modules.isEmpty()) { 279 modules.add(sym); 280 } else { 281 log.error(toplevel.pos(), Errors.TooManyModules); 282 } 283 284 Env<AttrContext> provisionalEnv = new Env<>(decl, null); 285 286 provisionalEnv.toplevel = toplevel; 287 typeEnvs.put(sym, provisionalEnv); 288 } else if (isModuleInfo) { 289 if (multiModuleMode) { 290 JCTree tree = toplevel.defs.isEmpty() ? toplevel : toplevel.defs.head; 291 log.error(tree.pos(), Errors.ExpectedModule); 292 } 293 } else if (isModuleDecl) { 294 JCTree tree = toplevel.defs.head; 295 log.error(tree.pos(), Errors.ModuleDeclSbInModuleInfoJava); 296 } 297 } 298 299 private void setCompilationUnitModules(List<JCCompilationUnit> trees, Set<ModuleSymbol> rootModules) { 300 // update the module for each compilation unit 301 if (multiModuleMode) { 302 checkNoAllModulePath(); 303 for (JCCompilationUnit tree: trees) { 304 if (tree.defs.isEmpty()) { 305 tree.modle = syms.unnamedModule; 306 continue; 307 } 308 309 JavaFileObject prev = log.useSource(tree.sourcefile); 310 try { 311 Location locn = getModuleLocation(tree); 312 if (locn != null) { 313 Name name = names.fromString(fileManager.inferModuleName(locn)); 314 ModuleSymbol msym; 315 if (tree.defs.head.hasTag(MODULEDEF)) { 316 JCModuleDecl decl = (JCModuleDecl) tree.defs.head; 317 msym = decl.sym; 318 if (msym.name != name) { 319 log.error(decl.qualId, Errors.ModuleNameMismatch(msym.name, name)); 320 } 321 } else { 322 msym = syms.enterModule(name); 323 } 324 if (msym.sourceLocation == null) { 325 msym.sourceLocation = locn; 326 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 327 msym.classLocation = fileManager.getModuleLocation( 328 StandardLocation.CLASS_OUTPUT, msym.name.toString()); 329 } 330 } 331 tree.modle = msym; 332 rootModules.add(msym); 333 } else { 334 log.error(tree.pos(), Errors.UnnamedPkgNotAllowedNamedModules); 335 tree.modle = syms.errModule; 336 } 337 } catch (IOException e) { 338 throw new Error(e); // FIXME 339 } finally { 340 log.useSource(prev); 341 } 342 } 343 if (syms.unnamedModule.sourceLocation == null) { 344 syms.unnamedModule.completer = getUnnamedModuleCompleter(); 345 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH; 346 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH; 347 } 348 defaultModule = syms.unnamedModule; 349 } else { 350 if (defaultModule == null) { 351 switch (rootModules.size()) { 352 case 0: 353 defaultModule = moduleFinder.findSingleModule(); 354 if (defaultModule == syms.unnamedModule) { 355 if (moduleOverride != null) { 356 checkNoAllModulePath(); 357 defaultModule = moduleFinder.findModule(names.fromString(moduleOverride)); 358 } else { 359 // Question: why not do findAllModules and initVisiblePackages here? 360 // i.e. body of unnamedModuleCompleter 361 defaultModule.completer = getUnnamedModuleCompleter(); 362 defaultModule.classLocation = StandardLocation.CLASS_PATH; 363 } 364 } else { 365 checkSpecifiedModule(trees, Errors.ModuleInfoWithXmoduleClasspath); 366 checkNoAllModulePath(); 367 defaultModule.complete(); 368 // Question: why not do completeModule here? 369 defaultModule.completer = new Completer() { 370 @Override 371 public void complete(Symbol sym) throws CompletionFailure { 372 completeModule((ModuleSymbol) sym); 373 } 374 }; 375 } 376 rootModules.add(defaultModule); 377 break; 378 case 1: 379 checkSpecifiedModule(trees, Errors.ModuleInfoWithXmoduleSourcepath); 380 checkNoAllModulePath(); 381 defaultModule = rootModules.iterator().next(); 382 defaultModule.classLocation = StandardLocation.CLASS_OUTPUT; 383 break; 384 default: 385 Assert.error("too many modules"); 386 } 387 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH; 388 } else if (rootModules.size() == 1 && defaultModule == rootModules.iterator().next()) { 389 defaultModule.complete(); 390 defaultModule.completer = sym -> completeModule((ModuleSymbol) sym); 391 } else { 392 Assert.check(rootModules.isEmpty()); 393 } 394 395 if (defaultModule != syms.unnamedModule) { 396 syms.unnamedModule.completer = getUnnamedModuleCompleter(); 397 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH; 398 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH; 399 } 400 401 for (JCCompilationUnit tree: trees) { 402 tree.modle = defaultModule; 403 } 404 } 405 } 406 407 private Location getModuleLocation(JCCompilationUnit tree) throws IOException { 408 switch (tree.defs.head.getTag()) { 409 case MODULEDEF: 410 return getModuleLocation(tree.sourcefile, null); 411 412 case PACKAGEDEF: 413 JCPackageDecl pkg = (JCPackageDecl) tree.defs.head; 414 return getModuleLocation(tree.sourcefile, TreeInfo.fullName(pkg.pid)); 415 416 default: 417 // code in unnamed module 418 return null; 419 } 420 } 421 422 private Location getModuleLocation(JavaFileObject fo, Name pkgName) throws IOException { 423 // For now, just check module source path. 424 // We may want to check source path as well. 425 return fileManager.getModuleLocation(StandardLocation.MODULE_SOURCE_PATH, 426 fo, (pkgName == null) ? null : pkgName.toString()); 427 } 428 429 private void checkSpecifiedModule(List<JCCompilationUnit> trees, JCDiagnostic.Error error) { 430 if (moduleOverride != null) { 431 JavaFileObject prev = log.useSource(trees.head.sourcefile); 432 try { 433 log.error(trees.head.pos(), error); 434 } finally { 435 log.useSource(prev); 436 } 437 } 438 } 439 440 private void checkNoAllModulePath() { 441 if (addModsOpt != null && Arrays.asList(addModsOpt.split(",")).contains(ALL_MODULE_PATH)) { 442 log.error(Errors.AddmodsAllModulePathInvalid); 443 } 444 } 445 446 private final Completer mainCompleter = new Completer() { 447 @Override 448 public void complete(Symbol sym) throws CompletionFailure { 449 ModuleSymbol msym = moduleFinder.findModule((ModuleSymbol) sym); 450 451 if (msym.kind == Kinds.Kind.ERR) { 452 log.error(Errors.CantFindModule(msym)); 453 //make sure the module is initialized: 454 msym.directives = List.nil(); 455 msym.exports = List.nil(); 456 msym.provides = List.nil(); 457 msym.requires = List.nil(); 458 msym.uses = List.nil(); 459 } else if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) { 460 completeAutomaticModule(msym); 461 } else { 462 msym.module_info.complete(); 463 } 464 465 // If module-info comes from a .java file, the underlying 466 // call of classFinder.fillIn will have called through the 467 // source completer, to Enter, and then to Modules.enter, 468 // which will call completeModule. 469 // But, if module-info comes from a .class file, the underlying 470 // call of classFinder.fillIn will just call ClassReader to read 471 // the .class file, and so we call completeModule here. 472 if (msym.module_info.classfile == null || msym.module_info.classfile.getKind() == Kind.CLASS) { 473 completeModule(msym); 474 } 475 } 476 477 @Override 478 public String toString() { 479 return "mainCompleter"; 480 } 481 }; 482 483 private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure { 484 try { 485 ListBuffer<Directive> directives = new ListBuffer<>(); 486 ListBuffer<ExportsDirective> exports = new ListBuffer<>(); 487 Set<String> seenPackages = new HashSet<>(); 488 489 for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) { 490 String binName = fileManager.inferBinaryName(msym.classLocation, clazz); 491 String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package???? 492 if (seenPackages.add(pack)) { 493 ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null); 494 directives.add(d); 495 exports.add(d); 496 } 497 } 498 499 ListBuffer<RequiresDirective> requires = new ListBuffer<>(); 500 501 //ensure all modules are found: 502 moduleFinder.findAllModules(); 503 504 for (ModuleSymbol ms : allModules()) { 505 if (ms == syms.unnamedModule || ms == msym) 506 continue; 507 Set<RequiresFlag> flags = (ms.flags_field & Flags.AUTOMATIC_MODULE) != 0 ? 508 EnumSet.of(RequiresFlag.PUBLIC) : EnumSet.noneOf(RequiresFlag.class); 509 RequiresDirective d = new RequiresDirective(ms, flags); 510 directives.add(d); 511 requires.add(d); 512 } 513 514 RequiresDirective requiresUnnamed = new RequiresDirective(syms.unnamedModule); 515 directives.add(requiresUnnamed); 516 requires.add(requiresUnnamed); 517 518 msym.exports = exports.toList(); 519 msym.provides = List.nil(); 520 msym.requires = requires.toList(); 521 msym.uses = List.nil(); 522 msym.directives = directives.toList(); 523 msym.flags_field |= Flags.ACYCLIC; 524 } catch (IOException ex) { 525 throw new IllegalStateException(ex); 526 } 527 } 528 529 private Completer getSourceCompleter(JCCompilationUnit tree) { 530 return new Completer() { 531 @Override 532 public void complete(Symbol sym) throws CompletionFailure { 533 ModuleSymbol msym = (ModuleSymbol) sym; 534 msym.flags_field |= UNATTRIBUTED; 535 ModuleVisitor v = new ModuleVisitor(); 536 JavaFileObject prev = log.useSource(tree.sourcefile); 537 try { 538 tree.defs.head.accept(v); 539 completeModule(msym); 540 checkCyclicDependencies((JCModuleDecl) tree.defs.head); 541 } finally { 542 log.useSource(prev); 543 msym.flags_field &= ~UNATTRIBUTED; 544 } 545 } 546 547 @Override 548 public String toString() { 549 return "SourceCompleter: " + tree.sourcefile.getName(); 550 } 551 552 }; 553 } 554 555 class ModuleVisitor extends JCTree.Visitor { 556 private ModuleSymbol sym; 557 private final Set<ModuleSymbol> allRequires = new HashSet<>(); 558 private final Set<PackageSymbol> allExports = new HashSet<>(); 559 560 @Override 561 public void visitModuleDef(JCModuleDecl tree) { 562 sym = Assert.checkNonNull(tree.sym); 563 564 sym.requires = List.nil(); 565 sym.exports = List.nil(); 566 tree.directives.forEach(t -> t.accept(this)); 567 sym.requires = sym.requires.reverse(); 568 sym.exports = sym.exports.reverse(); 569 ensureJavaBase(); 570 } 571 572 @Override 573 public void visitRequires(JCRequires tree) { 574 ModuleSymbol msym = lookupModule(tree.moduleName); 575 if (msym.kind != MDL) { 576 log.error(tree.moduleName.pos(), Errors.ModuleNotFound(msym)); 577 } else if (allRequires.contains(msym)) { 578 log.error(tree.moduleName.pos(), Errors.DuplicateRequires(msym)); 579 } else { 580 allRequires.add(msym); 581 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class); 582 if (tree.isPublic) 583 flags.add(RequiresFlag.PUBLIC); 584 RequiresDirective d = new RequiresDirective(msym, flags); 585 tree.directive = d; 586 sym.requires = sym.requires.prepend(d); 587 } 588 } 589 590 @Override 591 public void visitExports(JCExports tree) { 592 Name name = TreeInfo.fullName(tree.qualid); 593 PackageSymbol packge = syms.enterPackage(sym, name); 594 attr.setPackageSymbols(tree.qualid, packge); 595 if (!allExports.add(packge)) { 596 log.error(tree.qualid.pos(), Errors.DuplicateExports(packge)); 597 } 598 599 List<ModuleSymbol> toModules = null; 600 if (tree.moduleNames != null) { 601 Set<ModuleSymbol> to = new HashSet<>(); 602 for (JCExpression n: tree.moduleNames) { 603 ModuleSymbol msym = lookupModule(n); 604 if (msym.kind != MDL) { 605 log.error(n.pos(), Errors.ModuleNotFound(msym)); 606 } else if (!to.add(msym)) { 607 log.error(n.pos(), Errors.DuplicateExports(msym)); 608 } 609 } 610 toModules = List.from(to); 611 } 612 613 if (toModules == null || !toModules.isEmpty()) { 614 ExportsDirective d = new ExportsDirective(packge, toModules); 615 tree.directive = d; 616 sym.exports = sym.exports.prepend(d); 617 } 618 } 619 620 @Override 621 public void visitProvides(JCProvides tree) { } 622 623 @Override 624 public void visitUses(JCUses tree) { } 625 626 private void ensureJavaBase() { 627 if (sym.name == names.java_base) 628 return; 629 630 for (RequiresDirective d: sym.requires) { 631 if (d.module.name == names.java_base) 632 return; 633 } 634 635 ModuleSymbol java_base = syms.enterModule(names.java_base); 636 Directive.RequiresDirective d = 637 new Directive.RequiresDirective(java_base, 638 EnumSet.of(Directive.RequiresFlag.MANDATED)); 639 sym.requires = sym.requires.prepend(d); 640 } 641 642 private ModuleSymbol lookupModule(JCExpression moduleName) { 643 try { 644 Name name = TreeInfo.fullName(moduleName); 645 ModuleSymbol msym = moduleFinder.findModule(name); 646 TreeInfo.setSymbol(moduleName, msym); 647 return msym; 648 } catch (Throwable t) { 649 System.err.println("Module " + sym + "; lookup export " + moduleName); 650 throw t; 651 } 652 } 653 } 654 655 public Completer getUsesProvidesCompleter() { 656 return sym -> { 657 ModuleSymbol msym = (ModuleSymbol) sym; 658 Env<AttrContext> env = typeEnvs.get(msym); 659 UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env); 660 JavaFileObject prev = log.useSource(env.toplevel.sourcefile); 661 try { 662 env.toplevel.defs.head.accept(v); 663 } finally { 664 log.useSource(prev); 665 } 666 }; 667 } 668 669 class UsesProvidesVisitor extends JCTree.Visitor { 670 private final ModuleSymbol msym; 671 private final Env<AttrContext> env; 672 673 private final Set<Directive.UsesDirective> allUses = new HashSet<>(); 674 private final Set<Directive.ProvidesDirective> allProvides = new HashSet<>(); 675 676 public UsesProvidesVisitor(ModuleSymbol msym, Env<AttrContext> env) { 677 this.msym = msym; 678 this.env = env; 679 } 680 681 @Override @SuppressWarnings("unchecked") 682 public void visitModuleDef(JCModuleDecl tree) { 683 msym.directives = List.nil(); 684 msym.provides = List.nil(); 685 msym.uses = List.nil(); 686 tree.directives.forEach(t -> t.accept(this)); 687 msym.directives = msym.directives.reverse(); 688 msym.provides = msym.provides.reverse(); 689 msym.uses = msym.uses.reverse(); 690 691 if (msym.requires.nonEmpty() && msym.requires.head.flags.contains(RequiresFlag.MANDATED)) 692 msym.directives = msym.directives.prepend(msym.requires.head); 693 694 msym.directives = msym.directives.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet()))); 695 696 checkForCorrectness(); 697 } 698 699 @Override 700 public void visitExports(JCExports tree) { 701 if (tree.directive.packge.members().isEmpty()) { 702 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge)); 703 } 704 msym.directives = msym.directives.prepend(tree.directive); 705 } 706 707 MethodSymbol noArgsConstructor(ClassSymbol tsym) { 708 for (Symbol sym : tsym.members().getSymbolsByName(names.init)) { 709 MethodSymbol mSym = (MethodSymbol)sym; 710 if (mSym.params().isEmpty()) { 711 return mSym; 712 } 713 } 714 return null; 715 } 716 717 Map<Directive.ProvidesDirective, JCProvides> directiveToTreeMap = new HashMap<>(); 718 719 @Override 720 public void visitProvides(JCProvides tree) { 721 Type st = attr.attribType(tree.serviceName, env, syms.objectType); 722 Type it = attr.attribType(tree.implName, env, st); 723 ClassSymbol service = (ClassSymbol) st.tsym; 724 ClassSymbol impl = (ClassSymbol) it.tsym; 725 if ((impl.flags() & ABSTRACT) != 0) { 726 log.error(tree.implName.pos(), Errors.ServiceImplementationIsAbstract(impl)); 727 } else if (impl.isInner()) { 728 log.error(tree.implName.pos(), Errors.ServiceImplementationIsInner(impl)); 729 } else if (service.isInner()) { 730 log.error(tree.serviceName.pos(), Errors.ServiceDefinitionIsInner(service)); 731 } else { 732 MethodSymbol constr = noArgsConstructor(impl); 733 if (constr == null) { 734 log.error(tree.implName.pos(), Errors.ServiceImplementationDoesntHaveANoArgsConstructor(impl)); 735 } else if ((constr.flags() & PUBLIC) == 0) { 736 log.error(tree.implName.pos(), Errors.ServiceImplementationNoArgsConstructorNotPublic(impl)); 737 } 738 } 739 if (st.hasTag(CLASS) && it.hasTag(CLASS)) { 740 Directive.ProvidesDirective d = new Directive.ProvidesDirective(service, impl); 741 if (!allProvides.add(d)) { 742 log.error(tree.pos(), Errors.DuplicateProvides(service, impl)); 743 } 744 msym.provides = msym.provides.prepend(d); 745 msym.directives = msym.directives.prepend(d); 746 directiveToTreeMap.put(d, tree); 747 } 748 } 749 750 @Override 751 public void visitRequires(JCRequires tree) { 752 msym.directives = msym.directives.prepend(tree.directive); 753 } 754 755 @Override 756 public void visitUses(JCUses tree) { 757 Type st = attr.attribType(tree.qualid, env, syms.objectType); 758 if (st.hasTag(CLASS)) { 759 ClassSymbol service = (ClassSymbol) st.tsym; 760 Directive.UsesDirective d = new Directive.UsesDirective(service); 761 if (!allUses.add(d)) { 762 log.error(tree.pos(), Errors.DuplicateUses(service)); 763 } 764 msym.uses = msym.uses.prepend(d); 765 msym.directives = msym.directives.prepend(d); 766 } 767 } 768 769 private void checkForCorrectness() { 770 for (Directive.ProvidesDirective provides : allProvides) { 771 JCProvides tree = directiveToTreeMap.get(provides); 772 /* The implementation must be defined in the same module as the provides directive 773 * (else, error) 774 */ 775 PackageSymbol implementationDefiningPackage = provides.impl.packge(); 776 if (implementationDefiningPackage.modle != msym) { 777 log.error(tree.pos(), Errors.ServiceImplementationNotInRightModule(implementationDefiningPackage.modle)); 778 } 779 780 /* There is no inherent requirement that module that provides a service should actually 781 * use it itself. However, it is a pointless declaration if the service package is not 782 * exported and there is no uses for the service. 783 */ 784 PackageSymbol interfaceDeclaringPackage = provides.service.packge(); 785 boolean isInterfaceDeclaredInCurrentModule = interfaceDeclaringPackage.modle == msym; 786 boolean isInterfaceExportedFromAReadableModule = 787 msym.visiblePackages.get(interfaceDeclaringPackage.fullname) == interfaceDeclaringPackage; 788 if (isInterfaceDeclaredInCurrentModule && !isInterfaceExportedFromAReadableModule) { 789 // ok the interface is declared in this module. Let's check if it's exported 790 boolean warn = true; 791 for (ExportsDirective export : msym.exports) { 792 if (interfaceDeclaringPackage == export.packge) { 793 warn = false; 794 break; 795 } 796 } 797 if (warn) { 798 for (UsesDirective uses : msym.uses) { 799 if (provides.service == uses.service) { 800 warn = false; 801 break; 802 } 803 } 804 } 805 if (warn) { 806 log.warning(tree.pos(), Warnings.ServiceProvidedButNotExportedOrUsed(provides.service)); 807 } 808 } 809 } 810 } 811 } 812 813 private Set<ModuleSymbol> allModulesCache; 814 815 private Set<ModuleSymbol> allModules() { 816 if (allModulesCache != null) 817 return allModulesCache; 818 819 Set<ModuleSymbol> observable; 820 821 if (limitModsOpt == null) { 822 observable = null; 823 } else { 824 Set<ModuleSymbol> limitMods = new HashSet<>(); 825 for (String limit : limitModsOpt.split(",")) { 826 limitMods.add(syms.enterModule(names.fromString(limit))); 827 } 828 observable = computeTransitiveClosure(limitMods, null); 829 observable.addAll(rootModules); 830 } 831 832 Predicate<ModuleSymbol> observablePred = sym -> observable == null || observable.contains(sym); 833 Predicate<ModuleSymbol> systemModulePred = sym -> (sym.flags() & Flags.SYSTEM_MODULE) != 0; 834 Set<ModuleSymbol> enabledRoot = new LinkedHashSet<>(); 835 836 if (rootModules.contains(syms.unnamedModule)) { 837 ModuleSymbol javaSE = syms.getModule(java_se); 838 Predicate<ModuleSymbol> jdkModulePred; 839 840 if (javaSE != null && (observable == null || observable.contains(javaSE))) { 841 jdkModulePred = sym -> { 842 sym.complete(); 843 return !sym.name.startsWith(java_) 844 && sym.exports.stream().anyMatch(e -> e.modules == null); 845 }; 846 enabledRoot.add(javaSE); 847 } else { 848 jdkModulePred = sym -> true; 849 } 850 851 for (ModuleSymbol sym : new HashSet<>(syms.getAllModules())) { 852 if (systemModulePred.test(sym) && observablePred.test(sym) && jdkModulePred.test(sym)) { 853 enabledRoot.add(sym); 854 } 855 } 856 } 857 858 enabledRoot.addAll(rootModules); 859 860 if (addModsOpt != null) { 861 for (String added : addModsOpt.split(",")) { 862 Stream<ModuleSymbol> modules; 863 switch (added) { 864 case ALL_SYSTEM: 865 modules = syms.getAllModules() 866 .stream() 867 .filter(systemModulePred.and(observablePred)); 868 break; 869 case ALL_MODULE_PATH: 870 modules = syms.getAllModules() 871 .stream() 872 .filter(systemModulePred.negate().and(observablePred)); 873 break; 874 default: 875 modules = Stream.of(syms.enterModule(names.fromString(added))); 876 break; 877 } 878 modules.forEach(sym -> { 879 enabledRoot.add(sym); 880 if (observable != null) 881 observable.add(sym); 882 }); 883 } 884 } 885 886 Set<ModuleSymbol> result = computeTransitiveClosure(enabledRoot, observable); 887 888 result.add(syms.unnamedModule); 889 890 if (!rootModules.isEmpty()) 891 allModulesCache = result; 892 893 return result; 894 } 895 896 public void enableAllModules() { 897 allModulesCache = new HashSet<>(); 898 899 moduleFinder.findAllModules(); 900 901 for (ModuleSymbol msym : syms.getAllModules()) { 902 allModulesCache.add(msym); 903 } 904 } 905 906 private Set<ModuleSymbol> computeTransitiveClosure(Iterable<? extends ModuleSymbol> base, Set<ModuleSymbol> observable) { 907 List<ModuleSymbol> todo = List.nil(); 908 909 for (ModuleSymbol ms : base) { 910 todo = todo.prepend(ms); 911 } 912 913 Set<ModuleSymbol> result = new LinkedHashSet<>(); 914 result.add(syms.java_base); 915 916 while (todo.nonEmpty()) { 917 ModuleSymbol current = todo.head; 918 todo = todo.tail; 919 if (observable != null && !observable.contains(current)) 920 continue; 921 if (!result.add(current) || current == syms.unnamedModule || ((current.flags_field & Flags.AUTOMATIC_MODULE) != 0)) 922 continue; 923 current.complete(); 924 for (RequiresDirective rd : current.requires) { 925 todo = todo.prepend(rd.module); 926 } 927 } 928 929 return result; 930 } 931 932 public ModuleSymbol getObservableModule(Name name) { 933 ModuleSymbol mod = syms.getModule(name); 934 935 if (allModules().contains(mod)) { 936 return mod; 937 } 938 939 return null; 940 } 941 942 private Completer getUnnamedModuleCompleter() { 943 moduleFinder.findAllModules(); 944 return new Symbol.Completer() { 945 @Override 946 public void complete(Symbol sym) throws CompletionFailure { 947 ModuleSymbol msym = (ModuleSymbol) sym; 948 Set<ModuleSymbol> allModules = allModules(); 949 for (ModuleSymbol m : allModules) { 950 m.complete(); 951 } 952 initVisiblePackages(msym, allModules); 953 } 954 955 @Override 956 public String toString() { 957 return "unnamedModule Completer"; 958 } 959 }; 960 } 961 962 private final Map<ModuleSymbol, Set<ModuleSymbol>> requiresPublicCache = new HashMap<>(); 963 964 private void completeModule(ModuleSymbol msym) { 965 Assert.checkNonNull(msym.requires); 966 967 initAddReads(); 968 969 msym.requires = msym.requires.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet()))); 970 971 List<RequiresDirective> requires = msym.requires; 972 List<RequiresDirective> previous = null; 973 974 while (requires.nonEmpty()) { 975 if (!allModules().contains(requires.head.module)) { 976 Env<AttrContext> env = typeEnvs.get(msym); 977 if (env != null) { 978 JavaFileObject origSource = log.useSource(env.toplevel.sourcefile); 979 try { 980 log.error(/*XXX*/env.tree, Errors.ModuleNotFound(requires.head.module)); 981 } finally { 982 log.useSource(origSource); 983 } 984 } else { 985 Assert.check((msym.flags() & Flags.AUTOMATIC_MODULE) == 0); 986 } 987 if (previous != null) { 988 previous.tail = requires.tail; 989 } else { 990 msym.requires.tail = requires.tail; 991 } 992 } else { 993 previous = requires; 994 } 995 requires = requires.tail; 996 } 997 998 Set<ModuleSymbol> readable = new LinkedHashSet<>(); 999 Set<ModuleSymbol> requiresPublic = new HashSet<>(); 1000 1001 for (RequiresDirective d : msym.requires) { 1002 d.module.complete(); 1003 readable.add(d.module); 1004 Set<ModuleSymbol> s = retrieveRequiresPublic(d.module); 1005 Assert.checkNonNull(s, () -> "no entry in cache for " + d.module); 1006 readable.addAll(s); 1007 if (d.flags.contains(RequiresFlag.PUBLIC)) { 1008 requiresPublic.add(d.module); 1009 requiresPublic.addAll(s); 1010 } 1011 } 1012 1013 requiresPublicCache.put(msym, requiresPublic); 1014 initVisiblePackages(msym, readable); 1015 for (ExportsDirective d: msym.exports) { 1016 d.packge.modle = msym; 1017 } 1018 1019 } 1020 1021 private Set<ModuleSymbol> retrieveRequiresPublic(ModuleSymbol msym) { 1022 Set<ModuleSymbol> requiresPublic = requiresPublicCache.get(msym); 1023 1024 if (requiresPublic == null) { 1025 //the module graph may contain cycles involving automatic modules or -XaddReads edges 1026 requiresPublic = new HashSet<>(); 1027 1028 Set<ModuleSymbol> seen = new HashSet<>(); 1029 List<ModuleSymbol> todo = List.of(msym); 1030 1031 while (todo.nonEmpty()) { 1032 ModuleSymbol current = todo.head; 1033 todo = todo.tail; 1034 if (!seen.add(current)) 1035 continue; 1036 requiresPublic.add(current); 1037 current.complete(); 1038 Iterable<? extends RequiresDirective> requires; 1039 if (current != syms.unnamedModule) { 1040 Assert.checkNonNull(current.requires, () -> current + ".requires == null; " + msym); 1041 requires = current.requires; 1042 for (RequiresDirective rd : requires) { 1043 if (rd.isPublic()) 1044 todo = todo.prepend(rd.module); 1045 } 1046 } else { 1047 for (ModuleSymbol mod : allModules()) { 1048 todo = todo.prepend(mod); 1049 } 1050 } 1051 } 1052 1053 requiresPublic.remove(msym); 1054 } 1055 1056 return requiresPublic; 1057 } 1058 1059 private void initVisiblePackages(ModuleSymbol msym, Collection<ModuleSymbol> readable) { 1060 initAddExports(); 1061 1062 msym.visiblePackages = new LinkedHashMap<>(); 1063 1064 Map<Name, ModuleSymbol> seen = new HashMap<>(); 1065 1066 for (ModuleSymbol rm : readable) { 1067 if (rm == syms.unnamedModule) 1068 continue; 1069 addVisiblePackages(msym, seen, rm, rm.exports); 1070 } 1071 1072 for (Entry<ModuleSymbol, Set<ExportsDirective>> addExportsEntry : addExports.entrySet()) 1073 addVisiblePackages(msym, seen, addExportsEntry.getKey(), addExportsEntry.getValue()); 1074 } 1075 1076 private void addVisiblePackages(ModuleSymbol msym, 1077 Map<Name, ModuleSymbol> seenPackages, 1078 ModuleSymbol exportsFrom, 1079 Collection<ExportsDirective> exports) { 1080 for (ExportsDirective d : exports) { 1081 if (d.modules == null || d.modules.contains(msym)) { 1082 Name packageName = d.packge.fullname; 1083 ModuleSymbol previousModule = seenPackages.get(packageName); 1084 1085 if (previousModule != null && previousModule != exportsFrom) { 1086 Env<AttrContext> env = typeEnvs.get(msym); 1087 JavaFileObject origSource = env != null ? log.useSource(env.toplevel.sourcefile) 1088 : null; 1089 DiagnosticPosition pos = env != null ? env.tree.pos() : null; 1090 try { 1091 log.error(pos, Errors.PackageClashFromRequires(msym, packageName, 1092 previousModule, exportsFrom)); 1093 } finally { 1094 if (env != null) 1095 log.useSource(origSource); 1096 } 1097 continue; 1098 } 1099 1100 seenPackages.put(packageName, exportsFrom); 1101 msym.visiblePackages.put(d.packge.fullname, d.packge); 1102 } 1103 } 1104 } 1105 1106 private void initAddExports() { 1107 if (addExports != null) 1108 return; 1109 1110 addExports = new LinkedHashMap<>(); 1111 1112 if (addExportsOpt == null) 1113 return; 1114 1115 // System.err.println("Modules.addExports:\n " + addExportsOpt.replace("\0", "\n ")); 1116 1117 Pattern ep = Pattern.compile("([^/]+)/([^=]+)=(.*)"); 1118 for (String s: addExportsOpt.split("\0+")) { 1119 if (s.isEmpty()) 1120 continue; 1121 Matcher em = ep.matcher(s); 1122 if (!em.matches()) { 1123 continue; 1124 } 1125 1126 // Terminology comes from 1127 // -XaddExports:module/package=target,... 1128 // Compare to 1129 // module module { exports package to target, ... } 1130 String moduleName = em.group(1); 1131 String packageName = em.group(2); 1132 String targetNames = em.group(3); 1133 1134 ModuleSymbol msym = syms.enterModule(names.fromString(moduleName)); 1135 PackageSymbol p = syms.enterPackage(msym, names.fromString(packageName)); 1136 p.modle = msym; // TODO: do we need this? 1137 1138 List<ModuleSymbol> targetModules = List.nil(); 1139 for (String toModule : targetNames.split("[ ,]+")) { 1140 ModuleSymbol m; 1141 if (toModule.equals("ALL-UNNAMED")) { 1142 m = syms.unnamedModule; 1143 } else { 1144 if (!SourceVersion.isName(toModule)) { 1145 // TODO: error: invalid module name 1146 continue; 1147 } 1148 m = syms.enterModule(names.fromString(toModule)); 1149 } 1150 targetModules = targetModules.prepend(m); 1151 } 1152 1153 Set<ExportsDirective> extra = addExports.computeIfAbsent(msym, _x -> new LinkedHashSet<>()); 1154 ExportsDirective d = new ExportsDirective(p, targetModules); 1155 extra.add(d); 1156 } 1157 } 1158 1159 private void initAddReads() { 1160 if (addReads != null) 1161 return; 1162 1163 addReads = new LinkedHashMap<>(); 1164 1165 if (addReadsOpt == null) 1166 return; 1167 1168 // System.err.println("Modules.addReads:\n " + addReadsOpt.replace("\0", "\n ")); 1169 1170 Pattern rp = Pattern.compile("([^=]+)=(.*)"); 1171 for (String s : addReadsOpt.split("\0+")) { 1172 if (s.isEmpty()) 1173 continue; 1174 Matcher rm = rp.matcher(s); 1175 if (!rm.matches()) { 1176 continue; 1177 } 1178 1179 // Terminology comes from 1180 // -XaddReads:target-module=source-module,... 1181 // Compare to 1182 // module target-module { requires source-module; ... } 1183 String targetName = rm.group(1); 1184 String sources = rm.group(2); 1185 1186 ModuleSymbol msym = syms.enterModule(names.fromString(targetName)); 1187 for (String source : sources.split("[ ,]+")) { 1188 ModuleSymbol sourceModule; 1189 if (source.equals("ALL-UNNAMED")) { 1190 sourceModule = syms.unnamedModule; 1191 } else { 1192 if (!SourceVersion.isName(source)) { 1193 // TODO: error: invalid module name 1194 continue; 1195 } 1196 sourceModule = syms.enterModule(names.fromString(source)); 1197 } 1198 addReads.computeIfAbsent(msym, m -> new HashSet<>()) 1199 .add(new RequiresDirective(sourceModule, EnumSet.of(RequiresFlag.EXTRA))); 1200 } 1201 } 1202 } 1203 1204 private void checkCyclicDependencies(JCModuleDecl mod) { 1205 for (JCDirective d : mod.directives) { 1206 if (!d.hasTag(Tag.REQUIRES)) 1207 continue; 1208 JCRequires rd = (JCRequires) d; 1209 Set<ModuleSymbol> nonSyntheticDeps = new HashSet<>(); 1210 List<ModuleSymbol> queue = List.of(rd.directive.module); 1211 while (queue.nonEmpty()) { 1212 ModuleSymbol current = queue.head; 1213 queue = queue.tail; 1214 if (!nonSyntheticDeps.add(current)) 1215 continue; 1216 if ((current.flags() & Flags.ACYCLIC) != 0) 1217 continue; 1218 current.complete(); 1219 Assert.checkNonNull(current.requires, () -> current.toString()); 1220 for (RequiresDirective dep : current.requires) { 1221 if (!dep.flags.contains(RequiresFlag.EXTRA)) 1222 queue = queue.prepend(dep.module); 1223 } 1224 } 1225 if (nonSyntheticDeps.contains(mod.sym)) { 1226 log.error(rd.moduleName.pos(), Errors.CyclicRequires(rd.directive.module)); 1227 } 1228 mod.sym.flags_field |= Flags.ACYCLIC; 1229 } 1230 } 1231 1232 // DEBUG 1233 private String toString(ModuleSymbol msym) { 1234 return msym.name + "[" 1235 + "kind:" + msym.kind + ";" 1236 + "locn:" + toString(msym.sourceLocation) + "," + toString(msym.classLocation) + ";" 1237 + "info:" + toString(msym.module_info.sourcefile) + "," 1238 + toString(msym.module_info.classfile) + "," 1239 + msym.module_info.completer 1240 + "]"; 1241 } 1242 1243 // DEBUG 1244 String toString(Location locn) { 1245 return (locn == null) ? "--" : locn.getName(); 1246 } 1247 1248 // DEBUG 1249 String toString(JavaFileObject fo) { 1250 return (fo == null) ? "--" : fo.getName(); 1251 } 1252 1253 public void newRound() { 1254 } 1255 }