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