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