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