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