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