Modules.java revision 3993:ce416299fd2d
1/* 2 * Copyright (c) 2009, 2017, 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.Set; 40import java.util.function.Consumer; 41import java.util.function.Predicate; 42import java.util.regex.Matcher; 43import java.util.regex.Pattern; 44import java.util.stream.Collectors; 45import java.util.stream.Stream; 46 47import javax.lang.model.SourceVersion; 48import javax.tools.JavaFileManager; 49import javax.tools.JavaFileManager.Location; 50import javax.tools.JavaFileObject; 51import javax.tools.JavaFileObject.Kind; 52import javax.tools.StandardLocation; 53 54import com.sun.source.tree.ModuleTree.ModuleKind; 55import com.sun.tools.javac.code.ClassFinder; 56import com.sun.tools.javac.code.DeferredLintHandler; 57import com.sun.tools.javac.code.Directive; 58import com.sun.tools.javac.code.Directive.ExportsDirective; 59import com.sun.tools.javac.code.Directive.ExportsFlag; 60import com.sun.tools.javac.code.Directive.OpensDirective; 61import com.sun.tools.javac.code.Directive.OpensFlag; 62import com.sun.tools.javac.code.Directive.RequiresDirective; 63import com.sun.tools.javac.code.Directive.RequiresFlag; 64import com.sun.tools.javac.code.Directive.UsesDirective; 65import com.sun.tools.javac.code.Flags; 66import com.sun.tools.javac.code.Lint.LintCategory; 67import com.sun.tools.javac.code.ModuleFinder; 68import com.sun.tools.javac.code.Source; 69import com.sun.tools.javac.code.Symbol; 70import com.sun.tools.javac.code.Symbol.ClassSymbol; 71import com.sun.tools.javac.code.Symbol.Completer; 72import com.sun.tools.javac.code.Symbol.CompletionFailure; 73import com.sun.tools.javac.code.Symbol.MethodSymbol; 74import com.sun.tools.javac.code.Symbol.ModuleFlags; 75import com.sun.tools.javac.code.Symbol.ModuleSymbol; 76import com.sun.tools.javac.code.Symbol.PackageSymbol; 77import com.sun.tools.javac.code.Symtab; 78import com.sun.tools.javac.code.Type; 79import com.sun.tools.javac.code.Types; 80import com.sun.tools.javac.jvm.ClassWriter; 81import com.sun.tools.javac.jvm.JNIWriter; 82import com.sun.tools.javac.main.Option; 83import com.sun.tools.javac.resources.CompilerProperties.Errors; 84import com.sun.tools.javac.resources.CompilerProperties.Warnings; 85import com.sun.tools.javac.tree.JCTree; 86import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 87import com.sun.tools.javac.tree.JCTree.JCDirective; 88import com.sun.tools.javac.tree.JCTree.JCExports; 89import com.sun.tools.javac.tree.JCTree.JCExpression; 90import com.sun.tools.javac.tree.JCTree.JCModuleDecl; 91import com.sun.tools.javac.tree.JCTree.JCOpens; 92import com.sun.tools.javac.tree.JCTree.JCPackageDecl; 93import com.sun.tools.javac.tree.JCTree.JCProvides; 94import com.sun.tools.javac.tree.JCTree.JCRequires; 95import com.sun.tools.javac.tree.JCTree.JCUses; 96import com.sun.tools.javac.tree.JCTree.Tag; 97import com.sun.tools.javac.tree.TreeInfo; 98import com.sun.tools.javac.util.Abort; 99import com.sun.tools.javac.util.Assert; 100import com.sun.tools.javac.util.Context; 101import com.sun.tools.javac.util.JCDiagnostic; 102import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 103import com.sun.tools.javac.util.List; 104import com.sun.tools.javac.util.ListBuffer; 105import com.sun.tools.javac.util.Log; 106import com.sun.tools.javac.util.Name; 107import com.sun.tools.javac.util.Names; 108import com.sun.tools.javac.util.Options; 109import com.sun.tools.javac.util.Position; 110 111import static com.sun.tools.javac.code.Flags.ABSTRACT; 112import static com.sun.tools.javac.code.Flags.ENUM; 113import static com.sun.tools.javac.code.Flags.PUBLIC; 114import static com.sun.tools.javac.code.Flags.UNATTRIBUTED; 115import static com.sun.tools.javac.code.Kinds.Kind.ERR; 116import static com.sun.tools.javac.code.Kinds.Kind.MDL; 117import static com.sun.tools.javac.code.Kinds.Kind.MTH; 118import com.sun.tools.javac.code.Symbol.ModuleResolutionFlags; 119import static com.sun.tools.javac.code.TypeTag.CLASS; 120 121/** 122 * TODO: fill in 123 * 124 * <p><b>This is NOT part of any supported API. 125 * If you write code that depends on this, you do so at your own risk. 126 * This code and its internal interfaces are subject to change or 127 * deletion without notice.</b> 128 */ 129public class Modules extends JCTree.Visitor { 130 private static final String ALL_SYSTEM = "ALL-SYSTEM"; 131 private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; 132 133 private final Log log; 134 private final Names names; 135 private final Symtab syms; 136 private final Attr attr; 137 private final Check chk; 138 private final DeferredLintHandler deferredLintHandler; 139 private final TypeEnvs typeEnvs; 140 private final Types types; 141 private final JavaFileManager fileManager; 142 private final ModuleFinder moduleFinder; 143 private final Source source; 144 private final boolean allowModules; 145 146 public final boolean multiModuleMode; 147 148 private final String legacyModuleOverride; 149 150 private final Name java_se; 151 private final Name java_; 152 153 ModuleSymbol defaultModule; 154 155 private final String addExportsOpt; 156 private Map<ModuleSymbol, Set<ExportsDirective>> addExports; 157 private final String addReadsOpt; 158 private Map<ModuleSymbol, Set<RequiresDirective>> addReads; 159 private final String addModsOpt; 160 private final Set<String> extraAddMods = new HashSet<>(); 161 private final String limitModsOpt; 162 private final Set<String> extraLimitMods = new HashSet<>(); 163 private final String moduleVersionOpt; 164 165 private final boolean lintOptions; 166 167 private Set<ModuleSymbol> rootModules = null; 168 private final Set<ModuleSymbol> warnedMissing = new HashSet<>(); 169 170 public static Modules instance(Context context) { 171 Modules instance = context.get(Modules.class); 172 if (instance == null) 173 instance = new Modules(context); 174 return instance; 175 } 176 177 protected Modules(Context context) { 178 context.put(Modules.class, this); 179 log = Log.instance(context); 180 names = Names.instance(context); 181 syms = Symtab.instance(context); 182 attr = Attr.instance(context); 183 chk = Check.instance(context); 184 deferredLintHandler = DeferredLintHandler.instance(context); 185 typeEnvs = TypeEnvs.instance(context); 186 moduleFinder = ModuleFinder.instance(context); 187 types = Types.instance(context); 188 fileManager = context.get(JavaFileManager.class); 189 source = Source.instance(context); 190 allowModules = source.allowModules(); 191 Options options = Options.instance(context); 192 193 lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); 194 195 legacyModuleOverride = options.get(Option.XMODULE); 196 197 multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); 198 ClassWriter classWriter = ClassWriter.instance(context); 199 classWriter.multiModuleMode = multiModuleMode; 200 JNIWriter jniWriter = JNIWriter.instance(context); 201 jniWriter.multiModuleMode = multiModuleMode; 202 203 java_se = names.fromString("java.se"); 204 java_ = names.fromString("java."); 205 206 addExportsOpt = options.get(Option.ADD_EXPORTS); 207 addReadsOpt = options.get(Option.ADD_READS); 208 addModsOpt = options.get(Option.ADD_MODULES); 209 limitModsOpt = options.get(Option.LIMIT_MODULES); 210 moduleVersionOpt = options.get(Option.MODULE_VERSION); 211 } 212 213 int depth = -1; 214 private void dprintln(String msg) { 215 for (int i = 0; i < depth; i++) 216 System.err.print(" "); 217 System.err.println(msg); 218 } 219 220 public void addExtraAddModules(String... extras) { 221 extraAddMods.addAll(Arrays.asList(extras)); 222 } 223 224 public void addExtraLimitModules(String... extras) { 225 extraLimitMods.addAll(Arrays.asList(extras)); 226 } 227 228 boolean inInitModules; 229 public void initModules(List<JCCompilationUnit> trees) { 230 Assert.check(!inInitModules); 231 try { 232 inInitModules = true; 233 Assert.checkNull(rootModules); 234 enter(trees, modules -> { 235 Assert.checkNull(rootModules); 236 Assert.checkNull(allModules); 237 this.rootModules = modules; 238 setupAllModules(); //initialize the module graph 239 Assert.checkNonNull(allModules); 240 inInitModules = false; 241 }, null); 242 } finally { 243 inInitModules = false; 244 } 245 } 246 247 public boolean enter(List<JCCompilationUnit> trees, ClassSymbol c) { 248 Assert.check(rootModules != null || inInitModules || !allowModules); 249 return enter(trees, modules -> {}, c); 250 } 251 252 private boolean enter(List<JCCompilationUnit> trees, Consumer<Set<ModuleSymbol>> init, ClassSymbol c) { 253 if (!allowModules) { 254 for (JCCompilationUnit tree: trees) { 255 tree.modle = syms.noModule; 256 } 257 defaultModule = syms.noModule; 258 return true; 259 } 260 261 int startErrors = log.nerrors; 262 263 depth++; 264 try { 265 // scan trees for module defs 266 Set<ModuleSymbol> roots = enterModules(trees, c); 267 268 setCompilationUnitModules(trees, roots, c); 269 270 init.accept(roots); 271 272 for (ModuleSymbol msym: roots) { 273 msym.complete(); 274 } 275 } catch (CompletionFailure ex) { 276 log.error(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE, Position.NOPOS, "cant.access", ex.sym, ex.getDetailValue()); 277 if (ex instanceof ClassFinder.BadClassFile) throw new Abort(); 278 } finally { 279 depth--; 280 } 281 282 return (log.nerrors == startErrors); 283 } 284 285 public Completer getCompleter() { 286 return mainCompleter; 287 } 288 289 public ModuleSymbol getDefaultModule() { 290 return defaultModule; 291 } 292 293 public boolean modulesInitialized() { 294 return allModules != null; 295 } 296 297 private Set<ModuleSymbol> enterModules(List<JCCompilationUnit> trees, ClassSymbol c) { 298 Set<ModuleSymbol> modules = new LinkedHashSet<>(); 299 for (JCCompilationUnit tree : trees) { 300 JavaFileObject prev = log.useSource(tree.sourcefile); 301 try { 302 enterModule(tree, c, modules); 303 } finally { 304 log.useSource(prev); 305 } 306 } 307 return modules; 308 } 309 310 311 private void enterModule(JCCompilationUnit toplevel, ClassSymbol c, Set<ModuleSymbol> modules) { 312 boolean isModuleInfo = toplevel.sourcefile.isNameCompatible("module-info", Kind.SOURCE); 313 boolean isModuleDecl = toplevel.getModuleDecl() != null; 314 if (isModuleDecl) { 315 JCModuleDecl decl = toplevel.getModuleDecl(); 316 if (!isModuleInfo) { 317 log.error(decl.pos(), Errors.ModuleDeclSbInModuleInfoJava); 318 } 319 Name name = TreeInfo.fullName(decl.qualId); 320 ModuleSymbol sym; 321 if (c != null) { 322 sym = (ModuleSymbol) c.owner; 323 Assert.checkNonNull(sym.name); 324 Name treeName = TreeInfo.fullName(decl.qualId); 325 if (sym.name != treeName) { 326 log.error(decl.pos(), Errors.ModuleNameMismatch(name, sym.name)); 327 } 328 } else { 329 sym = syms.enterModule(name); 330 if (sym.module_info.sourcefile != null && sym.module_info.sourcefile != toplevel.sourcefile) { 331 log.error(decl.pos(), Errors.DuplicateModule(sym)); 332 return; 333 } 334 } 335 sym.completer = getSourceCompleter(toplevel); 336 sym.module_info.sourcefile = toplevel.sourcefile; 337 decl.sym = sym; 338 339 if (multiModuleMode || modules.isEmpty()) { 340 modules.add(sym); 341 } else { 342 log.error(toplevel.pos(), Errors.TooManyModules); 343 } 344 345 Env<AttrContext> provisionalEnv = new Env<>(decl, null); 346 347 provisionalEnv.toplevel = toplevel; 348 typeEnvs.put(sym, provisionalEnv); 349 } else if (isModuleInfo) { 350 if (multiModuleMode) { 351 JCTree tree = toplevel.defs.isEmpty() ? toplevel : toplevel.defs.head; 352 log.error(tree.pos(), Errors.ExpectedModule); 353 } 354 } 355 } 356 357 private void setCompilationUnitModules(List<JCCompilationUnit> trees, Set<ModuleSymbol> rootModules, ClassSymbol c) { 358 // update the module for each compilation unit 359 if (multiModuleMode) { 360 checkNoAllModulePath(); 361 for (JCCompilationUnit tree: trees) { 362 if (tree.defs.isEmpty()) { 363 tree.modle = syms.unnamedModule; 364 continue; 365 } 366 367 JavaFileObject prev = log.useSource(tree.sourcefile); 368 try { 369 Location msplocn = getModuleLocation(tree); 370 Location plocn = fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) ? 371 fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, 372 tree.sourcefile, getPackageName(tree)) : 373 null; 374 375 if (plocn != null) { 376 Name name = names.fromString(fileManager.inferModuleName(plocn)); 377 ModuleSymbol msym = moduleFinder.findModule(name); 378 tree.modle = msym; 379 rootModules.add(msym); 380 381 if (msplocn != null) { 382 Name mspname = names.fromString(fileManager.inferModuleName(msplocn)); 383 if (name != mspname) { 384 log.error(tree.pos(), Errors.FilePatchedAndMsp(name, mspname)); 385 } 386 } 387 } else if (msplocn != null) { 388 Name name = names.fromString(fileManager.inferModuleName(msplocn)); 389 ModuleSymbol msym; 390 JCModuleDecl decl = tree.getModuleDecl(); 391 if (decl != null) { 392 msym = decl.sym; 393 if (msym.name != name) { 394 log.error(decl.qualId, Errors.ModuleNameMismatch(msym.name, name)); 395 } 396 } else { 397 if (tree.getPackage() == null) { 398 log.error(tree.pos(), Errors.UnnamedPkgNotAllowedNamedModules); 399 } 400 msym = syms.enterModule(name); 401 } 402 if (msym.sourceLocation == null) { 403 msym.sourceLocation = msplocn; 404 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 405 msym.classLocation = fileManager.getLocationForModule( 406 StandardLocation.CLASS_OUTPUT, msym.name.toString()); 407 } 408 } 409 tree.modle = msym; 410 rootModules.add(msym); 411 } else if (c != null && c.packge().modle == syms.unnamedModule) { 412 tree.modle = syms.unnamedModule; 413 } else { 414 if (tree.getModuleDecl() != null) { 415 log.error(tree.pos(), Errors.ModuleNotFoundOnModuleSourcePath); 416 } else { 417 log.error(tree.pos(), Errors.NotInModuleOnModuleSourcePath); 418 } 419 tree.modle = syms.errModule; 420 } 421 } catch (IOException e) { 422 throw new Error(e); // FIXME 423 } finally { 424 log.useSource(prev); 425 } 426 } 427 if (syms.unnamedModule.sourceLocation == null) { 428 syms.unnamedModule.completer = getUnnamedModuleCompleter(); 429 syms.unnamedModule.sourceLocation = StandardLocation.SOURCE_PATH; 430 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH; 431 } 432 defaultModule = syms.unnamedModule; 433 } else { 434 ModuleSymbol module = null; 435 if (defaultModule == null) { 436 String moduleOverride = singleModuleOverride(trees); 437 switch (rootModules.size()) { 438 case 0: 439 defaultModule = moduleFinder.findSingleModule(); 440 if (defaultModule == syms.unnamedModule) { 441 if (moduleOverride != null) { 442 checkNoAllModulePath(); 443 defaultModule = moduleFinder.findModule(names.fromString(moduleOverride)); 444 if (legacyModuleOverride != null) { 445 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH; 446 } 447 defaultModule.patchOutputLocation = StandardLocation.CLASS_OUTPUT; 448 } else { 449 // Question: why not do findAllModules and initVisiblePackages here? 450 // i.e. body of unnamedModuleCompleter 451 defaultModule.completer = getUnnamedModuleCompleter(); 452 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH; 453 defaultModule.classLocation = StandardLocation.CLASS_PATH; 454 } 455 } else { 456 checkSpecifiedModule(trees, moduleOverride, Errors.ModuleInfoWithPatchedModuleClassoutput); 457 checkNoAllModulePath(); 458 defaultModule.complete(); 459 // Question: why not do completeModule here? 460 defaultModule.completer = sym -> completeModule((ModuleSymbol) sym); 461 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH; 462 } 463 rootModules.add(defaultModule); 464 break; 465 case 1: 466 checkSpecifiedModule(trees, moduleOverride, Errors.ModuleInfoWithPatchedModuleSourcepath); 467 checkNoAllModulePath(); 468 defaultModule = rootModules.iterator().next(); 469 defaultModule.sourceLocation = StandardLocation.SOURCE_PATH; 470 defaultModule.classLocation = StandardLocation.CLASS_OUTPUT; 471 break; 472 default: 473 Assert.error("too many modules"); 474 } 475 } else if (rootModules.size() == 1 && defaultModule == rootModules.iterator().next()) { 476 defaultModule.complete(); 477 defaultModule.completer = sym -> completeModule((ModuleSymbol) sym); 478 } else { 479 Assert.check(rootModules.isEmpty()); 480 String moduleOverride = singleModuleOverride(trees); 481 if (moduleOverride != null) { 482 module = moduleFinder.findModule(names.fromString(moduleOverride)); 483 } else { 484 module = defaultModule; 485 } 486 rootModules.add(module); 487 } 488 489 if (defaultModule != syms.unnamedModule) { 490 syms.unnamedModule.completer = getUnnamedModuleCompleter(); 491 syms.unnamedModule.classLocation = StandardLocation.CLASS_PATH; 492 } 493 494 if (module == null) { 495 module = defaultModule; 496 } 497 498 for (JCCompilationUnit tree: trees) { 499 tree.modle = module; 500 } 501 } 502 } 503 504 private String singleModuleOverride(List<JCCompilationUnit> trees) { 505 if (!fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) { 506 return legacyModuleOverride; 507 } 508 509 Set<String> override = new LinkedHashSet<>(); 510 for (JCCompilationUnit tree : trees) { 511 JavaFileObject fo = tree.sourcefile; 512 513 try { 514 Location loc = 515 fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, 516 fo, getPackageName(tree)); 517 518 if (loc != null) { 519 override.add(fileManager.inferModuleName(loc)); 520 } 521 } catch (IOException ex) { 522 throw new Error(ex); 523 } 524 } 525 526 switch (override.size()) { 527 case 0: return legacyModuleOverride; 528 case 1: return override.iterator().next(); 529 default: 530 log.error(Errors.TooManyPatchedModules(override)); 531 return null; 532 } 533 } 534 535 private String getPackageName(JCCompilationUnit tree) { 536 if (tree.getModuleDecl() != null) { 537 return null; 538 } else { 539 JCPackageDecl pkg = tree.getPackage(); 540 return (pkg == null) ? "" : TreeInfo.fullName(pkg.pid).toString(); 541 } 542 } 543 544 /** 545 * Determine the location for the module on the module source path 546 * or source output directory which contains a given CompilationUnit. 547 * If the source output directory is unset, the class output directory 548 * will be checked instead. 549 * {@code null} is returned if no such module can be found. 550 * @param tree the compilation unit tree 551 * @return the location for the enclosing module 552 * @throws IOException if there is a problem while searching for the module. 553 */ 554 private Location getModuleLocation(JCCompilationUnit tree) throws IOException { 555 String pkgName = getPackageName(tree); 556 JavaFileObject fo = tree.sourcefile; 557 558 Location loc = 559 fileManager.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, 560 fo, (pkgName == null) ? null : pkgName); 561 if (loc == null) { 562 Location sourceOutput = fileManager.hasLocation(StandardLocation.SOURCE_OUTPUT) ? 563 StandardLocation.SOURCE_OUTPUT : StandardLocation.CLASS_OUTPUT; 564 loc = 565 fileManager.getLocationForModule(sourceOutput, 566 fo, (pkgName == null) ? null : pkgName); 567 } 568 return loc; 569 } 570 571 private void checkSpecifiedModule(List<JCCompilationUnit> trees, String moduleOverride, JCDiagnostic.Error error) { 572 if (moduleOverride != null) { 573 JavaFileObject prev = log.useSource(trees.head.sourcefile); 574 try { 575 log.error(trees.head.pos(), error); 576 } finally { 577 log.useSource(prev); 578 } 579 } 580 } 581 582 private void checkNoAllModulePath() { 583 if (addModsOpt != null && Arrays.asList(addModsOpt.split(",")).contains(ALL_MODULE_PATH)) { 584 log.error(Errors.AddmodsAllModulePathInvalid); 585 } 586 } 587 588 private final Completer mainCompleter = new Completer() { 589 @Override 590 public void complete(Symbol sym) throws CompletionFailure { 591 ModuleSymbol msym = moduleFinder.findModule((ModuleSymbol) sym); 592 593 if (msym.kind == ERR) { 594 //make sure the module is initialized: 595 msym.directives = List.nil(); 596 msym.exports = List.nil(); 597 msym.provides = List.nil(); 598 msym.requires = List.nil(); 599 msym.uses = List.nil(); 600 } else if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) { 601 setupAutomaticModule(msym); 602 } else { 603 msym.module_info.complete(); 604 } 605 606 // If module-info comes from a .java file, the underlying 607 // call of classFinder.fillIn will have called through the 608 // source completer, to Enter, and then to Modules.enter, 609 // which will call completeModule. 610 // But, if module-info comes from a .class file, the underlying 611 // call of classFinder.fillIn will just call ClassReader to read 612 // the .class file, and so we call completeModule here. 613 if (msym.module_info.classfile == null || msym.module_info.classfile.getKind() == Kind.CLASS) { 614 completeModule(msym); 615 } 616 } 617 618 @Override 619 public String toString() { 620 return "mainCompleter"; 621 } 622 }; 623 624 private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure { 625 try { 626 ListBuffer<Directive> directives = new ListBuffer<>(); 627 ListBuffer<ExportsDirective> exports = new ListBuffer<>(); 628 Set<String> seenPackages = new HashSet<>(); 629 630 for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) { 631 String binName = fileManager.inferBinaryName(msym.classLocation, clazz); 632 String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package???? 633 if (seenPackages.add(pack)) { 634 ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null); 635 //TODO: opens? 636 directives.add(d); 637 exports.add(d); 638 } 639 } 640 641 msym.exports = exports.toList(); 642 msym.provides = List.nil(); 643 msym.requires = List.nil(); 644 msym.uses = List.nil(); 645 msym.directives = directives.toList(); 646 msym.flags_field |= Flags.ACYCLIC; 647 } catch (IOException ex) { 648 throw new IllegalStateException(ex); 649 } 650 } 651 652 private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure { 653 ListBuffer<Directive> directives = new ListBuffer<>(); 654 655 directives.addAll(msym.directives); 656 657 ListBuffer<RequiresDirective> requires = new ListBuffer<>(); 658 659 for (ModuleSymbol ms : allModules()) { 660 if (ms == syms.unnamedModule || ms == msym) 661 continue; 662 Set<RequiresFlag> flags = (ms.flags_field & Flags.AUTOMATIC_MODULE) != 0 ? 663 EnumSet.of(RequiresFlag.TRANSITIVE) : EnumSet.noneOf(RequiresFlag.class); 664 RequiresDirective d = new RequiresDirective(ms, flags); 665 directives.add(d); 666 requires.add(d); 667 } 668 669 RequiresDirective requiresUnnamed = new RequiresDirective(syms.unnamedModule); 670 directives.add(requiresUnnamed); 671 requires.add(requiresUnnamed); 672 673 msym.requires = requires.toList(); 674 msym.directives = directives.toList(); 675 } 676 677 private Completer getSourceCompleter(JCCompilationUnit tree) { 678 return new Completer() { 679 @Override 680 public void complete(Symbol sym) throws CompletionFailure { 681 ModuleSymbol msym = (ModuleSymbol) sym; 682 msym.flags_field |= UNATTRIBUTED; 683 ModuleVisitor v = new ModuleVisitor(); 684 JavaFileObject prev = log.useSource(tree.sourcefile); 685 JCModuleDecl moduleDecl = tree.getModuleDecl(); 686 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(moduleDecl.pos()); 687 688 try { 689 moduleDecl.accept(v); 690 completeModule(msym); 691 checkCyclicDependencies(moduleDecl); 692 } finally { 693 log.useSource(prev); 694 deferredLintHandler.setPos(prevLintPos); 695 msym.flags_field &= ~UNATTRIBUTED; 696 } 697 } 698 699 @Override 700 public String toString() { 701 return "SourceCompleter: " + tree.sourcefile.getName(); 702 } 703 704 }; 705 } 706 707 public boolean isRootModule(ModuleSymbol module) { 708 Assert.checkNonNull(rootModules); 709 return rootModules.contains(module); 710 } 711 712 class ModuleVisitor extends JCTree.Visitor { 713 private ModuleSymbol sym; 714 private final Set<ModuleSymbol> allRequires = new HashSet<>(); 715 private final Map<PackageSymbol,List<ExportsDirective>> allExports = new HashMap<>(); 716 private final Map<PackageSymbol,List<OpensDirective>> allOpens = new HashMap<>(); 717 718 @Override 719 public void visitModuleDef(JCModuleDecl tree) { 720 sym = Assert.checkNonNull(tree.sym); 721 722 if (tree.getModuleType() == ModuleKind.OPEN) { 723 sym.flags.add(ModuleFlags.OPEN); 724 } 725 sym.flags_field |= (tree.mods.flags & Flags.DEPRECATED); 726 727 sym.requires = List.nil(); 728 sym.exports = List.nil(); 729 sym.opens = List.nil(); 730 tree.directives.forEach(t -> t.accept(this)); 731 sym.requires = sym.requires.reverse(); 732 sym.exports = sym.exports.reverse(); 733 sym.opens = sym.opens.reverse(); 734 ensureJavaBase(); 735 } 736 737 @Override 738 public void visitRequires(JCRequires tree) { 739 ModuleSymbol msym = lookupModule(tree.moduleName); 740 if (msym.kind != MDL) { 741 log.error(tree.moduleName.pos(), Errors.ModuleNotFound(msym)); 742 warnedMissing.add(msym); 743 } else if (allRequires.contains(msym)) { 744 log.error(tree.moduleName.pos(), Errors.DuplicateRequires(msym)); 745 } else { 746 allRequires.add(msym); 747 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class); 748 if (tree.isTransitive) 749 flags.add(RequiresFlag.TRANSITIVE); 750 if (tree.isStaticPhase) 751 flags.add(RequiresFlag.STATIC_PHASE); 752 RequiresDirective d = new RequiresDirective(msym, flags); 753 tree.directive = d; 754 sym.requires = sym.requires.prepend(d); 755 } 756 } 757 758 @Override 759 public void visitExports(JCExports tree) { 760 Name name = TreeInfo.fullName(tree.qualid); 761 PackageSymbol packge = syms.enterPackage(sym, name); 762 attr.setPackageSymbols(tree.qualid, packge); 763 764 List<ExportsDirective> exportsForPackage = allExports.computeIfAbsent(packge, p -> List.nil()); 765 for (ExportsDirective d : exportsForPackage) { 766 reportExportsConflict(tree, packge); 767 } 768 769 List<ModuleSymbol> toModules = null; 770 if (tree.moduleNames != null) { 771 Set<ModuleSymbol> to = new LinkedHashSet<>(); 772 for (JCExpression n: tree.moduleNames) { 773 ModuleSymbol msym = lookupModule(n); 774 chk.checkModuleExists(n.pos(), msym); 775 for (ExportsDirective d : exportsForPackage) { 776 checkDuplicateExportsToModule(n, msym, d); 777 } 778 if (!to.add(msym)) { 779 reportExportsConflictToModule(n, msym); 780 } 781 } 782 toModules = List.from(to); 783 } 784 785 if (toModules == null || !toModules.isEmpty()) { 786 Set<ExportsFlag> flags = EnumSet.noneOf(ExportsFlag.class); 787 ExportsDirective d = new ExportsDirective(packge, toModules, flags); 788 sym.exports = sym.exports.prepend(d); 789 tree.directive = d; 790 791 allExports.put(packge, exportsForPackage.prepend(d)); 792 } 793 } 794 795 private void reportExportsConflict(JCExports tree, PackageSymbol packge) { 796 log.error(tree.qualid.pos(), Errors.ConflictingExports(packge)); 797 } 798 799 private void checkDuplicateExportsToModule(JCExpression name, ModuleSymbol msym, 800 ExportsDirective d) { 801 if (d.modules != null) { 802 for (ModuleSymbol other : d.modules) { 803 if (msym == other) { 804 reportExportsConflictToModule(name, msym); 805 } 806 } 807 } 808 } 809 810 private void reportExportsConflictToModule(JCExpression name, ModuleSymbol msym) { 811 log.error(name.pos(), Errors.ConflictingExportsToModule(msym)); 812 } 813 814 @Override 815 public void visitOpens(JCOpens tree) { 816 Name name = TreeInfo.fullName(tree.qualid); 817 PackageSymbol packge = syms.enterPackage(sym, name); 818 attr.setPackageSymbols(tree.qualid, packge); 819 820 if (sym.flags.contains(ModuleFlags.OPEN)) { 821 log.error(tree.pos(), Errors.NoOpensUnlessStrong); 822 } 823 List<OpensDirective> opensForPackage = allOpens.computeIfAbsent(packge, p -> List.nil()); 824 for (OpensDirective d : opensForPackage) { 825 reportOpensConflict(tree, packge); 826 } 827 828 List<ModuleSymbol> toModules = null; 829 if (tree.moduleNames != null) { 830 Set<ModuleSymbol> to = new LinkedHashSet<>(); 831 for (JCExpression n: tree.moduleNames) { 832 ModuleSymbol msym = lookupModule(n); 833 chk.checkModuleExists(n.pos(), msym); 834 for (OpensDirective d : opensForPackage) { 835 checkDuplicateOpensToModule(n, msym, d); 836 } 837 if (!to.add(msym)) { 838 reportOpensConflictToModule(n, msym); 839 } 840 } 841 toModules = List.from(to); 842 } 843 844 if (toModules == null || !toModules.isEmpty()) { 845 Set<OpensFlag> flags = EnumSet.noneOf(OpensFlag.class); 846 OpensDirective d = new OpensDirective(packge, toModules, flags); 847 sym.opens = sym.opens.prepend(d); 848 tree.directive = d; 849 850 allOpens.put(packge, opensForPackage.prepend(d)); 851 } 852 } 853 854 private void reportOpensConflict(JCOpens tree, PackageSymbol packge) { 855 log.error(tree.qualid.pos(), Errors.ConflictingOpens(packge)); 856 } 857 858 private void checkDuplicateOpensToModule(JCExpression name, ModuleSymbol msym, 859 OpensDirective d) { 860 if (d.modules != null) { 861 for (ModuleSymbol other : d.modules) { 862 if (msym == other) { 863 reportOpensConflictToModule(name, msym); 864 } 865 } 866 } 867 } 868 869 private void reportOpensConflictToModule(JCExpression name, ModuleSymbol msym) { 870 log.error(name.pos(), Errors.ConflictingOpensToModule(msym)); 871 } 872 873 @Override 874 public void visitProvides(JCProvides tree) { } 875 876 @Override 877 public void visitUses(JCUses tree) { } 878 879 private void ensureJavaBase() { 880 if (sym.name == names.java_base) 881 return; 882 883 for (RequiresDirective d: sym.requires) { 884 if (d.module.name == names.java_base) 885 return; 886 } 887 888 ModuleSymbol java_base = syms.enterModule(names.java_base); 889 Directive.RequiresDirective d = 890 new Directive.RequiresDirective(java_base, 891 EnumSet.of(Directive.RequiresFlag.MANDATED)); 892 sym.requires = sym.requires.prepend(d); 893 } 894 895 private ModuleSymbol lookupModule(JCExpression moduleName) { 896 Name name = TreeInfo.fullName(moduleName); 897 ModuleSymbol msym = moduleFinder.findModule(name); 898 TreeInfo.setSymbol(moduleName, msym); 899 return msym; 900 } 901 } 902 903 public Completer getUsesProvidesCompleter() { 904 return sym -> { 905 ModuleSymbol msym = (ModuleSymbol) sym; 906 907 msym.complete(); 908 909 Env<AttrContext> env = typeEnvs.get(msym); 910 UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env); 911 JavaFileObject prev = log.useSource(env.toplevel.sourcefile); 912 JCModuleDecl decl = env.toplevel.getModuleDecl(); 913 DiagnosticPosition prevLintPos = deferredLintHandler.setPos(decl.pos()); 914 915 try { 916 decl.accept(v); 917 } finally { 918 log.useSource(prev); 919 deferredLintHandler.setPos(prevLintPos); 920 } 921 }; 922 } 923 924 class UsesProvidesVisitor extends JCTree.Visitor { 925 private final ModuleSymbol msym; 926 private final Env<AttrContext> env; 927 928 private final Set<ClassSymbol> allUses = new HashSet<>(); 929 private final Map<ClassSymbol, Set<ClassSymbol>> allProvides = new HashMap<>(); 930 931 public UsesProvidesVisitor(ModuleSymbol msym, Env<AttrContext> env) { 932 this.msym = msym; 933 this.env = env; 934 } 935 936 @Override @SuppressWarnings("unchecked") 937 public void visitModuleDef(JCModuleDecl tree) { 938 msym.directives = List.nil(); 939 msym.provides = List.nil(); 940 msym.uses = List.nil(); 941 tree.directives.forEach(t -> t.accept(this)); 942 msym.directives = msym.directives.reverse(); 943 msym.provides = msym.provides.reverse(); 944 msym.uses = msym.uses.reverse(); 945 946 if (msym.requires.nonEmpty() && msym.requires.head.flags.contains(RequiresFlag.MANDATED)) 947 msym.directives = msym.directives.prepend(msym.requires.head); 948 949 msym.directives = msym.directives.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet()))); 950 951 checkForCorrectness(); 952 } 953 954 @Override 955 public void visitExports(JCExports tree) { 956 if (tree.directive.packge.members().isEmpty()) { 957 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge)); 958 } 959 msym.directives = msym.directives.prepend(tree.directive); 960 } 961 962 @Override 963 public void visitOpens(JCOpens tree) { 964 chk.checkPackageExistsForOpens(tree.qualid, tree.directive.packge); 965 msym.directives = msym.directives.prepend(tree.directive); 966 } 967 968 MethodSymbol noArgsConstructor(ClassSymbol tsym) { 969 for (Symbol sym : tsym.members().getSymbolsByName(names.init)) { 970 MethodSymbol mSym = (MethodSymbol)sym; 971 if (mSym.params().isEmpty()) { 972 return mSym; 973 } 974 } 975 return null; 976 } 977 978 MethodSymbol factoryMethod(ClassSymbol tsym) { 979 for (Symbol sym : tsym.members().getSymbolsByName(names.provider, sym -> sym.kind == MTH)) { 980 MethodSymbol mSym = (MethodSymbol)sym; 981 if (mSym.isStatic() && (mSym.flags() & Flags.PUBLIC) != 0 && mSym.params().isEmpty()) { 982 return mSym; 983 } 984 } 985 return null; 986 } 987 988 Map<Directive.ProvidesDirective, JCProvides> directiveToTreeMap = new HashMap<>(); 989 990 @Override 991 public void visitProvides(JCProvides tree) { 992 Type st = attr.attribType(tree.serviceName, env, syms.objectType); 993 ClassSymbol service = (ClassSymbol) st.tsym; 994 if (allProvides.containsKey(service)) { 995 log.error(tree.serviceName.pos(), Errors.RepeatedProvidesForService(service)); 996 } 997 ListBuffer<ClassSymbol> impls = new ListBuffer<>(); 998 for (JCExpression implName : tree.implNames) { 999 Type it; 1000 boolean prevVisitingServiceImplementation = env.info.visitingServiceImplementation; 1001 try { 1002 env.info.visitingServiceImplementation = true; 1003 it = attr.attribType(implName, env, syms.objectType); 1004 } finally { 1005 env.info.visitingServiceImplementation = prevVisitingServiceImplementation; 1006 } 1007 ClassSymbol impl = (ClassSymbol) it.tsym; 1008 if ((impl.flags_field & PUBLIC) == 0) { 1009 log.error(implName.pos(), Errors.NotDefPublic(impl, impl.location())); 1010 } 1011 //find provider factory: 1012 MethodSymbol factory = factoryMethod(impl); 1013 if (factory != null) { 1014 Type returnType = factory.type.getReturnType(); 1015 if (!types.isSubtype(returnType, st)) { 1016 log.error(implName.pos(), Errors.ServiceImplementationProviderReturnMustBeSubtypeOfServiceInterface); 1017 } 1018 } else { 1019 if (!types.isSubtype(it, st)) { 1020 log.error(implName.pos(), Errors.ServiceImplementationMustBeSubtypeOfServiceInterface); 1021 } else if ((impl.flags() & ABSTRACT) != 0) { 1022 log.error(implName.pos(), Errors.ServiceImplementationIsAbstract(impl)); 1023 } else if (impl.isInner()) { 1024 log.error(implName.pos(), Errors.ServiceImplementationIsInner(impl)); 1025 } else { 1026 MethodSymbol constr = noArgsConstructor(impl); 1027 if (constr == null) { 1028 log.error(implName.pos(), Errors.ServiceImplementationDoesntHaveANoArgsConstructor(impl)); 1029 } else if ((constr.flags() & PUBLIC) == 0) { 1030 log.error(implName.pos(), Errors.ServiceImplementationNoArgsConstructorNotPublic(impl)); 1031 } 1032 } 1033 } 1034 if (it.hasTag(CLASS)) { 1035 if (allProvides.computeIfAbsent(service, s -> new HashSet<>()).add(impl)) { 1036 impls.append(impl); 1037 } else { 1038 log.error(implName.pos(), Errors.DuplicateProvides(service, impl)); 1039 } 1040 } 1041 } 1042 if (st.hasTag(CLASS) && !impls.isEmpty()) { 1043 Directive.ProvidesDirective d = new Directive.ProvidesDirective(service, impls.toList()); 1044 msym.provides = msym.provides.prepend(d); 1045 msym.directives = msym.directives.prepend(d); 1046 directiveToTreeMap.put(d, tree); 1047 } 1048 } 1049 1050 @Override 1051 public void visitRequires(JCRequires tree) { 1052 if (tree.directive != null && allModules().contains(tree.directive.module)) { 1053 chk.checkDeprecated(tree.moduleName.pos(), msym, tree.directive.module); 1054 msym.directives = msym.directives.prepend(tree.directive); 1055 } 1056 } 1057 1058 @Override 1059 public void visitUses(JCUses tree) { 1060 Type st = attr.attribType(tree.qualid, env, syms.objectType); 1061 Symbol sym = TreeInfo.symbol(tree.qualid); 1062 if ((sym.flags() & ENUM) != 0) { 1063 log.error(tree.qualid.pos(), Errors.ServiceDefinitionIsEnum(st.tsym)); 1064 } else if (st.hasTag(CLASS)) { 1065 ClassSymbol service = (ClassSymbol) st.tsym; 1066 if (allUses.add(service)) { 1067 Directive.UsesDirective d = new Directive.UsesDirective(service); 1068 msym.uses = msym.uses.prepend(d); 1069 msym.directives = msym.directives.prepend(d); 1070 } else { 1071 log.error(tree.pos(), Errors.DuplicateUses(service)); 1072 } 1073 } 1074 } 1075 1076 private void checkForCorrectness() { 1077 for (Directive.ProvidesDirective provides : msym.provides) { 1078 JCProvides tree = directiveToTreeMap.get(provides); 1079 for (ClassSymbol impl : provides.impls) { 1080 /* The implementation must be defined in the same module as the provides directive 1081 * (else, error) 1082 */ 1083 PackageSymbol implementationDefiningPackage = impl.packge(); 1084 if (implementationDefiningPackage.modle != msym) { 1085 // TODO: should use tree for the implentation name, not the entire provides tree 1086 // TODO: should improve error message to identify the implementation type 1087 log.error(tree.pos(), Errors.ServiceImplementationNotInRightModule(implementationDefiningPackage.modle)); 1088 } 1089 1090 /* There is no inherent requirement that module that provides a service should actually 1091 * use it itself. However, it is a pointless declaration if the service package is not 1092 * exported and there is no uses for the service. 1093 */ 1094 PackageSymbol interfaceDeclaringPackage = provides.service.packge(); 1095 boolean isInterfaceDeclaredInCurrentModule = interfaceDeclaringPackage.modle == msym; 1096 boolean isInterfaceExportedFromAReadableModule = 1097 msym.visiblePackages.get(interfaceDeclaringPackage.fullname) == interfaceDeclaringPackage; 1098 if (isInterfaceDeclaredInCurrentModule && !isInterfaceExportedFromAReadableModule) { 1099 // ok the interface is declared in this module. Let's check if it's exported 1100 boolean warn = true; 1101 for (ExportsDirective export : msym.exports) { 1102 if (interfaceDeclaringPackage == export.packge) { 1103 warn = false; 1104 break; 1105 } 1106 } 1107 if (warn) { 1108 for (UsesDirective uses : msym.uses) { 1109 if (provides.service == uses.service) { 1110 warn = false; 1111 break; 1112 } 1113 } 1114 } 1115 if (warn) { 1116 log.warning(tree.pos(), Warnings.ServiceProvidedButNotExportedOrUsed(provides.service)); 1117 } 1118 } 1119 } 1120 } 1121 } 1122 } 1123 1124 private Set<ModuleSymbol> allModules; 1125 1126 public Set<ModuleSymbol> allModules() { 1127 Assert.checkNonNull(allModules); 1128 return allModules; 1129 } 1130 1131 private void setupAllModules() { 1132 Assert.checkNonNull(rootModules); 1133 Assert.checkNull(allModules); 1134 1135 Set<ModuleSymbol> observable; 1136 1137 if (limitModsOpt == null && extraLimitMods.isEmpty()) { 1138 observable = null; 1139 } else { 1140 Set<ModuleSymbol> limitMods = new HashSet<>(); 1141 if (limitModsOpt != null) { 1142 for (String limit : limitModsOpt.split(",")) { 1143 if (!isValidName(limit)) 1144 continue; 1145 limitMods.add(syms.enterModule(names.fromString(limit))); 1146 } 1147 } 1148 for (String limit : extraLimitMods) { 1149 limitMods.add(syms.enterModule(names.fromString(limit))); 1150 } 1151 observable = computeTransitiveClosure(limitMods, null); 1152 observable.addAll(rootModules); 1153 if (lintOptions) { 1154 for (ModuleSymbol msym : limitMods) { 1155 if (!observable.contains(msym)) { 1156 log.warning(LintCategory.OPTIONS, 1157 Warnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym)); 1158 } 1159 } 1160 } 1161 } 1162 1163 Predicate<ModuleSymbol> observablePred = sym -> 1164 (observable == null) ? (moduleFinder.findModule(sym).kind != ERR) : observable.contains(sym); 1165 Predicate<ModuleSymbol> systemModulePred = sym -> (sym.flags() & Flags.SYSTEM_MODULE) != 0; 1166 Predicate<ModuleSymbol> noIncubatorPred = sym -> { 1167 sym.complete(); 1168 return !sym.resolutionFlags.contains(ModuleResolutionFlags.DO_NOT_RESOLVE_BY_DEFAULT); 1169 }; 1170 Set<ModuleSymbol> enabledRoot = new LinkedHashSet<>(); 1171 1172 if (rootModules.contains(syms.unnamedModule)) { 1173 ModuleSymbol javaSE = syms.getModule(java_se); 1174 Predicate<ModuleSymbol> jdkModulePred; 1175 1176 if (javaSE != null && (observable == null || observable.contains(javaSE))) { 1177 jdkModulePred = sym -> { 1178 sym.complete(); 1179 return !sym.name.startsWith(java_) 1180 && sym.exports.stream().anyMatch(e -> e.modules == null); 1181 }; 1182 enabledRoot.add(javaSE); 1183 } else { 1184 jdkModulePred = sym -> true; 1185 } 1186 1187 for (ModuleSymbol sym : new HashSet<>(syms.getAllModules())) { 1188 if (systemModulePred.test(sym) && observablePred.test(sym) && jdkModulePred.test(sym) && noIncubatorPred.test(sym)) { 1189 enabledRoot.add(sym); 1190 } 1191 } 1192 } 1193 1194 enabledRoot.addAll(rootModules); 1195 1196 if (addModsOpt != null || !extraAddMods.isEmpty()) { 1197 Set<String> fullAddMods = new HashSet<>(); 1198 fullAddMods.addAll(extraAddMods); 1199 1200 if (addModsOpt != null) { 1201 fullAddMods.addAll(Arrays.asList(addModsOpt.split(","))); 1202 } 1203 1204 for (String added : fullAddMods) { 1205 Stream<ModuleSymbol> modules; 1206 switch (added) { 1207 case ALL_SYSTEM: 1208 modules = new HashSet<>(syms.getAllModules()) 1209 .stream() 1210 .filter(systemModulePred.and(observablePred).and(noIncubatorPred)); 1211 break; 1212 case ALL_MODULE_PATH: 1213 modules = new HashSet<>(syms.getAllModules()) 1214 .stream() 1215 .filter(systemModulePred.negate().and(observablePred)); 1216 break; 1217 default: 1218 if (!isValidName(added)) 1219 continue; 1220 modules = Stream.of(syms.enterModule(names.fromString(added))); 1221 break; 1222 } 1223 modules.forEach(sym -> { 1224 enabledRoot.add(sym); 1225 if (observable != null) 1226 observable.add(sym); 1227 }); 1228 } 1229 } 1230 1231 Set<ModuleSymbol> result = computeTransitiveClosure(enabledRoot, observable); 1232 1233 result.add(syms.unnamedModule); 1234 1235 String incubatingModules = result.stream() 1236 .filter(msym -> msym.resolutionFlags.contains(ModuleResolutionFlags.WARN_INCUBATING)) 1237 .map(msym -> msym.name.toString()) 1238 .collect(Collectors.joining(",")); 1239 1240 if (!incubatingModules.isEmpty()) { 1241 log.warning(Warnings.IncubatingModules(incubatingModules)); 1242 } 1243 1244 allModules = result; 1245 1246 //add module versions from options, if any: 1247 if (moduleVersionOpt != null) { 1248 Name version = names.fromString(moduleVersionOpt); 1249 rootModules.forEach(m -> m.version = version); 1250 } 1251 } 1252 1253 public boolean isInModuleGraph(ModuleSymbol msym) { 1254 return allModules == null || allModules.contains(msym); 1255 } 1256 1257 private Set<ModuleSymbol> computeTransitiveClosure(Set<? extends ModuleSymbol> base, Set<ModuleSymbol> observable) { 1258 List<ModuleSymbol> primaryTodo = List.nil(); 1259 List<ModuleSymbol> secondaryTodo = List.nil(); 1260 1261 for (ModuleSymbol ms : base) { 1262 primaryTodo = primaryTodo.prepend(ms); 1263 } 1264 1265 Set<ModuleSymbol> result = new LinkedHashSet<>(); 1266 result.add(syms.java_base); 1267 1268 while (primaryTodo.nonEmpty() || secondaryTodo.nonEmpty()) { 1269 ModuleSymbol current; 1270 boolean isPrimaryTodo; 1271 if (primaryTodo.nonEmpty()) { 1272 current = primaryTodo.head; 1273 primaryTodo = primaryTodo.tail; 1274 isPrimaryTodo = true; 1275 } else { 1276 current = secondaryTodo.head; 1277 secondaryTodo = secondaryTodo.tail; 1278 isPrimaryTodo = false; 1279 } 1280 if (observable != null && !observable.contains(current)) 1281 continue; 1282 if (!result.add(current) || current == syms.unnamedModule || ((current.flags_field & Flags.AUTOMATIC_MODULE) != 0)) 1283 continue; 1284 current.complete(); 1285 if (current.kind == ERR && isPrimaryTodo && warnedMissing.add(current)) { 1286 log.error(Errors.ModuleNotFound(current)); 1287 } 1288 for (RequiresDirective rd : current.requires) { 1289 if (rd.module == syms.java_base) continue; 1290 if ((rd.isTransitive() && isPrimaryTodo) || base.contains(current)) { 1291 primaryTodo = primaryTodo.prepend(rd.module); 1292 } else { 1293 secondaryTodo = secondaryTodo.prepend(rd.module); 1294 } 1295 } 1296 } 1297 1298 return result; 1299 } 1300 1301 public ModuleSymbol getObservableModule(Name name) { 1302 ModuleSymbol mod = syms.getModule(name); 1303 1304 if (allModules().contains(mod)) { 1305 return mod; 1306 } 1307 1308 return null; 1309 } 1310 1311 private Completer getUnnamedModuleCompleter() { 1312 moduleFinder.findAllModules(); 1313 return new Symbol.Completer() { 1314 @Override 1315 public void complete(Symbol sym) throws CompletionFailure { 1316 if (inInitModules) { 1317 sym.completer = this; 1318 return ; 1319 } 1320 ModuleSymbol msym = (ModuleSymbol) sym; 1321 Set<ModuleSymbol> allModules = new HashSet<>(allModules()); 1322 allModules.remove(syms.unnamedModule); 1323 for (ModuleSymbol m : allModules) { 1324 m.complete(); 1325 } 1326 initVisiblePackages(msym, allModules); 1327 } 1328 1329 @Override 1330 public String toString() { 1331 return "unnamedModule Completer"; 1332 } 1333 }; 1334 } 1335 1336 private final Map<ModuleSymbol, Set<ModuleSymbol>> requiresTransitiveCache = new HashMap<>(); 1337 1338 private void completeModule(ModuleSymbol msym) { 1339 if (inInitModules) { 1340 msym.completer = sym -> completeModule(msym); 1341 return ; 1342 } 1343 1344 if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) { 1345 completeAutomaticModule(msym); 1346 } 1347 1348 Assert.checkNonNull(msym.requires); 1349 1350 initAddReads(); 1351 1352 msym.requires = msym.requires.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet()))); 1353 1354 List<RequiresDirective> requires = msym.requires; 1355 1356 while (requires.nonEmpty()) { 1357 if (!allModules().contains(requires.head.module)) { 1358 Env<AttrContext> env = typeEnvs.get(msym); 1359 if (env != null) { 1360 JavaFileObject origSource = log.useSource(env.toplevel.sourcefile); 1361 try { 1362 log.error(/*XXX*/env.tree, Errors.ModuleNotFound(requires.head.module)); 1363 } finally { 1364 log.useSource(origSource); 1365 } 1366 } else { 1367 Assert.check((msym.flags() & Flags.AUTOMATIC_MODULE) == 0); 1368 } 1369 msym.requires = List.filter(msym.requires, requires.head); 1370 } 1371 requires = requires.tail; 1372 } 1373 1374 Set<ModuleSymbol> readable = new LinkedHashSet<>(); 1375 Set<ModuleSymbol> requiresTransitive = new HashSet<>(); 1376 1377 for (RequiresDirective d : msym.requires) { 1378 d.module.complete(); 1379 readable.add(d.module); 1380 Set<ModuleSymbol> s = retrieveRequiresTransitive(d.module); 1381 Assert.checkNonNull(s, () -> "no entry in cache for " + d.module); 1382 readable.addAll(s); 1383 if (d.flags.contains(RequiresFlag.TRANSITIVE)) { 1384 requiresTransitive.add(d.module); 1385 requiresTransitive.addAll(s); 1386 } 1387 } 1388 1389 requiresTransitiveCache.put(msym, requiresTransitive); 1390 initVisiblePackages(msym, readable); 1391 for (ExportsDirective d: msym.exports) { 1392 if (d.packge != null) { 1393 d.packge.modle = msym; 1394 } 1395 } 1396 1397 } 1398 1399 private Set<ModuleSymbol> retrieveRequiresTransitive(ModuleSymbol msym) { 1400 Set<ModuleSymbol> requiresTransitive = requiresTransitiveCache.get(msym); 1401 1402 if (requiresTransitive == null) { 1403 //the module graph may contain cycles involving automatic modules or --add-reads edges 1404 requiresTransitive = new HashSet<>(); 1405 1406 Set<ModuleSymbol> seen = new HashSet<>(); 1407 List<ModuleSymbol> todo = List.of(msym); 1408 1409 while (todo.nonEmpty()) { 1410 ModuleSymbol current = todo.head; 1411 todo = todo.tail; 1412 if (!seen.add(current)) 1413 continue; 1414 requiresTransitive.add(current); 1415 current.complete(); 1416 Iterable<? extends RequiresDirective> requires; 1417 if (current != syms.unnamedModule) { 1418 Assert.checkNonNull(current.requires, () -> current + ".requires == null; " + msym); 1419 requires = current.requires; 1420 for (RequiresDirective rd : requires) { 1421 if (rd.isTransitive()) 1422 todo = todo.prepend(rd.module); 1423 } 1424 } else { 1425 for (ModuleSymbol mod : allModules()) { 1426 todo = todo.prepend(mod); 1427 } 1428 } 1429 } 1430 1431 requiresTransitive.remove(msym); 1432 } 1433 1434 return requiresTransitive; 1435 } 1436 1437 private void initVisiblePackages(ModuleSymbol msym, Collection<ModuleSymbol> readable) { 1438 initAddExports(); 1439 1440 msym.visiblePackages = new LinkedHashMap<>(); 1441 msym.readModules = new HashSet<>(readable); 1442 1443 Map<Name, ModuleSymbol> seen = new HashMap<>(); 1444 1445 for (ModuleSymbol rm : readable) { 1446 if (rm == syms.unnamedModule) 1447 continue; 1448 addVisiblePackages(msym, seen, rm, rm.exports); 1449 } 1450 1451 addExports.forEach((exportsFrom, exports) -> { 1452 addVisiblePackages(msym, seen, exportsFrom, exports); 1453 }); 1454 } 1455 1456 private void addVisiblePackages(ModuleSymbol msym, 1457 Map<Name, ModuleSymbol> seenPackages, 1458 ModuleSymbol exportsFrom, 1459 Collection<ExportsDirective> exports) { 1460 for (ExportsDirective d : exports) { 1461 if (d.modules == null || d.modules.contains(msym)) { 1462 Name packageName = d.packge.fullname; 1463 ModuleSymbol previousModule = seenPackages.get(packageName); 1464 1465 if (previousModule != null && previousModule != exportsFrom) { 1466 Env<AttrContext> env = typeEnvs.get(msym); 1467 JavaFileObject origSource = env != null ? log.useSource(env.toplevel.sourcefile) 1468 : null; 1469 DiagnosticPosition pos = env != null ? env.tree.pos() : null; 1470 try { 1471 log.error(pos, Errors.PackageClashFromRequires(msym, packageName, 1472 previousModule, exportsFrom)); 1473 } finally { 1474 if (env != null) 1475 log.useSource(origSource); 1476 } 1477 continue; 1478 } 1479 1480 seenPackages.put(packageName, exportsFrom); 1481 msym.visiblePackages.put(d.packge.fullname, d.packge); 1482 } 1483 } 1484 } 1485 1486 private void initAddExports() { 1487 if (addExports != null) 1488 return; 1489 1490 addExports = new LinkedHashMap<>(); 1491 Set<ModuleSymbol> unknownModules = new HashSet<>(); 1492 1493 if (addExportsOpt == null) 1494 return; 1495 1496 Pattern ep = Pattern.compile("([^/]+)/([^=]+)=(.*)"); 1497 for (String s: addExportsOpt.split("\0+")) { 1498 if (s.isEmpty()) 1499 continue; 1500 Matcher em = ep.matcher(s); 1501 if (!em.matches()) { 1502 continue; 1503 } 1504 1505 // Terminology comes from 1506 // --add-exports module/package=target,... 1507 // Compare to 1508 // module module { exports package to target, ... } 1509 String moduleName = em.group(1); 1510 String packageName = em.group(2); 1511 String targetNames = em.group(3); 1512 1513 if (!isValidName(moduleName)) 1514 continue; 1515 1516 ModuleSymbol msym = syms.enterModule(names.fromString(moduleName)); 1517 if (!isKnownModule(msym, unknownModules)) 1518 continue; 1519 1520 if (!isValidName(packageName)) 1521 continue; 1522 PackageSymbol p = syms.enterPackage(msym, names.fromString(packageName)); 1523 p.modle = msym; // TODO: do we need this? 1524 1525 List<ModuleSymbol> targetModules = List.nil(); 1526 for (String toModule : targetNames.split("[ ,]+")) { 1527 ModuleSymbol m; 1528 if (toModule.equals("ALL-UNNAMED")) { 1529 m = syms.unnamedModule; 1530 } else { 1531 if (!isValidName(toModule)) 1532 continue; 1533 m = syms.enterModule(names.fromString(toModule)); 1534 if (!isKnownModule(m, unknownModules)) 1535 continue; 1536 } 1537 targetModules = targetModules.prepend(m); 1538 } 1539 1540 Set<ExportsDirective> extra = addExports.computeIfAbsent(msym, _x -> new LinkedHashSet<>()); 1541 ExportsDirective d = new ExportsDirective(p, targetModules); 1542 extra.add(d); 1543 } 1544 } 1545 1546 private boolean isKnownModule(ModuleSymbol msym, Set<ModuleSymbol> unknownModules) { 1547 if (allModules.contains(msym)) { 1548 return true; 1549 } 1550 1551 if (!unknownModules.contains(msym)) { 1552 if (lintOptions) { 1553 log.warning(LintCategory.OPTIONS, 1554 Warnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym)); 1555 } 1556 unknownModules.add(msym); 1557 } 1558 return false; 1559 } 1560 1561 private void initAddReads() { 1562 if (addReads != null) 1563 return; 1564 1565 addReads = new LinkedHashMap<>(); 1566 1567 if (addReadsOpt == null) 1568 return; 1569 1570 Pattern rp = Pattern.compile("([^=]+)=(.*)"); 1571 for (String s : addReadsOpt.split("\0+")) { 1572 if (s.isEmpty()) 1573 continue; 1574 Matcher rm = rp.matcher(s); 1575 if (!rm.matches()) { 1576 continue; 1577 } 1578 1579 // Terminology comes from 1580 // --add-reads source-module=target-module,... 1581 // Compare to 1582 // module source-module { requires target-module; ... } 1583 String sourceName = rm.group(1); 1584 String targetNames = rm.group(2); 1585 1586 if (!isValidName(sourceName)) 1587 continue; 1588 1589 ModuleSymbol msym = syms.enterModule(names.fromString(sourceName)); 1590 if (!allModules.contains(msym)) { 1591 if (lintOptions) { 1592 log.warning(Warnings.ModuleForOptionNotFound(Option.ADD_READS, msym)); 1593 } 1594 continue; 1595 } 1596 1597 for (String targetName : targetNames.split("[ ,]+", -1)) { 1598 ModuleSymbol targetModule; 1599 if (targetName.equals("ALL-UNNAMED")) { 1600 targetModule = syms.unnamedModule; 1601 } else { 1602 if (!isValidName(targetName)) 1603 continue; 1604 targetModule = syms.enterModule(names.fromString(targetName)); 1605 if (!allModules.contains(targetModule)) { 1606 if (lintOptions) { 1607 log.warning(LintCategory.OPTIONS, Warnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule)); 1608 } 1609 continue; 1610 } 1611 } 1612 addReads.computeIfAbsent(msym, m -> new HashSet<>()) 1613 .add(new RequiresDirective(targetModule, EnumSet.of(RequiresFlag.EXTRA))); 1614 } 1615 } 1616 } 1617 1618 private void checkCyclicDependencies(JCModuleDecl mod) { 1619 for (JCDirective d : mod.directives) { 1620 JCRequires rd; 1621 if (!d.hasTag(Tag.REQUIRES) || (rd = (JCRequires) d).directive == null) 1622 continue; 1623 Set<ModuleSymbol> nonSyntheticDeps = new HashSet<>(); 1624 List<ModuleSymbol> queue = List.of(rd.directive.module); 1625 while (queue.nonEmpty()) { 1626 ModuleSymbol current = queue.head; 1627 queue = queue.tail; 1628 if (!nonSyntheticDeps.add(current)) 1629 continue; 1630 current.complete(); 1631 if ((current.flags() & Flags.ACYCLIC) != 0) 1632 continue; 1633 Assert.checkNonNull(current.requires, current::toString); 1634 for (RequiresDirective dep : current.requires) { 1635 if (!dep.flags.contains(RequiresFlag.EXTRA)) 1636 queue = queue.prepend(dep.module); 1637 } 1638 } 1639 if (nonSyntheticDeps.contains(mod.sym)) { 1640 log.error(rd.moduleName.pos(), Errors.CyclicRequires(rd.directive.module)); 1641 } 1642 mod.sym.flags_field |= Flags.ACYCLIC; 1643 } 1644 } 1645 1646 private boolean isValidName(CharSequence name) { 1647 return SourceVersion.isName(name, Source.toSourceVersion(source)); 1648 } 1649 1650 // DEBUG 1651 private String toString(ModuleSymbol msym) { 1652 return msym.name + "[" 1653 + "kind:" + msym.kind + ";" 1654 + "locn:" + toString(msym.sourceLocation) + "," + toString(msym.classLocation) + ";" 1655 + "info:" + toString(msym.module_info.sourcefile) + "," 1656 + toString(msym.module_info.classfile) + "," 1657 + msym.module_info.completer 1658 + "]"; 1659 } 1660 1661 // DEBUG 1662 String toString(Location locn) { 1663 return (locn == null) ? "--" : locn.getName(); 1664 } 1665 1666 // DEBUG 1667 String toString(JavaFileObject fo) { 1668 return (fo == null) ? "--" : fo.getName(); 1669 } 1670 1671 public void newRound() { 1672 allModules = null; 1673 rootModules = null; 1674 warnedMissing.clear(); 1675 } 1676} 1677