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