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