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 
  27 package com.sun.tools.javac.comp;
  28 
  29 import java.io.IOException;
  30 import java.util.Arrays;
  31 import java.util.Collection;
  32 import java.util.Collections;
  33 import java.util.EnumSet;
  34 import java.util.HashMap;
  35 import java.util.HashSet;
  36 import java.util.LinkedHashMap;
  37 import java.util.LinkedHashSet;
  38 import java.util.Map;
  39 import java.util.Set;
  40 import java.util.function.Consumer;
  41 import java.util.function.Predicate;
  42 import java.util.regex.Matcher;
  43 import java.util.regex.Pattern;
  44 import java.util.stream.Stream;
  45 
  46 import javax.lang.model.SourceVersion;
  47 import javax.tools.JavaFileManager;
  48 import javax.tools.JavaFileManager.Location;
  49 import javax.tools.JavaFileObject;
  50 import javax.tools.JavaFileObject.Kind;
  51 import javax.tools.StandardLocation;
  52 
  53 import com.sun.source.tree.ModuleTree.ModuleKind;
  54 import com.sun.tools.javac.code.ClassFinder;
  55 import com.sun.tools.javac.code.DeferredLintHandler;
  56 import com.sun.tools.javac.code.Directive;
  57 import com.sun.tools.javac.code.Directive.ExportsDirective;
  58 import com.sun.tools.javac.code.Directive.ExportsFlag;
  59 import com.sun.tools.javac.code.Directive.OpensDirective;
  60 import com.sun.tools.javac.code.Directive.OpensFlag;
  61 import com.sun.tools.javac.code.Directive.RequiresDirective;
  62 import com.sun.tools.javac.code.Directive.RequiresFlag;
  63 import com.sun.tools.javac.code.Directive.UsesDirective;
  64 import com.sun.tools.javac.code.Flags;
  65 import com.sun.tools.javac.code.Kinds;
  66 import com.sun.tools.javac.code.Lint.LintCategory;
  67 import com.sun.tools.javac.code.ModuleFinder;
  68 import com.sun.tools.javac.code.Source;
  69 import com.sun.tools.javac.code.Symbol;
  70 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  71 import com.sun.tools.javac.code.Symbol.Completer;
  72 import com.sun.tools.javac.code.Symbol.CompletionFailure;
  73 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  74 import com.sun.tools.javac.code.Symbol.ModuleFlags;
  75 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
  76 import com.sun.tools.javac.code.Symbol.PackageSymbol;
  77 import com.sun.tools.javac.code.Symtab;
  78 import com.sun.tools.javac.code.Type;
  79 import com.sun.tools.javac.code.Types;
  80 import com.sun.tools.javac.jvm.ClassWriter;
  81 import com.sun.tools.javac.jvm.JNIWriter;
  82 import com.sun.tools.javac.main.Option;
  83 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  84 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  85 import com.sun.tools.javac.tree.JCTree;
  86 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
  87 import com.sun.tools.javac.tree.JCTree.JCDirective;
  88 import com.sun.tools.javac.tree.JCTree.JCExports;
  89 import com.sun.tools.javac.tree.JCTree.JCExpression;
  90 import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
  91 import com.sun.tools.javac.tree.JCTree.JCOpens;
  92 import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
  93 import com.sun.tools.javac.tree.JCTree.JCProvides;
  94 import com.sun.tools.javac.tree.JCTree.JCRequires;
  95 import com.sun.tools.javac.tree.JCTree.JCUses;
  96 import com.sun.tools.javac.tree.JCTree.Tag;
  97 import com.sun.tools.javac.tree.TreeInfo;
  98 import com.sun.tools.javac.util.Abort;
  99 import com.sun.tools.javac.util.Assert;
 100 import com.sun.tools.javac.util.Context;
 101 import com.sun.tools.javac.util.JCDiagnostic;
 102 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 103 import com.sun.tools.javac.util.List;
 104 import com.sun.tools.javac.util.ListBuffer;
 105 import com.sun.tools.javac.util.Log;
 106 import com.sun.tools.javac.util.Name;
 107 import com.sun.tools.javac.util.Names;
 108 import com.sun.tools.javac.util.Options;
 109 import com.sun.tools.javac.util.Position;
 110 
 111 import static com.sun.tools.javac.code.Flags.ABSTRACT;
 112 import static com.sun.tools.javac.code.Flags.ENUM;
 113 import static com.sun.tools.javac.code.Flags.PUBLIC;
 114 import static com.sun.tools.javac.code.Flags.UNATTRIBUTED;
 115 import static com.sun.tools.javac.code.Kinds.Kind.MDL;
 116 import static com.sun.tools.javac.code.Kinds.Kind.MTH;
 117 import static com.sun.tools.javac.code.TypeTag.CLASS;
 118 
 119 /**
 120  *  TODO: fill in
 121  *
 122  *  <p><b>This is NOT part of any supported API.
 123  *  If you write code that depends on this, you do so at your own risk.
 124  *  This code and its internal interfaces are subject to change or
 125  *  deletion without notice.</b>
 126  */
 127 public class Modules extends JCTree.Visitor {
 128     private static final String ALL_SYSTEM = "ALL-SYSTEM";
 129     private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
 130 
 131     private final Log log;
 132     private final Names names;
 133     private final Symtab syms;
 134     private final Attr attr;
 135     private final Check chk;
 136     private final DeferredLintHandler deferredLintHandler;
 137     private final TypeEnvs typeEnvs;
 138     private final Types types;
 139     private final JavaFileManager fileManager;
 140     private final ModuleFinder moduleFinder;
 141     private final Source source;
 142     private final boolean allowModules;
 143 
 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         return fileManager.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH,
 480                 fo, (pkgName == null) ? null : pkgName.toString());
 481     }
 482 
 483     private void checkSpecifiedModule(List<JCCompilationUnit> trees, JCDiagnostic.Error error) {
 484         if (moduleOverride != null) {
 485             JavaFileObject prev = log.useSource(trees.head.sourcefile);
 486             try {
 487                 log.error(trees.head.pos(), error);
 488             } finally {
 489                 log.useSource(prev);
 490             }
 491         }
 492     }
 493 
 494     private void checkNoAllModulePath() {
 495         if (addModsOpt != null && Arrays.asList(addModsOpt.split(",")).contains(ALL_MODULE_PATH)) {
 496             log.error(Errors.AddmodsAllModulePathInvalid);
 497         }
 498     }
 499 
 500     private final Completer mainCompleter = new Completer() {
 501         @Override
 502         public void complete(Symbol sym) throws CompletionFailure {
 503             ModuleSymbol msym = moduleFinder.findModule((ModuleSymbol) sym);
 504 
 505             if (msym.kind == Kinds.Kind.ERR) {
 506                 log.error(Errors.ModuleNotFound(msym));
 507                 //make sure the module is initialized:
 508                 msym.directives = List.nil();
 509                 msym.exports = List.nil();
 510                 msym.provides = List.nil();
 511                 msym.requires = List.nil();
 512                 msym.uses = List.nil();
 513             } else if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
 514                 setupAutomaticModule(msym);
 515             } else {
 516                 msym.module_info.complete();
 517             }
 518 
 519             // If module-info comes from a .java file, the underlying
 520             // call of classFinder.fillIn will have called through the
 521             // source completer, to Enter, and then to Modules.enter,
 522             // which will call completeModule.
 523             // But, if module-info comes from a .class file, the underlying
 524             // call of classFinder.fillIn will just call ClassReader to read
 525             // the .class file, and so we call completeModule here.
 526             if (msym.module_info.classfile == null || msym.module_info.classfile.getKind() == Kind.CLASS) {
 527                 completeModule(msym);
 528             }
 529         }
 530 
 531         @Override
 532         public String toString() {
 533             return "mainCompleter";
 534         }
 535     };
 536 
 537     private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
 538         try {
 539             ListBuffer<Directive> directives = new ListBuffer<>();
 540             ListBuffer<ExportsDirective> exports = new ListBuffer<>();
 541             Set<String> seenPackages = new HashSet<>();
 542 
 543             for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) {
 544                 String binName = fileManager.inferBinaryName(msym.classLocation, clazz);
 545                 String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package????
 546                 if (seenPackages.add(pack)) {
 547                     ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null);
 548                     //TODO: opens?
 549                     directives.add(d);
 550                     exports.add(d);
 551                 }
 552             }
 553 
 554             msym.exports = exports.toList();
 555             msym.provides = List.nil();
 556             msym.requires = List.nil();
 557             msym.uses = List.nil();
 558             msym.directives = directives.toList();
 559             msym.flags_field |= Flags.ACYCLIC;
 560         } catch (IOException ex) {
 561             throw new IllegalStateException(ex);
 562         }
 563     }
 564 
 565     private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure {
 566         ListBuffer<Directive> directives = new ListBuffer<>();
 567 
 568         directives.addAll(msym.directives);
 569 
 570         ListBuffer<RequiresDirective> requires = new ListBuffer<>();
 571 
 572         for (ModuleSymbol ms : allModules()) {
 573             if (ms == syms.unnamedModule || ms == msym)
 574                 continue;
 575             Set<RequiresFlag> flags = (ms.flags_field & Flags.AUTOMATIC_MODULE) != 0 ?
 576                     EnumSet.of(RequiresFlag.TRANSITIVE) : EnumSet.noneOf(RequiresFlag.class);
 577             RequiresDirective d = new RequiresDirective(ms, flags);
 578             directives.add(d);
 579             requires.add(d);
 580         }
 581 
 582         RequiresDirective requiresUnnamed = new RequiresDirective(syms.unnamedModule);
 583         directives.add(requiresUnnamed);
 584         requires.add(requiresUnnamed);
 585 
 586         msym.requires = requires.toList();
 587         msym.directives = directives.toList();
 588     }
 589 
 590     private Completer getSourceCompleter(JCCompilationUnit tree) {
 591         return new Completer() {
 592             @Override
 593             public void complete(Symbol sym) throws CompletionFailure {
 594                 ModuleSymbol msym = (ModuleSymbol) sym;
 595                 msym.flags_field |= UNATTRIBUTED;
 596                 ModuleVisitor v = new ModuleVisitor();
 597                 JavaFileObject prev = log.useSource(tree.sourcefile);
 598                 try {
 599                     JCModuleDecl moduleDecl = tree.getModuleDecl();
 600                     moduleDecl.accept(v);
 601                     completeModule(msym);
 602                     checkCyclicDependencies(moduleDecl);
 603                 } finally {
 604                     log.useSource(prev);
 605                     msym.flags_field &= ~UNATTRIBUTED;
 606                 }
 607             }
 608 
 609             @Override
 610             public String toString() {
 611                 return "SourceCompleter: " + tree.sourcefile.getName();
 612             }
 613 
 614         };
 615     }
 616 
 617     class ModuleVisitor extends JCTree.Visitor {
 618         private ModuleSymbol sym;
 619         private final Set<ModuleSymbol> allRequires = new HashSet<>();
 620         private final Map<PackageSymbol,List<ExportsDirective>> allExports = new HashMap<>();
 621         private final Map<PackageSymbol,List<OpensDirective>> allOpens = new HashMap<>();
 622 
 623         @Override
 624         public void visitModuleDef(JCModuleDecl tree) {
 625             sym = Assert.checkNonNull(tree.sym);
 626 
 627             if (tree.getModuleType() == ModuleKind.OPEN) {
 628                 sym.flags.add(ModuleFlags.OPEN);
 629             }
 630             sym.flags_field |= (tree.mods.flags & Flags.DEPRECATED);
 631 
 632             sym.requires = List.nil();
 633             sym.exports = List.nil();
 634             sym.opens = List.nil();
 635             tree.directives.forEach(t -> t.accept(this));
 636             sym.requires = sym.requires.reverse();
 637             sym.exports = sym.exports.reverse();
 638             sym.opens = sym.opens.reverse();
 639             ensureJavaBase();
 640         }
 641 
 642         @Override
 643         public void visitRequires(JCRequires tree) {
 644             ModuleSymbol msym = lookupModule(tree.moduleName);
 645             if (msym.kind != MDL) {
 646                 log.error(tree.moduleName.pos(), Errors.ModuleNotFound(msym));
 647             } else if (allRequires.contains(msym)) {
 648                 log.error(tree.moduleName.pos(), Errors.DuplicateRequires(msym));
 649             } else {
 650                 allRequires.add(msym);
 651                 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class);
 652                 if (tree.isTransitive)
 653                     flags.add(RequiresFlag.TRANSITIVE);
 654                 if (tree.isStaticPhase)
 655                     flags.add(RequiresFlag.STATIC_PHASE);
 656                 RequiresDirective d = new RequiresDirective(msym, flags);
 657                 tree.directive = d;
 658                 sym.requires = sym.requires.prepend(d);
 659             }
 660         }
 661 
 662         @Override
 663         public void visitExports(JCExports tree) {
 664             Name name = TreeInfo.fullName(tree.qualid);
 665             PackageSymbol packge = syms.enterPackage(sym, name);
 666             attr.setPackageSymbols(tree.qualid, packge);
 667 
 668             if (tree.hasTag(Tag.OPENS) && sym.flags.contains(ModuleFlags.OPEN)) {
 669                 log.error(tree.pos(), Errors.NoOpensUnlessStrong);
 670             }
 671             List<ExportsDirective> exportsForPackage = allExports.computeIfAbsent(packge, p -> List.nil());
 672             for (ExportsDirective d : exportsForPackage) {
 673                 reportExportsConflict(tree, packge);
 674             }
 675 
 676             List<ModuleSymbol> toModules = null;
 677             if (tree.moduleNames != null) {
 678                 Set<ModuleSymbol> to = new LinkedHashSet<>();
 679                 for (JCExpression n: tree.moduleNames) {
 680                     ModuleSymbol msym = lookupModule(n);
 681                     if (msym.kind != MDL) {
 682                         log.error(n.pos(), Errors.ModuleNotFound(msym));
 683                     } else {
 684                         for (ExportsDirective d : exportsForPackage) {
 685                             checkDuplicateExportsToModule(n, msym, d);
 686                         }
 687                         if (!to.add(msym)) {
 688                             reportExportsConflictToModule(n, msym);
 689                         }
 690                     }
 691                 }
 692                 toModules = List.from(to);
 693             }
 694 
 695             if (toModules == null || !toModules.isEmpty()) {
 696                 Set<ExportsFlag> flags = EnumSet.noneOf(ExportsFlag.class);
 697                 ExportsDirective d = new ExportsDirective(packge, toModules, flags);
 698                 sym.exports = sym.exports.prepend(d);
 699                 tree.directive = d;
 700 
 701                 allExports.put(packge, exportsForPackage.prepend(d));
 702             }
 703         }
 704 
 705         private void reportExportsConflict(JCExports tree, PackageSymbol packge) {
 706             log.error(tree.qualid.pos(), Errors.ConflictingExports(packge));
 707         }
 708 
 709         private void checkDuplicateExportsToModule(JCExpression name, ModuleSymbol msym,
 710                 ExportsDirective d) {
 711             if (d.modules != null) {
 712                 for (ModuleSymbol other : d.modules) {
 713                     if (msym == other) {
 714                         reportExportsConflictToModule(name, msym);
 715                     }
 716                 }
 717             }
 718         }
 719 
 720         private void reportExportsConflictToModule(JCExpression name, ModuleSymbol msym) {
 721             log.error(name.pos(), Errors.ConflictingExportsToModule(msym));
 722         }
 723 
 724         @Override
 725         public void visitOpens(JCOpens tree) {
 726             Name name = TreeInfo.fullName(tree.qualid);
 727             PackageSymbol packge = syms.enterPackage(sym, name);
 728             attr.setPackageSymbols(tree.qualid, packge);
 729 
 730             if (sym.flags.contains(ModuleFlags.OPEN)) {
 731                 log.error(tree.pos(), Errors.NoOpensUnlessStrong);
 732             }
 733             List<OpensDirective> opensForPackage = allOpens.computeIfAbsent(packge, p -> List.nil());
 734             for (OpensDirective d : opensForPackage) {
 735                 reportOpensConflict(tree, packge);
 736             }
 737 
 738             List<ModuleSymbol> toModules = null;
 739             if (tree.moduleNames != null) {
 740                 Set<ModuleSymbol> to = new LinkedHashSet<>();
 741                 for (JCExpression n: tree.moduleNames) {
 742                     ModuleSymbol msym = lookupModule(n);
 743                     if (msym.kind != MDL) {
 744                         log.error(n.pos(), Errors.ModuleNotFound(msym));
 745                     } else {
 746                         for (OpensDirective d : opensForPackage) {
 747                             checkDuplicateOpensToModule(n, msym, d);
 748                         }
 749                         if (!to.add(msym)) {
 750                             reportOpensConflictToModule(n, msym);
 751                         }
 752                     }
 753                 }
 754                 toModules = List.from(to);
 755             }
 756 
 757             if (toModules == null || !toModules.isEmpty()) {
 758                 Set<OpensFlag> flags = EnumSet.noneOf(OpensFlag.class);
 759                 OpensDirective d = new OpensDirective(packge, toModules, flags);
 760                 sym.opens = sym.opens.prepend(d);
 761                 tree.directive = d;
 762 
 763                 allOpens.put(packge, opensForPackage.prepend(d));
 764             }
 765         }
 766 
 767         private void reportOpensConflict(JCOpens tree, PackageSymbol packge) {
 768             log.error(tree.qualid.pos(), Errors.ConflictingOpens(packge));
 769         }
 770 
 771         private void checkDuplicateOpensToModule(JCExpression name, ModuleSymbol msym,
 772                 OpensDirective d) {
 773             if (d.modules != null) {
 774                 for (ModuleSymbol other : d.modules) {
 775                     if (msym == other) {
 776                         reportOpensConflictToModule(name, msym);
 777                     }
 778                 }
 779             }
 780         }
 781 
 782         private void reportOpensConflictToModule(JCExpression name, ModuleSymbol msym) {
 783             log.error(name.pos(), Errors.ConflictingOpensToModule(msym));
 784         }
 785 
 786         @Override
 787         public void visitProvides(JCProvides tree) { }
 788 
 789         @Override
 790         public void visitUses(JCUses tree) { }
 791 
 792         private void ensureJavaBase() {
 793             if (sym.name == names.java_base)
 794                 return;
 795 
 796             for (RequiresDirective d: sym.requires) {
 797                 if (d.module.name == names.java_base)
 798                     return;
 799             }
 800 
 801             ModuleSymbol java_base = syms.enterModule(names.java_base);
 802             Directive.RequiresDirective d =
 803                     new Directive.RequiresDirective(java_base,
 804                             EnumSet.of(Directive.RequiresFlag.MANDATED));
 805             sym.requires = sym.requires.prepend(d);
 806         }
 807 
 808         private ModuleSymbol lookupModule(JCExpression moduleName) {
 809             Name name = TreeInfo.fullName(moduleName);
 810             ModuleSymbol msym = moduleFinder.findModule(name);
 811             TreeInfo.setSymbol(moduleName, msym);
 812             return msym;
 813         }
 814     }
 815 
 816     public Completer getUsesProvidesCompleter() {
 817         return sym -> {
 818             ModuleSymbol msym = (ModuleSymbol) sym;
 819 
 820             msym.complete();
 821 
 822             Env<AttrContext> env = typeEnvs.get(msym);
 823             UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env);
 824             JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
 825             JCModuleDecl decl = env.toplevel.getModuleDecl();
 826             DiagnosticPosition prevLintPos = deferredLintHandler.setPos(decl.pos());
 827 
 828             try {
 829                 decl.accept(v);
 830             } finally {
 831                 log.useSource(prev);
 832                 deferredLintHandler.setPos(prevLintPos);
 833             }
 834         };
 835     }
 836 
 837     class UsesProvidesVisitor extends JCTree.Visitor {
 838         private final ModuleSymbol msym;
 839         private final Env<AttrContext> env;
 840 
 841         private final Set<ClassSymbol> allUses = new HashSet<>();
 842         private final Map<ClassSymbol, Set<ClassSymbol>> allProvides = new HashMap<>();
 843 
 844         public UsesProvidesVisitor(ModuleSymbol msym, Env<AttrContext> env) {
 845             this.msym = msym;
 846             this.env = env;
 847         }
 848 
 849         @Override @SuppressWarnings("unchecked")
 850         public void visitModuleDef(JCModuleDecl tree) {
 851             msym.directives = List.nil();
 852             msym.provides = List.nil();
 853             msym.uses = List.nil();
 854             tree.directives.forEach(t -> t.accept(this));
 855             msym.directives = msym.directives.reverse();
 856             msym.provides = msym.provides.reverse();
 857             msym.uses = msym.uses.reverse();
 858 
 859             if (msym.requires.nonEmpty() && msym.requires.head.flags.contains(RequiresFlag.MANDATED))
 860                 msym.directives = msym.directives.prepend(msym.requires.head);
 861 
 862             msym.directives = msym.directives.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
 863 
 864             checkForCorrectness();
 865         }
 866 
 867         @Override
 868         public void visitExports(JCExports tree) {
 869             if (tree.directive.packge.members().isEmpty()) {
 870                 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge));
 871             }
 872             msym.directives = msym.directives.prepend(tree.directive);
 873         }
 874 
 875         @Override
 876         public void visitOpens(JCOpens tree) {
 877             if (tree.directive.packge.members().isEmpty() &&
 878                 ((tree.directive.packge.flags() & Flags.HAS_RESOURCE) == 0)) {
 879                 log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge));
 880             }
 881             msym.directives = msym.directives.prepend(tree.directive);
 882         }
 883 
 884         MethodSymbol noArgsConstructor(ClassSymbol tsym) {
 885             for (Symbol sym : tsym.members().getSymbolsByName(names.init)) {
 886                 MethodSymbol mSym = (MethodSymbol)sym;
 887                 if (mSym.params().isEmpty()) {
 888                     return mSym;
 889                 }
 890             }
 891             return null;
 892         }
 893 
 894         MethodSymbol factoryMethod(ClassSymbol tsym) {
 895             for (Symbol sym : tsym.members().getSymbolsByName(names.provider, sym -> sym.kind == MTH)) {
 896                 MethodSymbol mSym = (MethodSymbol)sym;
 897                 if (mSym.isStatic() && (mSym.flags() & Flags.PUBLIC) != 0 && mSym.params().isEmpty()) {
 898                     return mSym;
 899                 }
 900             }
 901             return null;
 902         }
 903 
 904         Map<Directive.ProvidesDirective, JCProvides> directiveToTreeMap = new HashMap<>();
 905 
 906         @Override
 907         public void visitProvides(JCProvides tree) {
 908             Type st = attr.attribType(tree.serviceName, env, syms.objectType);
 909             ClassSymbol service = (ClassSymbol) st.tsym;
 910             ListBuffer<ClassSymbol> impls = new ListBuffer<>();
 911             for (JCExpression implName : tree.implNames) {
 912                 Type it = attr.attribType(implName, env, syms.objectType);
 913                 ClassSymbol impl = (ClassSymbol) it.tsym;
 914                 //find provider factory:
 915                 MethodSymbol factory = factoryMethod(impl);
 916                 if (factory != null) {
 917                     Type returnType = factory.type.getReturnType();
 918                     if (!types.isSubtype(returnType, st)) {
 919                         log.error(implName.pos(), Errors.ServiceImplementationProviderReturnMustBeSubtypeOfServiceInterface);
 920                     }
 921                 } else {
 922                     if (!types.isSubtype(it, st)) {
 923                         log.error(implName.pos(), Errors.ServiceImplementationMustBeSubtypeOfServiceInterface);
 924                     } else if ((impl.flags() & ABSTRACT) != 0) {
 925                         log.error(implName.pos(), Errors.ServiceImplementationIsAbstract(impl));
 926                     } else if (impl.isInner()) {
 927                         log.error(implName.pos(), Errors.ServiceImplementationIsInner(impl));
 928                     } else {
 929                         MethodSymbol constr = noArgsConstructor(impl);
 930                         if (constr == null) {
 931                             log.error(implName.pos(), Errors.ServiceImplementationDoesntHaveANoArgsConstructor(impl));
 932                         } else if ((constr.flags() & PUBLIC) == 0) {
 933                             log.error(implName.pos(), Errors.ServiceImplementationNoArgsConstructorNotPublic(impl));
 934                         }
 935                     }
 936                 }
 937                 if (it.hasTag(CLASS)) {
 938                     // For now, we just check the pair (service-type, impl-type) is unique
 939                     // TODO, check only one provides per service type as well
 940                     if (allProvides.computeIfAbsent(service, s -> new HashSet<>()).add(impl)) {
 941                         impls.append(impl);
 942                     } else {
 943                         log.error(implName.pos(), Errors.DuplicateProvides(service, impl));
 944                     }
 945                 }
 946             }
 947             if (st.hasTag(CLASS) && !impls.isEmpty()) {
 948                 Directive.ProvidesDirective d = new Directive.ProvidesDirective(service, impls.toList());
 949                 msym.provides = msym.provides.prepend(d);
 950                 msym.directives = msym.directives.prepend(d);
 951                 directiveToTreeMap.put(d, tree);
 952             }
 953         }
 954 
 955         @Override
 956         public void visitRequires(JCRequires tree) {
 957             if (tree.directive != null) {
 958                 chk.checkDeprecated(tree.moduleName.pos(), msym, tree.directive.module);
 959                 msym.directives = msym.directives.prepend(tree.directive);
 960             }
 961         }
 962 
 963         @Override
 964         public void visitUses(JCUses tree) {
 965             Type st = attr.attribType(tree.qualid, env, syms.objectType);
 966             Symbol sym = TreeInfo.symbol(tree.qualid);
 967             if ((sym.flags() & ENUM) != 0) {
 968                 log.error(tree.qualid.pos(), Errors.ServiceDefinitionIsEnum(st.tsym));
 969             } else if (st.hasTag(CLASS)) {
 970                 ClassSymbol service = (ClassSymbol) st.tsym;
 971                 if (allUses.add(service)) {
 972                     Directive.UsesDirective d = new Directive.UsesDirective(service);
 973                     msym.uses = msym.uses.prepend(d);
 974                     msym.directives = msym.directives.prepend(d);
 975                 } else {
 976                     log.error(tree.pos(), Errors.DuplicateUses(service));
 977                 }
 978             }
 979         }
 980 
 981         private void checkForCorrectness() {
 982             for (Directive.ProvidesDirective provides : msym.provides) {
 983                 JCProvides tree = directiveToTreeMap.get(provides);
 984                 for (ClassSymbol impl : provides.impls) {
 985                     /* The implementation must be defined in the same module as the provides directive
 986                      * (else, error)
 987                      */
 988                     PackageSymbol implementationDefiningPackage = impl.packge();
 989                     if (implementationDefiningPackage.modle != msym) {
 990                         // TODO: should use tree for the implentation name, not the entire provides tree
 991                         // TODO: should improve error message to identify the implementation type
 992                         log.error(tree.pos(), Errors.ServiceImplementationNotInRightModule(implementationDefiningPackage.modle));
 993                     }
 994 
 995                     /* There is no inherent requirement that module that provides a service should actually
 996                      * use it itself. However, it is a pointless declaration if the service package is not
 997                      * exported and there is no uses for the service.
 998                      */
 999                     PackageSymbol interfaceDeclaringPackage = provides.service.packge();
1000                     boolean isInterfaceDeclaredInCurrentModule = interfaceDeclaringPackage.modle == msym;
1001                     boolean isInterfaceExportedFromAReadableModule =
1002                             msym.visiblePackages.get(interfaceDeclaringPackage.fullname) == interfaceDeclaringPackage;
1003                     if (isInterfaceDeclaredInCurrentModule && !isInterfaceExportedFromAReadableModule) {
1004                         // ok the interface is declared in this module. Let's check if it's exported
1005                         boolean warn = true;
1006                         for (ExportsDirective export : msym.exports) {
1007                             if (interfaceDeclaringPackage == export.packge) {
1008                                 warn = false;
1009                                 break;
1010                             }
1011                         }
1012                         if (warn) {
1013                             for (UsesDirective uses : msym.uses) {
1014                                 if (provides.service == uses.service) {
1015                                     warn = false;
1016                                     break;
1017                                 }
1018                             }
1019                         }
1020                         if (warn) {
1021                             log.warning(tree.pos(), Warnings.ServiceProvidedButNotExportedOrUsed(provides.service));
1022                         }
1023                     }
1024                 }
1025             }
1026         }
1027     }
1028 
1029     private Set<ModuleSymbol> allModules;
1030 
1031     public Set<ModuleSymbol> allModules() {
1032         Assert.checkNonNull(allModules);
1033         return allModules;
1034     }
1035 
1036     private void setupAllModules() {
1037         Assert.checkNonNull(rootModules);
1038         Assert.checkNull(allModules);
1039 
1040         Set<ModuleSymbol> observable;
1041 
1042         if (limitModsOpt == null && extraLimitMods.isEmpty()) {
1043             observable = null;
1044         } else {
1045             Set<ModuleSymbol> limitMods = new HashSet<>();
1046             if (limitModsOpt != null) {
1047                 for (String limit : limitModsOpt.split(",")) {
1048                     if (!isValidName(limit))
1049                         continue;
1050                     limitMods.add(syms.enterModule(names.fromString(limit)));
1051                 }
1052             }
1053             for (String limit : extraLimitMods) {
1054                 limitMods.add(syms.enterModule(names.fromString(limit)));
1055             }
1056             observable = computeTransitiveClosure(limitMods, null);
1057             observable.addAll(rootModules);
1058             if (lintOptions) {
1059                 for (ModuleSymbol msym : limitMods) {
1060                     if (!observable.contains(msym)) {
1061                         log.warning(LintCategory.OPTIONS,
1062                                 Warnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym));
1063                     }
1064                 }
1065             }
1066         }
1067 
1068         Predicate<ModuleSymbol> observablePred = sym -> observable == null || observable.contains(sym);
1069         Predicate<ModuleSymbol> systemModulePred = sym -> (sym.flags() & Flags.SYSTEM_MODULE) != 0;
1070         Set<ModuleSymbol> enabledRoot = new LinkedHashSet<>();
1071 
1072         if (rootModules.contains(syms.unnamedModule)) {
1073             ModuleSymbol javaSE = syms.getModule(java_se);
1074             Predicate<ModuleSymbol> jdkModulePred;
1075 
1076             if (javaSE != null && (observable == null || observable.contains(javaSE))) {
1077                 jdkModulePred = sym -> {
1078                     sym.complete();
1079                     return   !sym.name.startsWith(java_)
1080                            && sym.exports.stream().anyMatch(e -> e.modules == null);
1081                 };
1082                 enabledRoot.add(javaSE);
1083             } else {
1084                 jdkModulePred = sym -> true;
1085             }
1086 
1087             for (ModuleSymbol sym : new HashSet<>(syms.getAllModules())) {
1088                 if (systemModulePred.test(sym) && observablePred.test(sym) && jdkModulePred.test(sym)) {
1089                     enabledRoot.add(sym);
1090                 }
1091             }
1092         }
1093 
1094         enabledRoot.addAll(rootModules);
1095 
1096         if (addModsOpt != null || !extraAddMods.isEmpty()) {
1097             Set<String> fullAddMods = new HashSet<>();
1098             fullAddMods.addAll(extraAddMods);
1099 
1100             if (addModsOpt != null) {
1101                 fullAddMods.addAll(Arrays.asList(addModsOpt.split(",")));
1102             }
1103 
1104             for (String added : fullAddMods) {
1105                 Stream<ModuleSymbol> modules;
1106                 switch (added) {
1107                     case ALL_SYSTEM:
1108                         modules = syms.getAllModules()
1109                                       .stream()
1110                                       .filter(systemModulePred.and(observablePred));
1111                         break;
1112                     case ALL_MODULE_PATH:
1113                         modules = syms.getAllModules()
1114                                       .stream()
1115                                       .filter(systemModulePred.negate().and(observablePred));
1116                         break;
1117                     default:
1118                         if (!isValidName(added))
1119                             continue;
1120                         modules = Stream.of(syms.enterModule(names.fromString(added)));
1121                         break;
1122                 }
1123                 modules.forEach(sym -> {
1124                     enabledRoot.add(sym);
1125                     if (observable != null)
1126                         observable.add(sym);
1127                 });
1128             }
1129         }
1130 
1131         Set<ModuleSymbol> result = computeTransitiveClosure(enabledRoot, observable);
1132 
1133         result.add(syms.unnamedModule);
1134 
1135         allModules = result;
1136     }
1137 
1138     public boolean isInModuleGraph(ModuleSymbol msym) {
1139         return allModules == null || allModules.contains(msym);
1140     }
1141 
1142     private Set<ModuleSymbol> computeTransitiveClosure(Iterable<? extends ModuleSymbol> base, Set<ModuleSymbol> observable) {
1143         List<ModuleSymbol> todo = List.nil();
1144 
1145         for (ModuleSymbol ms : base) {
1146             todo = todo.prepend(ms);
1147         }
1148 
1149         Set<ModuleSymbol> result = new LinkedHashSet<>();
1150         result.add(syms.java_base);
1151 
1152         while (todo.nonEmpty()) {
1153             ModuleSymbol current = todo.head;
1154             todo = todo.tail;
1155             if (observable != null && !observable.contains(current))
1156                 continue;
1157             if (!result.add(current) || current == syms.unnamedModule || ((current.flags_field & Flags.AUTOMATIC_MODULE) != 0))
1158                 continue;
1159             current.complete();
1160             for (RequiresDirective rd : current.requires) {
1161                 todo = todo.prepend(rd.module);
1162             }
1163         }
1164 
1165         return result;
1166     }
1167 
1168     public ModuleSymbol getObservableModule(Name name) {
1169         ModuleSymbol mod = syms.getModule(name);
1170 
1171         if (allModules().contains(mod)) {
1172             return mod;
1173         }
1174 
1175         return null;
1176     }
1177 
1178     private Completer getUnnamedModuleCompleter() {
1179         moduleFinder.findAllModules();
1180         return new Symbol.Completer() {
1181             @Override
1182             public void complete(Symbol sym) throws CompletionFailure {
1183                 if (inInitModules) {
1184                     sym.completer = this;
1185                     return ;
1186                 }
1187                 ModuleSymbol msym = (ModuleSymbol) sym;
1188                 Set<ModuleSymbol> allModules = new HashSet<>(allModules());
1189                 allModules.remove(syms.unnamedModule);
1190                 for (ModuleSymbol m : allModules) {
1191                     m.complete();
1192                 }
1193                 initVisiblePackages(msym, allModules);
1194             }
1195 
1196             @Override
1197             public String toString() {
1198                 return "unnamedModule Completer";
1199             }
1200         };
1201     }
1202 
1203     private final Map<ModuleSymbol, Set<ModuleSymbol>> requiresTransitiveCache = new HashMap<>();
1204 
1205     private void completeModule(ModuleSymbol msym) {
1206         if (inInitModules) {
1207             msym.completer = sym -> completeModule(msym);
1208             return ;
1209         }
1210 
1211         if ((msym.flags_field & Flags.AUTOMATIC_MODULE) != 0) {
1212             completeAutomaticModule(msym);
1213         }
1214 
1215         Assert.checkNonNull(msym.requires);
1216 
1217         initAddReads();
1218 
1219         msym.requires = msym.requires.appendList(List.from(addReads.getOrDefault(msym, Collections.emptySet())));
1220 
1221         List<RequiresDirective> requires = msym.requires;
1222         List<RequiresDirective> previous = null;
1223 
1224         while (requires.nonEmpty()) {
1225             if (!allModules().contains(requires.head.module)) {
1226                 Env<AttrContext> env = typeEnvs.get(msym);
1227                 if (env != null) {
1228                     JavaFileObject origSource = log.useSource(env.toplevel.sourcefile);
1229                     try {
1230                         log.error(/*XXX*/env.tree, Errors.ModuleNotFound(requires.head.module));
1231                     } finally {
1232                         log.useSource(origSource);
1233                     }
1234                 } else {
1235                     Assert.check((msym.flags() & Flags.AUTOMATIC_MODULE) == 0);
1236                 }
1237                 if (previous != null) {
1238                     previous.tail = requires.tail;
1239                 } else {
1240                     msym.requires.tail = requires.tail;
1241                 }
1242             } else {
1243                 previous = requires;
1244             }
1245             requires = requires.tail;
1246         }
1247 
1248         Set<ModuleSymbol> readable = new LinkedHashSet<>();
1249         Set<ModuleSymbol> requiresTransitive = new HashSet<>();
1250 
1251         for (RequiresDirective d : msym.requires) {
1252             d.module.complete();
1253             readable.add(d.module);
1254             Set<ModuleSymbol> s = retrieveRequiresTransitive(d.module);
1255             Assert.checkNonNull(s, () -> "no entry in cache for " + d.module);
1256             readable.addAll(s);
1257             if (d.flags.contains(RequiresFlag.TRANSITIVE)) {
1258                 requiresTransitive.add(d.module);
1259                 requiresTransitive.addAll(s);
1260             }
1261         }
1262 
1263         requiresTransitiveCache.put(msym, requiresTransitive);
1264         initVisiblePackages(msym, readable);
1265         for (ExportsDirective d: msym.exports) {
1266             if (d.packge != null) {
1267                 d.packge.modle = msym;
1268             }
1269         }
1270 
1271     }
1272 
1273     private Set<ModuleSymbol> retrieveRequiresTransitive(ModuleSymbol msym) {
1274         Set<ModuleSymbol> requiresTransitive = requiresTransitiveCache.get(msym);
1275 
1276         if (requiresTransitive == null) {
1277             //the module graph may contain cycles involving automatic modules or --add-reads edges
1278             requiresTransitive = new HashSet<>();
1279 
1280             Set<ModuleSymbol> seen = new HashSet<>();
1281             List<ModuleSymbol> todo = List.of(msym);
1282 
1283             while (todo.nonEmpty()) {
1284                 ModuleSymbol current = todo.head;
1285                 todo = todo.tail;
1286                 if (!seen.add(current))
1287                     continue;
1288                 requiresTransitive.add(current);
1289                 current.complete();
1290                 Iterable<? extends RequiresDirective> requires;
1291                 if (current != syms.unnamedModule) {
1292                     Assert.checkNonNull(current.requires, () -> current + ".requires == null; " + msym);
1293                     requires = current.requires;
1294                     for (RequiresDirective rd : requires) {
1295                         if (rd.isTransitive())
1296                             todo = todo.prepend(rd.module);
1297                     }
1298                 } else {
1299                     for (ModuleSymbol mod : allModules()) {
1300                         todo = todo.prepend(mod);
1301                     }
1302                 }
1303             }
1304 
1305             requiresTransitive.remove(msym);
1306         }
1307 
1308         return requiresTransitive;
1309     }
1310 
1311     private void initVisiblePackages(ModuleSymbol msym, Collection<ModuleSymbol> readable) {
1312         initAddExports();
1313 
1314         msym.visiblePackages = new LinkedHashMap<>();
1315 
1316         Map<Name, ModuleSymbol> seen = new HashMap<>();
1317 
1318         for (ModuleSymbol rm : readable) {
1319             if (rm == syms.unnamedModule)
1320                 continue;
1321             addVisiblePackages(msym, seen, rm, rm.exports);
1322         }
1323 
1324         addExports.forEach((exportsFrom, exports) -> {
1325             addVisiblePackages(msym, seen, exportsFrom, exports);
1326         });
1327     }
1328 
1329     private void addVisiblePackages(ModuleSymbol msym,
1330                                     Map<Name, ModuleSymbol> seenPackages,
1331                                     ModuleSymbol exportsFrom,
1332                                     Collection<ExportsDirective> exports) {
1333         for (ExportsDirective d : exports) {
1334             if (d.modules == null || d.modules.contains(msym)) {
1335                 Name packageName = d.packge.fullname;
1336                 ModuleSymbol previousModule = seenPackages.get(packageName);
1337 
1338                 if (previousModule != null && previousModule != exportsFrom) {
1339                     Env<AttrContext> env = typeEnvs.get(msym);
1340                     JavaFileObject origSource = env != null ? log.useSource(env.toplevel.sourcefile)
1341                                                             : null;
1342                     DiagnosticPosition pos = env != null ? env.tree.pos() : null;
1343                     try {
1344                         log.error(pos, Errors.PackageClashFromRequires(msym, packageName,
1345                                                                       previousModule, exportsFrom));
1346                     } finally {
1347                         if (env != null)
1348                             log.useSource(origSource);
1349                     }
1350                     continue;
1351                 }
1352 
1353                 seenPackages.put(packageName, exportsFrom);
1354                 msym.visiblePackages.put(d.packge.fullname, d.packge);
1355             }
1356         }
1357     }
1358 
1359     private void initAddExports() {
1360         if (addExports != null)
1361             return;
1362 
1363         addExports = new LinkedHashMap<>();
1364         Set<ModuleSymbol> unknownModules = new HashSet<>();
1365 
1366         if (addExportsOpt == null)
1367             return;
1368 
1369         Pattern ep = Pattern.compile("([^/]+)/([^=]+)=(.*)");
1370         for (String s: addExportsOpt.split("\0+")) {
1371             if (s.isEmpty())
1372                 continue;
1373             Matcher em = ep.matcher(s);
1374             if (!em.matches()) {
1375                 continue;
1376             }
1377 
1378             // Terminology comes from
1379             //  --add-exports module/package=target,...
1380             // Compare to
1381             //  module module { exports package to target, ... }
1382             String moduleName = em.group(1);
1383             String packageName = em.group(2);
1384             String targetNames = em.group(3);
1385 
1386             if (!isValidName(moduleName))
1387                 continue;
1388 
1389             ModuleSymbol msym = syms.enterModule(names.fromString(moduleName));
1390             if (!isKnownModule(msym, unknownModules))
1391                 continue;
1392 
1393             if (!isValidName(packageName))
1394                 continue;
1395             PackageSymbol p = syms.enterPackage(msym, names.fromString(packageName));
1396             p.modle = msym;  // TODO: do we need this?
1397 
1398             List<ModuleSymbol> targetModules = List.nil();
1399             for (String toModule : targetNames.split("[ ,]+")) {
1400                 ModuleSymbol m;
1401                 if (toModule.equals("ALL-UNNAMED")) {
1402                     m = syms.unnamedModule;
1403                 } else {
1404                     if (!isValidName(toModule))
1405                         continue;
1406                     m = syms.enterModule(names.fromString(toModule));
1407                     if (!isKnownModule(m, unknownModules))
1408                         continue;
1409                 }
1410                 targetModules = targetModules.prepend(m);
1411             }
1412 
1413             Set<ExportsDirective> extra = addExports.computeIfAbsent(msym, _x -> new LinkedHashSet<>());
1414             ExportsDirective d = new ExportsDirective(p, targetModules);
1415             extra.add(d);
1416         }
1417     }
1418 
1419     private boolean isKnownModule(ModuleSymbol msym, Set<ModuleSymbol> unknownModules) {
1420         if (allModules.contains(msym)) {
1421             return true;
1422         }
1423 
1424         if (!unknownModules.contains(msym)) {
1425             if (lintOptions) {
1426                 log.warning(LintCategory.OPTIONS,
1427                         Warnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym));
1428             }
1429             unknownModules.add(msym);
1430         }
1431         return false;
1432     }
1433 
1434     private void initAddReads() {
1435         if (addReads != null)
1436             return;
1437 
1438         addReads = new LinkedHashMap<>();
1439 
1440         if (addReadsOpt == null)
1441             return;
1442 
1443         Pattern rp = Pattern.compile("([^=]+)=(.*)");
1444         for (String s : addReadsOpt.split("\0+")) {
1445             if (s.isEmpty())
1446                 continue;
1447             Matcher rm = rp.matcher(s);
1448             if (!rm.matches()) {
1449                 continue;
1450             }
1451 
1452             // Terminology comes from
1453             //  --add-reads source-module=target-module,...
1454             // Compare to
1455             //  module source-module { requires target-module; ... }
1456             String sourceName = rm.group(1);
1457             String targetNames = rm.group(2);
1458 
1459             if (!isValidName(sourceName))
1460                 continue;
1461 
1462             ModuleSymbol msym = syms.enterModule(names.fromString(sourceName));
1463             if (!allModules.contains(msym)) {
1464                 if (lintOptions) {
1465                     log.warning(Warnings.ModuleForOptionNotFound(Option.ADD_READS, msym));
1466                 }
1467                 continue;
1468             }
1469 
1470             for (String targetName : targetNames.split("[ ,]+", -1)) {
1471                 ModuleSymbol targetModule;
1472                 if (targetName.equals("ALL-UNNAMED")) {
1473                     targetModule = syms.unnamedModule;
1474                 } else {
1475                     if (!isValidName(targetName))
1476                         continue;
1477                     targetModule = syms.enterModule(names.fromString(targetName));
1478                     if (!allModules.contains(targetModule)) {
1479                         if (lintOptions) {
1480                             log.warning(LintCategory.OPTIONS, Warnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule));
1481                         }
1482                         continue;
1483                     }
1484                 }
1485                 addReads.computeIfAbsent(msym, m -> new HashSet<>())
1486                         .add(new RequiresDirective(targetModule, EnumSet.of(RequiresFlag.EXTRA)));
1487             }
1488         }
1489     }
1490 
1491     private void checkCyclicDependencies(JCModuleDecl mod) {
1492         for (JCDirective d : mod.directives) {
1493             JCRequires rd;
1494             if (!d.hasTag(Tag.REQUIRES) || (rd = (JCRequires) d).directive == null)
1495                 continue;
1496             Set<ModuleSymbol> nonSyntheticDeps = new HashSet<>();
1497             List<ModuleSymbol> queue = List.of(rd.directive.module);
1498             while (queue.nonEmpty()) {
1499                 ModuleSymbol current = queue.head;
1500                 queue = queue.tail;
1501                 if (!nonSyntheticDeps.add(current))
1502                     continue;
1503                 current.complete();
1504                 if ((current.flags() & Flags.ACYCLIC) != 0)
1505                     continue;
1506                 Assert.checkNonNull(current.requires, () -> current.toString());
1507                 for (RequiresDirective dep : current.requires) {
1508                     if (!dep.flags.contains(RequiresFlag.EXTRA))
1509                         queue = queue.prepend(dep.module);
1510                 }
1511             }
1512             if (nonSyntheticDeps.contains(mod.sym)) {
1513                 log.error(rd.moduleName.pos(), Errors.CyclicRequires(rd.directive.module));
1514             }
1515             mod.sym.flags_field |= Flags.ACYCLIC;
1516         }
1517     }
1518 
1519     private boolean isValidName(CharSequence name) {
1520         return SourceVersion.isName(name, Source.toSourceVersion(source));
1521     }
1522 
1523     // DEBUG
1524     private String toString(ModuleSymbol msym) {
1525         return msym.name + "["
1526                 + "kind:" + msym.kind + ";"
1527                 + "locn:" + toString(msym.sourceLocation) + "," + toString(msym.classLocation) + ";"
1528                 + "info:" + toString(msym.module_info.sourcefile) + ","
1529                             + toString(msym.module_info.classfile) + ","
1530                             + msym.module_info.completer
1531                 + "]";
1532     }
1533 
1534     // DEBUG
1535     String toString(Location locn) {
1536         return (locn == null) ? "--" : locn.getName();
1537     }
1538 
1539     // DEBUG
1540     String toString(JavaFileObject fo) {
1541         return (fo == null) ? "--" : fo.getName();
1542     }
1543 
1544     public void newRound() {
1545         rootModules = null;
1546         allModules = null;
1547     }
1548 }