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