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