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