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