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